'use client'; import { useMemo, useState } from 'react'; import { useRouter } from 'next/navigation'; import { zodResolver } from '@hookform/resolvers/zod'; import { useSession } from 'next-auth/react'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields'; import type { Document, Field, Recipient } from '@documenso/prisma/client'; import { trpc } from '@documenso/trpc/react'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; import { Card, CardContent } from '@documenso/ui/primitives/card'; import { Input } from '@documenso/ui/primitives/input'; import { Label } from '@documenso/ui/primitives/label'; import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { useRequiredSigningContext } from './provider'; import { SignDialog } from './sign-dialog'; export type SigningFormProps = { document: Document; recipient: Recipient; fields: Field[]; }; const ZSigningpadSchema = z.union([ z.object({ signatureDataUrl: z.string().min(1), signatureText: z.null().or(z.string().max(0)), }), z.object({ signatureDataUrl: z.null().or(z.string().max(0)), signatureText: z.string().trim().min(1), }), ]); export type TSigningpadSchema = z.infer; export const SigningForm = ({ document, recipient, fields }: SigningFormProps) => { const router = useRouter(); const analytics = useAnalytics(); const { data: session } = useSession(); const { fullName, signature, setFullName, setSignature } = useRequiredSigningContext(); const [validateUninsertedFields, setValidateUninsertedFields] = useState(false); const { mutateAsync: completeDocumentWithToken } = trpc.recipient.completeDocumentWithToken.useMutation(); const { register, handleSubmit, setValue, watch, formState: { isSubmitting }, } = useForm({ mode: 'onChange', defaultValues: { signatureDataUrl: signature || null, signatureText: '', }, resolver: zodResolver(ZSigningpadSchema), }); const signatureDataUrl = watch('signatureDataUrl'); const signatureText = watch('signatureText'); const uninsertedFields = useMemo(() => { return sortFieldsByPosition(fields.filter((field) => !field.inserted)); }, [fields]); const fieldsValidated = () => { setValidateUninsertedFields(true); validateFieldsInserted(fields); }; const onFormSubmit = async () => { setValidateUninsertedFields(true); const isFieldsValid = validateFieldsInserted(fields); if (!isFieldsValid) { return; } await completeDocumentWithToken({ token: recipient.token, documentId: document.id, }); analytics.capture('App: Recipient has completed signing', { signerId: recipient.id, documentId: document.id, timestamp: new Date().toISOString(), }); router.push(`/sign/${recipient.token}/complete`); }; return (
{validateUninsertedFields && uninsertedFields[0] && ( Click to insert field )}

Sign Document

Please review the document before signing.


setFullName(e.target.value.trimStart())} />
{!signatureText && ( { setSignature(value); }} /> )} {signatureText && (

{signatureText}

)}
e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()} > { if (e.target.value !== '') { setValue('signatureDataUrl', null); } setValue('signatureText', e.target.value); }, onBlur: (e) => { if (e.target.value === '') { return setValue('signatureText', ''); } setSignature(e.target.value.trimStart()); }, })} /> {signatureText && (
)}
); };