From 70320cd24b07c4bd56ccc66280cc626577d2c05c Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Tue, 25 Feb 2025 02:34:44 +1100 Subject: [PATCH 01/27] chore: update API documentation --- .../pages/developers/public-api/index.mdx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/documentation/pages/developers/public-api/index.mdx b/apps/documentation/pages/developers/public-api/index.mdx index f2745ee82..8c6bd24b5 100644 --- a/apps/documentation/pages/developers/public-api/index.mdx +++ b/apps/documentation/pages/developers/public-api/index.mdx @@ -21,14 +21,20 @@ Check out the [API V1 documentation](https://app.documenso.com/api/v1/openapi) f ## API V2 - Beta -Our new API V2 is currently in Beta. The new API features typed SDKs for TypeScript, Python and Go and example code for many more. +API V2 is currently beta, and will be subject to breaking changes - - NOW IN BETA: [API V2 Documentation](https://documen.so/api-v2-docs) - +Check out the [API V2 documentation](https://documen.so/api-v2-docs) for details about the API endpoints, request parameters, response formats, and authentication methods. + +Our new API V2 supports the following typed SDKs: + +- [TypeScript](https://github.com/documenso/sdk-typescript) +- [Python](https://github.com/documenso/sdk-python) +- [Go](https://github.com/documenso/sdk-go) 🚀 [V2 Announcement](https://documen.so/sdk-blog) +📖 [Documentation](https://documen.so/api-v2-docs) + 💬 [Leave Feedback](https://documen.so/sdk-feedback) 🔔 [Breaking Changes](https://documen.so/sdk-breaking) From 2fbaf56c06dfb53308925c9151480ff2c2a8991b Mon Sep 17 00:00:00 2001 From: Mythie Date: Tue, 25 Feb 2025 07:54:28 +1100 Subject: [PATCH 02/27] fix: early adopters can use platform features --- .../app/embed/direct/[[...url]]/client.tsx | 6 +- .../src/app/embed/direct/[[...url]]/page.tsx | 9 ++- .../src/app/embed/sign/[[...url]]/client.tsx | 6 +- .../src/app/embed/sign/[[...url]]/page.tsx | 9 ++- .../ee/server-only/util/is-community-plan.ts | 56 +++++++++++++++++++ 5 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 packages/ee/server-only/util/is-community-plan.ts diff --git a/apps/web/src/app/embed/direct/[[...url]]/client.tsx b/apps/web/src/app/embed/direct/[[...url]]/client.tsx index dbb74a36a..5ff905e39 100644 --- a/apps/web/src/app/embed/direct/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/direct/[[...url]]/client.tsx @@ -49,7 +49,7 @@ export type EmbedDirectTemplateClientPageProps = { fields: Field[]; metadata?: DocumentMeta | TemplateMeta | null; hidePoweredBy?: boolean; - isPlatformOrEnterprise?: boolean; + allowWhiteLabelling?: boolean; }; export const EmbedDirectTemplateClientPage = ({ @@ -60,7 +60,7 @@ export const EmbedDirectTemplateClientPage = ({ fields, metadata, hidePoweredBy = false, - isPlatformOrEnterprise = false, + allowWhiteLabelling = false, }: EmbedDirectTemplateClientPageProps) => { const { _ } = useLingui(); const { toast } = useToast(); @@ -288,7 +288,7 @@ export const EmbedDirectTemplateClientPage = ({ document.documentElement.classList.add('dark-mode-disabled'); } - if (isPlatformOrEnterprise) { + if (allowWhiteLabelling) { injectCss({ css: data.css, cssVars: data.cssVars, diff --git a/apps/web/src/app/embed/direct/[[...url]]/page.tsx b/apps/web/src/app/embed/direct/[[...url]]/page.tsx index f4ef467d2..85ef40c78 100644 --- a/apps/web/src/app/embed/direct/[[...url]]/page.tsx +++ b/apps/web/src/app/embed/direct/[[...url]]/page.tsx @@ -2,6 +2,7 @@ import { notFound } from 'next/navigation'; import { match } from 'ts-pattern'; +import { isCommunityPlan as isUserCommunityPlan } from '@documenso/ee/server-only/util/is-community-plan'; import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise'; import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform'; import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app'; @@ -55,12 +56,16 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem documentAuth: template.authOptions, }); - const [isPlatformDocument, isEnterpriseDocument] = await Promise.all([ + const [isPlatformDocument, isEnterpriseDocument, isCommunityPlan] = await Promise.all([ isDocumentPlatform(template), isUserEnterprise({ userId: template.userId, teamId: template.teamId ?? undefined, }), + isUserCommunityPlan({ + userId: template.userId, + teamId: template.teamId ?? undefined, + }), ]); const isAccessAuthValid = match(derivedRecipientAccessAuth) @@ -106,7 +111,7 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem fields={fields} metadata={template.templateMeta} hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy} - isPlatformOrEnterprise={isPlatformDocument || isEnterpriseDocument} + allowWhiteLabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument} /> diff --git a/apps/web/src/app/embed/sign/[[...url]]/client.tsx b/apps/web/src/app/embed/sign/[[...url]]/client.tsx index f7635ddab..ea024bde0 100644 --- a/apps/web/src/app/embed/sign/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/sign/[[...url]]/client.tsx @@ -51,7 +51,7 @@ export type EmbedSignDocumentClientPageProps = { metadata?: DocumentMeta | TemplateMeta | null; isCompleted?: boolean; hidePoweredBy?: boolean; - isPlatformOrEnterprise?: boolean; + allowWhitelabelling?: boolean; allRecipients?: RecipientWithFields[]; }; @@ -64,7 +64,7 @@ export const EmbedSignDocumentClientPage = ({ metadata, isCompleted, hidePoweredBy = false, - isPlatformOrEnterprise = false, + allowWhitelabelling = false, allRecipients = [], }: EmbedSignDocumentClientPageProps) => { const { _ } = useLingui(); @@ -212,7 +212,7 @@ export const EmbedSignDocumentClientPage = ({ document.documentElement.classList.add('dark-mode-disabled'); } - if (isPlatformOrEnterprise) { + if (allowWhitelabelling) { injectCss({ css: data.css, cssVars: data.cssVars, diff --git a/apps/web/src/app/embed/sign/[[...url]]/page.tsx b/apps/web/src/app/embed/sign/[[...url]]/page.tsx index 0e9ac7a60..8b7223cb1 100644 --- a/apps/web/src/app/embed/sign/[[...url]]/page.tsx +++ b/apps/web/src/app/embed/sign/[[...url]]/page.tsx @@ -2,6 +2,7 @@ import { notFound } from 'next/navigation'; import { match } from 'ts-pattern'; +import { isCommunityPlan as isUserCommunityPlan } from '@documenso/ee/server-only/util/is-community-plan'; import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise'; import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform'; import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app'; @@ -62,12 +63,16 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen return ; } - const [isPlatformDocument, isEnterpriseDocument] = await Promise.all([ + const [isPlatformDocument, isEnterpriseDocument, isCommunityPlan] = await Promise.all([ isDocumentPlatform(document), isUserEnterprise({ userId: document.userId, teamId: document.teamId ?? undefined, }), + isUserCommunityPlan({ + userId: document.userId, + teamId: document.teamId ?? undefined, + }), ]); const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({ @@ -127,7 +132,7 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen metadata={document.documentMeta} isCompleted={document.status === DocumentStatus.COMPLETED} hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy} - isPlatformOrEnterprise={isPlatformDocument || isEnterpriseDocument} + allowWhitelabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument} allRecipients={allRecipients} /> diff --git a/packages/ee/server-only/util/is-community-plan.ts b/packages/ee/server-only/util/is-community-plan.ts new file mode 100644 index 000000000..b32769d41 --- /dev/null +++ b/packages/ee/server-only/util/is-community-plan.ts @@ -0,0 +1,56 @@ +import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing'; +import { prisma } from '@documenso/prisma'; +import type { Subscription } from '@documenso/prisma/client'; + +import { getCommunityPlanPriceIds } from '../stripe/get-community-plan-prices'; + +export type IsCommunityPlanOptions = { + userId: number; + teamId?: number; +}; + +/** + * Whether the user or team is on the community plan. + */ +export const isCommunityPlan = async ({ + userId, + teamId, +}: IsCommunityPlanOptions): Promise => { + let subscriptions: Subscription[] = []; + + if (teamId) { + subscriptions = await prisma.team + .findFirstOrThrow({ + where: { + id: teamId, + }, + select: { + owner: { + include: { + subscriptions: true, + }, + }, + }, + }) + .then((team) => team.owner.subscriptions); + } else { + subscriptions = await prisma.user + .findFirstOrThrow({ + where: { + id: userId, + }, + select: { + subscriptions: true, + }, + }) + .then((user) => user.subscriptions); + } + + if (subscriptions.length === 0) { + return false; + } + + const communityPlanPriceIds = await getCommunityPlanPriceIds(); + + return subscriptionsContainsActivePlan(subscriptions, communityPlanPriceIds); +}; From 59c1e55233c0efa413fd6e81ea676d0deccb66fc Mon Sep 17 00:00:00 2001 From: Mythie Date: Tue, 25 Feb 2025 08:03:13 +1100 Subject: [PATCH 03/27] v1.9.1-rc.3 --- apps/web/package.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 315d48ba5..40f2db3b5 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@documenso/web", - "version": "1.9.1-rc.2", + "version": "1.9.1-rc.3", "private": true, "license": "AGPL-3.0", "scripts": { diff --git a/package-lock.json b/package-lock.json index 470e39f0d..a390ee78c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@documenso/root", - "version": "1.9.1-rc.2", + "version": "1.9.1-rc.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@documenso/root", - "version": "1.9.1-rc.2", + "version": "1.9.1-rc.3", "workspaces": [ "apps/*", "packages/*" @@ -106,7 +106,7 @@ }, "apps/web": { "name": "@documenso/web", - "version": "1.9.1-rc.2", + "version": "1.9.1-rc.3", "license": "AGPL-3.0", "dependencies": { "@documenso/api": "*", diff --git a/package.json b/package.json index dd01c51d8..87c22bc1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.9.1-rc.2", + "version": "1.9.1-rc.3", "scripts": { "build": "turbo run build", "build:web": "turbo run build --filter=@documenso/web", From c51c32fdc62f75aee4efcf4118fd33522589f1f6 Mon Sep 17 00:00:00 2001 From: Mythie Date: Tue, 25 Feb 2025 09:44:40 +1100 Subject: [PATCH 04/27] feat: search by externalId --- .../server-only/document/find-documents.ts | 1 + .../document/search-documents-with-keyword.ts | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/packages/lib/server-only/document/find-documents.ts b/packages/lib/server-only/document/find-documents.ts index 160b1b2ce..2dcc3b316 100644 --- a/packages/lib/server-only/document/find-documents.ts +++ b/packages/lib/server-only/document/find-documents.ts @@ -88,6 +88,7 @@ export const findDocuments = async ({ const searchFilter: Prisma.DocumentWhereInput = { OR: [ { title: { contains: query, mode: 'insensitive' } }, + { externalId: { contains: query, mode: 'insensitive' } }, { recipients: { some: { name: { contains: query, mode: 'insensitive' } } } }, { recipients: { some: { email: { contains: query, mode: 'insensitive' } } } }, ], diff --git a/packages/lib/server-only/document/search-documents-with-keyword.ts b/packages/lib/server-only/document/search-documents-with-keyword.ts index 1fba24de1..136eed386 100644 --- a/packages/lib/server-only/document/search-documents-with-keyword.ts +++ b/packages/lib/server-only/document/search-documents-with-keyword.ts @@ -34,6 +34,14 @@ export const searchDocumentsWithKeyword = async ({ userId: userId, deletedAt: null, }, + { + externalId: { + contains: query, + mode: 'insensitive', + }, + userId: userId, + deletedAt: null, + }, { recipients: { some: { @@ -88,6 +96,23 @@ export const searchDocumentsWithKeyword = async ({ }, deletedAt: null, }, + { + externalId: { + contains: query, + mode: 'insensitive', + }, + teamId: { + not: null, + }, + team: { + members: { + some: { + userId: userId, + }, + }, + }, + deletedAt: null, + }, ], }, include: { From 84b4d5885674f5e5a0bf2d3849302ddfd7b41d01 Mon Sep 17 00:00:00 2001 From: Samuel Huber <40248495+SamuelLHuber@users.noreply.github.com> Date: Tue, 25 Feb 2025 06:59:20 +0100 Subject: [PATCH 05/27] chore: update readme (#1664) Resolve external link issue --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5239c79d0..77b023c4f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -> 🚨 We are live on Product Hunt 🎉 Check out our latest launch: The Platform Plan! +> 🚨 We are live on Product Hunt 🎉 Check out our latest launch: The Platform Plan! Documenso Platform Plan - Whitelabeled signing flows in your product | Product Hunt From b6a891acc8097ee2a2ecf04e9141e3fd0b641b89 Mon Sep 17 00:00:00 2001 From: Catalin Pit Date: Tue, 25 Feb 2025 14:02:12 +0200 Subject: [PATCH 06/27] docs: add the v2 api staging base url (#1671) --- apps/documentation/pages/developers/public-api/index.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/documentation/pages/developers/public-api/index.mdx b/apps/documentation/pages/developers/public-api/index.mdx index 8c6bd24b5..050810863 100644 --- a/apps/documentation/pages/developers/public-api/index.mdx +++ b/apps/documentation/pages/developers/public-api/index.mdx @@ -31,6 +31,11 @@ Our new API V2 supports the following typed SDKs: - [Python](https://github.com/documenso/sdk-python) - [Go](https://github.com/documenso/sdk-go) + + For the staging API, please use the following base URL: + `https://stg-app.documenso.dev/api/v2-beta/` + + 🚀 [V2 Announcement](https://documen.so/sdk-blog) 📖 [Documentation](https://documen.so/api-v2-docs) From 838e399c7381c3a58c4ca23c60124aeb40673c58 Mon Sep 17 00:00:00 2001 From: Mythie Date: Wed, 26 Feb 2025 15:27:44 +1100 Subject: [PATCH 07/27] fix: handle empty field meta for checkboxes --- apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx b/apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx index 627782838..a85d383c9 100644 --- a/apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx @@ -44,7 +44,12 @@ export const CheckboxField = ({ field, onSignField, onUnsignField }: CheckboxFie const [isPending, startTransition] = useTransition(); const { executeActionAuthProcedure } = useRequiredDocumentAuthContext(); - const parsedFieldMeta = ZCheckboxFieldMeta.parse(field.fieldMeta); + const parsedFieldMeta = ZCheckboxFieldMeta.parse( + field.fieldMeta ?? { + type: 'checkbox', + values: [{ id: 1, checked: false, value: '' }], + }, + ); const values = parsedFieldMeta.values?.map((item) => ({ ...item, From 42e39f7ef1f1ce0f1e24eb99b1ca1b77ae0e16ef Mon Sep 17 00:00:00 2001 From: Mythie Date: Wed, 26 Feb 2025 15:28:51 +1100 Subject: [PATCH 08/27] v1.9.1-rc.4 --- apps/web/package.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 40f2db3b5..c4984f0f5 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@documenso/web", - "version": "1.9.1-rc.3", + "version": "1.9.1-rc.4", "private": true, "license": "AGPL-3.0", "scripts": { diff --git a/package-lock.json b/package-lock.json index a390ee78c..791af3504 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@documenso/root", - "version": "1.9.1-rc.3", + "version": "1.9.1-rc.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@documenso/root", - "version": "1.9.1-rc.3", + "version": "1.9.1-rc.4", "workspaces": [ "apps/*", "packages/*" @@ -106,7 +106,7 @@ }, "apps/web": { "name": "@documenso/web", - "version": "1.9.1-rc.3", + "version": "1.9.1-rc.4", "license": "AGPL-3.0", "dependencies": { "@documenso/api": "*", diff --git a/package.json b/package.json index 87c22bc1f..3f7ec551e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.9.1-rc.3", + "version": "1.9.1-rc.4", "scripts": { "build": "turbo run build", "build:web": "turbo run build --filter=@documenso/web", From 7bb93e423310a5a541b47448f99044d6ad90246b Mon Sep 17 00:00:00 2001 From: David Nimon Date: Wed, 26 Feb 2025 01:04:05 -0400 Subject: [PATCH 09/27] docs: add documentation for embedding via web components (#1670) The web components version of embedding was missing documentation. I've added it here. Let me know if there's anything that should be changed/updated. --- .../pages/developers/embedding/_meta.json | 3 +- .../pages/developers/embedding/index.mdx | 19 ++-- .../developers/embedding/web-components.mdx | 89 +++++++++++++++++++ 3 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 apps/documentation/pages/developers/embedding/web-components.mdx diff --git a/apps/documentation/pages/developers/embedding/_meta.json b/apps/documentation/pages/developers/embedding/_meta.json index 96806de3e..58bcdf546 100644 --- a/apps/documentation/pages/developers/embedding/_meta.json +++ b/apps/documentation/pages/developers/embedding/_meta.json @@ -6,5 +6,6 @@ "solid": "Solid Integration", "preact": "Preact Integration", "angular": "Angular Integration", - "css-variables": "CSS Variables" + "css-variables": "CSS Variables", + "web-components": "Web Components" } diff --git a/apps/documentation/pages/developers/embedding/index.mdx b/apps/documentation/pages/developers/embedding/index.mdx index 27d6f6f8f..2e93941ae 100644 --- a/apps/documentation/pages/developers/embedding/index.mdx +++ b/apps/documentation/pages/developers/embedding/index.mdx @@ -73,14 +73,15 @@ These customization options are available for both Direct Templates and Signing We support embedding across a range of popular JavaScript frameworks, including: -| Framework | Package | -| --------- | ---------------------------------------------------------------------------------- | -| React | [@documenso/embed-react](https://www.npmjs.com/package/@documenso/embed-react) | -| Preact | [@documenso/embed-preact](https://www.npmjs.com/package/@documenso/embed-preact) | -| Vue | [@documenso/embed-vue](https://www.npmjs.com/package/@documenso/embed-vue) | -| Svelte | [@documenso/embed-svelte](https://www.npmjs.com/package/@documenso/embed-svelte) | -| Solid | [@documenso/embed-solid](https://www.npmjs.com/package/@documenso/embed-solid) | -| Angular | [@documenso/embed-angular](https://www.npmjs.com/package/@documenso/embed-angular) | +| Framework | Package | +| --------- | ---------------------------------------------------------------------------------- | +| React | [@documenso/embed-react](https://www.npmjs.com/package/@documenso/embed-react) | +| Preact | [@documenso/embed-preact](https://www.npmjs.com/package/@documenso/embed-preact) | +| Vue | [@documenso/embed-vue](https://www.npmjs.com/package/@documenso/embed-vue) | +| Svelte | [@documenso/embed-svelte](https://www.npmjs.com/package/@documenso/embed-svelte) | +| Solid | [@documenso/embed-solid](https://www.npmjs.com/package/@documenso/embed-solid) | +| Angular | [@documenso/embed-angular](https://www.npmjs.com/package/@documenso/embed-angular) | +| Web Components | [@documenso/embed-webcomponent](https://www.npmjs.com/package/@documenso/embed-webcomponent) | Additionally, we provide **web components** for more generalized use. However, please note that web components are still in their early stages and haven't been extensively tested. @@ -166,6 +167,7 @@ Once you've obtained the appropriate tokens, you can integrate the signing exper - [Svelte](/developers/embedding/svelte) - [Solid](/developers/embedding/solid) - [Angular](/developers/embedding/angular) +- [Web Components](/developers/embedding/web-components) If you're using **web components**, the integration process is slightly different. Keep in mind that web components are currently less tested but can still provide flexibility for general use cases. @@ -177,4 +179,5 @@ If you're using **web components**, the integration process is slightly differen - [Solid Integration](/developers/embedding/solid) - [Preact Integration](/developers/embedding/preact) - [Angular Integration](/developers/embedding/angular) +- [Web Components](/developers/embedding/web-components) - [CSS Variables](/developers/embedding/css-variables) diff --git a/apps/documentation/pages/developers/embedding/web-components.mdx b/apps/documentation/pages/developers/embedding/web-components.mdx new file mode 100644 index 000000000..43ea08788 --- /dev/null +++ b/apps/documentation/pages/developers/embedding/web-components.mdx @@ -0,0 +1,89 @@ +--- +title: Web Components Integration +description: Learn how to use our embedding SDK via Web Components on a framework-less web application. +--- + +# Web Components Integration + +Our Web Components SDK provides a simple way to embed a signing experience within your framework-less web application. It supports both direct link templates and signing tokens. + +## Installation + +To install the SDK, run the following command: + +```bash +npm install @documenso/embed-webcomponent +``` + +Then in your html file, add the following to add the script, replacing the path with the proper path to the web component script. + +```html + +``` + +## Usage + +To embed a signing experience, you'll need to provide the token for the document you want to embed. This can be done in a few different ways, depending on your use case. + +### Direct Link Template + +If you have a direct link template, you can simply provide the token for the template to the `documenso-embed-direct-template` tag. + +```html + +``` + +#### Attributes + +| Attribute | Type | Description | +| ------------------- | ------------------- | ------------------------------------------------------------------------------------------ | +| token | string | The token for the document you want to embed | +| host | string (optional) | The host to be used for the signing experience, relevant for self-hosters | +| name | string (optional) | The name the signer that will be used by default for signing | +| lockName | boolean (optional) | Whether or not the name field should be locked disallowing modifications | +| email | string (optional) | The email the signer that will be used by default for signing | +| lockEmail | boolean (optional) | Whether or not the email field should be locked disallowing modifications | +| onDocumentReady | function (optional) | A callback function that will be called when the document is loaded and ready to be signed | +| onDocumentCompleted | function (optional) | A callback function that will be called when the document has been completed | +| onDocumentError | function (optional) | A callback function that will be called when an error occurs with the document | +| onFieldSigned | function (optional) | A callback function that will be called when a field is signed | +| onFieldUnsigned | function (optional) | A callback function that will be called when a field is unsigned | + +### Signing Token + +If you have a signing token, you can provide it to the `documenso-embed-sign-document` tag. + +```html + +``` + +#### Attributes + +| Attribute | Type | Description | +| ------------------- | ------------------- | ------------------------------------------------------------------------------------------ | +| token | string | The token for the document you want to embed | +| host | string (optional) | The host to be used for the signing experience, relevant for self-hosters | +| name | string (optional) | The name the signer that will be used by default for signing | +| lockName | boolean (optional) | Whether or not the name field should be locked disallowing modifications | +| onDocumentReady | function (optional) | A callback function that will be called when the document is loaded and ready to be signed | +| onDocumentCompleted | function (optional) | A callback function that will be called when the document has been completed | +| onDocumentError | function (optional) | A callback function that will be called when an error occurs with the document | + +### Creating via JavaScript + +You can also create the tag element using javascript, for dynamic generation of either modes. For example, this would add the sign document embed to the DOM. + +```javascript +document.getElementById('my-wrapper-here').innerHTML = ''; + +const tag = document.createElement('documenso-embed-sign-document'); +tag.setAttribute('token', data.token); +tag.style.width = '100%'; +tag.style.height = '100%'; + +document.getElementById('my-wrapper-here').appendChild(tag); +``` From 5c7768c2538eff1493bed651469c515a089dc238 Mon Sep 17 00:00:00 2001 From: Mythie Date: Thu, 27 Feb 2025 11:43:42 +1100 Subject: [PATCH 10/27] fix: improve embed mobile experience --- .../app/embed/direct/[[...url]]/client.tsx | 85 +++++++++++++++--- .../src/app/embed/sign/[[...url]]/client.tsx | 86 ++++++++++++++++--- 2 files changed, 143 insertions(+), 28 deletions(-) diff --git a/apps/web/src/app/embed/direct/[[...url]]/client.tsx b/apps/web/src/app/embed/direct/[[...url]]/client.tsx index 5ff905e39..7788ab012 100644 --- a/apps/web/src/app/embed/direct/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/direct/[[...url]]/client.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useLayoutEffect, useState } from 'react'; +import { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { useSearchParams } from 'next/navigation'; @@ -83,12 +83,15 @@ export const EmbedDirectTemplateClientPage = ({ const [hasCompletedDocument, setHasCompletedDocument] = useState(false); const [isExpanded, setIsExpanded] = useState(false); + const [hasAutoExpanded, setHasAutoExpanded] = useState(false); const [isEmailLocked, setIsEmailLocked] = useState(false); const [isNameLocked, setIsNameLocked] = useState(false); const [showPendingFieldTooltip, setShowPendingFieldTooltip] = useState(false); + const documentEndRef = useRef(null); + const [throttledOnCompleteClick, isThrottled] = useThrottleFn(() => void onCompleteClick(), 500); const [localFields, setLocalFields] = useState(() => fields); @@ -317,6 +320,43 @@ export const EmbedDirectTemplateClientPage = ({ } }, [hasFinishedInit, hasDocumentLoaded]); + // Set up intersection observer to auto-expand widget when user reaches bottom of document + useEffect(() => { + if (!hasDocumentLoaded || !hasFinishedInit || hasAutoExpanded || isExpanded) { + return; + } + + // Add a delay to ensure document has fully rendered and stabilized + const timeoutId = setTimeout(() => { + // Get the number of pages in the document + const pageCount = document.querySelectorAll(PDF_VIEWER_PAGE_SELECTOR).length; + + // Only set up the observer if there's more than one page + if (pageCount <= 1) return; + + const observer = new IntersectionObserver( + (entries) => { + const [entry] = entries; + + if (entry.isIntersecting) { + setIsExpanded(true); + setHasAutoExpanded(true); + observer.disconnect(); + } + }, + { threshold: 1.0 }, + ); + + if (documentEndRef.current) { + observer.observe(documentEndRef.current); + } + }, 1500); // 1.5 second delay + + return () => { + clearTimeout(timeoutId); + }; + }, [hasDocumentLoaded, hasFinishedInit, hasAutoExpanded, isExpanded]); + if (hasCompletedDocument) { return ( setHasDocumentLoaded(true)} /> + {/* Observer target at the bottom of the document */} +
{/* Widget */} @@ -360,19 +402,34 @@ export const EmbedDirectTemplateClientPage = ({ Sign document - + {isExpanded ? ( + + ) : pendingFields.length > 0 ? ( + + ) : ( + + )} diff --git a/apps/web/src/app/embed/sign/[[...url]]/client.tsx b/apps/web/src/app/embed/sign/[[...url]]/client.tsx index ea024bde0..34289187d 100644 --- a/apps/web/src/app/embed/sign/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/sign/[[...url]]/client.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useId, useLayoutEffect, useState } from 'react'; +import { useEffect, useId, useLayoutEffect, useRef, useState } from 'react'; import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; @@ -91,11 +91,14 @@ export const EmbedSignDocumentClientPage = ({ ); const [isExpanded, setIsExpanded] = useState(false); + const [hasAutoExpanded, setHasAutoExpanded] = useState(false); const [isNameLocked, setIsNameLocked] = useState(false); const [showPendingFieldTooltip, setShowPendingFieldTooltip] = useState(false); const [allowDocumentRejection, setAllowDocumentRejection] = useState(false); + const documentEndRef = useRef(null); + const selectedSigner = allRecipients.find((r) => r.id === selectedSignerId); const isAssistantMode = recipient.role === RecipientRole.ASSISTANT; @@ -241,6 +244,42 @@ export const EmbedSignDocumentClientPage = ({ } }, [hasFinishedInit, hasDocumentLoaded]); + // Set up intersection observer to auto-expand widget when user reaches bottom of document + useEffect(() => { + if (!hasDocumentLoaded || !hasFinishedInit || hasAutoExpanded || isExpanded) { + return; + } + + // Add a delay to ensure document has fully rendered and stabilized + const timeoutId = setTimeout(() => { + const pageCount = document.querySelectorAll(PDF_VIEWER_PAGE_SELECTOR).length; + + // Only set up the observer if there's more than one page + if (pageCount <= 1) return; + + const observer = new IntersectionObserver( + (entries) => { + const [entry] = entries; + + if (entry.isIntersecting) { + setIsExpanded(true); + setHasAutoExpanded(true); + observer.disconnect(); + } + }, + { threshold: 1.0 }, + ); + + if (documentEndRef.current) { + observer.observe(documentEndRef.current); + } + }, 1500); // 1.5 second delay + + return () => { + clearTimeout(timeoutId); + }; + }, [hasDocumentLoaded, hasFinishedInit, hasAutoExpanded, isExpanded]); + if (hasRejectedDocument) { return ; } @@ -283,6 +322,8 @@ export const EmbedSignDocumentClientPage = ({ documentData={documentData} onDocumentLoad={() => setHasDocumentLoaded(true)} /> + {/* Observer target at the bottom of the document */} +
{/* Widget */} @@ -303,19 +344,36 @@ export const EmbedSignDocumentClientPage = ({ )} - + {isExpanded ? ( + + ) : pendingFields.length > 0 ? ( + + ) : ( + + )} From 1560218d4a4efc693005a6200799e9b114c6c09f Mon Sep 17 00:00:00 2001 From: Mythie Date: Thu, 27 Feb 2025 11:44:19 +1100 Subject: [PATCH 11/27] v1.9.1-rc.5 --- apps/web/package.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index c4984f0f5..ad6f7e51f 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@documenso/web", - "version": "1.9.1-rc.4", + "version": "1.9.1-rc.5", "private": true, "license": "AGPL-3.0", "scripts": { diff --git a/package-lock.json b/package-lock.json index 791af3504..bfe60c3c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@documenso/root", - "version": "1.9.1-rc.4", + "version": "1.9.1-rc.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@documenso/root", - "version": "1.9.1-rc.4", + "version": "1.9.1-rc.5", "workspaces": [ "apps/*", "packages/*" @@ -106,7 +106,7 @@ }, "apps/web": { "name": "@documenso/web", - "version": "1.9.1-rc.4", + "version": "1.9.1-rc.5", "license": "AGPL-3.0", "dependencies": { "@documenso/api": "*", diff --git a/package.json b/package.json index 3f7ec551e..ee3a0b3a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.9.1-rc.4", + "version": "1.9.1-rc.5", "scripts": { "build": "turbo run build", "build:web": "turbo run build --filter=@documenso/web", From 255c33cdab17f9049cea8655bb4c58f19ef627cc Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Fri, 28 Feb 2025 09:04:25 +1100 Subject: [PATCH 12/27] fix: stripe price fetch (#1677) Currently Stripe prices search is omitting a price for an unknown reason. Changed our fetch logic to use `list` instead of `search` allows us to work around the issue. It's unknown on the performance impact of using `list` vs `search` --- packages/ee/server-only/stripe/get-prices-by-plan.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/ee/server-only/stripe/get-prices-by-plan.ts b/packages/ee/server-only/stripe/get-prices-by-plan.ts index 45906d54a..52c9d1e5e 100644 --- a/packages/ee/server-only/stripe/get-prices-by-plan.ts +++ b/packages/ee/server-only/stripe/get-prices-by-plan.ts @@ -4,15 +4,14 @@ import { stripe } from '@documenso/lib/server-only/stripe'; type PlanType = (typeof STRIPE_PLAN_TYPE)[keyof typeof STRIPE_PLAN_TYPE]; export const getPricesByPlan = async (plan: PlanType | PlanType[]) => { - const planTypes = typeof plan === 'string' ? [plan] : plan; + const planTypes: string[] = typeof plan === 'string' ? [plan] : plan; - const query = planTypes.map((planType) => `metadata['plan']:'${planType}'`).join(' OR '); - - const { data: prices } = await stripe.prices.search({ - query, + const prices = await stripe.prices.list({ expand: ['data.product'], limit: 100, }); - return prices.filter((price) => price.type === 'recurring'); + return prices.data.filter( + (price) => price.type === 'recurring' && planTypes.includes(price.metadata.plan), + ); }; From 617e3a46e066759bd574a6f05136aa8731a4b301 Mon Sep 17 00:00:00 2001 From: Mythie Date: Fri, 28 Feb 2025 09:10:16 +1100 Subject: [PATCH 13/27] v1.9.1-rc.6 --- apps/web/package.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index ad6f7e51f..1ab42dcf7 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@documenso/web", - "version": "1.9.1-rc.5", + "version": "1.9.1-rc.6", "private": true, "license": "AGPL-3.0", "scripts": { diff --git a/package-lock.json b/package-lock.json index bfe60c3c5..03bd8da4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@documenso/root", - "version": "1.9.1-rc.5", + "version": "1.9.1-rc.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@documenso/root", - "version": "1.9.1-rc.5", + "version": "1.9.1-rc.6", "workspaces": [ "apps/*", "packages/*" @@ -106,7 +106,7 @@ }, "apps/web": { "name": "@documenso/web", - "version": "1.9.1-rc.5", + "version": "1.9.1-rc.6", "license": "AGPL-3.0", "dependencies": { "@documenso/api": "*", diff --git a/package.json b/package.json index ee3a0b3a6..04f8956bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.9.1-rc.5", + "version": "1.9.1-rc.6", "scripts": { "build": "turbo run build", "build:web": "turbo run build --filter=@documenso/web", From ca3d65ad101416b9ad67acf6e008209901c8cfbb Mon Sep 17 00:00:00 2001 From: Mythie Date: Fri, 28 Feb 2025 10:11:08 +1100 Subject: [PATCH 14/27] fix: remove auto-expand in embeddding --- .../app/embed/direct/[[...url]]/client.tsx | 46 +------------------ .../src/app/embed/direct/[[...url]]/page.tsx | 4 +- .../src/app/embed/sign/[[...url]]/client.tsx | 45 +----------------- .../src/app/embed/sign/[[...url]]/page.tsx | 4 +- 4 files changed, 10 insertions(+), 89 deletions(-) diff --git a/apps/web/src/app/embed/direct/[[...url]]/client.tsx b/apps/web/src/app/embed/direct/[[...url]]/client.tsx index 7788ab012..6b6dbc4c7 100644 --- a/apps/web/src/app/embed/direct/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/direct/[[...url]]/client.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useLayoutEffect, useRef, useState } from 'react'; +import { useEffect, useLayoutEffect, useState } from 'react'; import { useSearchParams } from 'next/navigation'; @@ -83,15 +83,12 @@ export const EmbedDirectTemplateClientPage = ({ const [hasCompletedDocument, setHasCompletedDocument] = useState(false); const [isExpanded, setIsExpanded] = useState(false); - const [hasAutoExpanded, setHasAutoExpanded] = useState(false); const [isEmailLocked, setIsEmailLocked] = useState(false); const [isNameLocked, setIsNameLocked] = useState(false); const [showPendingFieldTooltip, setShowPendingFieldTooltip] = useState(false); - const documentEndRef = useRef(null); - const [throttledOnCompleteClick, isThrottled] = useThrottleFn(() => void onCompleteClick(), 500); const [localFields, setLocalFields] = useState(() => fields); @@ -320,43 +317,6 @@ export const EmbedDirectTemplateClientPage = ({ } }, [hasFinishedInit, hasDocumentLoaded]); - // Set up intersection observer to auto-expand widget when user reaches bottom of document - useEffect(() => { - if (!hasDocumentLoaded || !hasFinishedInit || hasAutoExpanded || isExpanded) { - return; - } - - // Add a delay to ensure document has fully rendered and stabilized - const timeoutId = setTimeout(() => { - // Get the number of pages in the document - const pageCount = document.querySelectorAll(PDF_VIEWER_PAGE_SELECTOR).length; - - // Only set up the observer if there's more than one page - if (pageCount <= 1) return; - - const observer = new IntersectionObserver( - (entries) => { - const [entry] = entries; - - if (entry.isIntersecting) { - setIsExpanded(true); - setHasAutoExpanded(true); - observer.disconnect(); - } - }, - { threshold: 1.0 }, - ); - - if (documentEndRef.current) { - observer.observe(documentEndRef.current); - } - }, 1500); // 1.5 second delay - - return () => { - clearTimeout(timeoutId); - }; - }, [hasDocumentLoaded, hasFinishedInit, hasAutoExpanded, isExpanded]); - if (hasCompletedDocument) { return ( setHasDocumentLoaded(true)} /> - {/* Observer target at the bottom of the document */} -
{/* Widget */}
diff --git a/apps/web/src/app/embed/direct/[[...url]]/page.tsx b/apps/web/src/app/embed/direct/[[...url]]/page.tsx index 85ef40c78..97f7bb953 100644 --- a/apps/web/src/app/embed/direct/[[...url]]/page.tsx +++ b/apps/web/src/app/embed/direct/[[...url]]/page.tsx @@ -110,7 +110,9 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem recipient={recipient} fields={fields} metadata={template.templateMeta} - hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy} + hidePoweredBy={ + isCommunityPlan || isPlatformDocument || isEnterpriseDocument || hidePoweredBy + } allowWhiteLabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument} /> diff --git a/apps/web/src/app/embed/sign/[[...url]]/client.tsx b/apps/web/src/app/embed/sign/[[...url]]/client.tsx index 34289187d..4650f60ab 100644 --- a/apps/web/src/app/embed/sign/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/sign/[[...url]]/client.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useId, useLayoutEffect, useRef, useState } from 'react'; +import { useEffect, useId, useLayoutEffect, useState } from 'react'; import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; @@ -91,14 +91,11 @@ export const EmbedSignDocumentClientPage = ({ ); const [isExpanded, setIsExpanded] = useState(false); - const [hasAutoExpanded, setHasAutoExpanded] = useState(false); const [isNameLocked, setIsNameLocked] = useState(false); const [showPendingFieldTooltip, setShowPendingFieldTooltip] = useState(false); const [allowDocumentRejection, setAllowDocumentRejection] = useState(false); - const documentEndRef = useRef(null); - const selectedSigner = allRecipients.find((r) => r.id === selectedSignerId); const isAssistantMode = recipient.role === RecipientRole.ASSISTANT; @@ -244,42 +241,6 @@ export const EmbedSignDocumentClientPage = ({ } }, [hasFinishedInit, hasDocumentLoaded]); - // Set up intersection observer to auto-expand widget when user reaches bottom of document - useEffect(() => { - if (!hasDocumentLoaded || !hasFinishedInit || hasAutoExpanded || isExpanded) { - return; - } - - // Add a delay to ensure document has fully rendered and stabilized - const timeoutId = setTimeout(() => { - const pageCount = document.querySelectorAll(PDF_VIEWER_PAGE_SELECTOR).length; - - // Only set up the observer if there's more than one page - if (pageCount <= 1) return; - - const observer = new IntersectionObserver( - (entries) => { - const [entry] = entries; - - if (entry.isIntersecting) { - setIsExpanded(true); - setHasAutoExpanded(true); - observer.disconnect(); - } - }, - { threshold: 1.0 }, - ); - - if (documentEndRef.current) { - observer.observe(documentEndRef.current); - } - }, 1500); // 1.5 second delay - - return () => { - clearTimeout(timeoutId); - }; - }, [hasDocumentLoaded, hasFinishedInit, hasAutoExpanded, isExpanded]); - if (hasRejectedDocument) { return ; } @@ -322,14 +283,12 @@ export const EmbedSignDocumentClientPage = ({ documentData={documentData} onDocumentLoad={() => setHasDocumentLoaded(true)} /> - {/* Observer target at the bottom of the document */} -
{/* Widget */}
diff --git a/apps/web/src/app/embed/sign/[[...url]]/page.tsx b/apps/web/src/app/embed/sign/[[...url]]/page.tsx index 8b7223cb1..b6c36f113 100644 --- a/apps/web/src/app/embed/sign/[[...url]]/page.tsx +++ b/apps/web/src/app/embed/sign/[[...url]]/page.tsx @@ -131,7 +131,9 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen fields={fields} metadata={document.documentMeta} isCompleted={document.status === DocumentStatus.COMPLETED} - hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy} + hidePoweredBy={ + isCommunityPlan || isPlatformDocument || isEnterpriseDocument || hidePoweredBy + } allowWhitelabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument} allRecipients={allRecipients} /> From 235d846d2b5fc6d85df44a8aaf46ad24e6460676 Mon Sep 17 00:00:00 2001 From: Mythie Date: Fri, 28 Feb 2025 10:11:36 +1100 Subject: [PATCH 15/27] v1.9.1-rc.7 --- apps/web/package.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 1ab42dcf7..34cc78689 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@documenso/web", - "version": "1.9.1-rc.6", + "version": "1.9.1-rc.7", "private": true, "license": "AGPL-3.0", "scripts": { diff --git a/package-lock.json b/package-lock.json index 03bd8da4a..61fbbc9a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@documenso/root", - "version": "1.9.1-rc.6", + "version": "1.9.1-rc.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@documenso/root", - "version": "1.9.1-rc.6", + "version": "1.9.1-rc.7", "workspaces": [ "apps/*", "packages/*" @@ -106,7 +106,7 @@ }, "apps/web": { "name": "@documenso/web", - "version": "1.9.1-rc.6", + "version": "1.9.1-rc.7", "license": "AGPL-3.0", "dependencies": { "@documenso/api": "*", diff --git a/package.json b/package.json index 04f8956bc..d054f4641 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.9.1-rc.6", + "version": "1.9.1-rc.7", "scripts": { "build": "turbo run build", "build:web": "turbo run build --filter=@documenso/web", From 1789eff564fedb5ea9ee9c12a354c55c495031fd Mon Sep 17 00:00:00 2001 From: Catalin Pit Date: Fri, 28 Feb 2025 12:09:38 +0200 Subject: [PATCH 16/27] chore: add label for checkbox and radio fields (#1607) --- .../sign/[token]/signing-field-container.tsx | 17 ++++++++ .../primitives/document-flow/field-item.tsx | 40 +++++++++++++++++++ .../checkbox-field.tsx | 12 ++++++ .../radio-field.tsx | 17 +++++++- 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx b/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx index a2cdfe9c7..33c7745c0 100644 --- a/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx +++ b/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx @@ -182,6 +182,23 @@ export const SigningFieldContainer = ({ )} + {(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) && + field.fieldMeta?.label && ( +
+ {field.fieldMeta.label} +
+ )} + {children}
diff --git a/packages/ui/primitives/document-flow/field-item.tsx b/packages/ui/primitives/document-flow/field-item.tsx index 6515816be..896c7a9d0 100644 --- a/packages/ui/primitives/document-flow/field-item.tsx +++ b/packages/ui/primitives/document-flow/field-item.tsx @@ -12,6 +12,7 @@ import { match } from 'ts-pattern'; import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer'; import type { TFieldMetaSchema } from '@documenso/lib/types/field-meta'; import { ZCheckboxFieldMeta, ZRadioFieldMeta } from '@documenso/lib/types/field-meta'; +import { FieldType } from '@documenso/prisma/client'; import { useSignerColors } from '../../lib/signer-colors'; import { cn } from '../../lib/utils'; @@ -185,11 +186,35 @@ export const FieldItem = ({ () => hasFieldMetaValues('CHECKBOX', field.fieldMeta, ZCheckboxFieldMeta), [field.fieldMeta], ); + const radioHasValues = useMemo( () => hasFieldMetaValues('RADIO', field.fieldMeta, ZRadioFieldMeta), [field.fieldMeta], ); + const hasCheckedValues = (fieldMeta: TFieldMetaSchema, type: FieldType) => { + if (!fieldMeta || (type !== FieldType.RADIO && type !== FieldType.CHECKBOX)) { + return false; + } + + if (type === FieldType.RADIO) { + const parsed = ZRadioFieldMeta.parse(fieldMeta); + return parsed.values?.some((value) => value.checked) ?? false; + } + + if (type === FieldType.CHECKBOX) { + const parsed = ZCheckboxFieldMeta.parse(fieldMeta); + return parsed.values?.some((value) => value.checked) ?? false; + } + + return false; + }; + + const fieldHasCheckedValues = useMemo( + () => hasCheckedValues(field.fieldMeta, field.type), + [field.fieldMeta, field.type], + ); + const fixedSize = checkBoxHasValues || radioHasValues; return createPortal( @@ -229,6 +254,21 @@ export const FieldItem = ({ onMove?.(d.node); }} > + {(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) && + field.fieldMeta?.label && ( +
+ {field.fieldMeta.label} +
+ )} +
+
+ + handleFieldChange('label', e.target.value)} + /> +
diff --git a/apps/web/src/app/embed/sign/[[...url]]/client.tsx b/apps/web/src/app/embed/sign/[[...url]]/client.tsx index 4650f60ab..e5a556077 100644 --- a/apps/web/src/app/embed/sign/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/sign/[[...url]]/client.tsx @@ -436,40 +436,42 @@ export const EmbedSignDocumentClientPage = ({ />
-
- + {hasSignatureField && ( +
+ - - - { - setSignature(value); - }} - onValidityChange={(isValid) => { - setSignatureValid(isValid); - }} - allowTypedSignature={Boolean( - metadata && - 'typedSignatureEnabled' in metadata && - metadata.typedSignatureEnabled, - )} - /> - - + + + { + setSignature(value); + }} + onValidityChange={(isValid) => { + setSignatureValid(isValid); + }} + allowTypedSignature={Boolean( + metadata && + 'typedSignatureEnabled' in metadata && + metadata.typedSignatureEnabled, + )} + /> + + - {hasSignatureField && !signatureValid && ( -
- - Signature is too small. Please provide a more complete signature. - -
- )} -
+ {hasSignatureField && !signatureValid && ( +
+ + Signature is too small. Please provide a more complete signature. + +
+ )} +
+ )} )}
diff --git a/packages/app-tests/e2e/document-flow/stepper-component.spec.ts b/packages/app-tests/e2e/document-flow/stepper-component.spec.ts index d53f33d11..ba7c580a6 100644 --- a/packages/app-tests/e2e/document-flow/stepper-component.spec.ts +++ b/packages/app-tests/e2e/document-flow/stepper-component.spec.ts @@ -384,7 +384,9 @@ test('[DOCUMENT_FLOW]: should be able to approve a document', async ({ page }) = await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true'); } - await page.getByRole('button', { name: 'Complete' }).click(); + await page + .getByRole('button', { name: role === RecipientRole.SIGNER ? 'Complete' : 'Approve' }) + .click(); await page .getByRole('button', { name: role === RecipientRole.SIGNER ? 'Sign' : 'Approve' }) .click(); @@ -454,7 +456,7 @@ test('[DOCUMENT_FLOW]: should be able to create, send with redirect url, sign a const { status } = await getDocumentByToken(token); expect(status).toBe(DocumentStatus.PENDING); - await page.getByRole('button', { name: 'Complete' }).click(); + await page.getByRole('button', { name: 'Approve' }).click(); await expect(page.getByRole('dialog').getByText('Complete Approval').first()).toBeVisible(); await page.getByRole('button', { name: 'Approve' }).click(); From 123a709836ed60cb35b2ab9431f76ceccf50bb65 Mon Sep 17 00:00:00 2001 From: Mythie Date: Thu, 6 Mar 2025 20:30:15 +1100 Subject: [PATCH 19/27] v1.9.1-rc.8 --- apps/web/package.json | 2 +- package-lock.json | 21 +++------------------ package.json | 2 +- 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 34cc78689..2470a9712 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@documenso/web", - "version": "1.9.1-rc.7", + "version": "1.9.1-rc.8", "private": true, "license": "AGPL-3.0", "scripts": { diff --git a/package-lock.json b/package-lock.json index 77a165ad7..206eb8a19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@documenso/root", - "version": "1.9.1-rc.7", + "version": "1.9.1-rc.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@documenso/root", - "version": "1.9.1-rc.7", + "version": "1.9.1-rc.8", "workspaces": [ "apps/*", "packages/*" @@ -106,7 +106,7 @@ }, "apps/web": { "name": "@documenso/web", - "version": "1.9.1-rc.7", + "version": "1.9.1-rc.8", "license": "AGPL-3.0", "dependencies": { "@documenso/api": "*", @@ -35722,21 +35722,6 @@ "engines": { "node": ">=6" } - }, - "packages/trpc/node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.6.tgz", - "integrity": "sha512-hNukAxq7hu4o5/UjPp5jqoBEtrpCbOmnUqZSKNJG8GrUVzfq0ucdhQFVrHcLRMvQcwqqDh1a5AJN9ORnNDpgBQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } } diff --git a/package.json b/package.json index d054f4641..eb7b93814 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.9.1-rc.7", + "version": "1.9.1-rc.8", "scripts": { "build": "turbo run build", "build:web": "turbo run build --filter=@documenso/web", From 6ce0be67ab7df0a8bd02bc634f4bbab98e25d103 Mon Sep 17 00:00:00 2001 From: eddielu Date: Thu, 6 Mar 2025 14:11:32 -0800 Subject: [PATCH 20/27] =?UTF-8?q?docs:=20Update=20documentation=20to=20mat?= =?UTF-8?q?ch=20reality.=20colorPrimary,=20colorBackground,=E2=80=A6=20(#1?= =?UTF-8?q?666)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update documentation to match reality. colorPrimary, colorBackground, and borderRadius do not exist according to the schema: https://github.com/documenso/embeds/blob/280251cfddb56a6aeeb0d767098915ae7058c376/packages/react/src/css-vars.ts --- apps/documentation/pages/developers/embedding/index.mdx | 6 +++--- apps/documentation/pages/developers/embedding/preact.mdx | 6 +++--- apps/documentation/pages/developers/embedding/react.mdx | 6 +++--- apps/documentation/pages/developers/embedding/solid.mdx | 6 +++--- apps/documentation/pages/developers/embedding/svelte.mdx | 6 +++--- apps/documentation/pages/developers/embedding/vue.mdx | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/documentation/pages/developers/embedding/index.mdx b/apps/documentation/pages/developers/embedding/index.mdx index 2e93941ae..6714c65ee 100644 --- a/apps/documentation/pages/developers/embedding/index.mdx +++ b/apps/documentation/pages/developers/embedding/index.mdx @@ -52,9 +52,9 @@ Platform customers have access to advanced styling options to customize the embe ``` diff --git a/apps/documentation/pages/developers/embedding/preact.mdx b/apps/documentation/pages/developers/embedding/preact.mdx index 91176723f..3f6f579f5 100644 --- a/apps/documentation/pages/developers/embedding/preact.mdx +++ b/apps/documentation/pages/developers/embedding/preact.mdx @@ -95,9 +95,9 @@ const MyEmbeddingComponent = () => { } `; const cssVars = { - colorPrimary: '#0000FF', - colorBackground: '#F5F5F5', - borderRadius: '8px', + primary: '#0000FF', + background: '#F5F5F5', + radius: '8px', }; return ( diff --git a/apps/documentation/pages/developers/embedding/react.mdx b/apps/documentation/pages/developers/embedding/react.mdx index 05dc3a8aa..2c259ddb8 100644 --- a/apps/documentation/pages/developers/embedding/react.mdx +++ b/apps/documentation/pages/developers/embedding/react.mdx @@ -99,9 +99,9 @@ const MyEmbeddingComponent = () => { `} // CSS Variables cssVars={{ - colorPrimary: '#0000FF', - colorBackground: '#F5F5F5', - borderRadius: '8px', + primary: '#0000FF', + background: '#F5F5F5', + radius: '8px', }} // Dark Mode Control darkModeDisabled={true} diff --git a/apps/documentation/pages/developers/embedding/solid.mdx b/apps/documentation/pages/developers/embedding/solid.mdx index e19007a43..135e93ff6 100644 --- a/apps/documentation/pages/developers/embedding/solid.mdx +++ b/apps/documentation/pages/developers/embedding/solid.mdx @@ -95,9 +95,9 @@ const MyEmbeddingComponent = () => { } `; const cssVars = { - colorPrimary: '#0000FF', - colorBackground: '#F5F5F5', - borderRadius: '8px', + primary: '#0000FF', + background: '#F5F5F5', + radius: '8px', }; return ( diff --git a/apps/documentation/pages/developers/embedding/svelte.mdx b/apps/documentation/pages/developers/embedding/svelte.mdx index 46ec69c63..126b43e10 100644 --- a/apps/documentation/pages/developers/embedding/svelte.mdx +++ b/apps/documentation/pages/developers/embedding/svelte.mdx @@ -97,9 +97,9 @@ Platform customers have access to advanced styling options: } `; const cssVars = { - colorPrimary: '#0000FF', - colorBackground: '#F5F5F5', - borderRadius: '8px', + primary: '#0000FF', + background: '#F5F5F5', + radius: '8px', }; diff --git a/apps/documentation/pages/developers/embedding/vue.mdx b/apps/documentation/pages/developers/embedding/vue.mdx index 8051dbe35..dc68f979d 100644 --- a/apps/documentation/pages/developers/embedding/vue.mdx +++ b/apps/documentation/pages/developers/embedding/vue.mdx @@ -97,9 +97,9 @@ Platform customers have access to advanced styling options: } `; const cssVars = { - colorPrimary: '#0000FF', - colorBackground: '#F5F5F5', - borderRadius: '8px', + primary: '#0000FF', + background: '#F5F5F5', + radius: '8px', }; From 27b81bc807b62213e7e00ab56282eecdc088f7f9 Mon Sep 17 00:00:00 2001 From: Catalin Pit Date: Fri, 7 Mar 2025 10:25:28 +0200 Subject: [PATCH 21/27] docs: prefill fields (#1688) --- .../pages/developers/public-api/reference.mdx | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/apps/documentation/pages/developers/public-api/reference.mdx b/apps/documentation/pages/developers/public-api/reference.mdx index 906b499fd..db0232797 100644 --- a/apps/documentation/pages/developers/public-api/reference.mdx +++ b/apps/documentation/pages/developers/public-api/reference.mdx @@ -532,3 +532,93 @@ Replace the `text` value with the corresponding field type: - For the `SELECT` field it should be `select`. (check this before merge) You must pass this property at all times, even if you don't need to set any other properties. If you don't, the endpoint will throw an error. + +## Pre-fill Fields On Document Creation + +The API allows you to pre-fill fields on document creation. This is useful when you want to create a document from an existing template and pre-fill the fields with specific values. + +To pre-fill a field, you need to make a `POST` request to the `/api/v1/templates/{templateId}/generate-document` endpoint with the field information. Here's an example: + +```json +{ + "title": "my-document.pdf", + "recipients": [ + { + "id": 3, + "name": "Example User", + "email": "example@documenso.com", + "signingOrder": 1, + "role": "SIGNER" + } + ], + "prefillFields": [ + { + "id": 21, + "type": "text", + "label": "my-label", + "placeholder": "my-placeholder", + "value": "my-value" + }, + { + "id": 22, + "type": "number", + "label": "my-label", + "placeholder": "my-placeholder", + "value": "123" + }, + { + "id": 23, + "type": "checkbox", + "label": "my-label", + "placeholder": "my-placeholder", + "value": ["option-1", "option-2"] + } + ] +} +``` + +Check out the endpoint in the [API V1 documentation](https://app.documenso.com/api/v1/openapi#:~:text=/%7BtemplateId%7D/-,generate,-%2Ddocument). + +### API V2 + +For API V2, you need to make a `POST` request to the `/api/v2-beta/template/use` endpoint with the field(s) information. Here's an example: + +```json +{ + "templateId": 111, + "recipients": [ + { + "id": 3, + "name": "Example User", + "email": "example@documenso.com", + "signingOrder": 1, + "role": "SIGNER" + } + ], + "prefillFields": [ + { + "id": 21, + "type": "text", + "label": "my-label", + "placeholder": "my-placeholder", + "value": "my-value" + }, + { + "id": 22, + "type": "number", + "label": "my-label", + "placeholder": "my-placeholder", + "value": "123" + }, + { + "id": 23, + "type": "checkbox", + "label": "my-label", + "placeholder": "my-placeholder", + "value": ["option-1", "option-2"] + } + ] +} +``` + +Check out the endpoint in the [API V2 documentation](https://openapi.documenso.com/reference#tag/template/POST/template/use). From 5dfb17c2163e39db10aadaca6af7761e733d5b3b Mon Sep 17 00:00:00 2001 From: Catalin Pit Date: Sat, 8 Mar 2025 01:21:29 +0200 Subject: [PATCH 22/27] fix: optional fields in embeds (#1691) Fix optional fields blocking the signature process in embeds --- .../d/[token]/sign-direct-template.tsx | 6 ++-- .../app/embed/direct/[[...url]]/client.tsx | 36 +++++++++---------- .../src/app/embed/sign/[[...url]]/client.tsx | 16 ++++++--- .../create-document-from-direct-template.ts | 28 ++++++++++++--- packages/trpc/server/field-router/router.ts | 2 +- packages/trpc/server/field-router/schema.ts | 2 +- 6 files changed, 57 insertions(+), 33 deletions(-) diff --git a/apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx b/apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx index a0d92e595..cda99c458 100644 --- a/apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx +++ b/apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx @@ -90,7 +90,7 @@ export const SignDirectTemplateForm = ({ const tempField: DirectTemplateLocalField = { ...field, - customText: value.value, + customText: value.value ?? '', inserted: true, signedValue: value, }; @@ -101,8 +101,8 @@ export const SignDirectTemplateForm = ({ created: new Date(), recipientId: 1, fieldId: 1, - signatureImageAsBase64: value.value.startsWith('data:') ? value.value : null, - typedSignature: value.value.startsWith('data:') ? null : value.value, + signatureImageAsBase64: value.value?.startsWith('data:') ? value.value : null, + typedSignature: value.value && !value.value.startsWith('data:') ? value.value : null, } satisfies Signature; } diff --git a/apps/web/src/app/embed/direct/[[...url]]/client.tsx b/apps/web/src/app/embed/direct/[[...url]]/client.tsx index dbefcc084..7f446cdf7 100644 --- a/apps/web/src/app/embed/direct/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/direct/[[...url]]/client.tsx @@ -13,6 +13,10 @@ import { useThrottleFn } from '@documenso/lib/client-only/hooks/use-throttle-fn' import { DEFAULT_DOCUMENT_DATE_FORMAT } 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 { + isFieldUnsignedAndRequired, + isRequiredField, +} from '@documenso/lib/utils/advanced-fields-helpers'; import { validateFieldsInserted } from '@documenso/lib/utils/fields'; import type { DocumentMeta, Recipient, Signature, TemplateMeta } from '@documenso/prisma/client'; import { type DocumentData, type Field, FieldType } from '@documenso/prisma/client'; @@ -94,7 +98,7 @@ export const EmbedDirectTemplateClientPage = ({ const [localFields, setLocalFields] = useState(() => fields); const [pendingFields, _completedFields] = [ - localFields.filter((field) => !field.inserted), + localFields.filter((field) => isFieldUnsignedAndRequired(field)), localFields.filter((field) => field.inserted), ]; @@ -112,7 +116,7 @@ export const EmbedDirectTemplateClientPage = ({ const newField: DirectTemplateLocalField = structuredClone({ ...field, - customText: payload.value, + customText: payload.value ?? '', inserted: true, signedValue: payload, }); @@ -123,8 +127,10 @@ export const EmbedDirectTemplateClientPage = ({ created: new Date(), recipientId: 1, fieldId: 1, - signatureImageAsBase64: payload.value.startsWith('data:') ? payload.value : null, - typedSignature: payload.value.startsWith('data:') ? null : payload.value, + signatureImageAsBase64: + payload.value && payload.value.startsWith('data:') ? payload.value : null, + typedSignature: + payload.value && !payload.value.startsWith('data:') ? payload.value : null, } satisfies Signature; } @@ -182,7 +188,7 @@ export const EmbedDirectTemplateClientPage = ({ }; const onNextFieldClick = () => { - validateFieldsInserted(localFields); + validateFieldsInserted(pendingFields); setShowPendingFieldTooltip(true); setIsExpanded(false); @@ -194,7 +200,7 @@ export const EmbedDirectTemplateClientPage = ({ return; } - const valid = validateFieldsInserted(localFields); + const valid = validateFieldsInserted(pendingFields); if (!valid) { setShowPendingFieldTooltip(true); @@ -207,12 +213,6 @@ export const EmbedDirectTemplateClientPage = ({ directTemplateExternalId = decodeURIComponent(directTemplateExternalId); } - localFields.forEach((field) => { - if (!field.signedValue) { - throw new Error('Invalid configuration'); - } - }); - const { documentId, token: documentToken, @@ -223,13 +223,11 @@ export const EmbedDirectTemplateClientPage = ({ directRecipientName: fullName, directRecipientEmail: email, templateUpdatedAt: updatedAt, - signedFieldValues: localFields.map((field) => { - if (!field.signedValue) { - throw new Error('Invalid configuration'); - } - - return field.signedValue; - }), + signedFieldValues: localFields + .filter((field) => { + return field.signedValue && (isRequiredField(field) || field.inserted); + }) + .map((field) => field.signedValue!), }); if (window.parent) { diff --git a/apps/web/src/app/embed/sign/[[...url]]/client.tsx b/apps/web/src/app/embed/sign/[[...url]]/client.tsx index e5a556077..d60511909 100644 --- a/apps/web/src/app/embed/sign/[[...url]]/client.tsx +++ b/apps/web/src/app/embed/sign/[[...url]]/client.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useId, useLayoutEffect, useState } from 'react'; +import { useEffect, useId, useLayoutEffect, useMemo, useState } from 'react'; import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; @@ -8,6 +8,7 @@ import { LucideChevronDown, LucideChevronUp } from 'lucide-react'; import { useThrottleFn } from '@documenso/lib/client-only/hooks/use-throttle-fn'; import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer'; +import { isFieldUnsignedAndRequired } from '@documenso/lib/utils/advanced-fields-helpers'; import { validateFieldsInserted } from '@documenso/lib/utils/fields'; import type { DocumentMeta, TemplateMeta } from '@documenso/prisma/client'; import { @@ -102,19 +103,26 @@ export const EmbedSignDocumentClientPage = ({ const [throttledOnCompleteClick, isThrottled] = useThrottleFn(() => void onCompleteClick(), 500); const [pendingFields, _completedFields] = [ - fields.filter((field) => field.recipientId === recipient.id && !field.inserted), + fields.filter( + (field) => field.recipientId === recipient.id && isFieldUnsignedAndRequired(field), + ), fields.filter((field) => field.inserted), ]; const { mutateAsync: completeDocumentWithToken, isPending: isSubmitting } = trpc.recipient.completeDocumentWithToken.useMutation(); + const fieldsRequiringValidation = useMemo( + () => fields.filter(isFieldUnsignedAndRequired), + [fields], + ); + const hasSignatureField = fields.some((field) => field.type === FieldType.SIGNATURE); const assistantSignersId = useId(); const onNextFieldClick = () => { - validateFieldsInserted(fields); + validateFieldsInserted(fieldsRequiringValidation); setShowPendingFieldTooltip(true); setIsExpanded(false); @@ -126,7 +134,7 @@ export const EmbedSignDocumentClientPage = ({ return; } - const valid = validateFieldsInserted(fields); + const valid = validateFieldsInserted(fieldsRequiringValidation); if (!valid) { setShowPendingFieldTooltip(true); diff --git a/packages/lib/server-only/template/create-document-from-direct-template.ts b/packages/lib/server-only/template/create-document-from-direct-template.ts index b3e187688..9c74d0d53 100644 --- a/packages/lib/server-only/template/create-document-from-direct-template.ts +++ b/packages/lib/server-only/template/create-document-from-direct-template.ts @@ -37,6 +37,7 @@ import { mapDocumentToWebhookDocumentPayload, } from '../../types/webhook-payload'; import type { ApiRequestMetadata } from '../../universal/extract-request-metadata'; +import { isRequiredField } from '../../utils/advanced-fields-helpers'; import type { CreateDocumentAuditLogDataResponse } from '../../utils/document-audit-logs'; import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { @@ -175,20 +176,28 @@ export const createDocumentFromDirectTemplate = async ({ const metaSigningOrder = template.templateMeta?.signingOrder || DocumentSigningOrder.PARALLEL; // Associate, validate and map to a query every direct template recipient field with the provided fields. + // Only process fields that are either required or have been signed by the user + const fieldsToProcess = directTemplateRecipient.fields.filter((templateField) => { + const signedFieldValue = signedFieldValues.find((value) => value.fieldId === templateField.id); + + // Include if it's required or has a signed value + return isRequiredField(templateField) || signedFieldValue !== undefined; + }); + const createDirectRecipientFieldArgs = await Promise.all( - directTemplateRecipient.fields.map(async (templateField) => { + fieldsToProcess.map(async (templateField) => { const signedFieldValue = signedFieldValues.find( (value) => value.fieldId === templateField.id, ); - if (!signedFieldValue) { + if (isRequiredField(templateField) && !signedFieldValue) { throw new AppError(AppErrorCode.INVALID_BODY, { message: 'Invalid, missing or changed fields', }); } if (templateField.type === FieldType.NAME && directRecipientName === undefined) { - directRecipientName === signedFieldValue.value; + directRecipientName === signedFieldValue?.value; } const derivedRecipientActionAuth = await validateFieldAuth({ @@ -199,9 +208,18 @@ export const createDocumentFromDirectTemplate = async ({ }, field: templateField, userId: user?.id, - authOptions: signedFieldValue.authOptions, + authOptions: signedFieldValue?.authOptions, }); + if (!signedFieldValue) { + return { + templateField, + customText: '', + derivedRecipientActionAuth, + signature: null, + }; + } + const { value, isBase64 } = signedFieldValue; const isSignatureField = @@ -379,7 +397,7 @@ export const createDocumentFromDirectTemplate = async ({ positionY: templateField.positionY, width: templateField.width, height: templateField.height, - customText, + customText: customText ?? '', inserted: true, fieldMeta: templateField.fieldMeta || Prisma.JsonNull, })), diff --git a/packages/trpc/server/field-router/router.ts b/packages/trpc/server/field-router/router.ts index df17aef88..0ab4d6b21 100644 --- a/packages/trpc/server/field-router/router.ts +++ b/packages/trpc/server/field-router/router.ts @@ -452,7 +452,7 @@ export const fieldRouter = router({ return await signFieldWithToken({ token, fieldId, - value, + value: value ?? '', isBase64, userId: ctx.user?.id, authOptions, diff --git a/packages/trpc/server/field-router/schema.ts b/packages/trpc/server/field-router/schema.ts index 373d4a693..9297d6234 100644 --- a/packages/trpc/server/field-router/schema.ts +++ b/packages/trpc/server/field-router/schema.ts @@ -153,7 +153,7 @@ export const ZSetFieldsForTemplateResponseSchema = z.object({ export const ZSignFieldWithTokenMutationSchema = z.object({ token: z.string(), fieldId: z.number(), - value: z.string().trim(), + value: z.string().trim().optional(), isBase64: z.boolean().optional(), authOptions: ZRecipientActionAuthSchema.optional(), }); From e95e13021d7f17139b8d848ea6761e5479b25560 Mon Sep 17 00:00:00 2001 From: Mythie Date: Sat, 8 Mar 2025 10:28:53 +1100 Subject: [PATCH 23/27] v1.9.1-rc.9 --- apps/web/package.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 2470a9712..cb2385901 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@documenso/web", - "version": "1.9.1-rc.8", + "version": "1.9.1-rc.9", "private": true, "license": "AGPL-3.0", "scripts": { diff --git a/package-lock.json b/package-lock.json index 206eb8a19..ab992cde0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@documenso/root", - "version": "1.9.1-rc.8", + "version": "1.9.1-rc.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@documenso/root", - "version": "1.9.1-rc.8", + "version": "1.9.1-rc.9", "workspaces": [ "apps/*", "packages/*" @@ -106,7 +106,7 @@ }, "apps/web": { "name": "@documenso/web", - "version": "1.9.1-rc.8", + "version": "1.9.1-rc.9", "license": "AGPL-3.0", "dependencies": { "@documenso/api": "*", diff --git a/package.json b/package.json index eb7b93814..b66a88114 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.9.1-rc.8", + "version": "1.9.1-rc.9", "scripts": { "build": "turbo run build", "build:web": "turbo run build --filter=@documenso/web", From db90e1a34a739f148c710e5f169f4d5a8bc00bc0 Mon Sep 17 00:00:00 2001 From: Tom <64775524+tom-rh@users.noreply.github.com> Date: Wed, 12 Mar 2025 06:16:44 +0100 Subject: [PATCH 24/27] chore: update French translations (#1679) --- packages/lib/translations/fr/web.po | 82 ++++++++++++++--------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/packages/lib/translations/fr/web.po b/packages/lib/translations/fr/web.po index 47f1d6167..04417f695 100644 --- a/packages/lib/translations/fr/web.po +++ b/packages/lib/translations/fr/web.po @@ -360,7 +360,7 @@ msgstr "<0>{teamName} a demandé à utiliser votre adresse e-mail pour leur #: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:463 msgid "<0>Click to upload or drag and drop" -msgstr "<0>Cliquez pour télécharger ou faites glisser et déposez" +msgstr "<0>Cliquez pour importer ou faites glisser et déposez" #: packages/ui/primitives/template-flow/add-template-settings.tsx:287 msgid "<0>Email - The recipient will be emailed the document to sign, approve, etc." @@ -1009,7 +1009,7 @@ msgstr "Une erreur est survenue lors de la mise à jour de votre profil." #: apps/web/src/app/(dashboard)/documents/upload-document.tsx:108 msgid "An error occurred while uploading your document." -msgstr "Une erreur est survenue lors du téléchargement de votre document." +msgstr "Une erreur est survenue lors de l'importation de votre document." #: apps/web/src/app/(dashboard)/admin/documents/[id]/super-delete-document-dialog.tsx:58 #: apps/web/src/app/(dashboard)/admin/site-settings/banner-form.tsx:81 @@ -1430,7 +1430,7 @@ msgstr "Cliquez ici pour réessayer" #: apps/web/src/components/(teams)/dialogs/invite-team-member-dialog.tsx:392 msgid "Click here to upload" -msgstr "Cliquez ici pour télécharger" +msgstr "Cliquez ici pour importer" #: apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx:52 #: apps/web/src/components/(dashboard)/avatar/avatar-with-recipient.tsx:65 @@ -1597,11 +1597,11 @@ msgstr "Continuer vers la connexion" #: apps/web/src/app/(teams)/t/[teamUrl]/settings/preferences/document-preferences.tsx:185 msgid "Controls the default language of an uploaded document. This will be used as the language in email communications with the recipients." -msgstr "Contrôle la langue par défaut d'un document téléchargé. Cela sera utilisé comme langue dans les communications par e-mail avec les destinataires." +msgstr "Contrôle la langue par défaut d'un document importé. Cela sera utilisé comme langue dans les communications par e-mail avec les destinataires." #: apps/web/src/app/(teams)/t/[teamUrl]/settings/preferences/document-preferences.tsx:153 msgid "Controls the default visibility of an uploaded document." -msgstr "Contrôle la visibilité par défaut d'un document téléchargé." +msgstr "Contrôle la visibilité par défaut d'un document importé." #: apps/web/src/app/(teams)/t/[teamUrl]/settings/preferences/document-preferences.tsx:232 msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead." @@ -2255,11 +2255,11 @@ msgstr "Document mis à jour" #: apps/web/src/app/(dashboard)/documents/upload-document.tsx:55 msgid "Document upload disabled due to unpaid invoices" -msgstr "Téléchargement du document désactivé en raison de factures impayées" +msgstr "Importation de documents désactivé en raison de factures impayées" #: apps/web/src/app/(dashboard)/documents/upload-document.tsx:85 msgid "Document uploaded" -msgstr "Document téléchargé" +msgstr "Document importé" #: apps/web/src/app/(signing)/sign/[token]/complete/page.tsx:131 msgid "Document Viewed" @@ -2721,7 +2721,7 @@ msgstr "Pour toute question concernant cette divulgation, les signatures électr #: apps/web/src/components/templates/template-bulk-send-dialog.tsx:147 msgid "For each recipient, provide their email (required) and name (optional) in separate columns. Download the template CSV below for the correct format." -msgstr "Pour chaque destinataire, fournissez leur email (obligatoire) et leur nom (facultatif) dans des colonnes séparées. Téléchargez le modèle CSV ci-dessous pour le format correct." +msgstr "Pour chaque destinataire, fournissez son e-mail (obligatoire) et son nom (facultatif) dans des colonnes séparées. Téléchargez le modèle CSV ci-dessous pour obtenir le format requis." #: packages/lib/server-only/auth/send-forgot-password.ts:61 msgid "Forgot Password?" @@ -2770,7 +2770,7 @@ msgstr "Authentification d'action de destinataire globale" #: apps/web/src/components/partials/not-found.tsx:67 #: packages/ui/primitives/document-flow/document-flow-root.tsx:142 msgid "Go Back" -msgstr "Retourner" +msgstr "Retour" #: apps/web/src/app/(unauthenticated)/verify-email/[token]/client.tsx:48 #: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:73 @@ -2834,7 +2834,7 @@ msgstr "Salut, je suis Timur" #: packages/email/templates/bulk-send-complete.tsx:36 msgid "Hi {userName}," -msgstr "Bonjour, {userName}," +msgstr "Bonjour {userName}," #: packages/email/templates/reset-password.tsx:56 msgid "Hi, {userName} <0>({userEmail})" @@ -2856,7 +2856,7 @@ msgstr "Je suis un signataire de ce document" #: packages/lib/constants/recipient-roles.ts:47 msgid "I am a viewer of this document" -msgstr "Je suis un visualiseur de ce document" +msgstr "Je suis un lecteur de ce document" #: packages/lib/constants/recipient-roles.ts:45 msgid "I am an approver of this document" @@ -3234,11 +3234,11 @@ msgstr "MAU (document terminé)" #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:209 msgid "Max" -msgstr "" +msgstr "Maximum" #: apps/web/src/components/templates/template-bulk-send-dialog.tsx:227 msgid "Maximum file size: 4MB. Maximum 100 rows per upload. Blank values will use template defaults." -msgstr "Taille maximale du fichier : 4 Mo. Maximum de 100 lignes par téléversement. Les valeurs vides utiliseront les valeurs par défaut du modèle." +msgstr "Taille maximale du fichier : 4 Mo. Maximum de 100 lignes par importation. Les valeurs vides utiliseront les valeurs par défaut du modèle." #: packages/lib/constants/teams.ts:12 msgid "Member" @@ -5032,7 +5032,7 @@ msgstr "Modèle supprimé" #: apps/web/src/app/(dashboard)/templates/new-template-dialog.tsx:66 msgid "Template document uploaded" -msgstr "Document modèle téléchargé" +msgstr "Document modèle importé" #: apps/web/src/app/(dashboard)/templates/duplicate-template-dialog.tsx:41 msgid "Template duplicated" @@ -5097,11 +5097,11 @@ msgstr "Couleur du texte" #: apps/web/src/app/(unauthenticated)/articles/signature-disclosure/page.tsx:24 msgid "Thank you for using Documenso to perform your electronic document signing. The purpose of this disclosure is to inform you about the process, legality, and your rights regarding the use of electronic signatures on our platform. By opting to use an electronic signature, you are agreeing to the terms and conditions outlined below." -msgstr "Merci d'utiliser Documenso pour signer vos documents électroniquement. L'objectif de cette divulgation est de vous informer sur le processus, la légalité et vos droits concernant l'utilisation des signatures électroniques sur notre plateforme. En choisissant d'utiliser une signature électronique, vous acceptez les termes et conditions énoncés ci-dessous." +msgstr "Merci d'utiliser Documenso pour signer vos documents électroniquement. L'objectif de cette clause est de vous informer sur le processus, la légalité et vos droits concernant l'utilisation de la signature électronique sur notre plateforme. En choisissant d'utiliser un sytème de signature électronique, vous acceptez les termes et conditions exposés ci-dessous." #: packages/email/template-components/template-forgot-password.tsx:25 msgid "That's okay, it happens! Click the button below to reset your password." -msgstr "C'est d'accord, cela arrive ! Cliquez sur le bouton ci-dessous pour réinitialiser votre mot de passe." +msgstr "Ce n'est pas grave, cela arrive ! Cliquez sur le bouton ci-dessous pour réinitialiser votre mot de passe." #: apps/web/src/app/(dashboard)/admin/users/[id]/delete-user-dialog.tsx:52 msgid "The account has been deleted successfully." @@ -5331,7 +5331,7 @@ msgstr "Le webhook a été créé avec succès." #: apps/web/src/app/(dashboard)/documents/empty-state.tsx:25 msgid "There are no active drafts at the current moment. You can upload a document to start drafting." -msgstr "Il n'y a pas de brouillons actifs pour le moment. Vous pouvez télécharger un document pour commencer à rédiger." +msgstr "Il n'y a pas de brouillons actifs pour le moment. Vous pouvez importer un document pour commencer un brouillon." #: apps/web/src/app/(dashboard)/documents/empty-state.tsx:20 msgid "There are no completed documents yet. Documents that you have created or received will appear here once completed." @@ -5899,27 +5899,27 @@ msgstr "Améliorer" #: apps/web/src/components/templates/template-bulk-send-dialog.tsx:132 msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details." -msgstr "Téléchargez un fichier CSV pour créer plusieurs documents à partir de ce modèle. Chaque ligne représente un document avec ses détails de destinataire." +msgstr "Importer un fichier CSV pour créer plusieurs documents à partir de ce modèle. Chaque ligne représente un document avec les coordonnées de son destinataire." #: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:426 msgid "Upload a custom document to use instead of the template's default document" -msgstr "Téléchargez un document personnalisé à utiliser à la place du document par défaut du modèle" +msgstr "Importer un document personnalisé à utiliser à la place du modèle par défaut" #: apps/web/src/components/templates/template-bulk-send-dialog.tsx:267 msgid "Upload and Process" -msgstr "Télécharger et traiter" +msgstr "Importer et traiter" #: apps/web/src/components/forms/avatar-image.tsx:179 msgid "Upload Avatar" -msgstr "Télécharger un avatar" +msgstr "Importer un avatar" #: apps/web/src/components/templates/template-bulk-send-dialog.tsx:198 msgid "Upload CSV" -msgstr "Télécharger le CSV" +msgstr "Importer le CSV" #: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:419 msgid "Upload custom document" -msgstr "Télécharger un document personnalisé" +msgstr "Importer un document personnalisé" #: packages/ui/primitives/signature-pad/signature-pad.tsx:529 msgid "Upload Signature" @@ -5927,7 +5927,7 @@ msgstr "Importer une signature" #: packages/ui/primitives/document-dropzone.tsx:70 msgid "Upload Template Document" -msgstr "Télécharger le document modèle" +msgstr "Importer le document modèle" #: apps/web/src/app/(teams)/t/[teamUrl]/settings/preferences/branding-preferences.tsx:256 msgid "Upload your brand logo (max 5MB, JPG, PNG, or WebP)" @@ -5940,15 +5940,15 @@ msgstr "Téléversé par" #: apps/web/src/components/forms/avatar-image.tsx:91 msgid "Uploaded file is too large" -msgstr "Le fichier téléchargé est trop volumineux" +msgstr "Le fichier importé est trop volumineux" #: apps/web/src/components/forms/avatar-image.tsx:92 msgid "Uploaded file is too small" -msgstr "Le fichier téléchargé est trop petit" +msgstr "Le fichier importé est trop petit" #: apps/web/src/components/forms/avatar-image.tsx:93 msgid "Uploaded file not an allowed file type" -msgstr "Le fichier téléchargé n'est pas un type de fichier autorisé" +msgstr "Le fichier importé n'est pas un type de fichier autorisé" #: apps/web/src/app/(dashboard)/templates/[id]/template-page-view.tsx:175 msgid "Use" @@ -6040,7 +6040,7 @@ msgstr "Vérifiez votre adresse e-mail pour débloquer toutes les fonctionnalit #: apps/web/src/app/(dashboard)/documents/upload-document.tsx:60 msgid "Verify your email to upload documents." -msgstr "Vérifiez votre e-mail pour télécharger des documents." +msgstr "Vérifiez votre e-mail pour importer des documents." #: packages/email/templates/confirm-team-email.tsx:71 msgid "Verify your team email address" @@ -6137,11 +6137,11 @@ msgstr "Vu" #: packages/lib/constants/recipient-roles.ts:32 msgid "Viewer" -msgstr "Visiteur" +msgstr "Lecteur" #: packages/lib/constants/recipient-roles.ts:33 msgid "Viewers" -msgstr "Spectateurs" +msgstr "Lecteurs" #: packages/lib/constants/recipient-roles.ts:31 msgid "Viewing" @@ -6526,7 +6526,7 @@ msgstr "Vous êtes sur le point d'envoyer ce document aux destinataires. Êtes-v #: apps/web/src/app/(dashboard)/settings/billing/page.tsx:80 msgid "You are currently on the <0>Free Plan." -msgstr "Vous êtes actuellement sur le <0>Plan Gratuit." +msgstr "Vous êtes actuellement sur l'<0>Abonnement Gratuit." #: apps/web/src/components/(teams)/dialogs/update-team-member-dialog.tsx:148 msgid "You are currently updating <0>{teamMemberName}." @@ -6610,11 +6610,11 @@ msgstr "Vous ne pouvez pas modifier un membre de l'équipe qui a un rôle plus #: packages/ui/primitives/document-dropzone.tsx:43 msgid "You cannot upload documents at this time." -msgstr "Vous ne pouvez pas télécharger de documents pour le moment." +msgstr "Vous ne pouvez pas importer de documents pour le moment." #: apps/web/src/app/(dashboard)/documents/upload-document.tsx:103 msgid "You cannot upload encrypted PDFs" -msgstr "Vous ne pouvez pas télécharger de PDF cryptés" +msgstr "Vous ne pouvez pas importer de PDF cryptés" #: apps/web/src/app/(dashboard)/settings/billing/billing-portal-button.tsx:46 msgid "You do not currently have a customer record, this should not happen. Please contact support for assistance." @@ -6678,11 +6678,11 @@ msgstr "Vous n'avez pas encore de webhooks. Vos webhooks seront affichés ici un #: apps/web/src/app/(dashboard)/templates/empty-state.tsx:15 msgid "You have not yet created any templates. To create a template please upload one." -msgstr "Vous n'avez pas encore créé de modèles. Pour créer un modèle, veuillez en télécharger un." +msgstr "Vous n'avez pas encore créé de modèles. Pour créer un modèle, veuillez en importer un." #: apps/web/src/app/(dashboard)/documents/empty-state.tsx:30 msgid "You have not yet created or received any documents. To create a document please upload one." -msgstr "Vous n'avez pas encore créé ou reçu de documents. Pour créer un document, veuillez en télécharger un." +msgstr "Vous n'avez pas encore créé ou reçu de documents. Pour créer un document, veuillez en importer un." #: apps/web/src/app/(dashboard)/templates/template-direct-link-dialog.tsx:229 msgid "You have reached the maximum limit of {0} direct templates. <0>Upgrade your account to continue!" @@ -6690,7 +6690,7 @@ msgstr "Vous avez atteint la limite maximale de {0} modèles directs. <0>Mettez #: apps/web/src/app/(dashboard)/documents/upload-document.tsx:106 msgid "You have reached your document limit for this month. Please upgrade your plan." -msgstr "Vous avez atteint votre limite de documents pour ce mois. Veuillez passer à un plan supérieur." +msgstr "Vous avez atteint votre limite de documents pour ce mois. Veuillez passer à l'abonnement supérieur." #: apps/web/src/app/(dashboard)/documents/upload-document.tsx:56 #: packages/ui/primitives/document-dropzone.tsx:69 @@ -6811,11 +6811,11 @@ msgstr "Votre envoi groupé a été initié. Vous recevrez une notification par #: packages/email/templates/bulk-send-complete.tsx:40 msgid "Your bulk send operation for template \"{templateName}\" has completed." -msgstr "Votre opération d'envoi groupé pour le modèle \"{templateName}\" est terminée." +msgstr "Votre envoi groupé pour le modèle \"{templateName}\" est terminé." #: apps/web/src/app/(dashboard)/settings/billing/page.tsx:125 msgid "Your current plan is past due. Please update your payment information." -msgstr "Votre plan actuel est en retard. Veuillez mettre à jour vos informations de paiement." +msgstr "Votre abonnement actuel est arrivé à échéance. Veuillez mettre à jour vos informations de paiement." #: apps/web/src/components/templates/manage-public-template-dialog.tsx:249 msgid "Your direct signing templates" @@ -6823,7 +6823,7 @@ msgstr "Vos modèles de signature directe" #: apps/web/src/app/(dashboard)/documents/upload-document.tsx:123 msgid "Your document failed to upload." -msgstr "Votre document a échoué à se télécharger." +msgstr "L'importation de votre document a échoué." #: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:169 msgid "Your document has been created from the template successfully." @@ -6847,11 +6847,11 @@ msgstr "Votre document a été dupliqué avec succès." #: apps/web/src/app/(dashboard)/documents/upload-document.tsx:86 msgid "Your document has been uploaded successfully." -msgstr "Votre document a été téléchargé avec succès." +msgstr "Votre document a été importé avec succès." #: apps/web/src/app/(dashboard)/templates/new-template-dialog.tsx:68 msgid "Your document has been uploaded successfully. You will be redirected to the template page." -msgstr "Votre document a été téléchargé avec succès. Vous serez redirigé vers la page de modèle." +msgstr "Votre document a été importé avec succès. Vous serez redirigé vers la page de modèle." #: apps/web/src/app/(teams)/t/[teamUrl]/settings/preferences/document-preferences.tsx:104 msgid "Your document preferences have been updated" From 6f39e89d303ad4cf410202c41167f557922927ca Mon Sep 17 00:00:00 2001 From: Jenil Savani Date: Wed, 12 Mar 2025 10:59:48 +0530 Subject: [PATCH 25/27] fix: improve layout and truncate document information in logs page (#1656) --- .../[id]/logs/document-logs-page-view.tsx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) 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 877dad583..2020536e0 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 @@ -119,7 +119,7 @@ export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageVie Document -
+

{document.title}

- +
+
-
+
+ -
- - - + +
@@ -154,7 +154,7 @@ export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageVie {documentInformation.map((info, i) => (

{_(info.description)}

-

{info.value}

+

{info.value}

))} From 8890c5bee6f730fc1b672393f9087eda6e0e1480 Mon Sep 17 00:00:00 2001 From: Ephraim Duncan <55143799+ephraimduncan@users.noreply.github.com> Date: Wed, 12 Mar 2025 05:43:52 +0000 Subject: [PATCH 26/27] fix: signing field disabled when pointer is out of canvas (#1652) --- packages/ui/primitives/signature-pad/signature-pad.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/ui/primitives/signature-pad/signature-pad.tsx b/packages/ui/primitives/signature-pad/signature-pad.tsx index bb84bfd5c..272d02cb7 100644 --- a/packages/ui/primitives/signature-pad/signature-pad.tsx +++ b/packages/ui/primitives/signature-pad/signature-pad.tsx @@ -282,7 +282,11 @@ export const SignaturePad = ({ event.preventDefault(); } - onMouseUp(event, false); + if (isPressed) { + onMouseUp(event, true); + } else { + onMouseUp(event, false); + } }; const onClearClick = () => { From ad01e5af9419445e3617a00288591b9ea5365794 Mon Sep 17 00:00:00 2001 From: Jenil Savani Date: Thu, 13 Mar 2025 05:05:24 +0530 Subject: [PATCH 27/27] fix: adjust desktop nav search button width and spacing (#1699) --- apps/web/src/components/(dashboard)/layout/desktop-nav.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/(dashboard)/layout/desktop-nav.tsx b/apps/web/src/components/(dashboard)/layout/desktop-nav.tsx index 0eea958b7..dced962f4 100644 --- a/apps/web/src/components/(dashboard)/layout/desktop-nav.tsx +++ b/apps/web/src/components/(dashboard)/layout/desktop-nav.tsx @@ -73,7 +73,7 @@ export const DesktopNav = ({ className, setIsCommandMenuOpen, ...props }: Deskto