mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 08:13:56 +10:00
Merge branch 'main' into fix-#41-db-migration-Signature_recipientId_fkey
This commit is contained in:
@ -66,7 +66,7 @@ export default function RecipientSelector(props: any) {
|
||||
selected ? "font-semibold" : "font-normal",
|
||||
"ml-3 block truncate"
|
||||
)}>
|
||||
{`${selectedRecipient?.name} <${selectedRecipient?.email}>`}
|
||||
{`${recipient?.name} <${recipient?.email}>`}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -291,6 +291,7 @@ const DocumentsPage: NextPageWithLayout = (props: any) => {
|
||||
event.stopPropagation();
|
||||
router.push("/documents/" + document.id);
|
||||
}}
|
||||
disabled={document.status === "COMPLETED"}
|
||||
/>
|
||||
<IconButton
|
||||
icon={ArrowDownTrayIcon}
|
||||
|
||||
@ -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,10 +91,14 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
href={"/api/documents/" + props.document.id}>
|
||||
Download
|
||||
</Button>
|
||||
{props.document.status !== DocumentStatus.COMPLETED && (
|
||||
<>
|
||||
<Button
|
||||
icon={PencilSquareIcon}
|
||||
disabled={props.document.status === DocumentStatus.COMPLETED}
|
||||
color={props.document.status === DocumentStatus.COMPLETED ? "primary" : "secondary"}
|
||||
color={
|
||||
props.document.status === DocumentStatus.COMPLETED ? "primary" : "secondary"
|
||||
}
|
||||
className="mr-2"
|
||||
href={breadcrumbItems[1].href}>
|
||||
Edit Document
|
||||
@ -112,13 +119,17 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
}>
|
||||
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,9 +246,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
Seen
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
) : null}
|
||||
{item.signingStatus === "SIGNED" ? (
|
||||
<span id="signed_icon">
|
||||
<span
|
||||
@ -251,11 +256,10 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
Signed
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{props.document.status !== DocumentStatus.COMPLETED && (
|
||||
<div className="mr-1 flex">
|
||||
<IconButton
|
||||
icon={PaperAirplaneIcon}
|
||||
@ -290,11 +294,13 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
className="group-hover:text-neon-dark group-hover:disabled:text-gray-400"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{props.document.status !== "COMPLETED" && (
|
||||
<Button
|
||||
icon={UserPlusIcon}
|
||||
className="mt-3"
|
||||
@ -310,6 +316,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
|
||||
}}>
|
||||
Add Signer
|
||||
</Button>
|
||||
)}
|
||||
</form>
|
||||
</FormProvider>
|
||||
</div>
|
||||
|
||||
@ -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}"`,
|
||||
|
||||
@ -12,27 +12,36 @@ export async function insertTextInPDF(
|
||||
): Promise<string> {
|
||||
const fontBytes = fs.readFileSync("public/fonts/Qwigley-Regular.ttf");
|
||||
|
||||
const existingPdfBytes = pdfAsBase64;
|
||||
const pdfDoc = await PDFDocument.load(pdfAsBase64);
|
||||
|
||||
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
||||
pdfDoc.registerFontkit(fontkit);
|
||||
const customFont = await pdfDoc.embedFont(fontBytes);
|
||||
const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
||||
|
||||
const font = await pdfDoc.embedFont(useHandwritingFont ? fontBytes : StandardFonts.Helvetica);
|
||||
|
||||
const pages = pdfDoc.getPages();
|
||||
const pdfPage = pages[page];
|
||||
|
||||
const textSize = useHandwritingFont ? 50 : 15;
|
||||
const textWidth = customFont.widthOfTextAtSize(text, textSize);
|
||||
const textHeight = customFont.heightAtSize(textSize);
|
||||
const textWidth = font.widthOfTextAtSize(text, textSize);
|
||||
const textHeight = font.heightAtSize(textSize);
|
||||
const fieldSize = { width: 192, height: 64 };
|
||||
const invertedYPosition = pdfPage.getHeight() - positionY - fieldSize.height;
|
||||
|
||||
// Because pdf-lib use a bottom-left coordinate system, we need to invert the y position
|
||||
// we then center the text in the middle by adding half the height of the text
|
||||
// plus the height of the field and divide the result by 2
|
||||
const invertedYPosition =
|
||||
pdfPage.getHeight() - positionY - (fieldSize.height + textHeight / 2) / 2;
|
||||
|
||||
// We center the text by adding the width of the field, subtracting the width of the text
|
||||
// and dividing the result by 2
|
||||
const centeredXPosition = positionX + (fieldSize.width - textWidth) / 2;
|
||||
|
||||
pdfPage.drawText(text, {
|
||||
x: positionX,
|
||||
x: centeredXPosition,
|
||||
y: invertedYPosition,
|
||||
size: textSize,
|
||||
font: useHandwritingFont ? customFont : helveticaFont,
|
||||
color: rgb(0, 0, 0),
|
||||
font,
|
||||
});
|
||||
|
||||
const pdfAsUint8Array = await pdfDoc.save();
|
||||
|
||||
@ -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),
|
||||
|
||||
Reference in New Issue
Block a user