| import { useMemo } from 'react'; | |
| import { OGDialog, DialogTemplate, useToastContext } from '@librechat/client'; | |
| import type { TTermsOfService } from 'librechat-data-provider'; | |
| import MarkdownLite from '~/components/Chat/Messages/Content/MarkdownLite'; | |
| import { useAcceptTermsMutation } from '~/data-provider'; | |
| import { useLocalize } from '~/hooks'; | |
| const TermsAndConditionsModal = ({ | |
| open, | |
| onOpenChange, | |
| onAccept, | |
| onDecline, | |
| title, | |
| modalContent, | |
| }: { | |
| open: boolean; | |
| onOpenChange: (isOpen: boolean) => void; | |
| onAccept: () => void; | |
| onDecline: () => void; | |
| title?: string; | |
| contentUrl?: string; | |
| modalContent?: TTermsOfService['modalContent']; | |
| }) => { | |
| const localize = useLocalize(); | |
| const { showToast } = useToastContext(); | |
| const acceptTermsMutation = useAcceptTermsMutation({ | |
| onSuccess: () => { | |
| onAccept(); | |
| onOpenChange(false); | |
| }, | |
| onError: () => { | |
| showToast({ message: 'Failed to accept terms' }); | |
| }, | |
| }); | |
| const handleAccept = () => { | |
| acceptTermsMutation.mutate(); | |
| }; | |
| const handleDecline = () => { | |
| onDecline(); | |
| onOpenChange(false); | |
| }; | |
| const handleOpenChange = (isOpen: boolean) => { | |
| if (open && !isOpen) { | |
| return; | |
| } | |
| onOpenChange(isOpen); | |
| }; | |
| const content = useMemo(() => { | |
| if (typeof modalContent === 'string') { | |
| return modalContent; | |
| } | |
| if (Array.isArray(modalContent)) { | |
| return modalContent.join('\n'); | |
| } | |
| return ''; | |
| }, [modalContent]); | |
| return ( | |
| <OGDialog open={open} onOpenChange={handleOpenChange}> | |
| <DialogTemplate | |
| title={title ?? localize('com_ui_terms_and_conditions')} | |
| className="w-11/12 max-w-3xl sm:w-3/4 md:w-1/2 lg:w-2/5" | |
| showCloseButton={false} | |
| showCancelButton={false} | |
| main={ | |
| <section | |
| // Motivation: This is a dialog, so its content should be focusable | |
| tabIndex={0} | |
| className="max-h-[60vh] overflow-y-auto p-4" | |
| aria-label={localize('com_ui_terms_and_conditions')} | |
| > | |
| <div className="prose dark:prose-invert w-full max-w-none !text-text-primary"> | |
| {content !== '' ? ( | |
| <MarkdownLite content={content} /> | |
| ) : ( | |
| <p>{localize('com_ui_no_terms_content')}</p> | |
| )} | |
| </div> | |
| </section> | |
| } | |
| buttons={ | |
| <> | |
| <button | |
| onClick={handleDecline} | |
| className="inline-flex h-10 items-center justify-center rounded-lg border border-border-heavy bg-surface-secondary px-4 py-2 text-sm text-text-primary hover:bg-surface-active" | |
| > | |
| {localize('com_ui_decline')} | |
| </button> | |
| <button | |
| onClick={handleAccept} | |
| className="inline-flex h-10 items-center justify-center rounded-lg border border-border-heavy bg-surface-secondary px-4 py-2 text-sm text-text-primary hover:bg-green-500 hover:text-white focus:bg-green-500 focus:text-white dark:hover:bg-green-600 dark:focus:bg-green-600" | |
| > | |
| {localize('com_ui_accept')} | |
| </button> | |
| </> | |
| } | |
| /> | |
| </OGDialog> | |
| ); | |
| }; | |
| export default TermsAndConditionsModal; | |