diff --git a/apps/web/components/editor/pdf-signer.tsx b/apps/web/components/editor/pdf-signer.tsx index 73559e6ba..b8b32c9bd 100644 --- a/apps/web/components/editor/pdf-signer.tsx +++ b/apps/web/components/editor/pdf-signer.tsx @@ -16,6 +16,7 @@ export default function PDFSigner(props: any) { const router = useRouter(); const [open, setOpen] = useState(false); const [signatures, setSignatures] = useState([]); + const [fields, setFields] = useState(props.fields); const [dialogField, setDialogField] = useState(); function onClick(item: any) { @@ -26,16 +27,24 @@ export default function PDFSigner(props: any) { } function onDialogClose(dialogResult: any) { - console.log(dialogResult); - console.log(dialogField); - setSignatures( - signatures.concat({ - fieldId: dialogField.id, - type: dialogResult.type, - name: dialogResult.name, - signatureImage: null, - }) + const signature = { + fieldId: dialogField.id, + type: dialogResult.type, + name: dialogResult.name, + signatureImage: dialogResult.signatureImage, + }; + + setSignatures(signatures.concat(signature)); + + fields.splice( + fields.findIndex(function (i) { + return i.id === signature.fieldId; + }), + 1 ); + const signedField = { ...dialogField }; + signedField.signature = signature; + setFields(fields.concat(signedField)); setOpen(false); setDialogField(null); } @@ -98,7 +107,7 @@ export default function PDFSigner(props: any) { diff --git a/apps/web/components/editor/readonly-field.tsx b/apps/web/components/editor/readonly-field.tsx index 8d75a0371..95e241a55 100644 --- a/apps/web/components/editor/readonly-field.tsx +++ b/apps/web/components/editor/readonly-field.tsx @@ -39,7 +39,7 @@ export default function ReadOnlyField(props: FieldPropsType) { >
{ - props.onClick(props.field); + if (!field?.signature) props.onClick(props.field); }} ref={nodeRef} className="cursor-pointer opacity-80 p-2 m-auto w-auto flex-row-reverse text-lg font-bold text-center absolute top-0 left-0 select-none hover:brightness-50" @@ -47,9 +47,35 @@ export default function ReadOnlyField(props: FieldPropsType) { background: stc(props.field.Recipient.email), }} > -
+ +
); diff --git a/apps/web/components/editor/signature-dialog.tsx b/apps/web/components/editor/signature-dialog.tsx index ca6872095..da3a54a0d 100644 --- a/apps/web/components/editor/signature-dialog.tsx +++ b/apps/web/components/editor/signature-dialog.tsx @@ -2,25 +2,24 @@ import { classNames } from "@documenso/lib"; import { Button, IconButton } from "@documenso/ui"; import { Dialog, Transition } from "@headlessui/react"; import { - BuildingOfficeIcon, - CreditCardIcon, LanguageIcon, PencilIcon, - UserIcon, - UsersIcon, - XMarkIcon, + TrashIcon, } from "@heroicons/react/24/outline"; -import React from "react"; import { Fragment, useState } from "react"; +import SignatureCanvas from "react-signature-canvas"; const tabs = [ - { name: "Type", href: "#", icon: LanguageIcon, current: true }, - { name: "Draw", href: "#", icon: PencilIcon, current: false }, + { name: "Type", icon: LanguageIcon, current: true }, + { name: "Draw", icon: PencilIcon, current: false }, ]; export default function SignatureDialog(props: any) { const [currentTab, setCurrentTab] = useState(tabs[0]); const [typedName, setTypedName] = useState(""); + const [signatureEmpty, setSignatureEmpty] = useState(true); + + let signCanvas: any | undefined; return ( <> @@ -49,37 +48,35 @@ export default function SignatureDialog(props: any) { leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - +
@@ -87,14 +84,11 @@ export default function SignatureDialog(props: any) {
{ setTypedName(e.target.value); }} - className="mt-3 p-1 text-center block border-b w-full border-gray-300 focus:border-neon focus:ring-neon text-2xl" + className="font-qw leading-none h-10 align-bottom font-qwigley mt-14 text-center block border-b w-full border-gray-300 focus:border-neon focus:ring-neon text-2xl" placeholder="Kindly type your name" />
@@ -111,7 +105,7 @@ export default function SignatureDialog(props: any) { onClick={() => { props.onClose({ type: "type", - name: "typedName", + name: typedName, }); }} > @@ -123,15 +117,45 @@ export default function SignatureDialog(props: any) { "" )} {isCurrentTab("Draw") ? ( -
-
+
+ { + signCanvas = ref; + }} + canvasProps={{ + className: + "sigCanvas border-b b-2 border-slate w-full h-full mb-3", + }} + onEnd={() => { + setSignatureEmpty(signCanvas?.isEmpty()); + }} + /> + { + signCanvas?.clear(); + setSignatureEmpty(signCanvas?.isEmpty()); + }} + > +
-
diff --git a/apps/web/package.json b/apps/web/package.json index d1d336497..1dde04517 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -42,7 +42,6 @@ "react-tooltip": "^5.7.2", "sass": "^1.57.1", "short-uuid": "^4.2.2", - "signature_pad": "^4.1.4", "string-to-color": "^2.2.2", "typescript": "4.8.4" }, diff --git a/apps/web/pages/documents/[id]/sign.tsx b/apps/web/pages/documents/[id]/sign.tsx index 28c5200e3..60fa372ab 100644 --- a/apps/web/pages/documents/[id]/sign.tsx +++ b/apps/web/pages/documents/[id]/sign.tsx @@ -24,6 +24,8 @@ const SignPage: NextPageWithLayout = (props: any) => { export async function getServerSideProps(context: any) { const recipientToken: string = context.query["token"]; + // todo redirect to sigend of all already signed + await prisma.recipient.updateMany({ where: { token: recipientToken, diff --git a/apps/web/styles/tailwind.css b/apps/web/styles/tailwind.css index 698874ada..1a744e23d 100644 --- a/apps/web/styles/tailwind.css +++ b/apps/web/styles/tailwind.css @@ -6,7 +6,9 @@ min-height: 100%; } -body { +html, +body, +:host { font-family: montserrat; } diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js index 7a388d2da..a6a344329 100644 --- a/apps/web/tailwind.config.js +++ b/apps/web/tailwind.config.js @@ -13,7 +13,7 @@ module.exports = { extend: { fontFamily: { monteserrat: ["Monteserrat", "serif"], - // qwigley: ["Qwigley", "serif"], + qwigley: ["Qwigley", "serif"], }, colors: { neon: "#37f095", diff --git a/package-lock.json b/package-lock.json index f22785468..9ab94e74e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,9 +33,11 @@ "react-dom": "18.2.0", "react-hook-form": "^7.41.5", "react-hot-toast": "^2.4.0", + "react-signature-canvas": "^1.0.6", "typescript": "4.8.4" }, "devDependencies": { + "@types/react-signature-canvas": "^1.0.2", "file-loader": "^6.2.0" } }, @@ -75,7 +77,6 @@ "react-tooltip": "^5.7.2", "sass": "^1.57.1", "short-uuid": "^4.2.2", - "signature_pad": "^4.1.4", "string-to-color": "^2.2.2", "typescript": "4.8.4" }, @@ -872,6 +873,16 @@ "@types/react": "*" } }, + "node_modules/@types/react-signature-canvas": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/react-signature-canvas/-/react-signature-canvas-1.0.2.tgz", + "integrity": "sha512-uDHDoo2JM/9qYS+U7PRKDLBgOnQOOUEkDdXjCxBvI19fWtOLqXisrjiquPu+enu5iu2oWIfU7cVhn1QJZPkLTg==", + "dev": true, + "dependencies": { + "@types/react": "*", + "@types/signature_pad": "*" + } + }, "node_modules/@types/request": { "version": "2.48.8", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz", @@ -888,6 +899,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "node_modules/@types/signature_pad": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/signature_pad/-/signature_pad-2.3.2.tgz", + "integrity": "sha512-dDfWIgRcdzAaeVOpLRmLHIumGwc5inSD4Dj2Duis3yUEoSADZECLJHKkUQphbaANQ7kSC2bkf1JzyBPSmTlrPQ==", + "dev": true + }, "node_modules/@types/tough-cookie": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", @@ -7234,6 +7251,25 @@ "react": ">= 16.3" } }, + "node_modules/react-signature-canvas": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/react-signature-canvas/-/react-signature-canvas-1.0.6.tgz", + "integrity": "sha512-NoMHomYu9HxFeLjUGbIeV9abPdWSROfFxFNDekGdwmmaIx+w5ziOEiU2C34X0Ao4GxFnwqyUy/BpYlA4lCD1CA==", + "dependencies": { + "signature_pad": "^2.3.2", + "trim-canvas": "^0.1.0" + }, + "peerDependencies": { + "prop-types": "^15.5.8", + "react": "0.14 - 18", + "react-dom": "0.14 - 18" + } + }, + "node_modules/react-signature-canvas/node_modules/signature_pad": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-2.3.2.tgz", + "integrity": "sha512-peYXLxOsIY6MES2TrRLDiNg2T++8gGbpP2yaC+6Ohtxr+a2dzoaqWosWDY9sWqTAAk6E/TyQO+LJw9zQwyu5kA==" + }, "node_modules/react-tooltip": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.7.2.tgz", @@ -7573,11 +7609,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signature_pad": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-4.1.4.tgz", - "integrity": "sha512-NKLm9uhRfEPl8cdbbyYAiPsoJ3slIo1AyKg3tzwy8OCX2zQc6jrccUlgMBTJPcUsZXNU8o6fy7iX33rtYSoQHQ==" - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -7958,6 +7989,11 @@ "node": ">=0.8" } }, + "node_modules/trim-canvas": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/trim-canvas/-/trim-canvas-0.1.2.tgz", + "integrity": "sha512-nd4Ga3iLFV94mdhW9JFMLpQbHUyCQuhFOD71PEAt1NjtMD5wbZctzhX8c3agHNybMR5zXD1XTGoIEWk995E6pQ==" + }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -8566,7 +8602,6 @@ "react-tooltip": "^5.7.2", "sass": "^1.57.1", "short-uuid": "^4.2.2", - "signature_pad": "^4.1.4", "string-to-color": "^2.2.2", "tailwindcss": "^3.2.4", "typescript": "4.8.4" @@ -9094,6 +9129,16 @@ "@types/react": "*" } }, + "@types/react-signature-canvas": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/react-signature-canvas/-/react-signature-canvas-1.0.2.tgz", + "integrity": "sha512-uDHDoo2JM/9qYS+U7PRKDLBgOnQOOUEkDdXjCxBvI19fWtOLqXisrjiquPu+enu5iu2oWIfU7cVhn1QJZPkLTg==", + "dev": true, + "requires": { + "@types/react": "*", + "@types/signature_pad": "*" + } + }, "@types/request": { "version": "2.48.8", "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz", @@ -9110,6 +9155,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "@types/signature_pad": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/signature_pad/-/signature_pad-2.3.2.tgz", + "integrity": "sha512-dDfWIgRcdzAaeVOpLRmLHIumGwc5inSD4Dj2Duis3yUEoSADZECLJHKkUQphbaANQ7kSC2bkf1JzyBPSmTlrPQ==", + "dev": true + }, "@types/tough-cookie": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", @@ -13491,6 +13542,22 @@ "react-draggable": "^4.0.3" } }, + "react-signature-canvas": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/react-signature-canvas/-/react-signature-canvas-1.0.6.tgz", + "integrity": "sha512-NoMHomYu9HxFeLjUGbIeV9abPdWSROfFxFNDekGdwmmaIx+w5ziOEiU2C34X0Ao4GxFnwqyUy/BpYlA4lCD1CA==", + "requires": { + "signature_pad": "^2.3.2", + "trim-canvas": "^0.1.0" + }, + "dependencies": { + "signature_pad": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-2.3.2.tgz", + "integrity": "sha512-peYXLxOsIY6MES2TrRLDiNg2T++8gGbpP2yaC+6Ohtxr+a2dzoaqWosWDY9sWqTAAk6E/TyQO+LJw9zQwyu5kA==" + } + } + }, "react-tooltip": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.7.2.tgz", @@ -13718,11 +13785,6 @@ "object-inspect": "^1.9.0" } }, - "signature_pad": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-4.1.4.tgz", - "integrity": "sha512-NKLm9uhRfEPl8cdbbyYAiPsoJ3slIo1AyKg3tzwy8OCX2zQc6jrccUlgMBTJPcUsZXNU8o6fy7iX33rtYSoQHQ==" - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -13981,6 +14043,11 @@ "punycode": "^2.1.1" } }, + "trim-canvas": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/trim-canvas/-/trim-canvas-0.1.2.tgz", + "integrity": "sha512-nd4Ga3iLFV94mdhW9JFMLpQbHUyCQuhFOD71PEAt1NjtMD5wbZctzhX8c3agHNybMR5zXD1XTGoIEWk995E6pQ==" + }, "ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", diff --git a/package.json b/package.json index 60f996d64..c16cdfb15 100644 --- a/package.json +++ b/package.json @@ -38,9 +38,11 @@ "react-dom": "18.2.0", "react-hook-form": "^7.41.5", "react-hot-toast": "^2.4.0", + "react-signature-canvas": "^1.0.6", "typescript": "4.8.4" }, "devDependencies": { + "@types/react-signature-canvas": "^1.0.2", "file-loader": "^6.2.0" } }