diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7733637f2..962da1ec9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -65,6 +65,47 @@ jobs: env: BUILD_PLATFORM: ${{ matrix.os == 'warp-ubuntu-latest-arm64-4x' && 'arm64' || 'amd64' }} + - name: Build the chromium docker image + env: + BUILD_PLATFORM: ${{ matrix.os == 'warp-ubuntu-latest-arm64-4x' && 'arm64' || 'amd64' }} + run: | + APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')" + GIT_SHA="$(git rev-parse HEAD)" + + docker build \ + -f ./docker/Dockerfile.chromium \ + --progress=plain \ + --build-arg TAG="$GIT_SHA" \ + -t "documenso/documenso-$BUILD_PLATFORM:latest-chromium" \ + -t "documenso/documenso-$BUILD_PLATFORM:$GIT_SHA-chromium" \ + -t "documenso/documenso-$BUILD_PLATFORM:$APP_VERSION-chromium" \ + -t "ghcr.io/documenso/documenso-$BUILD_PLATFORM:latest-chromium" \ + -t "ghcr.io/documenso/documenso-$BUILD_PLATFORM:$GIT_SHA-chromium" \ + -t "ghcr.io/documenso/documenso-$BUILD_PLATFORM:$APP_VERSION-chromium" \ + . + + - name: Push the chromium docker image to DockerHub + env: + BUILD_PLATFORM: ${{ matrix.os == 'warp-ubuntu-latest-arm64-4x' && 'arm64' || 'amd64' }} + run: | + APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')" + GIT_SHA="$(git rev-parse HEAD)" + + docker push "documenso/documenso-$BUILD_PLATFORM:latest-chromium" + docker push "documenso/documenso-$BUILD_PLATFORM:$GIT_SHA-chromium" + docker push "documenso/documenso-$BUILD_PLATFORM:$APP_VERSION-chromium" \ + + - name: Push the chromium docker image to GitHub Container Registry + env: + BUILD_PLATFORM: ${{ matrix.os == 'warp-ubuntu-latest-arm64-4x' && 'arm64' || 'amd64' }} + run: | + APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')" + GIT_SHA="$(git rev-parse HEAD)" + + docker push "ghcr.io/documenso/documenso-$BUILD_PLATFORM:latest-chromium" + docker push "ghcr.io/documenso/documenso-$BUILD_PLATFORM:$GIT_SHA-chromium" + docker push "ghcr.io/documenso/documenso-$BUILD_PLATFORM:$APP_VERSION-chromium" + create_and_publish_manifest: name: Create and publish manifest runs-on: ubuntu-latest @@ -125,6 +166,43 @@ jobs: docker manifest push documenso/documenso:$GIT_SHA docker manifest push documenso/documenso:$APP_VERSION + - name: Create and push DockerHub chromium manifest + run: | + APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')" + GIT_SHA="$(git rev-parse HEAD)" + + # Check if the version is stable (no rc or beta in the version) + if [[ "$APP_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + docker manifest create \ + documenso/documenso:latest-chromium \ + --amend documenso/documenso-amd64:latest-chromium \ + --amend documenso/documenso-arm64:latest-chromium + + docker manifest push documenso/documenso:latest-chromium + fi + + if [[ "$APP_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then + docker manifest create \ + documenso/documenso:rc-chromium \ + --amend documenso/documenso-amd64:rc-chromium \ + --amend documenso/documenso-arm64:rc-chromium + + docker manifest push documenso/documenso:rc-chromium + fi + + docker manifest create \ + documenso/documenso:$GIT_SHA-chromium \ + --amend documenso/documenso-amd64:$GIT_SHA-chromium \ + --amend documenso/documenso-arm64:$GIT_SHA-chromium + + docker manifest create \ + documenso/documenso:$APP_VERSION-chromium \ + --amend documenso/documenso-amd64:$APP_VERSION-chromium \ + --amend documenso/documenso-arm64:$APP_VERSION-chromium + + docker manifest push documenso/documenso:$GIT_SHA-chromium + docker manifest push documenso/documenso:$APP_VERSION-chromium + - name: Create and push Github Container Registry manifest run: | APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')" @@ -161,3 +239,40 @@ jobs: docker manifest push ghcr.io/documenso/documenso:$GIT_SHA docker manifest push ghcr.io/documenso/documenso:$APP_VERSION + + - name: Create and push Github Container Registry chromium manifest + run: | + APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')" + GIT_SHA="$(git rev-parse HEAD)" + + # Check if the version is stable (no rc or beta in the version) + if [[ "$APP_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + docker manifest create \ + ghcr.io/documenso/documenso:latest-chromium \ + --amend ghcr.io/documenso/documenso-amd64:latest-chromium \ + --amend ghcr.io/documenso/documenso-arm64:latest-chromium + + docker manifest push ghcr.io/documenso/documenso:latest-chromium + fi + + if [[ "$APP_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-rc\.[0-9]+$ ]]; then + docker manifest create \ + ghcr.io/documenso/documenso:rc-chromium \ + --amend ghcr.io/documenso/documenso-amd64:rc-chromium \ + --amend ghcr.io/documenso/documenso-arm64:rc-chromium + + docker manifest push ghcr.io/documenso/documenso:rc-chromium + fi + + docker manifest create \ + ghcr.io/documenso/documenso:$GIT_SHA-chromium \ + --amend ghcr.io/documenso/documenso-amd64:$GIT_SHA-chromium \ + --amend ghcr.io/documenso/documenso-arm64:$GIT_SHA-chromium + + docker manifest create \ + ghcr.io/documenso/documenso:$APP_VERSION-chromium \ + --amend ghcr.io/documenso/documenso-amd64:$APP_VERSION-chromium \ + --amend ghcr.io/documenso/documenso-arm64:$APP_VERSION-chromium + + docker manifest push ghcr.io/documenso/documenso:$GIT_SHA-chromium + docker manifest push ghcr.io/documenso/documenso:$APP_VERSION-chromium diff --git a/.vscode/settings.json b/.vscode/settings.json index e6ff5d1a0..a39e263c5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,5 +17,6 @@ }, "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" - } + }, + "prisma.pinToPrisma6": true } diff --git a/apps/documentation/package.json b/apps/documentation/package.json index 730013c42..348d442ef 100644 --- a/apps/documentation/package.json +++ b/apps/documentation/package.json @@ -15,7 +15,7 @@ "@documenso/tailwind-config": "*", "@documenso/trpc": "*", "@documenso/ui": "*", - "next": "^15.5.7", + "next": "15.5.9", "next-plausible": "^3.12.5", "nextra": "^3", "nextra-theme-docs": "^3", @@ -29,4 +29,4 @@ "pagefind": "^1.2.0", "typescript": "5.6.2" } -} \ No newline at end of file +} diff --git a/apps/documentation/pages/developers/local-development/quickstart.mdx b/apps/documentation/pages/developers/local-development/quickstart.mdx index fdf1590f2..563505024 100644 --- a/apps/documentation/pages/developers/local-development/quickstart.mdx +++ b/apps/documentation/pages/developers/local-development/quickstart.mdx @@ -61,6 +61,6 @@ You can access the following services: - Main application - http://localhost:3000 - Incoming Mail Access - http://localhost:9000 - Database Connection Details: -- Port: 54320 -- Connection: Use your favourite database client to connect to the database. + - Port: 54320 + - Connection: Use your favorite database client to connect to the database. - S3 Storage Dashboard - http://localhost:9001 diff --git a/apps/documentation/pages/developers/public-api/rate-limits.mdx b/apps/documentation/pages/developers/public-api/rate-limits.mdx index 1941258cd..82ee1b046 100644 --- a/apps/documentation/pages/developers/public-api/rate-limits.mdx +++ b/apps/documentation/pages/developers/public-api/rate-limits.mdx @@ -1,3 +1,8 @@ +--- +title: Rate Limits +description: Learn about the rate limits for the Documenso Public API. +--- + import { Callout } from 'nextra/components'; # Rate Limits diff --git a/apps/documentation/pages/developers/self-hosting/telemetry.mdx b/apps/documentation/pages/developers/self-hosting/telemetry.mdx index 9d8b64437..3a0b83eac 100644 --- a/apps/documentation/pages/developers/self-hosting/telemetry.mdx +++ b/apps/documentation/pages/developers/self-hosting/telemetry.mdx @@ -1,3 +1,8 @@ +--- +title: Telemetry +description: Learn about the telemetry data that Documenso collects from self-hosted instances. +--- + # Telemetry Documenso collects anonymous telemetry data from self-hosted instances to help us understand how the software is being used and make improvements to the product. This telemetry is enabled by default, but you can easily disable it if you prefer. diff --git a/apps/documentation/pages/developers/webhooks.mdx b/apps/documentation/pages/developers/webhooks.mdx index 8fac71210..a9684a9cc 100644 --- a/apps/documentation/pages/developers/webhooks.mdx +++ b/apps/documentation/pages/developers/webhooks.mdx @@ -5,7 +5,7 @@ description: Learn how to use webhooks to receive real-time notifications about # Webhooks -Webhooks are HTTP callbacks triggered by specific events. When the user subscribes to a specific event, and that event occurs, the webhook makes an HTTP request to the URL provided by the user. The request can be a simple notification or carry a payload with more information about the event. +Webhooks are HTTP callbacks triggered by specific events. When you subscribe to a specific event and that event occurs, the webhook makes an HTTP request to the URL you provide. The request can be a simple notification or carry a payload with more information about the event. Some of the common use cases for webhooks include: @@ -25,13 +25,13 @@ Documenso supports Webhooks and allows you to subscribe to the following events: ## Create a webhook subscription -You can create a webhook subscription from the user settings page. Click on your avatar in the top right corner of the dashboard and select "**[User settings](https://app.documenso.com/settings)**" from the dropdown menu. +You can create a webhook subscription from the team settings page. Click your avatar in the top right corner of the dashboard and select "Team settings" from the dropdown menu. -![A screenshot of the Documenso's dashboard that shows the dropdown menu when you click on your user avatar](/webhook-images/dashboard-user-dropdown-menu.webp) +![A screenshot of the Documenso's dashboard that shows the dropdown menu when you click on your user avatar](/webhook-images/documenso-main-page.webp) -Then, navigate to the "**[Webhooks](https://app.documenso.com/settings/webhooks)**" tab, where you can see a list of your existing webhooks and create new ones. +Then, navigate to the "Webhooks" tab, which takes you to the webhooks main page. -![A screenshot of the Documenso's user settings page that shows the Webhooks tab and the Create Webhook button](/webhook-images/webhooks-settings-page.webp) +![A screenshot of the Documenso's team settings page that shows the Webhooks tab and the Create Webhook button](/webhook-images/webhooks-main-page.webp) Clicking on the "**Create Webhook**" button opens a modal to create a new webhook subscription. @@ -41,7 +41,7 @@ To create a new webhook subscription, you need to provide the following informat - Select the event(s) you want to subscribe to: `document.created`, `document.sent`, `document.opened`, `document.signed`, `document.completed`, `document.rejected`, `document.cancelled`. - Optionally, you can provide a secret key that will be used to sign the payload. This key will be included in the `X-Documenso-Secret` header of the request. -![A screenshot of the Create Webhook modal that shows the URL input field and the event checkboxes](/webhook-images/webhooks-page-create-webhook-modal.webp) +![A screenshot of the Create Webhook modal that shows the URL input field and the event checkboxes](/webhook-images/create-webhook-dialog.webp) After you have filled in the required information, click on the "**Create Webhook**" button to save your subscription. @@ -49,7 +49,22 @@ The screenshot below illustrates a newly created webhook subscription. ![A screenshot of the Documenso's user settings page that shows the newly created webhook subscription](/webhook-images/webhooks-page.webp) -You can edit or delete your webhook subscriptions by clicking the "**Edit**" or "**Delete**" buttons next to the webhook. +You can edit, view the logs, or delete your webhook subscriptions by clicking the three dots (...) under the "Action" column. You can also access the webhook logs by clicking on the webhook subscription directly. + +![A screenshot of the Documenso's team settings page that shows the webhook logs](/webhook-images/webhook-detail-page.webp) + +You can go even further and check the execution details of each call by clicking on a specific webhook call. + +![A screenshot of the Documenso's team settings page that shows the webhook call details](/webhook-images/webhook-run-page.webp) + +This page shows the details of the webhook call such as: + +- status +- event +- date when the webhook was sent +- response code +- request body +- response body and headers ## Webhook fields @@ -619,18 +634,26 @@ Example payload for the `document.rejected` event: } ``` -## Webhook Events Testing +## Webhook events testing -You can trigger test webhook events to test the webhook functionality. To trigger a test webhook, navigate to the [Webhooks page](/developers/webhooks) and click on the "Test Webhook" button. +You can trigger test webhook events to test the webhook functionality. To do so, navigate to the webhook subscription details page and click the "Test" button. -![Documenso's Webhooks Page](/webhook-images/test-webhooks-page.webp) +![A screenshot of the Documenso's team settings page that shows the webhook logs](/webhook-images/webhook-detail-page.webp) This opens a dialog where you can select the event type to test. -![Documenso's individual webhook page](/webhook-images/test-webhook-dialog.webp) +![Documenso's individual webhook page](/webhook-images/webhook-test-trigger.webp) -Choose the appropriate event and click "Send Test Webhook." You’ll shortly receive a test payload from Documenso with sample data. +Choose the event you want to test and click "Send". You’ll then receive a test payload from Documenso with sample data. + +## Webhook events resending + +To resend a webhook call, you need to navigate to the webhook call page and click the "Resend" button. + +![A screenshot of the Documenso's team settings page that shows the webhook call details](/webhook-images/webhook-run-page.webp) + +This will send the webhook event to the webhook URL again. ## Availability -Webhooks are available to individual users and teams. +Webhooks are available to teams only. diff --git a/apps/documentation/pages/users/compliance/signature-levels.mdx b/apps/documentation/pages/users/compliance/signature-levels.mdx index 62c6350b6..9b6933ed9 100644 --- a/apps/documentation/pages/users/compliance/signature-levels.mdx +++ b/apps/documentation/pages/users/compliance/signature-levels.mdx @@ -1,3 +1,8 @@ +--- +title: Signature Levels +description: Learn about the different signature levels for Documenso. +--- + import { Callout } from 'nextra/components'; # Signature Levels @@ -26,20 +31,20 @@ ensures the legal validity and enforceability of electronic signatures and recor ### Main Requirements -- [x] Intent to Sign: "Parties must demonstrate their intent to sign [..]" -- [x] Consent: "The ESIGN Act requires that all parties involved in a transaction consent to the use of electronic signatures and records [..]" -- [x] Consumer Disclosures: Before obtaining their consent, financial institutions must provide the consumer a clear and conspicuous statement informing the consumer [..] -- [x] Record Retention: Electronic Records must be maintained for later access by signers. -- [x] Security: The ESIGN Act does not mandate specific security measures, but it does require that parties take reasonable steps to ensure the security and integrity of electronic signatures and records. This may include implementing encryption, access controls, and authentication measures. +- [x] **Intent to Sign**: "Parties must demonstrate their intent to sign [..]" +- [x] **Consent**: "The ESIGN Act requires that all parties involved in a transaction consent to the use of electronic signatures and records [..]" +- [x] **Consumer Disclosures**: Before obtaining their consent, financial institutions must provide the consumer a clear and conspicuous statement informing the consumer [..] +- [x] **Record Retention**: Electronic Records must be maintained for later access by signers. +- [x] **Security**: The ESIGN Act does not mandate specific security measures, but it does require that parties take reasonable steps to ensure the security and integrity of electronic signatures and records. This may include implementing encryption, access controls, and authentication measures. ## UETA (Uniform Electronic Transactions Act) Status: Compliant -The Uniform Electronic Transactions Act is a law that provides a legal framework for the use of electronic -signatures and records in electronic transactions, ensuring they have the same validity and enforceability -as paper documents and handwritten signatures. +The Uniform Electronic Transactions Act is a law that provides a legal framework for the use of +electronic signatures and records in electronic transactions, ensuring they have the same validity +and enforceability as paper documents and handwritten signatures. ### Main Requirements @@ -50,9 +55,9 @@ _See [ESIGN](/users/compliance/signature-levels#-esign-electronic-signatures-in- Status: Compliant for Level 1 - SES (Simple Electronic Signatures) -eIDAS (Electronic Identification, Authentication and Trust Services) is an EU regulation that standardizes -electronic identification and trust services for secure and seamless electronic transactions across European -member states. +eIDAS (Electronic Identification, Authentication and Trust Services) is an EU regulation that +standardizes electronic identification and trust services for secure and seamless electronic +transactions across European member states. ### Level 1 - SES (Simple Electronic Signatures) @@ -69,8 +74,8 @@ eIDAS SES (Simple Electronic Signature) is a basic electronic signature with min Status: [Planned](https://github.com/documenso/backlog/issues/9) via third party until [Let's Sign](https://github.com/documenso/backlog/issues/21) is realized. -eIDAS AES (Advanced Electronic Signature) provides a higher level of security with unique identification -of the signer and data integrity. +eIDAS AES (Advanced Electronic Signature) provides a higher level of security with unique +identification of the signer and data integrity. ### Main Requirements @@ -85,8 +90,8 @@ of the signer and data integrity. Status: [Planned](https://github.com/documenso/backlog/issues/32) via third party until [Let's Sign](https://github.com/documenso/backlog/issues/21) is realized. -eIDAS QES (Qualified Electronic Signature) is the highest security level, legally equivalent to a handwritten -signature within the EU. +eIDAS QES (Qualified Electronic Signature) is the highest security level, legally equivalent to a +handwritten signature within the EU. ### Main Requirements diff --git a/apps/documentation/pages/users/compliance/standards-and-regulations.mdx b/apps/documentation/pages/users/compliance/standards-and-regulations.mdx index 4d5193944..aacad6275 100644 --- a/apps/documentation/pages/users/compliance/standards-and-regulations.mdx +++ b/apps/documentation/pages/users/compliance/standards-and-regulations.mdx @@ -1,3 +1,8 @@ +--- +title: Standards and Regulations +description: Learn about the different standards and regulations for Documenso. +--- + import { Callout } from 'nextra/components'; ## 21 CFR Part 11 diff --git a/apps/documentation/pages/users/email-domains.mdx b/apps/documentation/pages/users/email-domains.mdx index d28b467e3..4a84141dc 100644 --- a/apps/documentation/pages/users/email-domains.mdx +++ b/apps/documentation/pages/users/email-domains.mdx @@ -1,3 +1,8 @@ +--- +title: Email Domains +description: Learn how to create and manage email domains in Documenso. +--- + import { Callout, Steps } from 'nextra/components'; # Email Domains diff --git a/apps/documentation/pages/users/fair-use.mdx b/apps/documentation/pages/users/fair-use.mdx index 2b2eb605a..2fb4198dd 100644 --- a/apps/documentation/pages/users/fair-use.mdx +++ b/apps/documentation/pages/users/fair-use.mdx @@ -7,28 +7,28 @@ import { Callout } from 'nextra/components'; # Fair Use Policy -### Why +We offer our plans without any limits on volume because we want our users and customers to make the most of their accounts. Estimating volume is incredibly hard, especially for shorter intervals like a quarter. We are not interested in selling volume packages our customers end up not using. -We offer our plans without any limits on volume because we want our users and customers to make the most of their accounts. Estimating volume is incredibly hard, especially for shorter intervals like a quarter. We are not interested in selling volume packages our customers end up not using. This is why the individual plan and the team plan do not include a limit on signing or API volume. If you are a customer of these [plans](https://documen.so/pricing), we ask you to abide by this fair use policy: +This is why the individual plan and the team plan do not include a limit on signing or API volume. If you are a customer of these [plans](https://documen.so/pricing), we ask you to abide by this fair use policy: ### Spirit of the Plan -> Use the limitless accounts as much as you like (they are meant to offer a lot) while respecting the spirit and intended scope of the account. +Use the limitless accounts as much as you like (they are meant to offer a lot) while respecting the spirit and intended scope of the account. What happens if I violate this policy? We will ask you to upgrade to a fitting plan or custom - pricing. We won’t block your account without reaching out. [Message - us](mailto:support@documenso.com) for questions. It's probably fine, though. + pricing. We won’t block your account without reaching out. You can [message + us](mailto:support@documenso.com) for questions. ### DO -- Sign as many documents with the individual plan for your single business or organization you are part of -- Use the API and Zapier to automate all your signing to sign as much as possible -- Experiment with the plans and integrations, testing what you want to build: When in doubt, do it. Especially if you are just starting. +- Sign as many documents as you need with the individual plan for your single business or organization you are part of +- Use the API and automation tools to automate all your signing workflows +- Experiment with the plans and integrations, testing what you want to build ### DON'T - Use the individual account's API to power a platform - Run a huge company, signing thousands of documents per day on a two-user team plan using the API -- Let this policy make you overthink. If you are a paying customer, we want you to win, and it's probably fine +- Let this policy make you overthink. If you are a paying customer, we want you to win diff --git a/apps/documentation/pages/users/get-started/account-creation.mdx b/apps/documentation/pages/users/get-started/account-creation.mdx index bea55e060..305486e6a 100644 --- a/apps/documentation/pages/users/get-started/account-creation.mdx +++ b/apps/documentation/pages/users/get-started/account-creation.mdx @@ -10,7 +10,12 @@ import { Callout, Steps } from 'nextra/components'; ### Pick a Plan -The first step to start using Documenso is to pick a plan and create an account. At the moment of writing this guide, we have 3 plans available: Free, Individual, Teams and Platform. +The first step to start using Documenso is to pick a plan and create an account. At the moment of writing this guide, we have 4 plans available: + +- Free +- Individual +- Teams +- Platform Explore each plan's features and choose the one that best suits your needs. The [pricing page](https://documen.so/pricing) has more information about the plans. @@ -24,7 +29,7 @@ To create a free account, navigate to the [registration page](https://documen.so ### Optional: Claim a Premium Username -You can claim a premium username by upgrading to a paid plan. After upgrading to a paid plan, you can update your [public profile](https://app.documenso.com/settings/public-profile). +You can claim a premium username by upgrading to a paid plan. After upgrading to a paid plan, you can update your [public profile](/users/profile). ### Optional: Create a Team diff --git a/apps/documentation/pages/users/licenses/community-edition.mdx b/apps/documentation/pages/users/licenses/community-edition.mdx index 88dde8fa7..f259fc2e5 100644 --- a/apps/documentation/pages/users/licenses/community-edition.mdx +++ b/apps/documentation/pages/users/licenses/community-edition.mdx @@ -1,3 +1,8 @@ +--- +title: Community Edition +description: Learn about the Community Edition of Documenso. +--- + import { Callout } from 'nextra/components'; # Community Edition @@ -32,10 +37,10 @@ Documenso and the Community Edition are licensed under [AGPL3](https://github.co ### Conditions -ℹ️ License and copyright notice -ℹ️ State changes -ℹ️ Disclose source -ℹ️ Network use is distribution +- License and copyright notice +- State changes +- Disclose source +- Network use is distribution It's important to remember that you must keep the AGPL3 license for your modified or non-modified diff --git a/apps/documentation/pages/users/licenses/enterprise-edition.mdx b/apps/documentation/pages/users/licenses/enterprise-edition.mdx index 8dab8b9e8..b1f6a77b2 100644 --- a/apps/documentation/pages/users/licenses/enterprise-edition.mdx +++ b/apps/documentation/pages/users/licenses/enterprise-edition.mdx @@ -1,3 +1,8 @@ +--- +title: Enterprise Edition +description: Learn about the Enterprise Edition of Documenso. +--- + import { Callout } from 'nextra/components'; # Enterprise Edition diff --git a/apps/documentation/pages/users/licenses/index.mdx b/apps/documentation/pages/users/licenses/index.mdx index cf5614c96..968274f5d 100644 --- a/apps/documentation/pages/users/licenses/index.mdx +++ b/apps/documentation/pages/users/licenses/index.mdx @@ -1,3 +1,8 @@ +--- +title: Licenses +description: Learn about the different licenses for self-hosting Documenso. +--- + # Self-Hosting Licenses Documenso comes in two versions for self-hosting: diff --git a/apps/documentation/pages/users/profile.mdx b/apps/documentation/pages/users/profile.mdx index 780c226c8..227432a0b 100644 --- a/apps/documentation/pages/users/profile.mdx +++ b/apps/documentation/pages/users/profile.mdx @@ -15,7 +15,7 @@ Documenso allows you to create a public profile to share your templates for anyo ### Navigate to Your Profile Settings -Click on your profile picture in the top right corner and select "Settings" or "Team Settings". Then, navigate to the "Public Profile" tab to configure your profile. +Click on your profile picture in the top right corner and select "Team Settings". Then, navigate to the "Public Profile" tab to configure your profile. ![The profile settings page](/public-profile/documenso-public-profile-settings.webp) diff --git a/apps/documentation/pages/users/support.mdx b/apps/documentation/pages/users/support.mdx index 7eb00636f..a5c39427c 100644 --- a/apps/documentation/pages/users/support.mdx +++ b/apps/documentation/pages/users/support.mdx @@ -9,30 +9,30 @@ description: Learn what types of support we offer. If you are a developer or free user, you can reach out to the community or raise an issue: -### [Create Github Issues](https://github.com/documenso/documenso/issues) +**[Create Github Issues](https://github.com/documenso/documenso/issues)** The community and the core team address GitHub issues. Be sure to check if a similar issue already exists. Please note that while we want to address everything immediately, we must prioritize. -### [Join our Discord](https://documen.so/discord) +**[Join our Discord](https://documen.so/discord)** You can ask for help in the [community help channel](https://discord.com/channels/1132216843537485854/1133419426524430376). ## Paid Account Support -### Email: support@documenso.com +**Email: support@documenso.com** If you are paying customers facing issues, email our customer support, especially in urgent cases. -### Private Discord channel +**Private Discord channel** If you prefer Discord, we can invite you to a private channel. Message support to make this happen. ## Enterprise Support -### Email: support@documenso.com +**Email: support@documenso.com** If you are paying customers facing issues, email our customer support, especially in urgent cases. -### Slack +**Slack** If your team is on Slack, we can create a private workspace to support you more closely. diff --git a/apps/documentation/pages/users/templates.mdx b/apps/documentation/pages/users/templates.mdx index 6f03a5d41..1269ebd33 100644 --- a/apps/documentation/pages/users/templates.mdx +++ b/apps/documentation/pages/users/templates.mdx @@ -1,3 +1,8 @@ +--- +title: Templates +description: Learn how to create and use templates in Documenso. +--- + import { Callout, Steps } from 'nextra/components'; # Document Templates diff --git a/apps/documentation/public/webhook-images/create-webhook-dialog.webp b/apps/documentation/public/webhook-images/create-webhook-dialog.webp new file mode 100644 index 000000000..25fd1cac8 Binary files /dev/null and b/apps/documentation/public/webhook-images/create-webhook-dialog.webp differ diff --git a/apps/documentation/public/webhook-images/dashboard-user-dropdown-menu.webp b/apps/documentation/public/webhook-images/dashboard-user-dropdown-menu.webp deleted file mode 100644 index 71838b2d7..000000000 Binary files a/apps/documentation/public/webhook-images/dashboard-user-dropdown-menu.webp and /dev/null differ diff --git a/apps/documentation/public/webhook-images/documenso-main-page.webp b/apps/documentation/public/webhook-images/documenso-main-page.webp new file mode 100644 index 000000000..b9f67a5af Binary files /dev/null and b/apps/documentation/public/webhook-images/documenso-main-page.webp differ diff --git a/apps/documentation/public/webhook-images/test-webhook-dialog.webp b/apps/documentation/public/webhook-images/test-webhook-dialog.webp deleted file mode 100644 index 59cdbfead..000000000 Binary files a/apps/documentation/public/webhook-images/test-webhook-dialog.webp and /dev/null differ diff --git a/apps/documentation/public/webhook-images/test-webhooks-page.webp b/apps/documentation/public/webhook-images/test-webhooks-page.webp deleted file mode 100644 index b5b833ced..000000000 Binary files a/apps/documentation/public/webhook-images/test-webhooks-page.webp and /dev/null differ diff --git a/apps/documentation/public/webhook-images/webhook-detail-page.webp b/apps/documentation/public/webhook-images/webhook-detail-page.webp new file mode 100644 index 000000000..ba9e0bf9e Binary files /dev/null and b/apps/documentation/public/webhook-images/webhook-detail-page.webp differ diff --git a/apps/documentation/public/webhook-images/webhook-run-page.webp b/apps/documentation/public/webhook-images/webhook-run-page.webp new file mode 100644 index 000000000..0a103a1fb Binary files /dev/null and b/apps/documentation/public/webhook-images/webhook-run-page.webp differ diff --git a/apps/documentation/public/webhook-images/webhook-test-trigger.webp b/apps/documentation/public/webhook-images/webhook-test-trigger.webp new file mode 100644 index 000000000..c5c93f94d Binary files /dev/null and b/apps/documentation/public/webhook-images/webhook-test-trigger.webp differ diff --git a/apps/documentation/public/webhook-images/webhooks-main-page.webp b/apps/documentation/public/webhook-images/webhooks-main-page.webp new file mode 100644 index 000000000..ac8ea2e88 Binary files /dev/null and b/apps/documentation/public/webhook-images/webhooks-main-page.webp differ diff --git a/apps/documentation/public/webhook-images/webhooks-page-create-webhook-modal.webp b/apps/documentation/public/webhook-images/webhooks-page-create-webhook-modal.webp deleted file mode 100644 index ed0ef44df..000000000 Binary files a/apps/documentation/public/webhook-images/webhooks-page-create-webhook-modal.webp and /dev/null differ diff --git a/apps/documentation/public/webhook-images/webhooks-page.webp b/apps/documentation/public/webhook-images/webhooks-page.webp index fd3d8998e..9df677425 100644 Binary files a/apps/documentation/public/webhook-images/webhooks-page.webp and b/apps/documentation/public/webhook-images/webhooks-page.webp differ diff --git a/apps/documentation/public/webhook-images/webhooks-settings-page.webp b/apps/documentation/public/webhook-images/webhooks-settings-page.webp deleted file mode 100644 index 2def663d9..000000000 Binary files a/apps/documentation/public/webhook-images/webhooks-settings-page.webp and /dev/null differ diff --git a/apps/openpage-api/package.json b/apps/openpage-api/package.json index 143dac214..a38d1160b 100644 --- a/apps/openpage-api/package.json +++ b/apps/openpage-api/package.json @@ -12,11 +12,11 @@ "dependencies": { "@documenso/prisma": "*", "luxon": "^3.7.2", - "next": "^15.5.7" + "next": "15.5.9" }, "devDependencies": { "@types/node": "^20", "@types/react": "18.3.27", "typescript": "5.6.2" } -} \ No newline at end of file +} diff --git a/apps/remix/app/components/dialogs/admin-organisation-member-update-dialog.tsx b/apps/remix/app/components/dialogs/admin-organisation-member-update-dialog.tsx index 1e6be1e3f..1889369e3 100644 --- a/apps/remix/app/components/dialogs/admin-organisation-member-update-dialog.tsx +++ b/apps/remix/app/components/dialogs/admin-organisation-member-update-dialog.tsx @@ -156,8 +156,8 @@ export const AdminOrganisationMemberUpdateDialog = ({ - You are currently updating{' '} - {organisationMemberName}. + You are currently updating {organisationMemberName} + . diff --git a/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx b/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx index 4d57652f3..57454a7ab 100644 --- a/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx +++ b/apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx @@ -353,8 +353,10 @@ export const EnvelopeDistributeDialog = ({ render={({ field }) => ( - Reply To Email{' '} - (Optional) + + Reply To Email{' '} + (Optional) + @@ -372,8 +374,10 @@ export const EnvelopeDistributeDialog = ({ render={({ field }) => ( - Subject{' '} - (Optional) + + Subject{' '} + (Optional) + @@ -390,8 +394,10 @@ export const EnvelopeDistributeDialog = ({ render={({ field }) => ( - Message{' '} - (Optional) + + Message{' '} + (Optional) + diff --git a/apps/remix/app/components/dialogs/envelope-item-delete-dialog.tsx b/apps/remix/app/components/dialogs/envelope-item-delete-dialog.tsx index dfee36496..c26df6e34 100644 --- a/apps/remix/app/components/dialogs/envelope-item-delete-dialog.tsx +++ b/apps/remix/app/components/dialogs/envelope-item-delete-dialog.tsx @@ -117,7 +117,7 @@ export const EnvelopeItemDeleteDialog = ({ - You cannot delete this item because the document has been sent to recipients + You cannot delete this item because the document has been sent to recipients. diff --git a/apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx b/apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx index ce91f3d52..7271308f0 100644 --- a/apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx +++ b/apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx @@ -121,7 +121,7 @@ export const OrganisationEmailDomainRecordContent = ({ records }: { records: Dom Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the "Sync" - domains button + domains button. diff --git a/apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx b/apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx index 07db162a2..4fd88568b 100644 --- a/apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx +++ b/apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx @@ -148,8 +148,8 @@ export const OrganisationMemberUpdateDialog = ({ - You are currently updating{' '} - {organisationMemberName}. + You are currently updating {organisationMemberName} + . diff --git a/apps/remix/app/components/dialogs/sign-field-signature-dialog.tsx b/apps/remix/app/components/dialogs/sign-field-signature-dialog.tsx index 6ce8c8e77..7e2f41a36 100644 --- a/apps/remix/app/components/dialogs/sign-field-signature-dialog.tsx +++ b/apps/remix/app/components/dialogs/sign-field-signature-dialog.tsx @@ -17,6 +17,7 @@ import { DocumentSigningDisclosure } from '../general/document-signing/document- export type SignFieldSignatureDialogProps = { initialSignature?: string; + fullName?: string; typedSignatureEnabled?: boolean; uploadSignatureEnabled?: boolean; drawSignatureEnabled?: boolean; @@ -28,6 +29,7 @@ export const SignFieldSignatureDialog = createCallable< >( ({ call, + fullName, typedSignatureEnabled, uploadSignatureEnabled, drawSignatureEnabled, @@ -46,6 +48,7 @@ export const SignFieldSignatureDialog = createCallable< setLocalSignature(value)} typedSignatureEnabled={typedSignatureEnabled} diff --git a/apps/remix/app/components/dialogs/team-delete-dialog.tsx b/apps/remix/app/components/dialogs/team-delete-dialog.tsx index 670ba1a2a..ac735e1ff 100644 --- a/apps/remix/app/components/dialogs/team-delete-dialog.tsx +++ b/apps/remix/app/components/dialogs/team-delete-dialog.tsx @@ -8,6 +8,7 @@ import { useForm } from 'react-hook-form'; import { useNavigate } from 'react-router'; import { z } from 'zod'; +import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation'; import { useSession } from '@documenso/lib/client-only/providers/session'; import { AppError } from '@documenso/lib/errors/app-error'; import { trpc } from '@documenso/trpc/react'; @@ -30,6 +31,13 @@ import { 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 type { Toast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast'; @@ -53,26 +61,34 @@ export const TeamDeleteDialog = ({ const { toast } = useToast(); const { refreshSession } = useSession(); + const currentOrganisation = useCurrentOrganisation(); + const deleteMessage = _(msg`delete ${teamName}`); + const filteredTeams = currentOrganisation.teams.filter((team) => team.id !== teamId); + const ZDeleteTeamFormSchema = z.object({ teamName: z.literal(deleteMessage, { errorMap: () => ({ message: _(msg`You must enter '${deleteMessage}' to proceed`) }), }), + transferTeamId: z.string().optional(), }); const form = useForm({ resolver: zodResolver(ZDeleteTeamFormSchema), defaultValues: { teamName: '', + transferTeamId: undefined, }, }); const { mutateAsync: deleteTeam } = trpc.team.delete.useMutation(); - const onFormSubmit = async () => { + const onFormSubmit = async (data: z.infer) => { try { - await deleteTeam({ teamId }); + const transferTeamId = data.transferTeamId ? parseInt(data.transferTeamId, 10) : undefined; + + await deleteTeam({ teamId, transferTeamId: transferTeamId || undefined }); await refreshSession(); @@ -168,6 +184,43 @@ export const TeamDeleteDialog = ({ )} /> + {filteredTeams.length > 0 && ( + ( + + + Transfer documents to a different team + + + + + + + + )} + /> + )} + -

