Merge branch 'main' into feat/docker-environment

This commit is contained in:
Timur Ercan
2023-04-13 14:50:36 +02:00
committed by GitHub
7 changed files with 115 additions and 91 deletions

View File

@@ -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();

View File

@@ -291,6 +291,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
event.stopPropagation();
router.push("/documents/" + document.id);
}}
disabled={document.status === "COMPLETED"}
/>
<IconButton
icon={ArrowDownTrayIcon}

View File

@@ -34,7 +34,10 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
},
{
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
</Button>
<Button
icon={PencilSquareIcon}
disabled={props.document.status === DocumentStatus.COMPLETED}
color={props.document.status === DocumentStatus.COMPLETED ? "primary" : "secondary"}
className="mr-2"
href={breadcrumbItems[1].href}>
Edit Document
</Button>
<Button
className="min-w-[125px]"
color="primary"
icon={PaperAirplaneIcon}
onClick={() => {
setOpen(true);
}}
disabled={
(formValues.length || 0) === 0 ||
!formValues.some(
(r: any) => r.email && !hasEmailError(r) && r.sendStatus === "NOT_SENT"
) ||
loading
}>
Send
</Button>
{props.document.status !== DocumentStatus.COMPLETED && (
<>
<Button
icon={PencilSquareIcon}
disabled={props.document.status === DocumentStatus.COMPLETED}
color={
props.document.status === DocumentStatus.COMPLETED ? "primary" : "secondary"
}
className="mr-2"
href={breadcrumbItems[1].href}>
Edit Document
</Button>
<Button
className="min-w-[125px]"
color="primary"
icon={PaperAirplaneIcon}
onClick={() => {
setOpen(true);
}}
disabled={
(formValues.length || 0) === 0 ||
!formValues.some(
(r: any) => r.email && !hasEmailError(r) && r.sendStatus === "NOT_SENT"
) ||
loading
}>
Send
</Button>
</>
)}
</div>
</div>
<div className="mt-10 overflow-hidden rounded-md bg-white p-4 shadow sm:p-6">
<div className="border-b border-gray-200 pb-3 sm:pb-5">
<h3 className="text-lg font-medium leading-6 text-gray-900 ">Signers</h3>
<p className="mt-2 max-w-4xl text-sm text-gray-500">
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."}
</p>
</div>
<FormProvider {...form}>
@@ -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
</span>
) : (
""
)}
) : null}
{item.sendStatus === "SENT" && item.readStatus !== "OPENED" ? (
<span id="sent_icon">
<span
@@ -226,9 +235,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
<CheckIcon className="mr-1 inline h-5" /> Sent
</span>
</span>
) : (
""
)}
) : null}
{item.readStatus === "OPENED" && item.signingStatus === "NOT_SIGNED" ? (
<span id="read_icon">
<span
@@ -239,77 +246,77 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
Seen
</span>
</span>
) : (
""
)}
) : null}
{item.signingStatus === "SIGNED" ? (
<span id="signed_icon">
<span
id="sent_icon"
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">
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">
<CheckBadgeIcon className="mr-1 inline h-5"></CheckBadgeIcon>
Signed
</span>
</span>
) : (
""
)}
) : null}
</div>
</div>
<div className="mr-1 flex">
<IconButton
icon={PaperAirplaneIcon}
disabled={
!item.id ||
item.sendStatus !== "SENT" ||
item.signingStatus === "SIGNED" ||
loading
}
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);
});
{props.document.status !== DocumentStatus.COMPLETED && (
<div className="mr-1 flex">
<IconButton
icon={PaperAirplaneIcon}
disabled={
!item.id ||
item.sendStatus !== "SENT" ||
item.signingStatus === "SIGNED" ||
loading
}
}}>
Resend
</IconButton>
<IconButton
icon={TrashIcon}
disabled={!item.id || item.sendStatus === "SENT" || loading}
onClick={() => {
const removedItem = { ...fields }[index];
remove(index);
deleteRecipient(item)?.catch((err) => {
append(removedItem);
});
}}
className="group-hover:text-neon-dark group-hover:disabled:text-gray-400"
/>
</div>
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
</IconButton>
<IconButton
icon={TrashIcon}
disabled={!item.id || item.sendStatus === "SENT" || loading}
onClick={() => {
const removedItem = { ...fields }[index];
remove(index);
deleteRecipient(item)?.catch((err) => {
append(removedItem);
});
}}
className="group-hover:text-neon-dark group-hover:disabled:text-gray-400"
/>
</div>
)}
</div>
</div>
</li>
))}
</ul>
<Button
icon={UserPlusIcon}
className="mt-3"
onClick={() => {
createOrUpdateRecipient({
id: "",
email: "",
name: "",
documentId: props.document.id,
}).then((res) => {
append(res);
});
}}>
Add Signer
</Button>
{props.document.status !== "COMPLETED" && (
<Button
icon={UserPlusIcon}
className="mt-3"
onClick={() => {
createOrUpdateRecipient({
id: "",
email: "",
name: "",
documentId: props.document.id,
}).then((res) => {
append(res);
});
}}>
Add Signer
</Button>
)}
</form>
</FormProvider>
</div>

View File

@@ -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}"`,

View File

@@ -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;

View File

@@ -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)
}

View File

@@ -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),