From a902bec96db93b1b0fbbfdb95e77812a2663afe2 Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Tue, 7 Oct 2025 17:06:28 +1100 Subject: [PATCH 1/6] fix: use select account prompt for sso oidc (#2065) Use the `select_account` prompt for SSO OIDC to avoid constantly asking for credentials to be entered with a client has an existing session with the SSO provider. --- .../server/lib/utils/handle-oauth-authorize-url.ts | 11 ++++++++--- packages/auth/server/routes/oauth.ts | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/auth/server/lib/utils/handle-oauth-authorize-url.ts b/packages/auth/server/lib/utils/handle-oauth-authorize-url.ts index 6c2df1a07..d0fa72d1d 100644 --- a/packages/auth/server/lib/utils/handle-oauth-authorize-url.ts +++ b/packages/auth/server/lib/utils/handle-oauth-authorize-url.ts @@ -23,12 +23,17 @@ type HandleOAuthAuthorizeUrlOptions = { * Optional redirect path to redirect the user somewhere on the app after authorization. */ redirectPath?: string; + + /** + * Optional prompt to pass to the authorization endpoint. + */ + prompt?: 'login' | 'consent' | 'select_account'; }; const oauthCookieMaxAge = 60 * 10; // 10 minutes. export const handleOAuthAuthorizeUrl = async (options: HandleOAuthAuthorizeUrlOptions) => { - const { c, clientOptions, redirectPath } = options; + const { c, clientOptions, redirectPath, prompt = 'login' } = options; if (!clientOptions.clientId || !clientOptions.clientSecret) { throw new AppError(AppErrorCode.NOT_SETUP); @@ -57,8 +62,8 @@ export const handleOAuthAuthorizeUrl = async (options: HandleOAuthAuthorizeUrlOp scopes, ); - // Allow user to select account during login. - url.searchParams.append('prompt', 'login'); + // Pass the prompt to the authorization endpoint. + url.searchParams.append('prompt', prompt); setCookie(c, `${clientOptions.id}_oauth_state`, state, { ...sessionCookieOptions, diff --git a/packages/auth/server/routes/oauth.ts b/packages/auth/server/routes/oauth.ts index 4a8346ba9..9ac0f89fc 100644 --- a/packages/auth/server/routes/oauth.ts +++ b/packages/auth/server/routes/oauth.ts @@ -50,5 +50,6 @@ export const oauthRoute = new Hono() return await handleOAuthAuthorizeUrl({ c, clientOptions, + prompt: 'select_account', }); }); From 26f65dbdd7ace6866e8075c862b05f88545284eb Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Tue, 7 Oct 2025 17:07:11 +1100 Subject: [PATCH 2/6] v1.12.9 --- apps/remix/package.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/remix/package.json b/apps/remix/package.json index 45798c977..90b46e6d5 100644 --- a/apps/remix/package.json +++ b/apps/remix/package.json @@ -101,5 +101,5 @@ "vite-plugin-babel-macros": "^1.0.6", "vite-tsconfig-paths": "^5.1.4" }, - "version": "1.12.8" + "version": "1.12.9" } diff --git a/package-lock.json b/package-lock.json index 44b76a80c..31bf07f1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@documenso/root", - "version": "1.12.8", + "version": "1.12.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@documenso/root", - "version": "1.12.8", + "version": "1.12.9", "workspaces": [ "apps/*", "packages/*" @@ -89,7 +89,7 @@ }, "apps/remix": { "name": "@documenso/remix", - "version": "1.12.8", + "version": "1.12.9", "dependencies": { "@documenso/api": "*", "@documenso/assets": "*", diff --git a/package.json b/package.json index ea07e5c79..d7d688970 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.12.8", + "version": "1.12.9", "scripts": { "build": "turbo run build", "dev": "turbo run dev --filter=@documenso/remix", From 86e89e137e993fc85f776b3524d8641a0f3e0679 Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Thu, 9 Oct 2025 15:11:43 +1100 Subject: [PATCH 3/6] fix: bump search limit and path formatting (#2069) --- .../document/search-documents-with-keyword.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 fc9fed866..600d07df9 100644 --- a/packages/lib/server-only/document/search-documents-with-keyword.ts +++ b/packages/lib/server-only/document/search-documents-with-keyword.ts @@ -1,6 +1,5 @@ -import { DocumentStatus } from '@prisma/client'; import type { Document, Recipient, User } from '@prisma/client'; -import { DocumentVisibility, TeamMemberRole } from '@prisma/client'; +import { DocumentStatus, DocumentVisibility, TeamMemberRole } from '@prisma/client'; import { match } from 'ts-pattern'; import { @@ -19,7 +18,7 @@ export type SearchDocumentsWithKeywordOptions = { export const searchDocumentsWithKeyword = async ({ query, userId, - limit = 5, + limit = 20, }: SearchDocumentsWithKeywordOptions) => { const user = await prisma.user.findFirstOrThrow({ where: { @@ -122,6 +121,7 @@ export const searchDocumentsWithKeyword = async ({ }, }, }, + distinct: ['id'], orderBy: { createdAt: 'desc', }, @@ -129,6 +129,7 @@ export const searchDocumentsWithKeyword = async ({ }); const isOwner = (document: Document, user: User) => document.userId === user.id; + const getSigningLink = (recipients: Recipient[], user: User) => `/sign/${recipients.find((r) => r.email === user.email)?.token}`; @@ -164,7 +165,7 @@ export const searchDocumentsWithKeyword = async ({ if (isOwner(document, user)) { documentPath = `${formatDocumentsPath(document.team?.url)}/${document.id}`; - } else if (document.teamId && document.team) { + } else if (document.teamId && document.team.teamGroups.length > 0) { documentPath = `${formatDocumentsPath(document.team.url)}/${document.id}`; } else { documentPath = getSigningLink(recipients, user); From 7b17156e567ac921cf5908371d3cafb77ed2acf9 Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Thu, 9 Oct 2025 15:32:35 +1100 Subject: [PATCH 4/6] v1.12.10 --- apps/remix/package.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/remix/package.json b/apps/remix/package.json index 90b46e6d5..832bbde71 100644 --- a/apps/remix/package.json +++ b/apps/remix/package.json @@ -101,5 +101,5 @@ "vite-plugin-babel-macros": "^1.0.6", "vite-tsconfig-paths": "^5.1.4" }, - "version": "1.12.9" + "version": "1.12.10" } diff --git a/package-lock.json b/package-lock.json index 31bf07f1d..f40167d8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@documenso/root", - "version": "1.12.9", + "version": "1.12.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@documenso/root", - "version": "1.12.9", + "version": "1.12.10", "workspaces": [ "apps/*", "packages/*" @@ -89,7 +89,7 @@ }, "apps/remix": { "name": "@documenso/remix", - "version": "1.12.9", + "version": "1.12.10", "dependencies": { "@documenso/api": "*", "@documenso/assets": "*", diff --git a/package.json b/package.json index d7d688970..f8f8ba379 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "1.12.9", + "version": "1.12.10", "scripts": { "build": "turbo run build", "dev": "turbo run dev --filter=@documenso/remix", From 7f09ba72f45af3ae361774c077f1be082c1fb0fb Mon Sep 17 00:00:00 2001 From: David Nguyen Date: Tue, 14 Oct 2025 21:56:36 +1100 Subject: [PATCH 5/6] feat: add envelopes (#2025) This PR is handles the changes required to support envelopes. The new envelope editor/signing page will be hidden during release. The core changes here is to migrate the documents and templates model to a centralized envelopes model. Even though Documents and Templates are removed, from the user perspective they will still exist as we remap envelopes to documents and templates. --- .../growth/get-monthly-completed-document.ts | 11 +- .../dialogs/admin-document-delete-dialog.tsx | 7 +- .../dialogs/document-duplicate-dialog.tsx | 4 +- .../document-move-to-folder-dialog.tsx | 8 +- .../dialogs/document-resend-dialog.tsx | 10 +- .../dialogs/envelope-distribute-dialog.tsx | 449 +++ .../dialogs/envelope-duplicate-dialog.tsx | 113 + .../dialogs/envelope-item-delete-dialog.tsx | 134 + .../dialogs/envelope-redistribute-dialog.tsx | 187 + .../public-profile-template-manage-dialog.tsx | 6 +- .../dialogs/sign-field-dropdown-dialog.tsx | 117 + .../dialogs/sign-field-email-dialog.tsx | 91 + .../dialogs/sign-field-initials-dialog.tsx | 97 + .../dialogs/sign-field-name-dialog.tsx | 93 + .../dialogs/sign-field-number-dialog.tsx | 144 + .../dialogs/sign-field-signature-dialog.tsx | 76 + .../dialogs/sign-field-text-dialog.tsx | 120 + .../dialogs/template-create-dialog.tsx | 6 +- .../template-direct-link-dialog-wrapper.tsx | 46 - .../dialogs/template-direct-link-dialog.tsx | 590 +-- .../template-move-to-folder-dialog.tsx | 8 +- .../dialogs/template-use-dialog.tsx | 1 + .../configure-document-view.types.ts | 4 +- .../embed/authoring/configure-fields-view.tsx | 1 + .../embed-direct-template-client-page.tsx | 4 +- .../embed/embed-document-fields.tsx | 4 +- .../embed/embed-document-signing-page.tsx | 12 +- .../multi-sign-document-signing-view.tsx | 2 +- .../forms/document-preferences-form.tsx | 6 +- .../app/components/forms/editor/constants.ts | 31 + .../editor/editor-field-checkbox-form.tsx | 293 ++ .../forms/editor/editor-field-date-form.tsx | 75 + .../editor/editor-field-dropdown-form.tsx | 240 ++ .../forms/editor/editor-field-email-form.tsx | 75 + .../editor-field-generic-field-forms.tsx | 222 ++ .../editor/editor-field-initials-form.tsx | 75 + .../forms/editor/editor-field-name-form.tsx | 75 + .../forms/editor/editor-field-number-form.tsx | 277 ++ .../forms/editor/editor-field-radio-form.tsx | 190 + .../forms/editor/editor-field-text-form.tsx | 191 + .../app/components/general/app-header.tsx | 2 +- .../direct-template-signing-form.tsx | 2 +- .../document-signing-auth-provider.tsx | 16 +- ....tsx => document-signing-page-view-v1.tsx} | 8 +- .../document-signing-page-view-v2.tsx | 174 + .../document-signing-recipient-provider.tsx | 10 +- .../document-signing-reject-dialog.tsx | 7 +- .../envelope-signing-provider.tsx | 290 ++ .../document/document-certificate-qr-view.tsx | 47 +- .../document/document-drop-zone-wrapper.tsx | 2 +- .../general/document/document-edit-form.tsx | 12 +- .../document/document-page-view-button.tsx | 39 +- .../document/document-page-view-dropdown.tsx | 59 +- .../document-page-view-information.tsx | 17 +- .../document-page-view-recipients.tsx | 24 +- ...-upload.tsx => document-upload-button.tsx} | 10 +- .../document/envelope-upload-button.tsx | 198 + .../envelope-editor-fields-drag-drop.tsx | 316 ++ .../envelope-editor-fields-page-renderer.tsx | 686 ++++ .../envelope-editor-header.tsx | 183 + .../envelope-editor-page-fields.tsx | 258 ++ .../envelope-editor-page-preview-renderer.tsx | 176 + .../envelope-editor-page-preview.tsx | 158 + .../envelope-editor-page-upload.tsx | 348 ++ .../envelope-editor-recipient-form.tsx | 941 +++++ .../envelope-editor-settings-dialog.tsx | 832 ++++ .../envelope-editor-title-input.tsx | 85 + .../envelope-editor/envelope-editor.tsx | 356 ++ .../envelope-file-selector.tsx | 87 + .../envelope-generic-page-renderer.tsx | 181 + .../envelope-signing/envelope-signer-form.tsx | 59 + .../envelope-signer-header.tsx | 131 + .../envelope-signer-page-renderer.tsx | 417 ++ .../components/general/folder/folder-grid.tsx | 7 +- .../share-document-download-button.tsx | 1 + .../template/template-drop-zone-wrapper.tsx | 2 +- .../general/template/template-edit-form.tsx | 13 +- .../template-page-view-information.tsx | 7 +- .../template-page-view-recent-activity.tsx | 2 +- .../template-page-view-recipients.tsx | 14 +- .../tables/admin-dashboard-users-table.tsx | 11 +- .../tables/admin-document-jobs-table.tsx | 136 + .../tables/documents-table-action-button.tsx | 2 +- .../documents-table-action-dropdown.tsx | 53 +- .../tables/documents-table-title.tsx | 2 +- .../app/components/tables/documents-table.tsx | 2 +- ...ettings-public-profile-templates-table.tsx | 3 +- .../templates-table-action-dropdown.tsx | 37 +- .../app/components/tables/templates-table.tsx | 2 +- .../app/routes/_authenticated+/_layout.tsx | 18 +- .../_authenticated+/admin+/documents.$id.tsx | 51 +- .../admin+/documents._index.tsx | 2 +- .../t.$teamUrl+/documents.$id._index.tsx | 213 +- .../t.$teamUrl+/documents.$id._layout.tsx | 90 + .../t.$teamUrl+/documents.$id.edit.tsx | 238 +- .../documents.$id.legacy_editor.tsx | 139 + .../t.$teamUrl+/documents.$id.logs.tsx | 65 +- .../t.$teamUrl+/documents._index.tsx | 1 + .../t.$teamUrl+/settings.public-profile.tsx | 3 +- .../t.$teamUrl+/templates.$id._index.tsx | 210 +- .../t.$teamUrl+/templates.$id._layout.tsx | 90 + .../t.$teamUrl+/templates.$id.edit.tsx | 109 +- .../templates.$id.legacy_editor.tsx | 111 + .../_internal+/[__htmltopdf]+/audit-log.tsx | 40 +- .../_internal+/[__htmltopdf]+/certificate.tsx | 38 +- apps/remix/app/routes/_recipient+/_layout.tsx | 16 +- .../_recipient+/sign.$token+/_index.tsx | 268 +- .../_recipient+/sign.$token+/complete.tsx | 3 +- .../_recipient+/sign.$token+/waiting.tsx | 14 +- apps/remix/app/routes/_share+/share.$slug.tsx | 7 +- .../v1+/authoring+/document.edit.$id.tsx | 6 +- .../v1+/authoring+/template.edit.$id.tsx | 10 +- .../app/utils/field-signing/dropdown-field.ts | 48 + .../app/utils/field-signing/email-field.ts | 46 + .../app/utils/field-signing/initial-field.ts | 46 + .../app/utils/field-signing/name-field.ts | 48 + .../app/utils/field-signing/number-field.ts | 48 + .../utils/field-signing/signature-field.ts | 56 + .../app/utils/field-signing/text-field.ts | 48 + apps/remix/package.json | 4 +- apps/remix/server/api/files.ts | 2 +- apps/remix/server/redirects.ts | 158 +- package-lock.json | 141 +- package.json | 10 +- packages/api/v1/implementation.ts | 947 +++-- packages/api/v1/schema.ts | 4 +- .../e2e/api/v1/document-sending.spec.ts | 19 +- .../e2e/api/v1/template-field-prefill.spec.ts | 79 +- .../v1/test-unauthorized-api-access.spec.ts | 1558 +++++--- .../e2e/api/v2/template-field-prefill.spec.ts | 79 +- .../v2/test-unauthorized-api-access.spec.ts | 3535 ++++++++++++----- .../e2e/document-auth/access-auth.spec.ts | 4 +- .../next-recipient-dictation.spec.ts | 28 +- .../autosave-fields-step.spec.ts | 53 +- .../autosave-settings-step.spec.ts | 77 +- .../autosave-signers-step.spec.ts | 22 +- .../autosave-subject-step.spec.ts | 37 +- .../duplicate-recipients-simple.spec.ts | 2 +- .../duplicate-recipients.spec.ts | 10 +- .../document-flow/stepper-component.spec.ts | 51 +- .../test-unauthorized-document-access.spec.ts | 10 +- .../include-document-certificate.spec.ts | 56 +- .../e2e/folders/team-account-folders.spec.ts | 18 +- .../organisation-team-preferences.spec.ts | 6 +- .../e2e/teams/search-documents.spec.ts | 1 + .../e2e/teams/team-signature-settings.spec.ts | 15 +- .../duplicate-recipients.spec.ts | 14 +- .../template-autosave-fields-step.spec.ts | 24 +- .../template-autosave-settings-step.spec.ts | 48 +- .../template-autosave-signers-step.spec.ts | 53 +- .../template-settings-step.spec.ts | 11 +- .../template-signers-step.spec.ts | 2 +- .../create-document-from-template.spec.ts | 103 +- .../e2e/templates/direct-templates.spec.ts | 5 +- .../test-unauthorized-template-access.spec.ts | 4 +- packages/app-tests/package.json | 7 +- packages/ee/server-only/limits/server.ts | 8 +- .../validate-number.ts | 27 +- .../client-only/hooks/use-copy-share-link.ts | 6 +- .../client-only/hooks/use-editor-fields.ts | 281 ++ .../hooks/use-envelope-autosave.ts | 90 + .../hooks/use-field-page-coords.ts | 4 +- .../providers/envelope-editor-provider.tsx | 286 ++ .../providers/envelope-render-provider.tsx | 148 + packages/lib/constants/teams.ts | 12 +- .../send-document-cancelled-emails.handler.ts | 27 +- .../send-recipient-signed-email.handler.ts | 30 +- .../emails/send-rejection-emails.handler.ts | 39 +- .../emails/send-signing-email.handler.ts | 38 +- .../internal/bulk-send-template.handler.ts | 17 +- .../internal/seal-document.handler.ts | 401 +- packages/lib/package.json | 2 +- .../generate-2fa-credentials-from-email.ts | 6 +- .../email/generate-2fa-token-from-email.ts | 6 +- .../2fa/email/send-2fa-token-email.ts | 30 +- .../email/validate-2fa-token-from-email.ts | 6 +- .../server-only/admin/admin-find-documents.ts | 98 + .../admin-super-delete-document.ts} | 35 +- .../server-only/admin/get-all-documents.ts | 58 - .../server-only/admin/get-documents-stats.ts | 7 +- .../server-only/admin/get-entire-document.ts | 30 +- .../server-only/admin/get-signing-volume.ts | 13 +- .../lib/server-only/admin/get-users-stats.ts | 11 +- .../document-meta/upsert-document-meta.ts | 61 +- .../document/complete-document-with-token.ts | 88 +- .../document/create-document-v2.ts | 278 -- .../server-only/document/create-document.ts | 171 - .../server-only/document/delete-document.ts | 81 +- .../document/duplicate-document-by-id.ts | 152 - .../document/find-document-audit-logs.ts | 20 +- .../server-only/document/find-documents.ts | 37 +- .../document/get-document-by-access-token.ts | 54 +- .../document/get-document-by-id.ts | 156 - .../document/get-document-by-token.ts | 86 +- .../get-document-certificate-audit-logs.ts | 6 +- .../get-document-meta-by-document-id.ts | 13 - .../get-document-with-details-by-id.ts | 95 +- ...-recipient-or-sender-by-share-link-slug.ts | 6 +- .../lib/server-only/document/get-stats.ts | 41 +- .../document/is-recipient-authorized.ts | 14 +- .../document/reject-document-with-token.ts | 26 +- .../server-only/document/resend-document.ts | 71 +- .../lib/server-only/document/seal-document.ts | 267 -- .../document/search-documents-with-keyword.ts | 42 +- .../document/send-completed-email.ts | 97 +- .../server-only/document/send-delete-email.ts | 21 +- .../{send-document.tsx => send-document.ts} | 155 +- .../document/send-pending-email.ts | 25 +- .../server-only/document/update-document.ts | 248 -- .../document/validate-field-auth.ts | 6 +- .../server-only/document/viewed-document.ts | 43 +- .../server-only/envelope/create-envelope.ts | 376 ++ .../envelope/duplicate-envelope.ts | 196 + .../envelope/get-envelope-by-id.ts | 199 + .../get-envelope-for-recipient-signing.ts | 311 ++ .../get-envelope-required-access-data.ts | 56 + .../lib/server-only/envelope/increment-id.ts | 39 + .../server-only/envelope/update-envelope.ts | 344 ++ .../field/create-document-fields.ts | 126 - .../field/create-envelope-fields.ts | 168 + .../lib/server-only/field/create-field.ts | 136 - .../field/create-template-fields.ts | 101 - .../field/delete-document-field.ts | 40 +- .../lib/server-only/field/delete-field.ts | 78 - .../field/delete-template-field.ts | 22 +- .../field/get-completed-fields-for-token.ts | 6 +- .../lib/server-only/field/get-field-by-id.ts | 52 +- .../field/get-fields-for-document.ts | 41 - .../server-only/field/get-fields-for-token.ts | 8 +- .../field/remove-signed-field-with-token.ts | 12 +- .../field/set-fields-for-document.ts | 96 +- .../field/set-fields-for-template.ts | 95 +- .../field/sign-field-with-token.ts | 22 +- .../field/update-document-fields.ts | 31 +- .../lib/server-only/field/update-field.ts | 119 - .../field/update-template-fields.ts | 28 +- .../lib/server-only/folder/create-folder.ts | 7 +- .../lib/server-only/folder/delete-folder.ts | 16 +- .../lib/server-only/folder/find-folders.ts | 32 +- .../folder/move-document-to-folder.ts | 92 - .../folder/move-template-to-folder.ts | 63 - .../pdf/add-rejection-stamp-to-pdf.ts | 4 +- .../server-only/pdf/flatten-annotations.ts | 4 +- packages/lib/server-only/pdf/flatten-form.ts | 6 +- packages/lib/server-only/pdf/get-page-size.ts | 2 +- ...ld-in-pdf.ts => insert-field-in-pdf-v1.ts} | 10 +- .../server-only/pdf/insert-field-in-pdf-v2.ts | 133 + .../pdf/insert-form-values-in-pdf.ts | 8 +- .../server-only/pdf/insert-image-in-pdf.ts | 2 +- .../lib/server-only/pdf/insert-text-in-pdf.ts | 2 +- .../pdf/legacy-insert-field-in-pdf.ts | 4 +- packages/lib/server-only/pdf/normalize-pdf.ts | 2 +- .../pdf/normalize-signature-appearances.ts | 4 +- .../profile/get-public-profile-by-url.ts | 41 +- .../recipient/create-document-recipients.ts | 33 +- .../recipient/create-template-recipients.ts | 24 +- .../recipient/delete-document-recipient.ts | 41 +- .../server-only/recipient/delete-recipient.ts | 88 - .../recipient/delete-template-recipient.ts | 36 +- .../recipient/get-is-recipient-turn.ts | 9 +- .../recipient/get-next-pending-recipient.ts | 9 +- .../recipient/get-recipient-by-email.ts | 21 - .../recipient/get-recipient-by-id-v1-api.ts | 21 - .../recipient/get-recipient-by-id.ts | 36 +- .../recipient/get-recipient-suggestions.ts | 11 +- .../recipient/get-recipients-for-assistant.ts | 4 +- .../recipient/get-recipients-for-document.ts | 14 +- .../recipient/get-recipients-for-template.ts | 32 - .../recipient/set-document-recipients.ts | 59 +- .../recipient/set-template-recipients.ts | 54 +- .../recipient/update-document-recipients.ts | 42 +- .../recipient/update-template-recipients.ts | 36 +- .../share/create-or-get-share-link.ts | 31 +- .../share/get-share-link-by-slug.ts | 13 - .../lib/server-only/team/get-member-roles.ts | 9 +- .../create-document-from-direct-template.ts | 187 +- .../create-document-from-template-legacy.ts | 183 - .../template/create-document-from-template.ts | 230 +- .../template/create-template-direct-link.ts | 49 +- .../server-only/template/create-template.ts | 112 - .../template/delete-template-direct-link.ts | 27 +- .../server-only/template/delete-template.ts | 21 +- .../template/duplicate-template.ts | 110 - .../server-only/template/find-templates.ts | 77 +- .../get-template-by-direct-link-token.ts | 65 +- .../template/get-template-by-id.ts | 87 +- .../template/toggle-template-direct-link.ts | 36 +- .../server-only/template/update-template.ts | 138 - packages/lib/server-only/user/delete-user.ts | 5 +- .../lib/server-only/user/get-all-users.ts | 21 +- .../webhooks/trigger/generate-sample-data.ts | 1 - .../webhooks/zapier/list-documents.ts | 103 +- packages/lib/translations/de/web.po | 1062 ++++- packages/lib/translations/en/web.po | 1058 ++++- packages/lib/translations/es/web.po | 1062 ++++- packages/lib/translations/fr/web.po | 1062 ++++- packages/lib/translations/it/web.po | 1062 ++++- packages/lib/translations/pl/web.po | 1062 ++++- packages/lib/types/document-audit-logs.ts | 2 +- packages/lib/types/document-meta.ts | 92 +- packages/lib/types/document.ts | 59 +- packages/lib/types/envelope.ts | 138 + packages/lib/types/field-meta.ts | 91 +- packages/lib/types/field.ts | 128 +- packages/lib/types/recipient.ts | 23 +- packages/lib/types/template.ts | 45 +- packages/lib/types/webhook-payload.ts | 57 +- .../field-renderer/field-generic-items.ts | 119 + .../field-renderer/field-renderer.ts | 158 + .../field-renderer/render-checkbox-field.ts | 187 + .../field-renderer/render-dropdown-field.ts | 179 + .../universal/field-renderer/render-field.ts | 78 + .../field-renderer/render-grid-lines.ts | 326 ++ .../field-renderer/render-radio-field.ts | 176 + .../field-renderer/render-signature-field.ts | 209 + .../field-renderer/render-text-field.ts | 234 ++ packages/lib/universal/id.ts | 21 + .../lib/universal/upload/put-file.server.ts | 2 +- packages/lib/utils/document-audit-logs.ts | 15 +- packages/lib/utils/document-auth.ts | 4 +- packages/lib/utils/document-visibility.ts | 20 - packages/lib/utils/document.ts | 98 +- packages/lib/utils/envelope.ts | 167 + packages/lib/utils/fields.ts | 16 +- .../mask-recipient-tokens-for-document.ts | 8 +- packages/lib/utils/recipients.ts | 14 + packages/lib/utils/teams.ts | 21 +- packages/lib/utils/templates.ts | 28 +- .../migration.sql | 57 + .../migration.sql | 470 +++ packages/prisma/schema.prisma | 247 +- packages/prisma/seed/documents.ts | 250 +- packages/prisma/seed/initial-seed.ts | 49 +- packages/prisma/seed/templates.ts | 128 +- .../prisma/types/document-legacy-schema.ts | 52 + .../prisma/types/document-with-recipient.ts | 6 +- .../prisma/types/template-legacy-schema.ts | 40 + .../helpers/add-signing-placeholder.ts | 2 +- packages/signing/package.json | 5 +- .../server/admin-router/delete-document.ts | 8 +- .../admin-router/delete-document.types.ts | 2 +- .../server/admin-router/find-document-jobs.ts | 72 + .../admin-router/find-document-jobs.types.ts | 26 + .../server/admin-router/find-documents.ts | 10 +- .../server/admin-router/reseal-document.ts | 25 +- .../admin-router/reseal-document.types.ts | 2 +- packages/trpc/server/admin-router/router.ts | 2 + .../access-auth-request-2fa-email.ts | 12 +- .../create-document-temporary.ts | 71 +- .../create-document-temporary.types.ts | 30 +- .../server/document-router/create-document.ts | 27 +- .../document-router/create-document.types.ts | 6 +- .../server/document-router/delete-document.ts | 5 +- .../document-router/distribute-document.ts | 19 +- .../distribute-document.types.ts | 6 +- .../download-document-audit-logs.ts | 16 +- .../download-document-certificate.ts | 16 +- .../document-router/download-document.ts | 43 +- .../document-router/duplicate-document.ts | 14 +- .../duplicate-document.types.ts | 3 +- .../find-documents-internal.ts | 2 + .../server/document-router/find-documents.ts | 6 +- .../trpc/server/document-router/find-inbox.ts | 25 +- .../document-router/get-document-by-token.ts | 25 +- .../server/document-router/get-document.ts | 5 +- .../server/document-router/get-inbox-count.ts | 5 +- .../document-router/redistribute-document.ts | 5 +- .../trpc/server/document-router/router.ts | 2 + .../trpc/server/document-router/schema.ts | 61 +- .../server/document-router/share-document.ts | 28 + .../document-router/share-document.types.ts | 16 + .../server/document-router/update-document.ts | 48 +- .../document-router/update-document.types.ts | 34 +- .../apply-multi-sign-signature.ts | 2 +- .../create-embedding-document.ts | 33 +- .../create-embedding-document.types.ts | 25 +- .../create-embedding-template.ts | 66 +- .../create-embedding-template.types.ts | 22 +- .../get-multi-sign-document.ts | 8 +- .../get-multi-sign-document.types.ts | 2 - .../update-embedding-document.ts | 31 +- .../update-embedding-document.types.ts | 28 +- .../update-embedding-template.ts | 22 +- .../update-embedding-template.types.ts | 23 +- .../envelope-router/create-envelope-items.ts | 111 + .../create-envelope-items.types.ts | 38 + .../server/envelope-router/create-envelope.ts | 68 + .../envelope-router/create-envelope.types.ts | 86 + .../envelope-router/delete-envelope-item.ts | 64 + .../delete-envelope-item.types.ts | 11 + .../server/envelope-router/delete-envelope.ts | 50 + .../envelope-router/delete-envelope.types.ts | 21 + .../envelope-router/distribute-envelope.ts | 55 + .../distribute-envelope.types.ts | 34 + .../envelope-router/duplicate-envelope.ts | 34 + .../duplicate-envelope.types.ts | 12 + .../server/envelope-router/get-envelope.ts | 29 + .../envelope-router/get-envelope.types.ts | 22 + .../envelope-router/redistribute-envelope.ts | 34 + .../redistribute-envelope.types.ts | 25 + .../trpc/server/envelope-router/router.ts | 38 + .../envelope-router/set-envelope-fields.ts | 66 + .../set-envelope-fields.types.ts | 51 + .../set-envelope-recipients.ts | 51 + .../set-envelope-recipients.types.ts | 30 + .../envelope-router/sign-envelope-field.ts | 472 +++ .../sign-envelope-field.types.ts | 70 + .../envelope-router/update-envelope-items.ts | 99 + .../update-envelope-items.types.ts | 29 + .../server/envelope-router/update-envelope.ts | 36 + .../envelope-router/update-envelope.types.ts | 46 + packages/trpc/server/field-router/router.ts | 63 +- packages/trpc/server/field-router/schema.ts | 10 +- packages/trpc/server/folder-router/router.ts | 94 - packages/trpc/server/folder-router/schema.ts | 12 - .../update-organisation-settings.types.ts | 5 +- .../trpc/server/recipient-router/router.ts | 50 +- .../trpc/server/recipient-router/schema.ts | 4 +- packages/trpc/server/router.ts | 4 +- .../get-document-internal-url-for-qr-code.ts | 50 - ...document-internal-url-for-qr-code.types.ts | 15 - .../trpc/server/share-link-router/router.ts | 33 - .../trpc/server/share-link-router/schema.ts | 10 - .../team-router/update-team-settings.types.ts | 5 +- .../trpc/server/template-router/router.ts | 183 +- .../trpc/server/template-router/schema.ts | 44 +- .../document/document-read-only-fields.tsx | 9 +- .../document/document-share-button.tsx | 2 +- .../ui/components/field/field-tooltip.tsx | 6 +- .../pdf-viewer/pdf-viewer-konva-lazy.tsx | 23 + .../pdf-viewer/pdf-viewer-konva.tsx | 173 + packages/ui/components/signing-card.tsx | 4 +- packages/ui/lib/recipient-colors.ts | 22 +- packages/ui/primitives/card.tsx | 42 +- packages/ui/primitives/document-dropzone.tsx | 20 +- .../primitives/document-flow/add-fields.tsx | 26 +- .../document-flow/add-settings.types.ts | 4 +- .../primitives/document-flow/add-signers.tsx | 8 +- .../document-flow/field-content.tsx | 4 +- .../constants.ts | 3 + .../number-field.tsx | 6 +- packages/ui/primitives/document-upload.tsx | 14 +- packages/ui/primitives/pdf-viewer.tsx | 2 +- packages/ui/primitives/recipient-selector.tsx | 16 +- .../template-flow/add-template-fields.tsx | 2 +- .../template-flow/add-template-settings.tsx | 23 +- .../add-template-settings.types.tsx | 4 +- 447 files changed, 33467 insertions(+), 9622 deletions(-) create mode 100644 apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx create mode 100644 apps/remix/app/components/dialogs/envelope-duplicate-dialog.tsx create mode 100644 apps/remix/app/components/dialogs/envelope-item-delete-dialog.tsx create mode 100644 apps/remix/app/components/dialogs/envelope-redistribute-dialog.tsx create mode 100644 apps/remix/app/components/dialogs/sign-field-dropdown-dialog.tsx create mode 100644 apps/remix/app/components/dialogs/sign-field-email-dialog.tsx create mode 100644 apps/remix/app/components/dialogs/sign-field-initials-dialog.tsx create mode 100644 apps/remix/app/components/dialogs/sign-field-name-dialog.tsx create mode 100644 apps/remix/app/components/dialogs/sign-field-number-dialog.tsx create mode 100644 apps/remix/app/components/dialogs/sign-field-signature-dialog.tsx create mode 100644 apps/remix/app/components/dialogs/sign-field-text-dialog.tsx delete mode 100644 apps/remix/app/components/dialogs/template-direct-link-dialog-wrapper.tsx create mode 100644 apps/remix/app/components/forms/editor/constants.ts create mode 100644 apps/remix/app/components/forms/editor/editor-field-checkbox-form.tsx create mode 100644 apps/remix/app/components/forms/editor/editor-field-date-form.tsx create mode 100644 apps/remix/app/components/forms/editor/editor-field-dropdown-form.tsx create mode 100644 apps/remix/app/components/forms/editor/editor-field-email-form.tsx create mode 100644 apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx create mode 100644 apps/remix/app/components/forms/editor/editor-field-initials-form.tsx create mode 100644 apps/remix/app/components/forms/editor/editor-field-name-form.tsx create mode 100644 apps/remix/app/components/forms/editor/editor-field-number-form.tsx create mode 100644 apps/remix/app/components/forms/editor/editor-field-radio-form.tsx create mode 100644 apps/remix/app/components/forms/editor/editor-field-text-form.tsx rename apps/remix/app/components/general/document-signing/{document-signing-page-view.tsx => document-signing-page-view-v1.tsx} (98%) create mode 100644 apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx create mode 100644 apps/remix/app/components/general/document-signing/envelope-signing-provider.tsx rename apps/remix/app/components/general/document/{document-upload.tsx => document-upload-button.tsx} (93%) create mode 100644 apps/remix/app/components/general/document/envelope-upload-button.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page-renderer.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor-header.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor-page-fields.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor-page-preview-renderer.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor-page-preview.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor-page-upload.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor-title-input.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-editor.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-file-selector.tsx create mode 100644 apps/remix/app/components/general/envelope-editor/envelope-generic-page-renderer.tsx create mode 100644 apps/remix/app/components/general/envelope-signing/envelope-signer-form.tsx create mode 100644 apps/remix/app/components/general/envelope-signing/envelope-signer-header.tsx create mode 100644 apps/remix/app/components/general/envelope-signing/envelope-signer-page-renderer.tsx create mode 100644 apps/remix/app/components/tables/admin-document-jobs-table.tsx create mode 100644 apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._layout.tsx create mode 100644 apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.legacy_editor.tsx create mode 100644 apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._layout.tsx create mode 100644 apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id.legacy_editor.tsx create mode 100644 apps/remix/app/utils/field-signing/dropdown-field.ts create mode 100644 apps/remix/app/utils/field-signing/email-field.ts create mode 100644 apps/remix/app/utils/field-signing/initial-field.ts create mode 100644 apps/remix/app/utils/field-signing/name-field.ts create mode 100644 apps/remix/app/utils/field-signing/number-field.ts create mode 100644 apps/remix/app/utils/field-signing/signature-field.ts create mode 100644 apps/remix/app/utils/field-signing/text-field.ts create mode 100644 packages/lib/client-only/hooks/use-editor-fields.ts create mode 100644 packages/lib/client-only/hooks/use-envelope-autosave.ts create mode 100644 packages/lib/client-only/providers/envelope-editor-provider.tsx create mode 100644 packages/lib/client-only/providers/envelope-render-provider.tsx create mode 100644 packages/lib/server-only/admin/admin-find-documents.ts rename packages/lib/server-only/{document/super-delete-document.ts => admin/admin-super-delete-document.ts} (81%) delete mode 100644 packages/lib/server-only/admin/get-all-documents.ts delete mode 100644 packages/lib/server-only/document/create-document-v2.ts delete mode 100644 packages/lib/server-only/document/create-document.ts delete mode 100644 packages/lib/server-only/document/duplicate-document-by-id.ts delete mode 100644 packages/lib/server-only/document/get-document-by-id.ts delete mode 100644 packages/lib/server-only/document/get-document-meta-by-document-id.ts delete mode 100644 packages/lib/server-only/document/seal-document.ts rename packages/lib/server-only/document/{send-document.tsx => send-document.ts} (61%) delete mode 100644 packages/lib/server-only/document/update-document.ts create mode 100644 packages/lib/server-only/envelope/create-envelope.ts create mode 100644 packages/lib/server-only/envelope/duplicate-envelope.ts create mode 100644 packages/lib/server-only/envelope/get-envelope-by-id.ts create mode 100644 packages/lib/server-only/envelope/get-envelope-for-recipient-signing.ts create mode 100644 packages/lib/server-only/envelope/get-envelope-required-access-data.ts create mode 100644 packages/lib/server-only/envelope/increment-id.ts create mode 100644 packages/lib/server-only/envelope/update-envelope.ts delete mode 100644 packages/lib/server-only/field/create-document-fields.ts create mode 100644 packages/lib/server-only/field/create-envelope-fields.ts delete mode 100644 packages/lib/server-only/field/create-field.ts delete mode 100644 packages/lib/server-only/field/create-template-fields.ts delete mode 100644 packages/lib/server-only/field/delete-field.ts delete mode 100644 packages/lib/server-only/field/get-fields-for-document.ts delete mode 100644 packages/lib/server-only/field/update-field.ts delete mode 100644 packages/lib/server-only/folder/move-document-to-folder.ts delete mode 100644 packages/lib/server-only/folder/move-template-to-folder.ts rename packages/lib/server-only/pdf/{insert-field-in-pdf.ts => insert-field-in-pdf-v1.ts} (99%) create mode 100644 packages/lib/server-only/pdf/insert-field-in-pdf-v2.ts delete mode 100644 packages/lib/server-only/recipient/delete-recipient.ts delete mode 100644 packages/lib/server-only/recipient/get-recipient-by-email.ts delete mode 100644 packages/lib/server-only/recipient/get-recipient-by-id-v1-api.ts delete mode 100644 packages/lib/server-only/recipient/get-recipients-for-template.ts delete mode 100644 packages/lib/server-only/share/get-share-link-by-slug.ts delete mode 100644 packages/lib/server-only/template/create-document-from-template-legacy.ts delete mode 100644 packages/lib/server-only/template/create-template.ts delete mode 100644 packages/lib/server-only/template/duplicate-template.ts delete mode 100644 packages/lib/server-only/template/update-template.ts create mode 100644 packages/lib/types/envelope.ts create mode 100644 packages/lib/universal/field-renderer/field-generic-items.ts create mode 100644 packages/lib/universal/field-renderer/field-renderer.ts create mode 100644 packages/lib/universal/field-renderer/render-checkbox-field.ts create mode 100644 packages/lib/universal/field-renderer/render-dropdown-field.ts create mode 100644 packages/lib/universal/field-renderer/render-field.ts create mode 100644 packages/lib/universal/field-renderer/render-grid-lines.ts create mode 100644 packages/lib/universal/field-renderer/render-radio-field.ts create mode 100644 packages/lib/universal/field-renderer/render-signature-field.ts create mode 100644 packages/lib/universal/field-renderer/render-text-field.ts delete mode 100644 packages/lib/utils/document-visibility.ts create mode 100644 packages/lib/utils/envelope.ts create mode 100644 packages/prisma/migrations/20250918070643_remove_template_metadata/migration.sql create mode 100644 packages/prisma/migrations/20250918070645_add_envelopes/migration.sql create mode 100644 packages/prisma/types/document-legacy-schema.ts create mode 100644 packages/prisma/types/template-legacy-schema.ts create mode 100644 packages/trpc/server/admin-router/find-document-jobs.ts create mode 100644 packages/trpc/server/admin-router/find-document-jobs.types.ts create mode 100644 packages/trpc/server/document-router/share-document.ts create mode 100644 packages/trpc/server/document-router/share-document.types.ts create mode 100644 packages/trpc/server/envelope-router/create-envelope-items.ts create mode 100644 packages/trpc/server/envelope-router/create-envelope-items.types.ts create mode 100644 packages/trpc/server/envelope-router/create-envelope.ts create mode 100644 packages/trpc/server/envelope-router/create-envelope.types.ts create mode 100644 packages/trpc/server/envelope-router/delete-envelope-item.ts create mode 100644 packages/trpc/server/envelope-router/delete-envelope-item.types.ts create mode 100644 packages/trpc/server/envelope-router/delete-envelope.ts create mode 100644 packages/trpc/server/envelope-router/delete-envelope.types.ts create mode 100644 packages/trpc/server/envelope-router/distribute-envelope.ts create mode 100644 packages/trpc/server/envelope-router/distribute-envelope.types.ts create mode 100644 packages/trpc/server/envelope-router/duplicate-envelope.ts create mode 100644 packages/trpc/server/envelope-router/duplicate-envelope.types.ts create mode 100644 packages/trpc/server/envelope-router/get-envelope.ts create mode 100644 packages/trpc/server/envelope-router/get-envelope.types.ts create mode 100644 packages/trpc/server/envelope-router/redistribute-envelope.ts create mode 100644 packages/trpc/server/envelope-router/redistribute-envelope.types.ts create mode 100644 packages/trpc/server/envelope-router/router.ts create mode 100644 packages/trpc/server/envelope-router/set-envelope-fields.ts create mode 100644 packages/trpc/server/envelope-router/set-envelope-fields.types.ts create mode 100644 packages/trpc/server/envelope-router/set-envelope-recipients.ts create mode 100644 packages/trpc/server/envelope-router/set-envelope-recipients.types.ts create mode 100644 packages/trpc/server/envelope-router/sign-envelope-field.ts create mode 100644 packages/trpc/server/envelope-router/sign-envelope-field.types.ts create mode 100644 packages/trpc/server/envelope-router/update-envelope-items.ts create mode 100644 packages/trpc/server/envelope-router/update-envelope-items.types.ts create mode 100644 packages/trpc/server/envelope-router/update-envelope.ts create mode 100644 packages/trpc/server/envelope-router/update-envelope.types.ts delete mode 100644 packages/trpc/server/share-link-router/get-document-internal-url-for-qr-code.ts delete mode 100644 packages/trpc/server/share-link-router/get-document-internal-url-for-qr-code.types.ts delete mode 100644 packages/trpc/server/share-link-router/router.ts delete mode 100644 packages/trpc/server/share-link-router/schema.ts create mode 100644 packages/ui/components/pdf-viewer/pdf-viewer-konva-lazy.tsx create mode 100644 packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx diff --git a/apps/openpage-api/lib/growth/get-monthly-completed-document.ts b/apps/openpage-api/lib/growth/get-monthly-completed-document.ts index f429b0a54..808d7259d 100644 --- a/apps/openpage-api/lib/growth/get-monthly-completed-document.ts +++ b/apps/openpage-api/lib/growth/get-monthly-completed-document.ts @@ -1,4 +1,4 @@ -import { DocumentStatus } from '@prisma/client'; +import { DocumentStatus, EnvelopeType } from '@prisma/client'; import { DateTime } from 'luxon'; import { kyselyPrisma, sql } from '@documenso/prisma'; @@ -7,18 +7,19 @@ import { addZeroMonth } from '../add-zero-month'; export const getCompletedDocumentsMonthly = async (type: 'count' | 'cumulative' = 'count') => { const qb = kyselyPrisma.$kysely - .selectFrom('Document') + .selectFrom('Envelope') .select(({ fn }) => [ - fn('DATE_TRUNC', [sql.lit('MONTH'), 'Document.updatedAt']).as('month'), + fn('DATE_TRUNC', [sql.lit('MONTH'), 'Envelope.updatedAt']).as('month'), fn.count('id').as('count'), fn .sum(fn.count('id')) // Feels like a bug in the Kysely extension but I just can not do this orderBy in a type-safe manner // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any - .over((ob) => ob.orderBy(fn('DATE_TRUNC', [sql.lit('MONTH'), 'Document.updatedAt']) as any)) + .over((ob) => ob.orderBy(fn('DATE_TRUNC', [sql.lit('MONTH'), 'Envelope.updatedAt']) as any)) .as('cume_count'), ]) - .where(() => sql`"Document"."status" = ${DocumentStatus.COMPLETED}::"DocumentStatus"`) + .where(() => sql`"Envelope"."status" = ${DocumentStatus.COMPLETED}::"DocumentStatus"`) + .where(() => sql`"Envelope"."type" = ${EnvelopeType.DOCUMENT}::"EnvelopeType"`) .groupBy('month') .orderBy('month', 'desc') .limit(12); diff --git a/apps/remix/app/components/dialogs/admin-document-delete-dialog.tsx b/apps/remix/app/components/dialogs/admin-document-delete-dialog.tsx index 9f82d8551..aee9167cc 100644 --- a/apps/remix/app/components/dialogs/admin-document-delete-dialog.tsx +++ b/apps/remix/app/components/dialogs/admin-document-delete-dialog.tsx @@ -3,7 +3,6 @@ import { useState } from 'react'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react'; import { Trans } from '@lingui/react/macro'; -import type { Document } from '@prisma/client'; import { useNavigate } from 'react-router'; import { trpc } from '@documenso/trpc/react'; @@ -22,10 +21,10 @@ import { Input } from '@documenso/ui/primitives/input'; import { useToast } from '@documenso/ui/primitives/use-toast'; export type AdminDocumentDeleteDialogProps = { - document: Document; + envelopeId: string; }; -export const AdminDocumentDeleteDialog = ({ document }: AdminDocumentDeleteDialogProps) => { +export const AdminDocumentDeleteDialog = ({ envelopeId }: AdminDocumentDeleteDialogProps) => { const { _ } = useLingui(); const { toast } = useToast(); @@ -42,7 +41,7 @@ export const AdminDocumentDeleteDialog = ({ document }: AdminDocumentDeleteDialo return; } - await deleteDocument({ id: document.id, reason }); + await deleteDocument({ id: envelopeId, reason }); toast({ title: _(msg`Document deleted`), diff --git a/apps/remix/app/components/dialogs/document-duplicate-dialog.tsx b/apps/remix/app/components/dialogs/document-duplicate-dialog.tsx index 57146ed9f..81ec5bdf4 100644 --- a/apps/remix/app/components/dialogs/document-duplicate-dialog.tsx +++ b/apps/remix/app/components/dialogs/document-duplicate-dialog.tsx @@ -57,14 +57,14 @@ export const DocumentDuplicateDialog = ({ const { mutateAsync: duplicateDocument, isPending: isDuplicateLoading } = trpcReact.document.duplicate.useMutation({ - onSuccess: async ({ documentId }) => { + onSuccess: async ({ id }) => { toast({ title: _(msg`Document Duplicated`), description: _(msg`Your document has been successfully duplicated.`), duration: 5000, }); - await navigate(`${documentsPath}/${documentId}/edit`); + await navigate(`${documentsPath}/${id}/edit`); onOpenChange(false); }, }); diff --git a/apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx b/apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx index 401fb3529..c4c85c051 100644 --- a/apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx +++ b/apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx @@ -81,7 +81,7 @@ export const DocumentMoveToFolderDialog = ({ }, ); - const { mutateAsync: moveDocumentToFolder } = trpc.folder.moveDocumentToFolder.useMutation(); + const { mutateAsync: updateDocument } = trpc.document.update.useMutation(); useEffect(() => { if (!open) { @@ -94,9 +94,11 @@ export const DocumentMoveToFolderDialog = ({ const onSubmit = async (data: TMoveDocumentFormSchema) => { try { - await moveDocumentToFolder({ + await updateDocument({ documentId, - folderId: data.folderId ?? null, + data: { + folderId: data.folderId ?? null, + }, }); const documentsPath = formatDocumentsPath(team.url); diff --git a/apps/remix/app/components/dialogs/document-resend-dialog.tsx b/apps/remix/app/components/dialogs/document-resend-dialog.tsx index d93f29e84..d8c0a73ee 100644 --- a/apps/remix/app/components/dialogs/document-resend-dialog.tsx +++ b/apps/remix/app/components/dialogs/document-resend-dialog.tsx @@ -4,15 +4,15 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react'; import { Trans } from '@lingui/react/macro'; -import { type Recipient, SigningStatus } from '@prisma/client'; +import { type Recipient, SigningStatus, type Team, type User } from '@prisma/client'; import { History } from 'lucide-react'; import { useForm, useWatch } from 'react-hook-form'; import * as z from 'zod'; import { useSession } from '@documenso/lib/client-only/providers/session'; import { getRecipientType } from '@documenso/lib/client-only/recipient-type'; -import type { TDocumentMany as TDocumentRow } from '@documenso/lib/types/document'; import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter'; +import type { Document } from '@documenso/prisma/types/document-legacy-schema'; import { trpc as trpcReact } from '@documenso/trpc/react'; import { cn } from '@documenso/ui/lib/utils'; import { Button } from '@documenso/ui/primitives/button'; @@ -43,7 +43,11 @@ import { StackAvatar } from '../general/stack-avatar'; const FORM_ID = 'resend-email'; export type DocumentResendDialogProps = { - document: TDocumentRow; + document: Pick & { + user: Pick; + recipients: Recipient[]; + team: Pick | null; + }; recipients: Recipient[]; }; diff --git a/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx b/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx new file mode 100644 index 000000000..a3aac4436 --- /dev/null +++ b/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx @@ -0,0 +1,449 @@ +import { useMemo, useState } from 'react'; + +import { zodResolver } from '@hookform/resolvers/zod'; +import { useLingui } from '@lingui/react/macro'; +import { Trans } from '@lingui/react/macro'; +import { + DocumentDistributionMethod, + DocumentStatus, + EnvelopeType, + type Field, + FieldType, + type Recipient, + RecipientRole, +} from '@prisma/client'; +import { AnimatePresence, motion } from 'framer-motion'; +import { InfoIcon } from 'lucide-react'; +import { useForm } from 'react-hook-form'; +import * as z from 'zod'; + +import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation'; +import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles'; +import type { TEnvelope } from '@documenso/lib/types/envelope'; +import { formatSigningLink } from '@documenso/lib/utils/recipients'; +import { trpc, trpc as trpcReact } from '@documenso/trpc/react'; +import { CopyTextButton } from '@documenso/ui/components/common/copy-text-button'; +import { DocumentSendEmailMessageHelper } from '@documenso/ui/components/document/document-send-email-message-helper'; +import { Alert, AlertDescription } from '@documenso/ui/primitives/alert'; +import { AvatarWithText } from '@documenso/ui/primitives/avatar'; +import { Button } from '@documenso/ui/primitives/button'; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@documenso/ui/primitives/dialog'; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@documenso/ui/primitives/form/form'; +import { Input } from '@documenso/ui/primitives/input'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@documenso/ui/primitives/select'; +import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs'; +import { Textarea } from '@documenso/ui/primitives/textarea'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip'; +import { useToast } from '@documenso/ui/primitives/use-toast'; + +export type EnvelopeDistributeDialogProps = { + envelope: Pick & { + recipients: Recipient[]; + fields: Field[]; + }; + trigger?: React.ReactNode; +}; + +export const ZEnvelopeDistributeFormSchema = z.object({ + meta: z.object({ + emailId: z.string().nullable(), + emailReplyTo: z.preprocess( + (val) => (val === '' ? undefined : val), + z.string().email().optional(), + ), + subject: z.string(), + message: z.string(), + distributionMethod: z + .nativeEnum(DocumentDistributionMethod) + .optional() + .default(DocumentDistributionMethod.EMAIL), + }), +}); + +export type TEnvelopeDistributeFormSchema = z.infer; + +export const EnvelopeDistributeDialog = ({ envelope, trigger }: EnvelopeDistributeDialogProps) => { + const organisation = useCurrentOrganisation(); + + const recipients = envelope.recipients; + + const { toast } = useToast(); + const { t } = useLingui(); + + const [isOpen, setIsOpen] = useState(false); + + const { mutateAsync: distributeEnvelope } = trpcReact.envelope.distribute.useMutation(); + + const form = useForm({ + defaultValues: { + meta: { + emailId: envelope.documentMeta?.emailId ?? null, + emailReplyTo: envelope.documentMeta?.emailReplyTo || undefined, + subject: envelope.documentMeta?.subject ?? '', + message: envelope.documentMeta?.message ?? '', + distributionMethod: + envelope.documentMeta?.distributionMethod || DocumentDistributionMethod.EMAIL, + }, + }, + resolver: zodResolver(ZEnvelopeDistributeFormSchema), + }); + + const { + handleSubmit, + setValue, + watch, + formState: { isSubmitting }, + } = form; + + const { data: emailData, isLoading: isLoadingEmails } = + trpc.enterprise.organisation.email.find.useQuery({ + organisationId: organisation.id, + perPage: 100, + }); + + const emails = emailData?.data || []; + + const distributionMethod = watch('meta.distributionMethod'); + + const everySignerHasSignature = useMemo( + () => + envelope.recipients + .filter((recipient) => recipient.role === RecipientRole.SIGNER) + .every((recipient) => + envelope.fields.some( + (field) => field.type === FieldType.SIGNATURE && field.recipientId === recipient.id, + ), + ), + [envelope.recipients, envelope.fields], + ); + + const onFormSubmit = async ({ meta }: TEnvelopeDistributeFormSchema) => { + try { + await distributeEnvelope({ envelopeId: envelope.id, meta }); + + toast({ + title: t`Envelope distributed`, + description: t`Your envelope has been distributed successfully.`, + duration: 5000, + }); + + setIsOpen(false); + } catch (err) { + toast({ + title: t`Something went wrong`, + description: t`This envelope could not be distributed at this time. Please try again.`, + variant: 'destructive', + duration: 7500, + }); + } + }; + + if (envelope.status !== DocumentStatus.DRAFT || envelope.type !== EnvelopeType.DOCUMENT) { + return null; + } + + return ( + + {trigger} + + + + + Send Document + + + + Recipients will be able to sign the document once sent + + + {everySignerHasSignature ? ( +
+ +
+ + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + setValue('meta.distributionMethod', value as DocumentDistributionMethod) + } + value={distributionMethod} + className="mb-2" + > + + + Email + + + None + + + + +
+ + {distributionMethod === DocumentDistributionMethod.EMAIL && ( + + +
+ {organisation.organisationClaim.flags.emailDomains && ( + ( + + + Email Sender + + + + + + + + )} + /> + )} + + ( + + + Reply To Email{' '} + (Optional) + + + + + + + + + )} + /> + + ( + + + Subject{' '} + (Optional) + + + + + + + + )} + /> + + ( + + + Message{' '} + (Optional) + + + + + + + + + + + +