import { useEffect, useMemo, useState } from "react"; import dynamic from "next/dynamic"; import { useRouter } from "next/router"; import { createField } from "@documenso/features/editor"; import { createOrUpdateField, deleteField, signDocument } from "@documenso/lib/api"; import { NEXT_PUBLIC_WEBAPP_URL } from "@documenso/lib/constants"; import { Button } from "@documenso/ui"; import Logo from "../logo"; import NameDialog from "./name-dialog"; import SignatureDialog from "./signature-dialog"; import { CheckBadgeIcon, InformationCircleIcon } from "@heroicons/react/24/outline"; import { FieldType } from "@prisma/client"; const PDFViewer = dynamic(() => import("./pdf-viewer"), { ssr: false, }); export default function PDFSigner(props: any) { const router = useRouter(); const [signatureDialogOpen, setSignatureDialogOpen] = useState(false); const [nameDialogOpen, setNameDialogOpen] = useState(false); const [signingDone, setSigningDone] = useState(false); const [localSignatures, setLocalSignatures] = useState([]); const [fields, setFields] = useState(props.fields); const signatureFields = useMemo( () => fields.filter((field) => [FieldType.SIGNATURE].includes(field.type)), [fields] ); const [dialogField, setDialogField] = useState(); function signField(options: { fieldId: string; type: string; typedSignature?: string; signatureImage?: string; }) { const { fieldId, type, typedSignature, signatureImage } = options; const signature = { fieldId, type, typedSignature, signatureImage, }; const field = fields.find((e) => e.id == fieldId); if (!field) { return; } setLocalSignatures((s) => [...s.filter((e) => e.fieldId !== fieldId), signature]); setFields((prevState) => { const newState = [...prevState]; const index = newState.findIndex((e) => e.id == fieldId); newState[index] = { ...newState[index], signature, }; return newState; }); } function onClick(item: any) { if (item.type === FieldType.SIGNATURE) { setDialogField(item); setSignatureDialogOpen(true); } if (item.type === FieldType.NAME) { setDialogField(item); setNameDialogOpen(true); } } function onDialogClose(dialogResult: any) { // todo handle signature removed from field, remove free field if dialogresult is empty (or the id ) if (!dialogResult && dialogField.type === "FREE_SIGNATURE") { onDeleteHandler(dialogField.id); return; } if (!dialogResult) return; signField({ fieldId: dialogField.id, type: dialogResult.type, typedSignature: dialogResult.typedSignature, signatureImage: dialogResult.signatureImage, }); setSignatureDialogOpen(false); setNameDialogOpen(false); setDialogField(null); } function checkIfSigningIsDone(): boolean { // Check if all fields are signed.. if (signatureFields.length > 0) { // If there are no fields to sign at least one signature is enough return fields .filter((field) => field.type === FieldType.SIGNATURE) .every((field) => field.signature); } else { // If we don't have a signature field, we need at least one free signature // to be able to complete signing const freeSignatureFields = fields.filter((field) => field.type === FieldType.FREE_SIGNATURE); return freeSignatureFields.length > 0 && freeSignatureFields.every((field) => field.signature); } } function addFreeSignature(e: any, page: number, recipient: any): any { const freeSignatureField = createField(e, page, recipient, FieldType.FREE_SIGNATURE); createOrUpdateField(props.document, freeSignatureField, recipient.token).then((res) => { setFields((prevState) => [...prevState, res]); setDialogField(res); setSignatureDialogOpen(true); }); return freeSignatureField; } function onDeleteHandler(id: any) { const field = fields.find((e) => e.id == id); const fieldIndex = fields.map((item) => item.id).indexOf(id); if (fieldIndex > -1) { const fieldWithoutRemoved = [...fields]; const removedField = fieldWithoutRemoved.splice(fieldIndex, 1); setFields(fieldWithoutRemoved); const signaturesWithoutRemoved = [...localSignatures]; const removedSignature = signaturesWithoutRemoved.splice( signaturesWithoutRemoved.findIndex(function (i) { return i.fieldId === id; }), 1 ); setLocalSignatures(signaturesWithoutRemoved); deleteField(field).catch((err) => { setFields(fieldWithoutRemoved.concat(removedField)); setLocalSignatures(signaturesWithoutRemoved.concat(removedSignature)); }); } } useEffect(() => { setSigningDone(checkIfSigningIsDone()); }, [fields]); useEffect(() => { const nameFields = fields.filter((field) => field.type === FieldType.NAME); if (nameFields.length > 0) { nameFields.forEach((field) => { if (!field.signature && props.recipient?.name) { signField({ fieldId: field.id, type: "type", typedSignature: props.recipient.name, }); } }); } // We are intentionally not specifying deps here // because we want to run this effect on the initial render }, []); return ( <>

{props.document.User.name ? `${props.document.User.name} (${props.document.User.email})` : props.document.User.email}{" "} would like you to sign this document.

{signatureFields.length === 0 ? (

You can sign this document anywhere you like, but maybe look for a signature line.

) : null} {}} onDelete={onDeleteHandler}> ); }