diff --git a/apps/web/pages/api/documents/[id]/sign.ts b/apps/web/pages/api/documents/[id]/sign.ts index ef68e1a78..537d7ee72 100644 --- a/apps/web/pages/api/documents/[id]/sign.ts +++ b/apps/web/pages/api/documents/[id]/sign.ts @@ -73,6 +73,13 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) { }, }); + const signedRecipients = await prisma.recipient.findMany({ + where: { + documentId: recipient.documentId, + signingStatus: SigningStatus.SIGNED, + }, + }); + // Don't check for inserted, because currently no "sign again" scenarios exist and // this is probably the expected behaviour in unclean states. const nonSignatureFields = await prisma.field.findMany({ @@ -126,7 +133,11 @@ async function postHandler(req: NextApiRequest, res: NextApiResponse) { }); document.document = documentWithInserts; - if (documentOwner) await sendSigningDoneMail(recipient, document, documentOwner); + if (documentOwner) await sendSigningDoneMail(document, documentOwner); + + for (const signer of signedRecipients) { + await sendSigningDoneMail(document, signer); + } } return res.status(200).end(); diff --git a/apps/web/pages/documents.tsx b/apps/web/pages/documents.tsx index b1ef1fce7..eaad896a5 100644 --- a/apps/web/pages/documents.tsx +++ b/apps/web/pages/documents.tsx @@ -291,6 +291,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => { event.stopPropagation(); router.push("/documents/" + document.id); }} + disabled={document.status === "COMPLETED"} /> { }, { title: props.document.title, - href: NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id, + href: + props.document.status !== DocumentStatus.COMPLETED + ? NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id + : NEXT_PUBLIC_WEBAPP_URL + "/documents/" + props.document.id + "/recipients", }, { title: "Recipients", @@ -88,37 +91,45 @@ const RecipientsPage: NextPageWithLayout = (props: any) => { href={"/api/documents/" + props.document.id}> Download - - + {props.document.status !== DocumentStatus.COMPLETED && ( + <> + + + + )}

Signers

- The people who will sign the document. + {props.document.status !== DocumentStatus.COMPLETED + ? "The people who will sign the document." + : "The people who signed the document."}

@@ -215,9 +226,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => { className="mt-3 inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800"> Not Sent - ) : ( - "" - )} + ) : null} {item.sendStatus === "SENT" && item.readStatus !== "OPENED" ? ( { Sent - ) : ( - "" - )} + ) : null} {item.readStatus === "OPENED" && item.signingStatus === "NOT_SIGNED" ? ( { Seen - ) : ( - "" - )} + ) : null} {item.signingStatus === "SIGNED" ? ( + className="mt-3 inline-block flex-shrink-0 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800"> Signed - ) : ( - "" - )} + ) : null}
-
- { - if (confirm("Resend this signing request?")) { - setLoading(true); - sendSigningRequests(props.document, [item.id]).finally(() => { - setLoading(false); - }); + {props.document.status !== DocumentStatus.COMPLETED && ( +
+ - Resend - - { - const removedItem = { ...fields }[index]; - remove(index); - deleteRecipient(item)?.catch((err) => { - append(removedItem); - }); - }} - className="group-hover:text-neon-dark group-hover:disabled:text-gray-400" - /> -
+ color="secondary" + className="my-auto mr-4 h-9" + onClick={() => { + if (confirm("Resend this signing request?")) { + setLoading(true); + sendSigningRequests(props.document, [item.id]).finally(() => { + setLoading(false); + }); + } + }}> + Resend +
+ { + const removedItem = { ...fields }[index]; + remove(index); + deleteRecipient(item)?.catch((err) => { + append(removedItem); + }); + }} + className="group-hover:text-neon-dark group-hover:disabled:text-gray-400" + /> +
+ )} ))} - + {props.document.status !== "COMPLETED" && ( + + )} diff --git a/packages/lib/mail/sendSigningDoneMail.ts b/packages/lib/mail/sendSigningDoneMail.ts index 67c73868e..c7b35bf93 100644 --- a/packages/lib/mail/sendSigningDoneMail.ts +++ b/packages/lib/mail/sendSigningDoneMail.ts @@ -3,7 +3,7 @@ import { addDigitalSignature } from "@documenso/signing/addDigitalSignature"; import { sendMail } from "./sendMail"; import { Document as PrismaDocument } from "@prisma/client"; -export const sendSigningDoneMail = async (recipient: any, document: PrismaDocument, user: any) => { +export const sendSigningDoneMail = async (document: PrismaDocument, user: any) => { await sendMail( user.email, `Completed: "${document.title}"`, diff --git a/packages/prisma/migrations/20230411134605_doc_208/migration.sql b/packages/prisma/migrations/20230411134605_doc_208/migration.sql new file mode 100644 index 000000000..c4800e305 --- /dev/null +++ b/packages/prisma/migrations/20230411134605_doc_208/migration.sql @@ -0,0 +1,5 @@ +-- DropForeignKey +ALTER TABLE "Signature" DROP CONSTRAINT "Signature_recipientId_fkey"; + +-- AddForeignKey +ALTER TABLE "Signature" ADD CONSTRAINT "Signature_recipientId_fkey" FOREIGN KEY ("recipientId") REFERENCES "Recipient"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 93d7d9125..e5fa887c3 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -130,6 +130,6 @@ model Signature { signatureImageAsBase64 String? typedSignature String? - Recipient Recipient @relation(fields: [recipientId], references: [id], onDelete: Restrict) + Recipient Recipient @relation(fields: [recipientId], references: [id], onDelete: Cascade) Field Field @relation(fields: [fieldId], references: [id], onDelete: Restrict) } diff --git a/packages/prisma/seed.ts b/packages/prisma/seed.ts index 42812331f..d686e02ac 100644 --- a/packages/prisma/seed.ts +++ b/packages/prisma/seed.ts @@ -24,7 +24,7 @@ async function createUser(userData: { email: string; password: string }) { async function main() { console.info("Start seeding..."); const password = "123456789"; - const email = "example6@documenso.com"; + const email = "example@documenso.com"; const user = await createUser({ email: email, password: await hashPassword(password),