import { useId, useMemo, useState } from 'react'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react'; import { Trans } from '@lingui/react/macro'; import { type Field, FieldType, type Recipient, RecipientRole } from '@prisma/client'; import { Controller, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router'; import type { DocumentAndSender } from '@documenso/lib/server-only/document/get-document-by-token'; import type { TRecipientAccessAuth } from '@documenso/lib/types/document-auth'; import { isFieldUnsignedAndRequired } from '@documenso/lib/utils/advanced-fields-helpers'; import { sortFieldsByPosition } from '@documenso/lib/utils/fields'; import type { RecipientWithFields } from '@documenso/prisma/types/recipient-with-fields'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { Button } from '@documenso/ui/primitives/button'; import { Input } from '@documenso/ui/primitives/input'; import { Label } from '@documenso/ui/primitives/label'; import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog'; import { useToast } from '@documenso/ui/primitives/use-toast'; import { AssistantConfirmationDialog, type NextSigner, } from '../../dialogs/assistant-confirmation-dialog'; import { DocumentSigningCompleteDialog } from './document-signing-complete-dialog'; import { useRequiredDocumentSigningContext } from './document-signing-provider'; export type DocumentSigningFormProps = { document: DocumentAndSender; recipient: Recipient; fields: Field[]; isRecipientsTurn: boolean; allRecipients?: RecipientWithFields[]; setSelectedSignerId?: (id: number | null) => void; completeDocument: (options: { accessAuthOptions?: TRecipientAccessAuth; nextSigner?: { email: string; name: string }; }) => Promise; isSubmitting: boolean; fieldsValidated: () => void; nextRecipient?: RecipientWithFields; }; export const DocumentSigningForm = ({ document, recipient, fields, isRecipientsTurn, allRecipients = [], setSelectedSignerId, completeDocument, isSubmitting, fieldsValidated, nextRecipient, }: DocumentSigningFormProps) => { const { _ } = useLingui(); const { toast } = useToast(); const navigate = useNavigate(); const assistantSignersId = useId(); const { fullName, signature, setFullName, setSignature } = useRequiredDocumentSigningContext(); const [validateUninsertedFields, setValidateUninsertedFields] = useState(false); const [isConfirmationDialogOpen, setIsConfirmationDialogOpen] = useState(false); const [isAssistantSubmitting, setIsAssistantSubmitting] = useState(false); const assistantForm = useForm<{ selectedSignerId: number | undefined }>({ defaultValues: { selectedSignerId: undefined, }, }); const fieldsRequiringValidation = useMemo( () => fields.filter(isFieldUnsignedAndRequired), [fields], ); const hasSignatureField = fields.some((field) => field.type === FieldType.SIGNATURE); const uninsertedFields = useMemo(() => { return sortFieldsByPosition(fieldsRequiringValidation.filter((field) => !field.inserted)); }, [fieldsRequiringValidation]); const uninsertedRecipientFields = useMemo(() => { return fieldsRequiringValidation.filter((field) => field.recipientId === recipient.id); }, [fieldsRequiringValidation, recipient]); const localFieldsValidated = () => { setValidateUninsertedFields(true); fieldsValidated(); }; const onAssistantFormSubmit = () => { if (uninsertedRecipientFields.length > 0) { return; } setIsConfirmationDialogOpen(true); }; const handleAssistantConfirmDialogSubmit = async (nextSigner?: NextSigner) => { setIsAssistantSubmitting(true); try { await completeDocument({ nextSigner }); } catch (err) { toast({ title: 'Error', description: 'An error occurred while completing the document. Please try again.', variant: 'destructive', }); setIsAssistantSubmitting(false); setIsConfirmationDialogOpen(false); } }; return (
{validateUninsertedFields && uninsertedFields[0] && ( Click to insert field )}
{recipient.role === RecipientRole.VIEWER ? ( <>
completeDocument({ nextSigner, accessAuthOptions }) } recipient={recipient} allowDictateNextSigner={document.documentMeta?.allowDictateNextSigner} defaultNextSigner={ nextRecipient ? { name: nextRecipient.name, email: nextRecipient.email } : undefined } />
) : recipient.role === RecipientRole.ASSISTANT ? ( <>
( { field.onChange(value); setSelectedSignerId?.(Number(value)); }} > {allRecipients .filter((r) => r.fields.length > 0) .map((r) => (

{r.email}

{r.fields.length} {r.fields.length === 1 ? 'field' : 'fields'}
))}
)} />
0} isOpen={isConfirmationDialogOpen} onClose={() => !isAssistantSubmitting && setIsConfirmationDialogOpen(false)} onConfirm={handleAssistantConfirmDialogSubmit} isSubmitting={isAssistantSubmitting} allowDictateNextSigner={ nextRecipient && document.documentMeta?.allowDictateNextSigner } defaultNextSigner={ nextRecipient ? { name: nextRecipient.name, email: nextRecipient.email } : undefined } /> ) : ( <>
setFullName(e.target.value.trimStart())} />
{hasSignatureField && (
setSignature(v ?? '')} typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled} uploadSignatureEnabled={document.documentMeta?.uploadSignatureEnabled} drawSignatureEnabled={document.documentMeta?.drawSignatureEnabled} />
)}
completeDocument({ accessAuthOptions, nextSigner, }) } recipient={recipient} allowDictateNextSigner={ nextRecipient && document.documentMeta?.allowDictateNextSigner } defaultNextSigner={ nextRecipient ? { name: nextRecipient.name, email: nextRecipient.email } : undefined } />
)}
); };