mirror of
https://github.com/documenso/documenso.git
synced 2025-11-14 08:42:12 +10:00
🚧 signign workflow
This commit is contained in:
@ -6,6 +6,7 @@ import SignatureDialog from "./signature-dialog";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button } from "@documenso/ui";
|
import { Button } from "@documenso/ui";
|
||||||
import { CheckBadgeIcon } from "@heroicons/react/24/outline";
|
import { CheckBadgeIcon } from "@heroicons/react/24/outline";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
const PDFViewer = dynamic(() => import("./pdf-viewer"), {
|
const PDFViewer = dynamic(() => import("./pdf-viewer"), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
@ -14,17 +15,61 @@ const PDFViewer = dynamic(() => import("./pdf-viewer"), {
|
|||||||
export default function PDFSigner(props: any) {
|
export default function PDFSigner(props: any) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [fields, setFields] = useState<any[]>(props.fields);
|
const [signatures, setSignatures] = useState<any[]>([]);
|
||||||
|
const [dialogField, setDialogField] = useState<any>();
|
||||||
|
|
||||||
function onClick(item: any) {
|
function onClick(item: any) {
|
||||||
if (item.type === "SIGNATURE") {
|
if (item.type === "SIGNATURE") {
|
||||||
|
setDialogField(item);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onDialogClose(dialogResult: any) {
|
||||||
|
console.log(dialogResult);
|
||||||
|
console.log(dialogField);
|
||||||
|
setSignatures(
|
||||||
|
signatures.concat({
|
||||||
|
fieldId: dialogField.id,
|
||||||
|
type: dialogResult.type,
|
||||||
|
name: dialogResult.name,
|
||||||
|
signatureImage: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setOpen(false);
|
||||||
|
setDialogField(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sign() {
|
||||||
|
const body = { documentId: props.document.id, signatures: signatures };
|
||||||
|
toast.promise(
|
||||||
|
fetch(
|
||||||
|
`/api/documents/${props.document.id}/sign?token=${router.query.token}`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
router.push(
|
||||||
|
`/documents/${props.document.id}/signed?token=${router.query.token}`
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
loading: "Signing...",
|
||||||
|
success: `"${props.document.title}" signed successfully.`,
|
||||||
|
error: "Could not sign :/",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// goto signing done page
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SignatureDialog open={open} setOpen={setOpen}></SignatureDialog>
|
<SignatureDialog open={open} setOpen={setOpen} onClose={onDialogClose} />
|
||||||
<div className="bg-neon p-4">
|
<div className="bg-neon p-4">
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
@ -36,9 +81,13 @@ export default function PDFSigner(props: any) {
|
|||||||
document.
|
document.
|
||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
|
disabled={signatures.length < props.fields.length}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
icon={CheckBadgeIcon}
|
icon={CheckBadgeIcon}
|
||||||
className="float-right"
|
className="float-right"
|
||||||
|
onClick={() => {
|
||||||
|
sign();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Done
|
Done
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -105,7 +105,16 @@ export default function SignatureDialog(props: any) {
|
|||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button className="ml-3" disabled={!typedName}>
|
<Button
|
||||||
|
className="ml-3"
|
||||||
|
disabled={!typedName}
|
||||||
|
onClick={() => {
|
||||||
|
props.onClose({
|
||||||
|
type: "type",
|
||||||
|
name: "typedName",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
Sign
|
Sign
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -14,8 +14,7 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
const { token: recipientToken } = req.query;
|
const { token: recipientToken } = req.query;
|
||||||
|
|
||||||
if (!recipientToken) {
|
if (!recipientToken) {
|
||||||
res.status(401).send("Missing recipient token.");
|
return res.status(401).send("Missing recipient token.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const recipient = await prisma.recipient.findFirstOrThrow({
|
const recipient = await prisma.recipient.findFirstOrThrow({
|
||||||
@ -23,36 +22,18 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!recipient) {
|
if (!recipient) {
|
||||||
res.status(401).send("Recipient not found.");
|
return res.status(401).send("Recipient not found.");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const document: PrismaDocument = await getDocument(
|
const document: PrismaDocument = await getDocument(
|
||||||
recipient.documentId,
|
recipient.documentId,
|
||||||
res,
|
req,
|
||||||
req
|
res
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!document) res.status(404).end(`No document found.`);
|
if (!document) res.status(404).end(`No document found.`);
|
||||||
|
|
||||||
// todo sign stuff
|
// save signature to db for later use
|
||||||
|
|
||||||
const unsignedRecipients = await prisma.recipient.findMany({
|
|
||||||
where: {
|
|
||||||
signingStatus: SigningStatus.NOT_SIGNED,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (unsignedRecipients.length === 0) {
|
|
||||||
await prisma.document.update({
|
|
||||||
where: {
|
|
||||||
id: recipient.documentId,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
status: DocumentStatus.COMPLETED,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.recipient.update({
|
await prisma.recipient.update({
|
||||||
where: {
|
where: {
|
||||||
@ -63,6 +44,35 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const unsignedRecipients = await prisma.recipient.findMany({
|
||||||
|
where: {
|
||||||
|
documentId: recipient.documentId,
|
||||||
|
signingStatus: SigningStatus.NOT_SIGNED,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.document.update({
|
||||||
|
where: {
|
||||||
|
id: recipient.documentId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: DocumentStatus.COMPLETED,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (unsignedRecipients.length === 0) {
|
||||||
|
// if everybody signed insert images and create signature
|
||||||
|
await prisma.document.update({
|
||||||
|
where: {
|
||||||
|
id: recipient.documentId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: DocumentStatus.COMPLETED,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// send notifications
|
||||||
|
}
|
||||||
|
|
||||||
return res.status(200).end();
|
return res.status(200).end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
46
apps/web/pages/documents/[id]/signed.tsx
Normal file
46
apps/web/pages/documents/[id]/signed.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import prisma from "@documenso/prisma";
|
||||||
|
import Head from "next/head";
|
||||||
|
import { NextPageWithLayout } from "../../_app";
|
||||||
|
import { ReadStatus } from "@prisma/client";
|
||||||
|
|
||||||
|
const SignPage: NextPageWithLayout = (props: any) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>Sign | Documenso</title>
|
||||||
|
</Head>
|
||||||
|
You signed,thanks for playing.
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getServerSideProps(context: any) {
|
||||||
|
const recipientToken: string = context.query["token"];
|
||||||
|
|
||||||
|
const recipient = await prisma.recipient.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
token: recipientToken,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Document: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const fields = await prisma.field.findMany({
|
||||||
|
where: {
|
||||||
|
documentId: recipient.Document.id,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Recipient: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
document: recipient.Document,
|
||||||
|
fields: fields,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SignPage;
|
||||||
Reference in New Issue
Block a user