From 1126fe4bff9d1f48ec77b576ece303164e72ed9a Mon Sep 17 00:00:00 2001 From: Adithya Krishna Date: Wed, 17 Apr 2024 03:52:59 +0530 Subject: [PATCH 01/13] chore: added filename extension check --- packages/lib/server-only/document/send-completed-email.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/server-only/document/send-completed-email.ts b/packages/lib/server-only/document/send-completed-email.ts index a397e47e7..f5cef426c 100644 --- a/packages/lib/server-only/document/send-completed-email.ts +++ b/packages/lib/server-only/document/send-completed-email.ts @@ -80,7 +80,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo text: render(template, { plainText: true }), attachments: [ { - filename: document.title, + filename: document.title.endsWith('.pdf') ? document.title : document.title + '.pdf', content: Buffer.from(completedDocument), }, ], From f8ddb0f9225e5c63a74377f71ab6e65d94ebbbe7 Mon Sep 17 00:00:00 2001 From: Adithya Krishna Date: Thu, 18 Apr 2024 18:12:08 +0530 Subject: [PATCH 02/13] chore: update filename for bulk recipients --- packages/lib/server-only/document/send-completed-email.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lib/server-only/document/send-completed-email.ts b/packages/lib/server-only/document/send-completed-email.ts index f5cef426c..f841aef33 100644 --- a/packages/lib/server-only/document/send-completed-email.ts +++ b/packages/lib/server-only/document/send-completed-email.ts @@ -130,7 +130,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo text: render(template, { plainText: true }), attachments: [ { - filename: document.title, + filename: document.title.endsWith('.pdf') ? document.title : document.title + '.pdf', content: Buffer.from(completedDocument), }, ], From 6526377f1bd51d19b9e5677bd67c11cd612e5ddb Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Thu, 18 Apr 2024 21:56:31 +0700 Subject: [PATCH 03/13] feat: add visible completed fields --- .../documents/[id]/document-page-view.tsx | 24 ++- .../src/app/(signing)/sign/[token]/page.tsx | 11 +- .../sign/[token]/signing-page-view.tsx | 12 +- .../avatar/stack-avatars-with-tooltip.tsx | 156 ++++++------------ .../document/document-read-only-fields.tsx | 113 +++++++++++++ .../get-completed-fields-for-document.ts | 29 ++++ .../field/get-completed-fields-for-token.ts | 33 ++++ .../template/create-document-from-template.ts | 6 +- .../template/duplicate-template.ts | 6 +- packages/lib/types/fields.ts | 3 + .../migration.sql | 8 + packages/prisma/schema.prisma | 4 +- packages/ui/components/field/field.tsx | 4 +- packages/ui/primitives/popover.tsx | 64 ++++++- 14 files changed, 356 insertions(+), 117 deletions(-) create mode 100644 apps/web/src/components/document/document-read-only-fields.tsx create mode 100644 packages/lib/server-only/field/get-completed-fields-for-document.ts create mode 100644 packages/lib/server-only/field/get-completed-fields-for-token.ts create mode 100644 packages/lib/types/fields.ts create mode 100644 packages/prisma/migrations/20240418140819_remove_impossible_field_optional_states/migration.sql diff --git a/apps/web/src/app/(dashboard)/documents/[id]/document-page-view.tsx b/apps/web/src/app/(dashboard)/documents/[id]/document-page-view.tsx index e20c88a27..3b89f63f5 100644 --- a/apps/web/src/app/(dashboard)/documents/[id]/document-page-view.tsx +++ b/apps/web/src/app/(dashboard)/documents/[id]/document-page-view.tsx @@ -8,6 +8,7 @@ import { DOCUMENSO_ENCRYPTION_KEY } from '@documenso/lib/constants/crypto'; import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session'; import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id'; import { getServerComponentFlag } from '@documenso/lib/server-only/feature-flags/get-server-component-feature-flag'; +import { getCompletedFieldsForDocument } from '@documenso/lib/server-only/field/get-completed-fields-for-document'; import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document'; import { symmetricDecrypt } from '@documenso/lib/universal/crypto'; import { formatDocumentsPath } from '@documenso/lib/utils/teams'; @@ -19,6 +20,7 @@ import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer'; import { StackAvatarsWithTooltip } from '~/components/(dashboard)/avatar/stack-avatars-with-tooltip'; import { DocumentHistorySheet } from '~/components/document/document-history-sheet'; +import { DocumentReadOnlyFields } from '~/components/document/document-read-only-fields'; import { DocumentStatus as DocumentStatusComponent, FRIENDLY_STATUS_MAP, @@ -83,11 +85,16 @@ export const DocumentPageView = async ({ params, team }: DocumentPageViewProps) documentMeta.password = securePassword; } - const recipients = await getRecipientsForDocument({ - documentId, - teamId: team?.id, - userId: user.id, - }); + const [recipients, completedFields] = await Promise.all([ + getRecipientsForDocument({ + documentId, + teamId: team?.id, + userId: user.id, + }), + getCompletedFieldsForDocument({ + documentId, + }), + ]); const documentWithRecipients = { ...document, @@ -148,6 +155,13 @@ export const DocumentPageView = async ({ params, team }: DocumentPageViewProps) + {document.status === DocumentStatus.PENDING && ( + + )} +
diff --git a/apps/web/src/app/(signing)/sign/[token]/page.tsx b/apps/web/src/app/(signing)/sign/[token]/page.tsx index e83f675ce..95c9b6512 100644 --- a/apps/web/src/app/(signing)/sign/[token]/page.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/page.tsx @@ -6,6 +6,7 @@ import { getServerComponentSession } from '@documenso/lib/next-auth/get-server-c import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token'; import { isRecipientAuthorized } from '@documenso/lib/server-only/document/is-recipient-authorized'; import { viewedDocument } from '@documenso/lib/server-only/document/viewed-document'; +import { getCompletedFieldsForToken } from '@documenso/lib/server-only/field/get-completed-fields-for-token'; import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token'; import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token'; import { getRecipientSignatures } from '@documenso/lib/server-only/recipient/get-recipient-signatures'; @@ -37,7 +38,7 @@ export default async function SigningPage({ params: { token } }: SigningPageProp const requestMetadata = extractNextHeaderRequestMetadata(requestHeaders); - const [document, fields, recipient] = await Promise.all([ + const [document, fields, recipient, completedFields] = await Promise.all([ getDocumentAndSenderByToken({ token, userId: user?.id, @@ -45,6 +46,7 @@ export default async function SigningPage({ params: { token } }: SigningPageProp }).catch(() => null), getFieldsForToken({ token }), getRecipientByToken({ token }).catch(() => null), + getCompletedFieldsForToken({ token }), ]); if (!document || !document.documentData || !recipient) { @@ -120,7 +122,12 @@ export default async function SigningPage({ params: { token } }: SigningPageProp signature={user?.email === recipient.email ? user.signature : undefined} > - + ); diff --git a/apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx b/apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx index c04679956..4691d0d4c 100644 --- a/apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx @@ -4,12 +4,14 @@ import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-form import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer'; import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones'; import type { DocumentAndSender } from '@documenso/lib/server-only/document/get-document-by-token'; +import type { CompletedField } from '@documenso/lib/types/fields'; import type { Field, Recipient } from '@documenso/prisma/client'; import { FieldType, RecipientRole } from '@documenso/prisma/client'; import { Card, CardContent } from '@documenso/ui/primitives/card'; import { ElementVisible } from '@documenso/ui/primitives/element-visible'; import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer'; +import { DocumentReadOnlyFields } from '~/components/document/document-read-only-fields'; import { truncateTitle } from '~/helpers/truncate-title'; import { DateField } from './date-field'; @@ -23,9 +25,15 @@ export type SigningPageViewProps = { document: DocumentAndSender; recipient: Recipient; fields: Field[]; + completedFields: CompletedField[]; }; -export const SigningPageView = ({ document, recipient, fields }: SigningPageViewProps) => { +export const SigningPageView = ({ + document, + recipient, + fields, + completedFields, +}: SigningPageViewProps) => { const truncatedTitle = truncateTitle(document.title); const { documentData, documentMeta } = document; @@ -70,6 +78,8 @@ export const SigningPageView = ({ document, recipient, fields }: SigningPageView
+ + {fields.map((field) => match(field.type) diff --git a/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx b/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx index 10f7d1e6a..b6a13b911 100644 --- a/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx +++ b/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx @@ -1,12 +1,10 @@ 'use client'; -import { useRef, useState } from 'react'; - import { getRecipientType } from '@documenso/lib/client-only/recipient-type'; import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles'; import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter'; import type { Recipient } from '@documenso/prisma/client'; -import { Popover, PopoverContent, PopoverTrigger } from '@documenso/ui/primitives/popover'; +import { PopoverHover } from '@documenso/ui/primitives/popover'; import { AvatarWithRecipient } from './avatar-with-recipient'; import { StackAvatar } from './stack-avatar'; @@ -23,11 +21,6 @@ export const StackAvatarsWithTooltip = ({ position, children, }: StackAvatarsWithTooltipProps) => { - const [open, setOpen] = useState(false); - - const isControlled = useRef(false); - const isMouseOverTimeout = useRef(null); - const waitingRecipients = recipients.filter( (recipient) => getRecipientType(recipient) === 'waiting', ); @@ -44,105 +37,62 @@ export const StackAvatarsWithTooltip = ({ (recipient) => getRecipientType(recipient) === 'unsigned', ); - const onMouseEnter = () => { - if (isMouseOverTimeout.current) { - clearTimeout(isMouseOverTimeout.current); - } - - if (isControlled.current) { - return; - } - - isMouseOverTimeout.current = setTimeout(() => { - setOpen((o) => (!o ? true : o)); - }, 200); - }; - - const onMouseLeave = () => { - if (isMouseOverTimeout.current) { - clearTimeout(isMouseOverTimeout.current); - } - - if (isControlled.current) { - return; - } - - setTimeout(() => { - setOpen((o) => (o ? false : o)); - }, 200); - }; - - const onOpenChange = (newOpen: boolean) => { - isControlled.current = newOpen; - - setOpen(newOpen); - }; - return ( - - - {children || } - - - - {completedRecipients.length > 0 && ( -
-

Completed

- {completedRecipients.map((recipient: Recipient) => ( -
- -
-

{recipient.email}

-

- {RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName} -

-
+ } + contentProps={{ + className: 'flex flex-col gap-y-5 py-2', + side: position, + }} + > + {completedRecipients.length > 0 && ( +
+

Completed

+ {completedRecipients.map((recipient: Recipient) => ( +
+ +
+

{recipient.email}

+

+ {RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName} +

- ))} -
- )} +
+ ))} +
+ )} - {waitingRecipients.length > 0 && ( -
-

Waiting

- {waitingRecipients.map((recipient: Recipient) => ( - - ))} -
- )} + {waitingRecipients.length > 0 && ( +
+

Waiting

+ {waitingRecipients.map((recipient: Recipient) => ( + + ))} +
+ )} - {openedRecipients.length > 0 && ( -
-

Opened

- {openedRecipients.map((recipient: Recipient) => ( - - ))} -
- )} + {openedRecipients.length > 0 && ( +
+

Opened

+ {openedRecipients.map((recipient: Recipient) => ( + + ))} +
+ )} - {uncompletedRecipients.length > 0 && ( -
-

Uncompleted

- {uncompletedRecipients.map((recipient: Recipient) => ( - - ))} -
- )} - - + {uncompletedRecipients.length > 0 && ( +
+

Uncompleted

+ {uncompletedRecipients.map((recipient: Recipient) => ( + + ))} +
+ )} + ); }; diff --git a/apps/web/src/components/document/document-read-only-fields.tsx b/apps/web/src/components/document/document-read-only-fields.tsx new file mode 100644 index 000000000..530066fa8 --- /dev/null +++ b/apps/web/src/components/document/document-read-only-fields.tsx @@ -0,0 +1,113 @@ +'use client'; + +import { useState } from 'react'; + +import { P, match } from 'ts-pattern'; + +import { + DEFAULT_DOCUMENT_DATE_FORMAT, + convertToLocalSystemFormat, +} from '@documenso/lib/constants/date-formats'; +import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer'; +import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones'; +import type { CompletedField } from '@documenso/lib/types/fields'; +import { extractInitials } from '@documenso/lib/utils/recipient-formatter'; +import type { DocumentMeta } from '@documenso/prisma/client'; +import { FieldType } from '@documenso/prisma/client'; +import { FieldRootContainer } from '@documenso/ui/components/field/field'; +import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar'; +import { Button } from '@documenso/ui/primitives/button'; +import { FRIENDLY_FIELD_TYPE } from '@documenso/ui/primitives/document-flow/types'; +import { ElementVisible } from '@documenso/ui/primitives/element-visible'; +import { PopoverHover } from '@documenso/ui/primitives/popover'; + +export type DocumentReadOnlyFieldsProps = { + fields: CompletedField[]; + documentMeta?: DocumentMeta; +}; + +export const DocumentReadOnlyFields = ({ documentMeta, fields }: DocumentReadOnlyFieldsProps) => { + const [hiddenFieldIds, setHiddenFieldIds] = useState>({}); + + const handleHideField = (fieldId: string) => { + setHiddenFieldIds((prev) => ({ ...prev, [fieldId]: true })); + }; + + return ( + + {fields.map( + (field) => + !hiddenFieldIds[field.secondaryId] && ( + +
+ + + {extractInitials(field.Recipient.name || field.Recipient.email)} + + + } + contentProps={{ + className: 'flex w-fit flex-col py-2.5 text-sm', + }} + > +

+ + {field.Recipient.name + ? `${field.Recipient.name} (${field.Recipient.email})` + : field.Recipient.email}{' '} + + inserted a {FRIENDLY_FIELD_TYPE[field.type].toLowerCase()} +

+ + +
+
+ +
+ {match(field) + .with({ type: FieldType.SIGNATURE }, (field) => + field.Signature?.signatureImageAsBase64 ? ( + Signature + ) : ( +

+ {field.Signature?.typedSignature} +

+ ), + ) + .with({ type: P.union(FieldType.NAME, FieldType.TEXT, FieldType.EMAIL) }, () => ( +

{field.customText}

+ )) + .with({ type: FieldType.DATE }, () => ( +

+ {convertToLocalSystemFormat( + field.customText, + documentMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT, + documentMeta?.timezone ?? DEFAULT_DOCUMENT_TIME_ZONE, + )} +

+ )) + .with({ type: FieldType.FREE_SIGNATURE }, () => null) + .exhaustive()} +
+
+ ), + )} +
+ ); +}; diff --git a/packages/lib/server-only/field/get-completed-fields-for-document.ts b/packages/lib/server-only/field/get-completed-fields-for-document.ts new file mode 100644 index 000000000..304be95ba --- /dev/null +++ b/packages/lib/server-only/field/get-completed-fields-for-document.ts @@ -0,0 +1,29 @@ +import { prisma } from '@documenso/prisma'; +import { SigningStatus } from '@documenso/prisma/client'; + +export type GetCompletedFieldsForDocumentOptions = { + documentId: number; +}; + +export const getCompletedFieldsForDocument = async ({ + documentId, +}: GetCompletedFieldsForDocumentOptions) => { + return await prisma.field.findMany({ + where: { + documentId, + Recipient: { + signingStatus: SigningStatus.SIGNED, + }, + inserted: true, + }, + include: { + Signature: true, + Recipient: { + select: { + name: true, + email: true, + }, + }, + }, + }); +}; diff --git a/packages/lib/server-only/field/get-completed-fields-for-token.ts b/packages/lib/server-only/field/get-completed-fields-for-token.ts new file mode 100644 index 000000000..d84fa1343 --- /dev/null +++ b/packages/lib/server-only/field/get-completed-fields-for-token.ts @@ -0,0 +1,33 @@ +import { prisma } from '@documenso/prisma'; +import { SigningStatus } from '@documenso/prisma/client'; + +export type GetCompletedFieldsForTokenOptions = { + token: string; +}; + +export const getCompletedFieldsForToken = async ({ token }: GetCompletedFieldsForTokenOptions) => { + return await prisma.field.findMany({ + where: { + Document: { + Recipient: { + some: { + token, + }, + }, + }, + Recipient: { + signingStatus: SigningStatus.SIGNED, + }, + inserted: true, + }, + include: { + Signature: true, + Recipient: { + select: { + name: true, + email: true, + }, + }, + }, + }); +}; diff --git a/packages/lib/server-only/template/create-document-from-template.ts b/packages/lib/server-only/template/create-document-from-template.ts index 8ae5fecaf..79a3f6f25 100644 --- a/packages/lib/server-only/template/create-document-from-template.ts +++ b/packages/lib/server-only/template/create-document-from-template.ts @@ -89,6 +89,10 @@ export const createDocumentFromTemplate = async ({ const documentRecipient = document.Recipient.find((doc) => doc.email === recipient?.email); + if (!documentRecipient) { + throw new Error('Recipient not found.'); + } + return { type: field.type, page: field.page, @@ -99,7 +103,7 @@ export const createDocumentFromTemplate = async ({ customText: field.customText, inserted: field.inserted, documentId: document.id, - recipientId: documentRecipient?.id || null, + recipientId: documentRecipient.id, }; }), }); diff --git a/packages/lib/server-only/template/duplicate-template.ts b/packages/lib/server-only/template/duplicate-template.ts index 97b3f0a0b..963d78bde 100644 --- a/packages/lib/server-only/template/duplicate-template.ts +++ b/packages/lib/server-only/template/duplicate-template.ts @@ -81,6 +81,10 @@ export const duplicateTemplate = async ({ (doc) => doc.email === recipient?.email, ); + if (!duplicatedTemplateRecipient) { + throw new Error('Recipient not found.'); + } + return { type: field.type, page: field.page, @@ -91,7 +95,7 @@ export const duplicateTemplate = async ({ customText: field.customText, inserted: field.inserted, templateId: duplicatedTemplate.id, - recipientId: duplicatedTemplateRecipient?.id || null, + recipientId: duplicatedTemplateRecipient.id, }; }), }); diff --git a/packages/lib/types/fields.ts b/packages/lib/types/fields.ts new file mode 100644 index 000000000..1b999310d --- /dev/null +++ b/packages/lib/types/fields.ts @@ -0,0 +1,3 @@ +import type { getCompletedFieldsForToken } from '../server-only/field/get-completed-fields-for-token'; + +export type CompletedField = Awaited>[number]; diff --git a/packages/prisma/migrations/20240418140819_remove_impossible_field_optional_states/migration.sql b/packages/prisma/migrations/20240418140819_remove_impossible_field_optional_states/migration.sql new file mode 100644 index 000000000..62845de28 --- /dev/null +++ b/packages/prisma/migrations/20240418140819_remove_impossible_field_optional_states/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Made the column `recipientId` on table `Field` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "Field" ALTER COLUMN "recipientId" SET NOT NULL; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 35d429779..c0e68d53f 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -386,7 +386,7 @@ model Field { secondaryId String @unique @default(cuid()) documentId Int? templateId Int? - recipientId Int? + recipientId Int type FieldType page Int positionX Decimal @default(0) @@ -397,7 +397,7 @@ model Field { inserted Boolean Document Document? @relation(fields: [documentId], references: [id], onDelete: Cascade) Template Template? @relation(fields: [templateId], references: [id], onDelete: Cascade) - Recipient Recipient? @relation(fields: [recipientId], references: [id], onDelete: Cascade) + Recipient Recipient @relation(fields: [recipientId], references: [id], onDelete: Cascade) Signature Signature? @@index([documentId]) diff --git a/packages/ui/components/field/field.tsx b/packages/ui/components/field/field.tsx index e40b2e3d9..ce62443de 100644 --- a/packages/ui/components/field/field.tsx +++ b/packages/ui/components/field/field.tsx @@ -19,6 +19,7 @@ export type FieldContainerPortalProps = { field: Field; className?: string; children: React.ReactNode; + cardClassName?: string; }; export function FieldContainerPortal({ @@ -44,7 +45,7 @@ export function FieldContainerPortal({ ); } -export function FieldRootContainer({ field, children }: FieldContainerPortalProps) { +export function FieldRootContainer({ field, children, cardClassName }: FieldContainerPortalProps) { const [isValidating, setIsValidating] = useState(false); const ref = React.useRef(null); @@ -78,6 +79,7 @@ export function FieldRootContainer({ field, children }: FieldContainerPortalProp { 'border-orange-300 ring-1 ring-orange-300': !field.inserted && isValidating, }, + cardClassName, )} ref={ref} data-inserted={field.inserted ? 'true' : 'false'} diff --git a/packages/ui/primitives/popover.tsx b/packages/ui/primitives/popover.tsx index e84f6cc6d..62462322b 100644 --- a/packages/ui/primitives/popover.tsx +++ b/packages/ui/primitives/popover.tsx @@ -30,4 +30,66 @@ const PopoverContent = React.forwardRef< PopoverContent.displayName = PopoverPrimitive.Content.displayName; -export { Popover, PopoverTrigger, PopoverContent }; +type PopoverHoverProps = { + trigger: React.ReactNode; + children: React.ReactNode; + contentProps?: React.ComponentPropsWithoutRef; +}; + +const PopoverHover = ({ trigger, children, contentProps }: PopoverHoverProps) => { + const [open, setOpen] = React.useState(false); + + const isControlled = React.useRef(false); + const isMouseOver = React.useRef(false); + + const onMouseEnter = () => { + isMouseOver.current = true; + + if (isControlled.current) { + return; + } + + setOpen(true); + }; + + const onMouseLeave = () => { + isMouseOver.current = false; + + if (isControlled.current) { + return; + } + + setTimeout(() => { + setOpen(isMouseOver.current); + }, 200); + }; + + const onOpenChange = (newOpen: boolean) => { + isControlled.current = newOpen; + + setOpen(newOpen); + }; + + return ( + + + {trigger} + + + + {children} + + + ); +}; + +export { Popover, PopoverTrigger, PopoverContent, PopoverHover }; From f6e6dac46c681f6666ec90cd5245482f4030c0a5 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Fri, 19 Apr 2024 17:58:32 +0700 Subject: [PATCH 04/13] fix: update migration to drop invalid fields --- .../migration.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/prisma/migrations/20240418140819_remove_impossible_field_optional_states/migration.sql b/packages/prisma/migrations/20240418140819_remove_impossible_field_optional_states/migration.sql index 62845de28..ee027d90e 100644 --- a/packages/prisma/migrations/20240418140819_remove_impossible_field_optional_states/migration.sql +++ b/packages/prisma/migrations/20240418140819_remove_impossible_field_optional_states/migration.sql @@ -4,5 +4,8 @@ - Made the column `recipientId` on table `Field` required. This step will fail if there are existing NULL values in that column. */ +-- Drop all Fields where the recipientId is null +DELETE FROM "Field" WHERE "recipientId" IS NULL; + -- AlterTable ALTER TABLE "Field" ALTER COLUMN "recipientId" SET NOT NULL; From afaeba97393094017dc1c85f544ea4b6bf4cc16d Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Mon, 22 Apr 2024 13:31:49 +0700 Subject: [PATCH 05/13] fix: resize fields --- .../document/document-read-only-fields.tsx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/apps/web/src/components/document/document-read-only-fields.tsx b/apps/web/src/components/document/document-read-only-fields.tsx index 530066fa8..95a907d8f 100644 --- a/apps/web/src/components/document/document-read-only-fields.tsx +++ b/apps/web/src/components/document/document-read-only-fields.tsx @@ -75,7 +75,7 @@ export const DocumentReadOnlyFields = ({ documentMeta, fields }: DocumentReadOnl
-
+
{match(field) .with({ type: FieldType.SIGNATURE }, (field) => field.Signature?.signatureImageAsBase64 ? ( @@ -90,18 +90,17 @@ export const DocumentReadOnlyFields = ({ documentMeta, fields }: DocumentReadOnl

), ) - .with({ type: P.union(FieldType.NAME, FieldType.TEXT, FieldType.EMAIL) }, () => ( -

{field.customText}

- )) - .with({ type: FieldType.DATE }, () => ( -

- {convertToLocalSystemFormat( - field.customText, - documentMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT, - documentMeta?.timezone ?? DEFAULT_DOCUMENT_TIME_ZONE, - )} -

- )) + .with( + { type: P.union(FieldType.NAME, FieldType.TEXT, FieldType.EMAIL) }, + () => field.customText, + ) + .with({ type: FieldType.DATE }, () => + convertToLocalSystemFormat( + field.customText, + documentMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT, + documentMeta?.timezone ?? DEFAULT_DOCUMENT_TIME_ZONE, + ), + ) .with({ type: FieldType.FREE_SIGNATURE }, () => null) .exhaustive()}
From 87423e240af3c915b7ec70eca865f3fc1f3a5fe1 Mon Sep 17 00:00:00 2001 From: Mythie Date: Wed, 24 Apr 2024 17:32:11 +1000 Subject: [PATCH 06/13] chore: update foreign key constraints --- .../migration.sql | 23 +++++++++++++++++++ packages/prisma/schema.prisma | 10 ++++---- 2 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 packages/prisma/migrations/20240424072655_update_foreign_key_constraints/migration.sql diff --git a/packages/prisma/migrations/20240424072655_update_foreign_key_constraints/migration.sql b/packages/prisma/migrations/20240424072655_update_foreign_key_constraints/migration.sql new file mode 100644 index 000000000..89c38943d --- /dev/null +++ b/packages/prisma/migrations/20240424072655_update_foreign_key_constraints/migration.sql @@ -0,0 +1,23 @@ +-- DropForeignKey +ALTER TABLE "PasswordResetToken" DROP CONSTRAINT "PasswordResetToken_userId_fkey"; + +-- DropForeignKey +ALTER TABLE "Signature" DROP CONSTRAINT "Signature_fieldId_fkey"; + +-- DropForeignKey +ALTER TABLE "Team" DROP CONSTRAINT "Team_ownerUserId_fkey"; + +-- DropForeignKey +ALTER TABLE "TeamMember" DROP CONSTRAINT "TeamMember_userId_fkey"; + +-- AddForeignKey +ALTER TABLE "PasswordResetToken" ADD CONSTRAINT "PasswordResetToken_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Signature" ADD CONSTRAINT "Signature_fieldId_fkey" FOREIGN KEY ("fieldId") REFERENCES "Field"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Team" ADD CONSTRAINT "Team_ownerUserId_fkey" FOREIGN KEY ("ownerUserId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "TeamMember" ADD CONSTRAINT "TeamMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 8971f837f..97b6e9eeb 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -98,7 +98,7 @@ model PasswordResetToken { createdAt DateTime @default(now()) expiry DateTime userId Int - User User @relation(fields: [userId], references: [id]) + User User @relation(fields: [userId], references: [id], onDelete: Cascade) } model Passkey { @@ -415,7 +415,7 @@ model Signature { typedSignature String? Recipient Recipient @relation(fields: [recipientId], references: [id], onDelete: Cascade) - Field Field @relation(fields: [fieldId], references: [id], onDelete: Restrict) + Field Field @relation(fields: [fieldId], references: [id], onDelete: Cascade) @@index([recipientId]) } @@ -457,7 +457,7 @@ model Team { emailVerification TeamEmailVerification? transferVerification TeamTransferVerification? - owner User @relation(fields: [ownerUserId], references: [id]) + owner User @relation(fields: [ownerUserId], references: [id], onDelete: Cascade) subscription Subscription? document Document[] @@ -483,7 +483,7 @@ model TeamMember { createdAt DateTime @default(now()) role TeamMemberRole userId Int - user User @relation(fields: [userId], references: [id]) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) @@unique([userId, teamId]) @@ -564,5 +564,5 @@ model SiteSettings { data Json lastModifiedByUserId Int? lastModifiedAt DateTime @default(now()) - lastModifiedByUser User? @relation(fields: [lastModifiedByUserId], references: [id]) + lastModifiedByUser User? @relation(fields: [lastModifiedByUserId], references: [id], onDelete: SetNull) } From 713cd09a063d1d97db6e753fb0b2edef29b5357f Mon Sep 17 00:00:00 2001 From: Mythie Date: Wed, 24 Apr 2024 19:07:18 +1000 Subject: [PATCH 07/13] fix: downgrade playwright --- package-lock.json | 74 +++++++++++++++++++++++---------------- package.json | 2 +- packages/lib/package.json | 4 +-- 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index fb03b3a67..83d9523c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "eslint-config-custom": "*", "husky": "^9.0.11", "lint-staged": "^15.2.2", - "playwright": "^1.43.0", + "playwright": "1.41.0", "prettier": "^2.5.1", "rimraf": "^5.0.1", "turbo": "^1.9.3" @@ -4702,19 +4702,6 @@ "node": ">=14" } }, - "node_modules/@playwright/browser-chromium": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.43.0.tgz", - "integrity": "sha512-F0S4KIqSqQqm9EgsdtWjaJRpgP8cD2vWZHPSB41YI00PtXUobiv/3AnYISeL7wNuTanND7giaXQ4SIjkcIq3KQ==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "playwright-core": "1.43.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/@playwright/test": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.0.tgz", @@ -17673,11 +17660,11 @@ } }, "node_modules/playwright": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.0.tgz", - "integrity": "sha512-SiOKHbVjTSf6wHuGCbqrEyzlm6qvXcv7mENP+OZon1I07brfZLGdfWV0l/efAzVx7TF3Z45ov1gPEkku9q25YQ==", + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0.tgz", + "integrity": "sha512-XOsfl5ZtAik/T9oek4V0jAypNlaCNzuKOwVhqhgYT3os6kH34PzbRb74F0VWcLYa5WFdnmxl7qyAHBXvPv7lqQ==", "dependencies": { - "playwright-core": "1.43.0" + "playwright-core": "1.41.0" }, "bin": { "playwright": "cli.js" @@ -17689,17 +17676,6 @@ "fsevents": "2.3.2" } }, - "node_modules/playwright-core": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", - "integrity": "sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/playwright/node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -17713,6 +17689,17 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/playwright/node_modules/playwright-core": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0.tgz", + "integrity": "sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw==", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -24981,7 +24968,7 @@ "next-auth": "4.24.5", "oslo": "^0.17.0", "pdf-lib": "^1.17.1", - "playwright": "^1.43.0", + "playwright": "1.41.0", "react": "18.2.0", "remeda": "^1.27.1", "stripe": "^12.7.0", @@ -24989,10 +24976,23 @@ "zod": "^3.22.4" }, "devDependencies": { - "@playwright/browser-chromium": "^1.43.0", + "@playwright/browser-chromium": "1.41.0", "@types/luxon": "^3.3.1" } }, + "packages/lib/node_modules/@playwright/browser-chromium": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.41.0.tgz", + "integrity": "sha512-TaHfh3rDsz4+tVKdMMo4kdFOk8/4U6cPyMXHhoiJVmhOhjHXjR0qPMoa5gz5jDGl478cn5SoXmtgKPgTDFuS0g==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "playwright-core": "1.41.0" + }, + "engines": { + "node": ">=16" + } + }, "packages/lib/node_modules/nanoid": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", @@ -25010,6 +25010,18 @@ "node": "^14 || ^16 || >=18" } }, + "packages/lib/node_modules/playwright-core": { + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0.tgz", + "integrity": "sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "packages/prettier-config": { "name": "@documenso/prettier-config", "version": "0.0.0", diff --git a/package.json b/package.json index 396b2ecfd..70ed541e1 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "eslint-config-custom": "*", "husky": "^9.0.11", "lint-staged": "^15.2.2", - "playwright": "^1.43.0", + "playwright": "1.41.0", "prettier": "^2.5.1", "rimraf": "^5.0.1", "turbo": "^1.9.3" diff --git a/packages/lib/package.json b/packages/lib/package.json index 1aa7e431e..5e40e047b 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -39,7 +39,7 @@ "next-auth": "4.24.5", "oslo": "^0.17.0", "pdf-lib": "^1.17.1", - "playwright": "^1.43.0", + "playwright": "1.41.0", "react": "18.2.0", "remeda": "^1.27.1", "stripe": "^12.7.0", @@ -48,6 +48,6 @@ }, "devDependencies": { "@types/luxon": "^3.3.1", - "@playwright/browser-chromium": "^1.43.0" + "@playwright/browser-chromium": "1.41.0" } } \ No newline at end of file From 41ed6c9ad7f34c9ea430808a8e4f5cf95d9e7037 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 24 Apr 2024 19:49:10 +0700 Subject: [PATCH 08/13] fix: disable cert download when document not complete --- .../documents/[id]/logs/document-logs-page-view.tsx | 6 +++++- .../documents/[id]/logs/download-certificate-button.tsx | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/(dashboard)/documents/[id]/logs/document-logs-page-view.tsx b/apps/web/src/app/(dashboard)/documents/[id]/logs/document-logs-page-view.tsx index 2d786b9c9..0556fcd2d 100644 --- a/apps/web/src/app/(dashboard)/documents/[id]/logs/document-logs-page-view.tsx +++ b/apps/web/src/app/(dashboard)/documents/[id]/logs/document-logs-page-view.tsx @@ -133,7 +133,11 @@ export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageVie
- +
diff --git a/apps/web/src/app/(dashboard)/documents/[id]/logs/download-certificate-button.tsx b/apps/web/src/app/(dashboard)/documents/[id]/logs/download-certificate-button.tsx index 49a330b94..1f2028358 100644 --- a/apps/web/src/app/(dashboard)/documents/[id]/logs/download-certificate-button.tsx +++ b/apps/web/src/app/(dashboard)/documents/[id]/logs/download-certificate-button.tsx @@ -2,6 +2,7 @@ import { DownloadIcon } from 'lucide-react'; +import { DocumentStatus } from '@documenso/prisma/client'; import { trpc } from '@documenso/trpc/react'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; @@ -10,11 +11,13 @@ import { useToast } from '@documenso/ui/primitives/use-toast'; export type DownloadCertificateButtonProps = { className?: string; documentId: number; + documentStatus: DocumentStatus; }; export const DownloadCertificateButton = ({ className, documentId, + documentStatus, }: DownloadCertificateButtonProps) => { const { toast } = useToast(); @@ -69,6 +72,7 @@ export const DownloadCertificateButton = ({ className={cn('w-full sm:w-auto', className)} loading={isLoading} variant="outline" + disabled={documentStatus !== DocumentStatus.COMPLETED} onClick={() => void onDownloadCertificatesClick()} > {!isLoading && } From e4cf9c82518a6f38ca1626c8899c89abe92efa46 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 24 Apr 2024 19:51:18 +0700 Subject: [PATCH 09/13] fix: add server logic --- packages/trpc/server/document-router/router.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/trpc/server/document-router/router.ts b/packages/trpc/server/document-router/router.ts index d12002674..64f3c2480 100644 --- a/packages/trpc/server/document-router/router.ts +++ b/packages/trpc/server/document-router/router.ts @@ -4,6 +4,7 @@ import { DateTime } from 'luxon'; import { getServerLimits } from '@documenso/ee/server-only/limits/server'; import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { DOCUMENSO_ENCRYPTION_KEY } from '@documenso/lib/constants/crypto'; +import { AppError } from '@documenso/lib/errors/app-error'; import { encryptSecondaryData } from '@documenso/lib/server-only/crypto/encrypt'; import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta'; import { createDocument } from '@documenso/lib/server-only/document/create-document'; @@ -20,6 +21,7 @@ import { updateDocumentSettings } from '@documenso/lib/server-only/document/upda import { updateTitle } from '@documenso/lib/server-only/document/update-title'; import { symmetricEncrypt } from '@documenso/lib/universal/crypto'; import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; +import { DocumentStatus } from '@documenso/prisma/client'; import { authenticatedProcedure, procedure, router } from '../trpc'; import { @@ -413,6 +415,10 @@ export const documentRouter = router({ teamId, }); + if (document.status !== DocumentStatus.COMPLETED) { + throw new AppError('DOCUMENT_NOT_COMPLETE'); + } + const encrypted = encryptSecondaryData({ data: document.id.toString(), expiresAt: DateTime.now().plus({ minutes: 5 }).toJSDate().valueOf(), From 4de122f814b4cbb50ddb9b838f42c5d198102489 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Wed, 24 Apr 2024 20:07:38 +0700 Subject: [PATCH 10/13] fix: hide account action reauth --- .../primitives/document-flow/add-settings.tsx | 22 ++++++++++++------- .../primitives/document-flow/add-signers.tsx | 16 ++++++++------ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/packages/ui/primitives/document-flow/add-settings.tsx b/packages/ui/primitives/document-flow/add-settings.tsx index ea962dee5..ce52e03c2 100644 --- a/packages/ui/primitives/document-flow/add-settings.tsx +++ b/packages/ui/primitives/document-flow/add-settings.tsx @@ -9,7 +9,11 @@ import { useForm } from 'react-hook-form'; import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats'; import { DOCUMENT_AUTH_TYPES } from '@documenso/lib/constants/document-auth'; import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones'; -import { DocumentAccessAuth, DocumentActionAuth } from '@documenso/lib/types/document-auth'; +import { + DocumentAccessAuth, + DocumentActionAuth, + DocumentAuth, +} from '@documenso/lib/types/document-auth'; import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth'; import { DocumentStatus, type Field, type Recipient, SendStatus } from '@documenso/prisma/client'; import type { DocumentWithData } from '@documenso/prisma/types/document-with-data'; @@ -216,9 +220,9 @@ export const AddSettingsFormPartial = ({

    -
  • + {/*
  • Require account - The recipient must be signed in -
  • + */}
  • Require passkey - The recipient must have an account and passkey configured via their settings @@ -242,11 +246,13 @@ export const AddSettingsFormPartial = ({ - {Object.values(DocumentActionAuth).map((authType) => ( - - {DOCUMENT_AUTH_TYPES[authType].value} - - ))} + {Object.values(DocumentActionAuth) + .filter((auth) => auth !== DocumentAuth.ACCOUNT) + .map((authType) => ( + + {DOCUMENT_AUTH_TYPES[authType].value} + + ))} {/* Note: -1 is remapped in the Zod schema to the required value. */} None diff --git a/packages/ui/primitives/document-flow/add-signers.tsx b/packages/ui/primitives/document-flow/add-signers.tsx index b796f4328..2f9f2f234 100644 --- a/packages/ui/primitives/document-flow/add-signers.tsx +++ b/packages/ui/primitives/document-flow/add-signers.tsx @@ -302,10 +302,10 @@ export const AddSignersFormPartial = ({ global action signing authentication method configured in the "General Settings" step
  • -
  • + {/*
  • Require account - The recipient must be signed in -
  • + */}
  • Require passkey - The recipient must have an account and passkey configured via their settings @@ -326,11 +326,13 @@ export const AddSignersFormPartial = ({ {/* Note: -1 is remapped in the Zod schema to the required value. */} Inherit authentication method - {Object.values(RecipientActionAuth).map((authType) => ( - - {DOCUMENT_AUTH_TYPES[authType].value} - - ))} + {Object.values(RecipientActionAuth) + .filter((auth) => auth !== RecipientActionAuth.ACCOUNT) + .map((authType) => ( + + {DOCUMENT_AUTH_TYPES[authType].value} + + ))} From e1573465f6e66b67b47bb83bcb4e5fa0899a3711 Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Thu, 25 Apr 2024 23:32:59 +0700 Subject: [PATCH 11/13] fix: hide team webhooks from users --- packages/lib/server-only/webhooks/get-webhooks-by-user-id.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/lib/server-only/webhooks/get-webhooks-by-user-id.ts b/packages/lib/server-only/webhooks/get-webhooks-by-user-id.ts index 121fc670d..0877d878f 100644 --- a/packages/lib/server-only/webhooks/get-webhooks-by-user-id.ts +++ b/packages/lib/server-only/webhooks/get-webhooks-by-user-id.ts @@ -4,6 +4,7 @@ export const getWebhooksByUserId = async (userId: number) => { return await prisma.webhook.findMany({ where: { userId, + teamId: null, }, orderBy: { createdAt: 'desc', From 88dedc98298a29d9c0438d9491ab45f3fc38e315 Mon Sep 17 00:00:00 2001 From: Mythie Date: Fri, 26 Apr 2024 13:18:31 +1000 Subject: [PATCH 12/13] fix: use cdp and upgrade playwright again --- package-lock.json | 26 +++++++++---------- package.json | 2 +- packages/lib/package.json | 4 +-- .../htmltopdf/get-certificate-pdf.ts | 4 ++- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 83d9523c5..e9e822cf2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "eslint-config-custom": "*", "husky": "^9.0.11", "lint-staged": "^15.2.2", - "playwright": "1.41.0", + "playwright": "1.43.0", "prettier": "^2.5.1", "rimraf": "^5.0.1", "turbo": "^1.9.3" @@ -17660,11 +17660,11 @@ } }, "node_modules/playwright": { - "version": "1.41.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.0.tgz", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.0.tgz", "integrity": "sha512-XOsfl5ZtAik/T9oek4V0jAypNlaCNzuKOwVhqhgYT3os6kH34PzbRb74F0VWcLYa5WFdnmxl7qyAHBXvPv7lqQ==", "dependencies": { - "playwright-core": "1.41.0" + "playwright-core": "1.43.0" }, "bin": { "playwright": "cli.js" @@ -17690,8 +17690,8 @@ } }, "node_modules/playwright/node_modules/playwright-core": { - "version": "1.41.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0.tgz", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", "integrity": "sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw==", "bin": { "playwright-core": "cli.js" @@ -24968,7 +24968,7 @@ "next-auth": "4.24.5", "oslo": "^0.17.0", "pdf-lib": "^1.17.1", - "playwright": "1.41.0", + "playwright": "1.43.0", "react": "18.2.0", "remeda": "^1.27.1", "stripe": "^12.7.0", @@ -24976,18 +24976,18 @@ "zod": "^3.22.4" }, "devDependencies": { - "@playwright/browser-chromium": "1.41.0", + "@playwright/browser-chromium": "1.43.0", "@types/luxon": "^3.3.1" } }, "packages/lib/node_modules/@playwright/browser-chromium": { - "version": "1.41.0", - "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.41.0.tgz", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.43.0.tgz", "integrity": "sha512-TaHfh3rDsz4+tVKdMMo4kdFOk8/4U6cPyMXHhoiJVmhOhjHXjR0qPMoa5gz5jDGl478cn5SoXmtgKPgTDFuS0g==", "dev": true, "hasInstallScript": true, "dependencies": { - "playwright-core": "1.41.0" + "playwright-core": "1.43.0" }, "engines": { "node": ">=16" @@ -25011,8 +25011,8 @@ } }, "packages/lib/node_modules/playwright-core": { - "version": "1.41.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.0.tgz", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", "integrity": "sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw==", "dev": true, "bin": { diff --git a/package.json b/package.json index 70ed541e1..3480aae28 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "eslint-config-custom": "*", "husky": "^9.0.11", "lint-staged": "^15.2.2", - "playwright": "1.41.0", + "playwright": "1.43.0", "prettier": "^2.5.1", "rimraf": "^5.0.1", "turbo": "^1.9.3" diff --git a/packages/lib/package.json b/packages/lib/package.json index 5e40e047b..c6144df92 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -39,7 +39,7 @@ "next-auth": "4.24.5", "oslo": "^0.17.0", "pdf-lib": "^1.17.1", - "playwright": "1.41.0", + "playwright": "1.43.0", "react": "18.2.0", "remeda": "^1.27.1", "stripe": "^12.7.0", @@ -48,6 +48,6 @@ }, "devDependencies": { "@types/luxon": "^3.3.1", - "@playwright/browser-chromium": "1.41.0" + "@playwright/browser-chromium": "1.43.0" } } \ No newline at end of file diff --git a/packages/lib/server-only/htmltopdf/get-certificate-pdf.ts b/packages/lib/server-only/htmltopdf/get-certificate-pdf.ts index a7182410e..dee40d41a 100644 --- a/packages/lib/server-only/htmltopdf/get-certificate-pdf.ts +++ b/packages/lib/server-only/htmltopdf/get-certificate-pdf.ts @@ -18,7 +18,9 @@ export const getCertificatePdf = async ({ documentId }: GetCertificatePdfOptions let browser: Browser; if (process.env.NEXT_PRIVATE_BROWSERLESS_URL) { - browser = await chromium.connect(process.env.NEXT_PRIVATE_BROWSERLESS_URL); + // !: Use CDP rather than the default `connect` method to avoid coupling to the playwright version. + // !: Previously we would have to keep the playwright version in sync with the browserless version to avoid errors. + browser = await chromium.connectOverCDP(process.env.NEXT_PRIVATE_BROWSERLESS_URL); } else { browser = await chromium.launch(); } From 481d739c37e7ba147b9c3120a1bac47ea0f7919c Mon Sep 17 00:00:00 2001 From: Mythie Date: Fri, 26 Apr 2024 13:25:16 +1000 Subject: [PATCH 13/13] chore: update package-lock --- package-lock.json | 84 ++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 48 deletions(-) diff --git a/package-lock.json b/package-lock.json index e9e822cf2..479463b25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4702,13 +4702,26 @@ "node": ">=14" } }, + "node_modules/@playwright/browser-chromium": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.43.0.tgz", + "integrity": "sha512-F0S4KIqSqQqm9EgsdtWjaJRpgP8cD2vWZHPSB41YI00PtXUobiv/3AnYISeL7wNuTanND7giaXQ4SIjkcIq3KQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "playwright-core": "1.43.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@playwright/test": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.40.0.tgz", - "integrity": "sha512-PdW+kn4eV99iP5gxWNSDQCbhMaDVej+RXL5xr6t04nbKLCBwYtA046t7ofoczHOm8u6c+45hpDKQVZqtqwkeQg==", + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.1.tgz", + "integrity": "sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==", "dev": true, "dependencies": { - "playwright": "1.40.0" + "playwright": "1.43.1" }, "bin": { "playwright": "cli.js" @@ -4732,12 +4745,12 @@ } }, "node_modules/@playwright/test/node_modules/playwright": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.40.0.tgz", - "integrity": "sha512-gyHAgQjiDf1m34Xpwzaqb76KgfzYrhK7iih+2IzcOCoZWr/8ZqmdBw+t0RU85ZmfJMgtgAiNtBQ/KS2325INXw==", + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz", + "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==", "dev": true, "dependencies": { - "playwright-core": "1.40.0" + "playwright-core": "1.43.1" }, "bin": { "playwright": "cli.js" @@ -4750,9 +4763,9 @@ } }, "node_modules/@playwright/test/node_modules/playwright-core": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.40.0.tgz", - "integrity": "sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q==", + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz", + "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -17662,7 +17675,7 @@ "node_modules/playwright": { "version": "1.43.0", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.0.tgz", - "integrity": "sha512-XOsfl5ZtAik/T9oek4V0jAypNlaCNzuKOwVhqhgYT3os6kH34PzbRb74F0VWcLYa5WFdnmxl7qyAHBXvPv7lqQ==", + "integrity": "sha512-SiOKHbVjTSf6wHuGCbqrEyzlm6qvXcv7mENP+OZon1I07brfZLGdfWV0l/efAzVx7TF3Z45ov1gPEkku9q25YQ==", "dependencies": { "playwright-core": "1.43.0" }, @@ -17676,6 +17689,17 @@ "fsevents": "2.3.2" } }, + "node_modules/playwright-core": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", + "integrity": "sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/playwright/node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -17689,17 +17713,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/playwright/node_modules/playwright-core": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", - "integrity": "sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw==", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -24980,19 +24993,6 @@ "@types/luxon": "^3.3.1" } }, - "packages/lib/node_modules/@playwright/browser-chromium": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.43.0.tgz", - "integrity": "sha512-TaHfh3rDsz4+tVKdMMo4kdFOk8/4U6cPyMXHhoiJVmhOhjHXjR0qPMoa5gz5jDGl478cn5SoXmtgKPgTDFuS0g==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "playwright-core": "1.43.0" - }, - "engines": { - "node": ">=16" - } - }, "packages/lib/node_modules/nanoid": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", @@ -25010,18 +25010,6 @@ "node": "^14 || ^16 || >=18" } }, - "packages/lib/node_modules/playwright-core": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", - "integrity": "sha512-UGKASUhXmvqm2Lxa1fNr8sFwAtqjpgBRr9jQ7XBI8Rn5uFiEowGUGwrruUQsVPIom4bk7Lt+oLGpXobnXzrBIw==", - "dev": true, - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=16" - } - }, "packages/prettier-config": { "name": "@documenso/prettier-config", "version": "0.0.0",