+

Pre-formatted CSV template with example data.

@@ -200,14 +200,14 @@ export const TemplateBulkSendDialog = ({ ) : (
- + {value.name}
- - - - - - New Template - - - - Templates allow you to quickly generate documents with pre-filled recipients and - fields. - - - - -
- - - {isUploadingFile && ( -
- -
- )} -
- - - - - - -
- - ); -}; diff --git a/apps/remix/app/components/dialogs/webhook-create-dialog.tsx b/apps/remix/app/components/dialogs/webhook-create-dialog.tsx index da0a5f04c..9bc4b51e2 100644 --- a/apps/remix/app/components/dialogs/webhook-create-dialog.tsx +++ b/apps/remix/app/components/dialogs/webhook-create-dialog.tsx @@ -219,9 +219,8 @@ export const WebhookCreateDialog = ({ trigger, ...props }: WebhookCreateDialogPr A secret that will be sent to your URL so you can verify that the request - has been sent by Documenso + has been sent by Documenso. - . diff --git a/apps/remix/app/components/embed/embed-direct-template-client-page.tsx b/apps/remix/app/components/embed/embed-direct-template-client-page.tsx index 10b6d0f16..25fd330ba 100644 --- a/apps/remix/app/components/embed/embed-direct-template-client-page.tsx +++ b/apps/remix/app/components/embed/embed-direct-template-client-page.tsx @@ -438,6 +438,7 @@ export const EmbedDirectTemplateClientPage = ({ className="mt-2" disabled={isThrottled || isSubmitting} disableAnimation + fullName={fullName} value={signature ?? ''} onChange={(v) => setSignature(v ?? '')} typedSignatureEnabled={metadata?.typedSignatureEnabled} diff --git a/apps/remix/app/components/embed/embed-document-signing-page-v1.tsx b/apps/remix/app/components/embed/embed-document-signing-page-v1.tsx index 740597160..0d1c93b27 100644 --- a/apps/remix/app/components/embed/embed-document-signing-page-v1.tsx +++ b/apps/remix/app/components/embed/embed-document-signing-page-v1.tsx @@ -455,6 +455,7 @@ export const EmbedSignDocumentV1ClientPage = ({ className="mt-2" disabled={isThrottled || isSubmitting} disableAnimation + fullName={fullName} value={signature ?? ''} onChange={(v) => setSignature(v ?? '')} typedSignatureEnabled={metadata?.typedSignatureEnabled} diff --git a/apps/remix/app/components/embed/multisign/multi-sign-document-signing-view.tsx b/apps/remix/app/components/embed/multisign/multi-sign-document-signing-view.tsx index 4cc5fc4ae..9d994c759 100644 --- a/apps/remix/app/components/embed/multisign/multi-sign-document-signing-view.tsx +++ b/apps/remix/app/components/embed/multisign/multi-sign-document-signing-view.tsx @@ -319,6 +319,7 @@ export const MultiSignDocumentSigningView = ({ className="mt-2" disabled={isSubmitting} disableAnimation + fullName={fullName} value={signature ?? ''} onChange={(v) => setSignature(v ?? '')} typedSignatureEnabled={ diff --git a/apps/remix/app/components/forms/document-preferences-form.tsx b/apps/remix/app/components/forms/document-preferences-form.tsx index 0ffb4c839..c3d615f10 100644 --- a/apps/remix/app/components/forms/document-preferences-form.tsx +++ b/apps/remix/app/components/forms/document-preferences-form.tsx @@ -58,6 +58,7 @@ export type TDocumentPreferencesFormSchema = { includeSigningCertificate: boolean | null; includeAuditLog: boolean | null; signatureTypes: DocumentSignatureType[]; + delegateDocumentOwnership: boolean | null; aiFeaturesEnabled: boolean | null; }; @@ -73,6 +74,7 @@ type SettingsSubset = Pick< | 'typedSignatureEnabled' | 'uploadSignatureEnabled' | 'drawSignatureEnabled' + | 'delegateDocumentOwnership' | 'aiFeaturesEnabled' >; @@ -109,6 +111,7 @@ export const DocumentPreferencesForm = ({ signatureTypes: z.array(z.nativeEnum(DocumentSignatureType)).min(canInherit ? 0 : 1, { message: msg`At least one signature type must be enabled`.id, }), + delegateDocumentOwnership: z.boolean().nullable(), aiFeaturesEnabled: z.boolean().nullable(), }); @@ -125,6 +128,7 @@ export const DocumentPreferencesForm = ({ includeSigningCertificate: settings.includeSigningCertificate, includeAuditLog: settings.includeAuditLog, signatureTypes: extractTeamSignatureSettings({ ...settings }), + delegateDocumentOwnership: settings.delegateDocumentOwnership, aiFeaturesEnabled: settings.aiFeaturesEnabled, }, resolver: zodResolver(ZDocumentPreferencesFormSchema), @@ -515,6 +519,52 @@ export const DocumentPreferencesForm = ({ )} /> + ( + + + Delegate Document Ownership + + + + + + + Enable team API tokens to delegate document ownership to another team member. + + + + )} + /> + {isAiFeaturesConfigured && ( { - +
{ onChange(v ?? '')} /> diff --git a/apps/remix/app/components/forms/signin.tsx b/apps/remix/app/components/forms/signin.tsx index d32fc39a2..625b92af5 100644 --- a/apps/remix/app/components/forms/signin.tsx +++ b/apps/remix/app/components/forms/signin.tsx @@ -244,11 +244,11 @@ export const SignInForm = ({ const errorMessage = match(error.code) .with( AuthenticationErrorCode.InvalidCredentials, - () => msg`The email or password provided is incorrect`, + () => msg`The email or password provided is incorrect.`, ) .with( AuthenticationErrorCode.InvalidTwoFactorCode, - () => msg`The two-factor authentication code provided is incorrect`, + () => msg`The two-factor authentication code provided is incorrect.`, ) .otherwise(() => handleFallbackErrorMessages(error.code)); @@ -368,7 +368,7 @@ export const SignInForm = ({

Forgot your password? @@ -390,11 +390,11 @@ export const SignInForm = ({ <> {hasSocialAuthEnabled && (

-
- +
+ Or continue with -
+
)} @@ -403,7 +403,7 @@ export const SignInForm = ({ type="button" size="lg" variant="outline" - className="bg-background text-muted-foreground border" + className="border bg-background text-muted-foreground" disabled={isSubmitting} onClick={onSignInWithGoogleClick} > @@ -417,7 +417,7 @@ export const SignInForm = ({ type="button" size="lg" variant="outline" - className="bg-background text-muted-foreground border" + className="border bg-background text-muted-foreground" disabled={isSubmitting} onClick={onSignInWithMicrosoftClick} > @@ -435,7 +435,7 @@ export const SignInForm = ({ type="button" size="lg" variant="outline" - className="bg-background text-muted-foreground border" + className="border bg-background text-muted-foreground" disabled={isSubmitting} onClick={onSignInWithOIDCClick} > @@ -452,7 +452,7 @@ export const SignInForm = ({ variant="outline" disabled={isSubmitting} loading={isPasskeyLoading} - className="bg-background text-muted-foreground border" + className="border bg-background text-muted-foreground" onClick={onSignInWithPasskey} > {!isPasskeyLoading && } diff --git a/apps/remix/app/components/forms/token.tsx b/apps/remix/app/components/forms/token.tsx index 1b8e4f3e3..95b1a8830 100644 --- a/apps/remix/app/components/forms/token.tsx +++ b/apps/remix/app/components/forms/token.tsx @@ -131,7 +131,7 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => { const errorMessage = match(error.code) .with( AppErrorCode.UNAUTHORIZED, - () => msg`You do not have permission to create a token for this team`, + () => msg`You do not have permission to create a token for this team.`, ) .otherwise(() => msg`Something went wrong. Please try again later.`); @@ -212,12 +212,12 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => { />
- + Never expire
@@ -254,14 +254,14 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => { > -

+

Your token was created successfully! Make sure to copy it because you won't be able to see it again!

-

+

{newlyCreatedToken.token}

diff --git a/apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx b/apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx index 932a693fd..69d1ae8b1 100644 --- a/apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx +++ b/apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx @@ -417,6 +417,7 @@ export const DirectTemplateSigningForm = ({ setSignature(value)} typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled} @@ -433,7 +434,7 @@ export const DirectTemplateSigningForm = ({
)}
-

+

{match(recipient.role) .with(RecipientRole.VIEWER, () => View Document) .with(RecipientRole.SIGNER, () => Sign Document) @@ -65,7 +65,7 @@ export const DocumentSigningMobileWidget = () => { .otherwise(() => null)}

-

+

{recipientFieldsRemaining.length === 0 ? ( match(recipient.role) .with(RecipientRole.VIEWER, () => ( @@ -102,11 +102,11 @@ export const DocumentSigningMobileWidget = () => { {recipient.role !== RecipientRole.VIEWER && recipient.role !== RecipientRole.ASSISTANT && (

-
+
{ {/* Expandable Content */} {isExpanded && ( -
+
{!hidePoweredBy && ( -
+
Powered by
diff --git a/apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx b/apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx index e1ac12e3c..a4cb4a59c 100644 --- a/apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx +++ b/apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx @@ -109,7 +109,7 @@ export const DocumentSigningPageViewV2 = () => { (null); const [fontSize, setFontSize] = useState(2); - const { signature: providedSignature, setSignature: setProvidedSignature } = - useRequiredDocumentSigningContext(); + const { + fullName, + signature: providedSignature, + setSignature: setProvidedSignature, + } = useRequiredDocumentSigningContext(); const { executeActionAuthProcedure } = useRequiredDocumentSigningAuthContext(); @@ -236,13 +239,13 @@ export const DocumentSigningSignatureField = ({ type="Signature" > {isLoading && ( -
- +
+
)} {state === 'empty' && ( -

+

Signature

)} @@ -259,7 +262,7 @@ export const DocumentSigningSignatureField = ({

{signature?.typedSignature} @@ -272,12 +275,13 @@ export const DocumentSigningSignatureField = ({ Sign as {recipient.name}{' '} -

({recipient.email})
+
({recipient.email})
setLocalSignature(value)} typedSignatureEnabled={typedSignatureEnabled} diff --git a/apps/remix/app/components/general/document/document-upload-button-legacy.tsx b/apps/remix/app/components/general/document/document-upload-button-legacy.tsx index f4f13aaf0..9c031e821 100644 --- a/apps/remix/app/components/general/document/document-upload-button-legacy.tsx +++ b/apps/remix/app/components/general/document/document-upload-button-legacy.tsx @@ -14,9 +14,10 @@ import { useSession } from '@documenso/lib/client-only/providers/session'; import { APP_DOCUMENT_UPLOAD_SIZE_LIMIT } from '@documenso/lib/constants/app'; import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; -import { formatDocumentsPath } from '@documenso/lib/utils/teams'; +import { formatDocumentsPath, formatTemplatesPath } from '@documenso/lib/utils/teams'; import { trpc } from '@documenso/trpc/react'; import type { TCreateDocumentPayloadSchema } from '@documenso/trpc/server/document-router/create-document.types'; +import type { TCreateTemplatePayloadSchema } from '@documenso/trpc/server/template-router/schema'; import { cn } from '@documenso/ui/lib/utils'; import { DocumentUploadButton as DocumentUploadButtonPrimitive } from '@documenso/ui/primitives/document-upload-button'; import { @@ -31,9 +32,13 @@ import { useCurrentTeam } from '~/providers/team'; export type DocumentUploadButtonLegacyProps = { className?: string; + type: EnvelopeType; }; -export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLegacyProps) => { +export const DocumentUploadButtonLegacy = ({ + className, + type, +}: DocumentUploadButtonLegacyProps) => { const { _ } = useLingui(); const { toast } = useToast(); const { user } = useSession(); @@ -54,8 +59,18 @@ export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLe const [isLoading, setIsLoading] = useState(false); const { mutateAsync: createDocument } = trpc.document.create.useMutation(); + const { mutateAsync: createTemplate } = trpc.template.createTemplate.useMutation(); const disabledMessage = useMemo(() => { + if (!user.emailVerified) { + return msg`Verify your email to upload documents.`; + } + + // No errors for templates. + if (type === EnvelopeType.TEMPLATE) { + return; + } + if (organisation.subscription && remaining.documents === 0) { return msg`Document upload disabled due to unpaid invoices`; } @@ -64,11 +79,8 @@ export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLe return msg`You have reached your document limit.`; } - if (!user.emailVerified) { - return msg`Verify your email to upload documents.`; - } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [remaining.documents, user.emailVerified, team]); + }, [remaining.documents, user.emailVerified, team, type]); const onFileDrop = async (file: File) => { try { @@ -80,44 +92,62 @@ export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLe meta: { timezone: userTimezone, }, - } satisfies TCreateDocumentPayloadSchema; + } satisfies TCreateDocumentPayloadSchema | TCreateTemplatePayloadSchema; const formData = new FormData(); formData.append('payload', JSON.stringify(payload)); formData.append('file', file); - const { envelopeId: id } = await createDocument(formData); + // Handle legacy document creation. + if (type === EnvelopeType.DOCUMENT) { + const { envelopeId: id } = await createDocument(formData); - void refreshLimits(); + void refreshLimits(); - await navigate(`${formatDocumentsPath(team.url)}/${id}/edit`); + await navigate(`${formatDocumentsPath(team.url)}/${id}/edit`); - toast({ - title: _(msg`Document uploaded`), - description: _(msg`Your document has been uploaded successfully.`), - duration: 5000, - }); + toast({ + title: _(msg`Document uploaded`), + description: _(msg`Your document has been uploaded successfully.`), + duration: 5000, + }); - analytics.capture('App: Document Uploaded', { - userId: user.id, - documentId: id, - timestamp: new Date().toISOString(), - }); + analytics.capture('App: Document Uploaded', { + userId: user.id, + documentId: id, + timestamp: new Date().toISOString(), + }); + } + + // Handle legacy template creation. + if (type === EnvelopeType.TEMPLATE) { + const { envelopeId: id } = await createTemplate(formData); + + await navigate(`${formatTemplatesPath(team.url)}/${id}/edit`); + + toast({ + title: _(msg`Template document uploaded`), + description: _( + msg`Your document has been uploaded successfully. You will be redirected to the template page.`, + ), + duration: 5000, + }); + } } catch (err) { const error = AppError.parseError(err); console.error(err); const errorMessage = match(error.code) - .with('INVALID_DOCUMENT_FILE', () => msg`You cannot upload encrypted PDFs`) + .with('INVALID_DOCUMENT_FILE', () => msg`You cannot upload encrypted PDFs.`) .with( AppErrorCode.LIMIT_EXCEEDED, () => msg`You have reached your document limit for this month. Please upgrade your plan.`, ) .with( 'ENVELOPE_ITEM_LIMIT_EXCEEDED', - () => msg`You have reached the limit of the number of files per envelope`, + () => msg`You have reached the limit of the number of files per envelope.`, ) .otherwise(() => msg`An error occurred while uploading your document.`); @@ -149,17 +179,18 @@ export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLe
onFileDrop(files[0])} onDropRejected={onFileDropRejected} - type={EnvelopeType.DOCUMENT} + type={type} internalVersion="1" />
{team?.id === undefined && + type === EnvelopeType.DOCUMENT && remaining.documents > 0 && Number.isFinite(remaining.documents) && ( diff --git a/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx b/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx index 49166b44a..475dc5b62 100644 --- a/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx +++ b/apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx @@ -339,7 +339,7 @@ export const EnvelopeEditorSettingsDialog = ({ {/* Sidebar. */} -
+
Document Settings @@ -390,7 +390,7 @@ export const EnvelopeEditorSettingsDialog = ({ - + Controls the language for the document, including the language to be used for email notifications, and the final certificate @@ -441,7 +441,7 @@ export const EnvelopeEditorSettingsDialog = ({ }))} selectedValues={field.value} onChange={field.onChange} - className="bg-background w-full" + className="w-full bg-background" emptySelectionPlaceholder="Select signature types" /> @@ -518,7 +518,7 @@ export const EnvelopeEditorSettingsDialog = ({ - + Add an external ID to the document. This can be used to identify the document in external systems. @@ -548,7 +548,7 @@ export const EnvelopeEditorSettingsDialog = ({ - + Add a URL to redirect the user to once the document is signed @@ -576,7 +576,7 @@ export const EnvelopeEditorSettingsDialog = ({ - +

Document Distribution Method @@ -687,8 +687,10 @@ export const EnvelopeEditorSettingsDialog = ({ render={({ field }) => ( - Reply To Email{' '} - (Optional) + + Reply To Email{' '} + (Optional) + @@ -726,20 +728,21 @@ export const EnvelopeEditorSettingsDialog = ({ render={({ field }) => ( - Message{' '} - (Optional) + + Message (Optional) + - + -