mirror of
https://github.com/documenso/documenso.git
synced 2026-07-11 05:25:08 +10:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b0d004c7cf | |||
| 403c1ca916 | |||
| b79895b38c |
+1
-1
@@ -103,7 +103,7 @@ NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID="documenso"
|
|||||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY="password"
|
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY="password"
|
||||||
|
|
||||||
# [[SMTP]]
|
# [[SMTP]]
|
||||||
# OPTIONAL: Defines the transport to use for sending emails. Available options: smtp-auth (default) | smtp-api | resend | mailchannels
|
# OPTIONAL: Defines the transport to use for sending emails. Available options: smtp-auth (default) | smtp-api | mailchannels
|
||||||
NEXT_PRIVATE_SMTP_TRANSPORT="smtp-auth"
|
NEXT_PRIVATE_SMTP_TRANSPORT="smtp-auth"
|
||||||
# OPTIONAL: Defines the host to use for sending emails.
|
# OPTIONAL: Defines the host to use for sending emails.
|
||||||
NEXT_PRIVATE_SMTP_HOST="127.0.0.1"
|
NEXT_PRIVATE_SMTP_HOST="127.0.0.1"
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ body:
|
|||||||
label: Browser [e.g., Chrome, Firefox]
|
label: Browser [e.g., Chrome, Firefox]
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Version [e.g., 2.13.0]
|
label: Version [e.g., 2.0.1]
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Please check the boxes that apply to this issue report.
|
label: Please check the boxes that apply to this issue report.
|
||||||
@@ -44,3 +44,4 @@ body:
|
|||||||
- label: I have included relevant environment information.
|
- label: I have included relevant environment information.
|
||||||
- label: I have included any relevant screenshots.
|
- label: I have included any relevant screenshots.
|
||||||
- label: I understand that this is a voluntary contribution and that there is no guarantee of resolution.
|
- label: I understand that this is a voluntary contribution and that there is no guarantee of resolution.
|
||||||
|
- label: I want to work on creating a PR for this issue if approved
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: Security vulnerability
|
|
||||||
url: https://github.com/documenso/documenso/security/advisories/new
|
|
||||||
about: Please report security vulnerabilities privately via GitHub Security Advisories. Do not open a public issue.
|
|
||||||
- name: Questions & Discussions
|
|
||||||
url: https://github.com/documenso/documenso/discussions
|
|
||||||
about: Ask questions, share ideas, and discuss Documenso with the community.
|
|
||||||
- name: Discord
|
|
||||||
url: https://documen.so/discord
|
|
||||||
about: Chat with the community and the team.
|
|
||||||
@@ -33,3 +33,4 @@ body:
|
|||||||
- label: I have explained the use case or scenario for this feature.
|
- label: I have explained the use case or scenario for this feature.
|
||||||
- label: I have included any relevant technical details or design suggestions.
|
- label: I have included any relevant technical details or design suggestions.
|
||||||
- label: I understand that this is a suggestion and that there is no guarantee of implementation.
|
- label: I understand that this is a suggestion and that there is no guarantee of implementation.
|
||||||
|
- label: I want to work on creating a PR for this issue if approved
|
||||||
|
|||||||
@@ -15,6 +15,17 @@ body:
|
|||||||
description: 'Are there any additional context or information that might be relevant to the improvement suggestion.'
|
description: 'Are there any additional context or information that might be relevant to the improvement suggestion.'
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: assignee
|
||||||
|
attributes:
|
||||||
|
label: 'Do you want to work on this improvement?'
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- 'No'
|
||||||
|
- 'Yes'
|
||||||
|
default: 0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: 'Please check the boxes that apply to this improvement suggestion.'
|
label: 'Please check the boxes that apply to this improvement suggestion.'
|
||||||
|
|||||||
@@ -1,14 +1,3 @@
|
|||||||
<!--
|
|
||||||
We are no longer accepting external pull requests.
|
|
||||||
|
|
||||||
Aside from a small group of trusted contributors we reach out to directly,
|
|
||||||
new external PRs will usually be closed with a request to open an issue instead.
|
|
||||||
This is a security decision. See https://documenso.com/blog/why-we-re-pausing-external-pull-requests
|
|
||||||
|
|
||||||
If you're a trusted contributor or maintainer, continue below.
|
|
||||||
Otherwise, please open a detailed issue: https://github.com/documenso/documenso/issues/new/choose
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
<!--- Describe the changes introduced by this pull request. -->
|
<!--- Describe the changes introduced by this pull request. -->
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
name: Test Addition
|
||||||
|
about: Submit a new test, either unit or end-to-end (E2E), for review and inclusion
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!--- Provide a clear and concise description of the new test you are adding. -->
|
||||||
|
<!--- Explain the purpose of the test and what it aims to validate. -->
|
||||||
|
|
||||||
|
## Related Issue
|
||||||
|
|
||||||
|
<!--- If this test addition is related to a specific issue, reference it here using #issue_number. -->
|
||||||
|
<!--- For example, "Fixes #123" or "Addresses #456". -->
|
||||||
|
|
||||||
|
## Test Details
|
||||||
|
|
||||||
|
<!--- Describe the details of the test you're adding. -->
|
||||||
|
<!--- Include information about inputs, expected outputs, and any specific scenarios. -->
|
||||||
|
|
||||||
|
- Test Name: Name of the test
|
||||||
|
- Type: [Unit / E2E]
|
||||||
|
- Description: Brief description of what the test checks
|
||||||
|
- Inputs: What inputs the test uses (if applicable)
|
||||||
|
- Expected Output: What output or behavior the test expects
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
<!--- Please check the boxes that apply to this pull request. -->
|
||||||
|
<!--- You can add or remove items as needed. -->
|
||||||
|
|
||||||
|
- [ ] I have written the new test and ensured it works as intended.
|
||||||
|
- [ ] I have added necessary documentation to explain the purpose of the test.
|
||||||
|
- [ ] I have followed the project's testing guidelines and coding style.
|
||||||
|
- [ ] I have addressed any review feedback from previous submissions, if applicable.
|
||||||
|
|
||||||
|
## Additional Notes
|
||||||
|
|
||||||
|
<!--- Provide any additional context or notes for the reviewers. -->
|
||||||
|
<!--- This might include explanations about the testing approach or any potential concerns. -->
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
name: 'Welcome New Contributors'
|
name: 'Welcome New Contributors'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: ['opened']
|
||||||
issues:
|
issues:
|
||||||
types: ['opened']
|
types: ['opened']
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
issues: write
|
issues: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -17,7 +20,10 @@ jobs:
|
|||||||
- uses: actions/first-interaction@v1
|
- uses: actions/first-interaction@v1
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
pr-message: |
|
||||||
|
Thank you for creating your first Pull Request and for being a part of the open signing revolution! 💚🚀
|
||||||
|
<br /> Feel free to hop into our community in [Discord](https://documen.so/discord)
|
||||||
issue-message: |
|
issue-message: |
|
||||||
Thank you for opening your first issue and for being a part of the open signing revolution!
|
Thank you for opening your first issue and for being a part of the open signing revolution!
|
||||||
<br /> One of our team members will review it and get back to you as soon as possible 💚
|
<br /> One of our team members will review it and get back to you as soon as it possible 💚
|
||||||
<br /> Meanwhile, please feel free to hop into our community in [Discord](https://documen.so/discord)
|
<br /> Meanwhile, please feel free to hop into our community in [Discord](https://documen.so/discord)
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
name: 'Issue Assignee Check'
|
||||||
|
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: ['assigned']
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
countIssues:
|
||||||
|
if: ${{ github.event.issue.assignee }} && github.event.action == 'assigned' && github.event.sender.type == 'User'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: Install Octokit
|
||||||
|
run: npm install @octokit/rest@18
|
||||||
|
|
||||||
|
- name: Check Assigned User's Issue Count
|
||||||
|
id: parse-comment
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
const { Octokit } = require("@octokit/rest");
|
||||||
|
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
|
||||||
|
|
||||||
|
const username = context.payload.issue.assignee.login;
|
||||||
|
console.log(`Username Extracted: ${username}`);
|
||||||
|
|
||||||
|
const { data: issues } = await octokit.issues.listForRepo({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
assignee: username,
|
||||||
|
state: 'open'
|
||||||
|
});
|
||||||
|
|
||||||
|
const issueCount = issues.length;
|
||||||
|
console.log(`Issue Count For ${username}: ${issueCount}`);
|
||||||
|
|
||||||
|
if (issueCount > 3) {
|
||||||
|
let issueCountMessage = `### 🚨 Documenso Police 🚨`;
|
||||||
|
issueCountMessage += `\n@${username} has ${issueCount} open issues assigned already. Consider whether this issue should be assigned to them or left open for another contributor.`;
|
||||||
|
|
||||||
|
await octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', {
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
body: issueCountMessage,
|
||||||
|
headers: {
|
||||||
|
'Authorization': `token ${{ secrets.GITHUB_TOKEN }}`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
name: 'PR Review Reminder'
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: ['opened', 'ready_for_review']
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
checkPRs:
|
||||||
|
if: ${{ github.event.pull_request.user.login }} && github.event.action == ('opened' || 'ready_for_review')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '18'
|
||||||
|
|
||||||
|
- name: Install Octokit
|
||||||
|
run: npm install @octokit/rest@18
|
||||||
|
|
||||||
|
- name: Check user's PRs awaiting review
|
||||||
|
id: parse-prs
|
||||||
|
uses: actions/github-script@v5
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
const { Octokit } = require("@octokit/rest");
|
||||||
|
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
|
||||||
|
|
||||||
|
const username = context.payload.pull_request.user.login;
|
||||||
|
console.log(`Username Extracted: ${username}`);
|
||||||
|
|
||||||
|
const { data: pullRequests } = await octokit.pulls.list({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
state: 'open',
|
||||||
|
sort: 'created',
|
||||||
|
direction: 'asc',
|
||||||
|
});
|
||||||
|
|
||||||
|
const userPullRequests = pullRequests.filter(pr => pr.user.login === username && (pr.state === 'open' || pr.state === 'ready_for_review'));
|
||||||
|
const prCount = userPullRequests.length;
|
||||||
|
console.log(`PR Count for ${username}: ${prCount}`);
|
||||||
|
|
||||||
|
if (prCount > 3) {
|
||||||
|
let prReminderMessage = `🚨 @${username} has ${prCount} pull requests awaiting review. Please consider reviewing them when possible. 🚨`;
|
||||||
|
|
||||||
|
await octokit.request('POST /repos/{owner}/{repo}/issues/{issue_number}/comments', {
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.payload.pull_request.number,
|
||||||
|
body: prReminderMessage,
|
||||||
|
headers: {
|
||||||
|
'Authorization': `token ${{ secrets.GITHUB_TOKEN }}`,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -16,6 +16,24 @@ jobs:
|
|||||||
name: Validate PR title
|
name: Validate PR title
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Check PR creator's previous activity
|
||||||
|
id: check_activity
|
||||||
|
run: |
|
||||||
|
CREATOR=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}" | jq -r '.user.login')
|
||||||
|
ACTIVITY=$(curl -s "https://api.github.com/search/commits?q=author:${CREATOR}+repo:${{ github.repository }}" | jq -r '.total_count')
|
||||||
|
if [ "$ACTIVITY" -eq 0 ]; then
|
||||||
|
echo "::set-output name=is_new::true"
|
||||||
|
else
|
||||||
|
echo "::set-output name=is_new::false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Count PRs created by user
|
||||||
|
id: count_prs
|
||||||
|
run: |
|
||||||
|
CREATOR=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}" | jq -r '.user.login')
|
||||||
|
PR_COUNT=$(curl -s "https://api.github.com/search/issues?q=type:pr+is:open+author:${CREATOR}+repo:${{ github.repository }}" | jq -r '.total_count')
|
||||||
|
echo "::set-output name=pr_count::$PR_COUNT"
|
||||||
|
|
||||||
- uses: amannn/action-semantic-pull-request@v5
|
- uses: amannn/action-semantic-pull-request@v5
|
||||||
id: lint_pr_title
|
id: lint_pr_title
|
||||||
env:
|
env:
|
||||||
@@ -26,6 +44,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
header: pr-title-lint-error
|
header: pr-title-lint-error
|
||||||
message: |
|
message: |
|
||||||
|
Hey There! and thank you for opening this pull request! 📝👋🏼
|
||||||
|
|
||||||
We require pull request titles to follow the [Conventional Commits Spec](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted.
|
We require pull request titles to follow the [Conventional Commits Spec](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted.
|
||||||
|
|
||||||
Details:
|
Details:
|
||||||
@@ -33,3 +53,10 @@ jobs:
|
|||||||
```
|
```
|
||||||
${{ steps.lint_pr_title.outputs.error_message }}
|
${{ steps.lint_pr_title.outputs.error_message }}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- if: ${{ steps.lint_pr_title.outputs.error_message == null && steps.check_activity.outputs.is_new == 'false' && steps.count_prs.outputs.pr_count < 2}}
|
||||||
|
uses: marocchino/sticky-pull-request-comment@v2
|
||||||
|
with:
|
||||||
|
header: pr-title-lint-error
|
||||||
|
message: |
|
||||||
|
Thank you for following the naming conventions for pull request titles! 💚🚀
|
||||||
|
|||||||
@@ -21,4 +21,4 @@ jobs:
|
|||||||
stale-pr-message: 'This PR has not seen activitiy for a while. It will be closed in 30 days unless further activity is detected.'
|
stale-pr-message: 'This PR has not seen activitiy for a while. It will be closed in 30 days unless further activity is detected.'
|
||||||
close-pr-message: 'This PR has been closed because of inactivity.'
|
close-pr-message: 'This PR has been closed because of inactivity.'
|
||||||
exempt-pr-labels: 'WIP,on-hold,needs review'
|
exempt-pr-labels: 'WIP,on-hold,needs review'
|
||||||
exempt-issue-labels: 'WIP,on-hold,needs review,roadmap,status: assigned,status: triage'
|
exempt-issue-labels: 'WIP,on-hold,needs review,roadmap,assigned,needs triage'
|
||||||
|
|||||||
+2
-1
@@ -31,7 +31,8 @@ vscode:
|
|||||||
extensions:
|
extensions:
|
||||||
- aaron-bond.better-comments
|
- aaron-bond.better-comments
|
||||||
- bradlc.vscode-tailwindcss
|
- bradlc.vscode-tailwindcss
|
||||||
- biomejs.biome
|
- dbaeumer.vscode-eslint
|
||||||
|
- esbenp.prettier-vscode
|
||||||
- mikestead.dotenv
|
- mikestead.dotenv
|
||||||
- unifiedjs.vscode-mdx
|
- unifiedjs.vscode-mdx
|
||||||
- GitHub.vscode-pull-request-github
|
- GitHub.vscode-pull-request-github
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
# Report security vulnerabilities privately via GitHub Security Advisories (preferred).
|
# General Issues
|
||||||
Contact: https://github.com/documenso/documenso/security/advisories/new
|
|
||||||
|
|
||||||
# Alternatively, report critical issues privately by email.
|
|
||||||
Contact: mailto:security@documenso.com
|
|
||||||
|
|
||||||
# Security policy
|
|
||||||
Policy: https://github.com/documenso/documenso/security/policy
|
|
||||||
|
|
||||||
# General (non-security) issues
|
|
||||||
Contact: https://github.com/documenso/documenso/issues/new?assignees=&labels=bug&projects=&template=bug-report.yml
|
Contact: https://github.com/documenso/documenso/issues/new?assignees=&labels=bug&projects=&template=bug-report.yml
|
||||||
|
|
||||||
|
# Report critical issues privately to let us take appropriate action before publishing.
|
||||||
|
Contact: mailto:security@documenso.com
|
||||||
Preferred-Languages: en
|
Preferred-Languages: en
|
||||||
Canonical: https://documenso.com/.well-known/security.txt
|
Canonical: https://documenso.com/.well-known/security.txt
|
||||||
@@ -65,7 +65,6 @@ Documenso is an open-source document signing platform built as a **monorepo** us
|
|||||||
| Package | Description |
|
| Package | Description |
|
||||||
| ---------------------------- | ------------------------- |
|
| ---------------------------- | ------------------------- |
|
||||||
| `@documenso/app-tests` | E2E tests (Playwright) |
|
| `@documenso/app-tests` | E2E tests (Playwright) |
|
||||||
| `@documenso/tailwind-config` | Shared Tailwind config |
|
|
||||||
| `@documenso/tsconfig` | Shared TypeScript configs |
|
| `@documenso/tsconfig` | Shared TypeScript configs |
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|||||||
+6
-20
@@ -1,27 +1,13 @@
|
|||||||
# Contributing to Documenso
|
# Contributing to Documenso
|
||||||
|
|
||||||
> **We are no longer accepting external pull requests.**
|
If you plan to contribute to Documenso, please take a moment to feel awesome ✨ People like you are what open source is about ♥. Any contributions, no matter how big or small, are highly appreciated.
|
||||||
>
|
|
||||||
> Aside from a small group of trusted contributors we reach out to directly, we no longer merge external PRs. New pull requests will usually be closed with a request to open an issue instead. This is a security decision, not a judgement on your work. Read [Why We're Pausing External Pull Requests](https://documenso.com/blog/why-we-re-pausing-external-pull-requests) for the full reasoning.
|
|
||||||
>
|
|
||||||
> Documenso stays open source. You can still read, audit, run, and fork the code. The best way to contribute is through detailed issues.
|
|
||||||
|
|
||||||
## How to contribute now
|
## Before getting started
|
||||||
|
|
||||||
The most useful contribution is a detailed issue. Treat it like a spec. The more detail, the better:
|
- Before jumping into a PR be sure to search [existing PRs](https://github.com/documenso/documenso/pulls) or [issues](https://github.com/documenso/documenso/issues) for an open or closed item that relates to your submission.
|
||||||
|
- Select an issue from [here](https://github.com/documenso/documenso/issues) or create a new one
|
||||||
- The problem you're trying to solve, and who it affects
|
- Consider the results from the discussion on the issue
|
||||||
- How you expect the feature or change to behave
|
- Accept the [Contributor License Agreement](https://documen.so/cla) to ensure we can accept your contributions.
|
||||||
- Edge cases, constraints, and anything you've already considered
|
|
||||||
- Examples, mockups, or references where they help
|
|
||||||
|
|
||||||
Before opening an issue, search [existing issues](https://github.com/documenso/documenso/issues) and [discussions](https://github.com/documenso/documenso/discussions) for related items. If a proposal is detailed and fits where Documenso is heading, we'll pick it up and build against it.
|
|
||||||
|
|
||||||
For security vulnerabilities, do not open a public issue. Follow our [Security Policy](./SECURITY.md) instead.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
The sections below are for trusted contributors working with us directly, and for anyone running Documenso locally or maintaining a fork.
|
|
||||||
|
|
||||||
## English only PRs and Issues
|
## English only PRs and Issues
|
||||||
|
|
||||||
|
|||||||
@@ -51,18 +51,16 @@ Join us in creating the next generation of open trust infrastructure.
|
|||||||
|
|
||||||
## Community and Next Steps 🎯
|
## Community and Next Steps 🎯
|
||||||
|
|
||||||
- Try Documenso by self-hosting it or signing up at [documenso.com](https://documenso.com).
|
- Check out the first source code release in this repository and test it.
|
||||||
- Tell us what you think in the [Discussions](https://github.com/documenso/documenso/discussions).
|
- Tell us what you think in the [Discussions](https://github.com/documenso/documenso/discussions).
|
||||||
- Join the [Discord server](https://documen.so/discord) for any questions and getting to know other community members.
|
- Join the [Discord server](https://documen.so/discord) for any questions and getting to know to other community members.
|
||||||
- ⭐ the repository to help us raise awareness.
|
- ⭐ the repository to help us raise awareness.
|
||||||
- Open detailed [issues](https://github.com/documenso/documenso/issues) to report bugs or propose features.
|
- Spread the word on Twitter that Documenso is working towards a more open signing tool.
|
||||||
|
- Fix or create [issues](https://github.com/documenso/documenso/issues), that are needed for the first production release.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
> **Note**: We no longer accept external pull requests, aside from a small group of trusted contributors we reach out to directly. The best way to contribute is through detailed issues. Read [Why We're Pausing External Pull Requests](https://documenso.com/blog/why-we-re-pausing-external-pull-requests) for the reasoning.
|
- To contribute, please see our [contribution guide](https://github.com/documenso/documenso/blob/main/CONTRIBUTING.md).
|
||||||
|
|
||||||
- Documenso stays open source. You can read, audit, run, and fork the code.
|
|
||||||
- To report issues or propose changes, see our [contribution guide](https://github.com/documenso/documenso/blob/main/CONTRIBUTING.md).
|
|
||||||
|
|
||||||
## Contact us
|
## Contact us
|
||||||
|
|
||||||
@@ -83,21 +81,17 @@ Contact us if you are interested in our Enterprise plan for large organizations
|
|||||||
<a href=""><img src="" alt=""></a>
|
<a href=""><img src="" alt=""></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
- [TypeScript](https://www.typescriptlang.org/) - Language
|
- [Typescript](https://www.typescriptlang.org/) - Language
|
||||||
- [React Router v7](https://reactrouter.com/) - Framework
|
- [ReactRouter](https://reactrouter.com/) - Framework
|
||||||
- [Hono](https://hono.dev/) - Server
|
|
||||||
- [Prisma](https://www.prisma.io/) - ORM
|
- [Prisma](https://www.prisma.io/) - ORM
|
||||||
- [Tailwind CSS](https://tailwindcss.com/) - CSS
|
- [Tailwind](https://tailwindcss.com/) - CSS
|
||||||
- [shadcn/ui](https://ui.shadcn.com/) + [Radix UI](https://www.radix-ui.com/) - Component Library
|
- [shadcn/ui](https://ui.shadcn.com/) - Component Library
|
||||||
- [react-email](https://react.email/) - Email Templates
|
- [react-email](https://react.email/) - Email Templates
|
||||||
- [Lingui](https://lingui.dev/) - Internationalization
|
|
||||||
- [tRPC](https://trpc.io/) - API
|
- [tRPC](https://trpc.io/) - API
|
||||||
- [@libpdf/core](https://www.npmjs.com/package/@libpdf/core) - PDF Signatures
|
- [@documenso/pdf-sign](https://www.npmjs.com/package/@documenso/pdf-sign) - PDF Signatures (launching soon)
|
||||||
- [pdf.js](https://mozilla.github.io/pdf.js/) - Viewing PDFs
|
- [React-PDF](https://github.com/wojtekmaj/react-pdf) - Viewing PDFs
|
||||||
- [@cantoo/pdf-lib](https://github.com/cantoo-scribe/pdf-lib) - PDF manipulation
|
- [PDF-Lib](https://github.com/Hopding/pdf-lib) - PDF manipulation
|
||||||
- [Stripe](https://stripe.com/) - Payments
|
- [Stripe](https://stripe.com/) - Payments
|
||||||
- [Biome](https://biomejs.dev/) - Linting & Formatting
|
|
||||||
- [Playwright](https://playwright.dev/) - E2E Testing
|
|
||||||
|
|
||||||
<!-- - Support for [opensignpdf (requires Java on server)](https://github.com/open-pdf-sign) is currently planned. -->
|
<!-- - Support for [opensignpdf (requires Java on server)](https://github.com/open-pdf-sign) is currently planned. -->
|
||||||
|
|
||||||
@@ -202,10 +196,6 @@ For full instructions, requirements, and configuration details, see the [Self Ho
|
|||||||
|
|
||||||
[](https://elest.io/open-source/documenso)
|
[](https://elest.io/open-source/documenso)
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
If you believe you have found a security vulnerability in Documenso, please report it through our [Security Policy](https://github.com/documenso/documenso/security/policy). We prioritize private reports via [GitHub Security Advisories](https://github.com/documenso/documenso/security/advisories/new). See [SECURITY.md](./SECURITY.md) for scope and details.
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
For troubleshooting self-hosted deployments, see the [Troubleshooting guide](https://docs.documenso.com/docs/self-hosting/maintenance/troubleshooting) and [Tips & Common Pitfalls](https://docs.documenso.com/docs/self-hosting/getting-started/tips).
|
For troubleshooting self-hosted deployments, see the [Troubleshooting guide](https://docs.documenso.com/docs/self-hosting/maintenance/troubleshooting) and [Tips & Common Pitfalls](https://docs.documenso.com/docs/self-hosting/getting-started/tips).
|
||||||
|
|||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
We take the security of Documenso seriously. As a platform trusted with legally binding documents, the safety of the project and the people who rely on it is a priority for us. We're grateful to the security researchers who help keep it that way. If you've found an issue, we'd genuinely like to hear about it.
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
Report security vulnerabilities privately. Do not open a public issue, discussion, or pull request for security reports.
|
|
||||||
|
|
||||||
We accept reports through two channels, in order of preference:
|
|
||||||
|
|
||||||
1. **GitHub Security Advisories (preferred)**. Use the [private vulnerability reporting form](https://github.com/documenso/documenso/security/advisories/new). This is our primary channel and lets us triage and work with you on a fix.
|
|
||||||
2. **Email**. If you cannot use GitHub Security Advisories, email [security@documenso.com](mailto:security@documenso.com).
|
|
||||||
|
|
||||||
Include the affected version, a clear description, steps to reproduce, and the potential impact.
|
|
||||||
|
|
||||||
## Triage and Response
|
|
||||||
|
|
||||||
We triage reports as we have availability. We read every report we receive, and we appreciate the time and effort it takes to put one together.
|
|
||||||
|
|
||||||
We also run [Codex](https://openai.com/codex/) security analysis across the codebase. If Codex has already reported the issue you're sending us, we may close your report as a duplicate. Please don't take this as a reflection on your work; it just means our automated tooling happened to surface the same thing first.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This policy covers vulnerabilities in the Documenso application code in this repository.
|
|
||||||
|
|
||||||
The items below are out of scope and will not be accepted. They are deployment, infrastructure, and configuration concerns that belong with the operator's firewall, network, and environment setup, not the application:
|
|
||||||
|
|
||||||
- Server-Side Request Forgery (SSRF) and related network-egress concerns
|
|
||||||
- DNS rebinding and other DNS-level issues
|
|
||||||
- Rate limiting, denial of service, and volumetric attacks
|
|
||||||
- TLS and certificate configuration, HTTP security headers, and other reverse-proxy or web-server configuration
|
|
||||||
- Findings that depend on insecure self-hosted infrastructure or misconfiguration
|
|
||||||
|
|
||||||
If you're unsure whether something is in scope, report it privately anyway and we'll happily take a look.
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
Security fixes are applied to the latest release. Run the most recent version of Documenso.
|
|
||||||
+64
-6
@@ -1,9 +1,67 @@
|
|||||||
# Signing Certificate
|
# Creating your own signing certificate
|
||||||
|
|
||||||
Documenso needs a signing certificate to digitally sign documents. For full, up-to-date instructions on generating, converting, and configuring a certificate, see the official documentation:
|
For the digital signature of your documents you need a signing certificate in .p12 format (public and private key). You can buy one (not recommended for dev) or use the steps to create a self-signed one:
|
||||||
|
|
||||||
- [Signing Certificate](https://docs.documenso.com/docs/self-hosting/configuration/signing-certificate): Overview and all certificate options
|
1. Generate a private key using the OpenSSL command. You can run the following command to generate a 2048-bit RSA key:
|
||||||
- [Local Certificate](https://docs.documenso.com/docs/self-hosting/configuration/signing-certificate/local): Generate a self-signed `.p12` certificate with OpenSSL
|
|
||||||
- [Google Cloud HSM](https://docs.documenso.com/docs/self-hosting/configuration/signing-certificate/google-cloud-hsm): Sign using Google Cloud KMS
|
|
||||||
|
|
||||||
For deploying Documenso with Docker, see the [Docker Deployment](https://docs.documenso.com/docs/self-hosting/deployment/docker) and [Docker Compose](https://docs.documenso.com/docs/self-hosting/deployment/docker-compose) guides.
|
`openssl genrsa -out private.key 2048`
|
||||||
|
|
||||||
|
2. Generate a self-signed certificate using the private key. You can run the following command to generate a self-signed certificate:
|
||||||
|
|
||||||
|
`openssl req -new -x509 -key private.key -out certificate.crt -days 365`
|
||||||
|
|
||||||
|
This will prompt you to enter some information, such as the Common Name (CN) for the certificate. Make sure you enter the correct information. The `-days` parameter sets the number of days for which the certificate is valid.
|
||||||
|
|
||||||
|
3. Combine the private key and the self-signed certificate to create the p12 certificate. You can run the following commands to do this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set certificate password securely (won't appear in command history)
|
||||||
|
read -s -p "Enter certificate password: " CERT_PASS
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Create the p12 certificate using the environment variable
|
||||||
|
openssl pkcs12 -export -out certificate.p12 -inkey private.key -in certificate.crt \
|
||||||
|
-password env:CERT_PASS \
|
||||||
|
-keypbe PBE-SHA1-3DES \
|
||||||
|
-certpbe PBE-SHA1-3DES \
|
||||||
|
-macalg sha1
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **IMPORTANT**: A certificate password is required to prevent signing failures. Make sure to use a strong password (minimum 4 characters) when prompted. Certificates without passwords will cause "Failed to get private key bags" errors during document signing.
|
||||||
|
|
||||||
|
5. Place the certificate `/apps/remix/resources/certificate.p12` (If the path does not exist, it needs to be created)
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
> We are still working on the publishing of docker images, in the meantime you can follow the steps below to create a production ready docker image.
|
||||||
|
|
||||||
|
Want to create a production ready docker image? Follow these steps:
|
||||||
|
|
||||||
|
- cd into `docker` directory
|
||||||
|
- Make `build.sh` executable by running `chmod +x build.sh`
|
||||||
|
- Run `./build.sh` to start building the docker image.
|
||||||
|
- Publish the image to your docker registry of choice (or) If you prefer running the image from local, run the below command
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -d --restart=unless-stopped -p 3000:3000 -v documenso:/app/data --name documenso documenso:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Command Breakdown:
|
||||||
|
|
||||||
|
- `-d` - Let's you run the container in background
|
||||||
|
- `-p` - Passes down which ports to use. First half is the host port, Second half is the app port. You can change the first half anything you want and reverse proxy to that port.
|
||||||
|
- `-v` - Volume let's you persist the data
|
||||||
|
- `--name` - Name of the container
|
||||||
|
- `documenso:latest` - Image you have built
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
We support a variety of deployment methods, and are actively working on adding more. Stay tuned for updates!
|
||||||
|
|
||||||
|
## Railway
|
||||||
|
|
||||||
|
[](https://railway.com/deploy/DjrRRX?referralCode=EZR3s0&utm_medium=integration&utm_source=template&utm_campaign=generic)
|
||||||
|
|
||||||
|
## Render
|
||||||
|
|
||||||
|
[](https://render.com/deploy?repo=https://github.com/documenso/documenso)
|
||||||
|
|||||||
+38
-9
@@ -1,16 +1,45 @@
|
|||||||
# @documenso/docs
|
# docs
|
||||||
|
|
||||||
The Documenso documentation site, built with [Next.js](https://nextjs.org/) and [Fumadocs](https://fumadocs.dev/). Published at [docs.documenso.com](https://docs.documenso.com).
|
This is a Next.js application generated with
|
||||||
|
[Create Fumadocs](https://github.com/fuma-nama/fumadocs).
|
||||||
|
|
||||||
Content lives under `content/docs/` as MDX. See [WRITING_STYLE.md](../../WRITING_STYLE.md) for the documentation writing conventions.
|
Run development server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# From the monorepo root
|
npm run dev
|
||||||
npm run dev --filter=@documenso/docs
|
# or
|
||||||
|
pnpm dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Structure
|
Open http://localhost:3000 with your browser to see the result.
|
||||||
|
|
||||||
- `content/docs/`: Documentation pages (MDX).
|
## Explore
|
||||||
- `lib/source.ts`: Content source adapter.
|
|
||||||
- `lib/layout.shared.tsx`: Shared layout options.
|
In the project, you can see:
|
||||||
|
|
||||||
|
- `lib/source.ts`: Code for content source adapter, [`loader()`](https://fumadocs.dev/docs/headless/source-api) provides the interface to access your content.
|
||||||
|
- `lib/layout.shared.tsx`: Shared options for layouts, optional but preferred to keep.
|
||||||
|
|
||||||
|
| Route | Description |
|
||||||
|
| ------------------------- | ------------------------------------------------------ |
|
||||||
|
| `app/(home)` | The route group for your landing page and other pages. |
|
||||||
|
| `app/docs` | The documentation layout and pages. |
|
||||||
|
| `app/api/search/route.ts` | The Route Handler for search. |
|
||||||
|
|
||||||
|
### Fumadocs MDX
|
||||||
|
|
||||||
|
A `source.config.ts` config file has been included, you can customise different options like frontmatter schema.
|
||||||
|
|
||||||
|
Read the [Introduction](https://fumadocs.dev/docs/mdx) for further details.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about Next.js and Fumadocs, take a look at the following
|
||||||
|
resources:
|
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js
|
||||||
|
features and API.
|
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
- [Fumadocs](https://fumadocs.dev) - learn about Fumadocs
|
||||||
|
|||||||
@@ -15,17 +15,16 @@ Pick the one that fits your needs the best.
|
|||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
- [TypeScript](https://www.typescriptlang.org/) - Language
|
- [Typescript](https://www.typescriptlang.org/) - Language
|
||||||
- [React Router v7](https://reactrouter.com/) - Framework
|
- [React Router](https://reactrouter.com/) - Framework
|
||||||
- [Hono](https://hono.dev/) - Server
|
|
||||||
- [Prisma](https://www.prisma.io/) - ORM
|
- [Prisma](https://www.prisma.io/) - ORM
|
||||||
- [Tailwind CSS](https://tailwindcss.com/) - CSS
|
- [Tailwind](https://tailwindcss.com/) - CSS
|
||||||
- [shadcn/ui](https://ui.shadcn.com/) + [Radix UI](https://www.radix-ui.com/) - Component Library
|
- [shadcn/ui](https://ui.shadcn.com/) - Component Library
|
||||||
- [react-email](https://react.email/) - Email Templates
|
- [react-email](https://react.email/) - Email Templates
|
||||||
- [Lingui](https://lingui.dev/) - Internationalization
|
|
||||||
- [tRPC](https://trpc.io/) - API
|
- [tRPC](https://trpc.io/) - API
|
||||||
- [@libpdf/core](https://www.npmjs.com/package/@libpdf/core) - PDF Signing and Manipulation
|
- [@documenso/pdf-sign](https://www.npmjs.com/package/@documenso/pdf-sign) - PDF Signatures
|
||||||
- [pdf.js](https://mozilla.github.io/pdf.js/) - Viewing PDFs
|
- [React-PDF](https://github.com/wojtekmaj/react-pdf) - Viewing PDFs
|
||||||
|
- [PDF-Lib](https://github.com/Hopding/pdf-lib) - PDF manipulation
|
||||||
- [Stripe](https://stripe.com/) - Payments
|
- [Stripe](https://stripe.com/) - Payments
|
||||||
|
|
||||||
<div className="mt-16 flex items-center justify-center gap-4">
|
<div className="mt-16 flex items-center justify-center gap-4">
|
||||||
|
|||||||
@@ -278,9 +278,7 @@ Test your email configuration by creating an account or resetting a password. Th
|
|||||||
|
|
||||||
### Using a Test SMTP Server
|
### Using a Test SMTP Server
|
||||||
|
|
||||||
For development or testing, use a local SMTP server like [Inbucket](https://www.inbucket.org/), [Mailpit](https://github.com/axllent/mailpit), or [Mailhog](https://github.com/mailhog/MailHog). The default development setup (`docker/development/compose.yml`) already runs Inbucket, with its web UI on port 9000 and SMTP on port 2500.
|
For development or testing, use a local SMTP server like [Mailhog](https://github.com/mailhog/MailHog) or [Mailpit](https://github.com/axllent/mailpit):
|
||||||
|
|
||||||
To run one standalone instead:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Using Docker
|
# Using Docker
|
||||||
|
|||||||
@@ -86,21 +86,6 @@ Callback URL: `https://<your-domain>/api/auth/callback/microsoft`
|
|||||||
| `NEXT_PRIVATE_OIDC_SKIP_VERIFY` | `false` | Skip email verification for OIDC accounts |
|
| `NEXT_PRIVATE_OIDC_SKIP_VERIFY` | `false` | Skip email verification for OIDC accounts |
|
||||||
| `NEXT_PRIVATE_OIDC_PROMPT` | `login` | OIDC prompt parameter. Set to empty string to omit |
|
| `NEXT_PRIVATE_OIDC_PROMPT` | `login` | OIDC prompt parameter. Set to empty string to omit |
|
||||||
|
|
||||||
### Webhooks
|
|
||||||
|
|
||||||
| Variable | Default | Description |
|
|
||||||
| --------------------------------------- | ------- | ------------------------------------------------------------------------ |
|
|
||||||
| `NEXT_PRIVATE_WEBHOOK_SSRF_BYPASS_HOSTS` | - | Comma-separated hostnames or IPs allowed to resolve to private addresses |
|
|
||||||
|
|
||||||
Before delivering a webhook, Documenso checks whether the target resolves to a
|
|
||||||
private or loopback address and blocks it if so. This check is best-effort and
|
|
||||||
fails open. Use `NEXT_PRIVATE_WEBHOOK_SSRF_BYPASS_HOSTS` to allow specific
|
|
||||||
internal hosts, for example when delivering to a service on your own network:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
NEXT_PRIVATE_WEBHOOK_SSRF_BYPASS_HOSTS="hooks.internal.example,10.0.0.5"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Email Configuration
|
## Email Configuration
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ spec:
|
|||||||
```
|
```
|
||||||
|
|
||||||
<Callout type="info">
|
<Callout type="info">
|
||||||
Pin to a specific image tag (e.g., `documenso/documenso:<version>`) in production instead of `latest`
|
Pin to a specific image tag (e.g., `documenso/documenso:1.5.0`) in production instead of `latest`
|
||||||
to ensure predictable deployments.
|
to ensure predictable deployments.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import { Step, Steps } from 'fumadocs-ui/components/steps';
|
|||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
- Node.js 22 or later
|
- Node.js 20 or later
|
||||||
- npm 11 or later
|
- npm
|
||||||
- PostgreSQL 14 or later
|
- PostgreSQL 14 or later
|
||||||
- A Linux server (for systemd service setup)
|
- A Linux server (for systemd service setup)
|
||||||
|
|
||||||
|
|||||||
@@ -124,16 +124,12 @@ docker compose -f docker/development/compose.yml exec database \
|
|||||||
|
|
||||||
The quick start setup runs the following containers:
|
The quick start setup runs the following containers:
|
||||||
|
|
||||||
| Container | Purpose | Port |
|
| Container | Purpose | Port |
|
||||||
| ----------- | ------------------------------------ | ----------------------------- |
|
| ----------- | -------------------------------- | ----- |
|
||||||
| `documenso` | Main application | 3000 |
|
| `documenso` | Main application | 3000 |
|
||||||
| `database` | PostgreSQL database | 54320 |
|
| `database` | PostgreSQL database | 54320 |
|
||||||
| `inbucket` | Local email testing server | 9000 (web UI), 2500 (SMTP) |
|
| `maildev` | Local email testing server | 2500 |
|
||||||
| `redis` | Cache and background job queue | 63790 |
|
| `minio` | S3-compatible storage (optional) | 9000 |
|
||||||
| `minio` | S3-compatible storage | 9002 (API), 9001 (console) |
|
|
||||||
| `gotenberg` | Document conversion (optional) | 3005 |
|
|
||||||
|
|
||||||
The local email server is [Inbucket](https://www.inbucket.org/). Open its web UI at [http://localhost:9000](http://localhost:9000) to view emails Documenso sends during development. For your own deployment you can use any SMTP-compatible mailserver, such as Inbucket, [Mailpit](https://github.com/axllent/mailpit), or [Mailhog](https://github.com/mailhog/MailHog).
|
|
||||||
|
|
||||||
## Useful Commands
|
## Useful Commands
|
||||||
|
|
||||||
|
|||||||
@@ -141,8 +141,8 @@ If building from source (not using Docker images):
|
|||||||
|
|
||||||
| Requirement | Version |
|
| Requirement | Version |
|
||||||
| ----------- | ------- |
|
| ----------- | ------- |
|
||||||
| Node.js | 22+ |
|
| Node.js | 18+ |
|
||||||
| npm | 11+ |
|
| npm | 8+ |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ Documenso runs on:
|
|||||||
| MySQL/MariaDB | PostgreSQL-specific features required |
|
| MySQL/MariaDB | PostgreSQL-specific features required |
|
||||||
| SQLite | Not suitable for production workloads |
|
| SQLite | Not suitable for production workloads |
|
||||||
| MongoDB | Relational database required |
|
| MongoDB | Relational database required |
|
||||||
| Node.js < 22 | Modern JavaScript features required |
|
| Node.js < 18 | Modern JavaScript features required |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ Use a specific version tag in production:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Good — predictable, reproducible
|
# Good — predictable, reproducible
|
||||||
docker pull documenso/documenso:<version>
|
docker pull documenso/documenso:1.8.0
|
||||||
|
|
||||||
# Risky — may pull breaking changes
|
# Risky — may pull breaking changes
|
||||||
docker pull documenso/documenso:latest
|
docker pull documenso/documenso:latest
|
||||||
|
|||||||
@@ -27,14 +27,6 @@ import { Callout } from 'fumadocs-ui/components/callout';
|
|||||||
Please see all the [requirements](/docs/self-hosting/getting-started/requirements) before proceeding.
|
Please see all the [requirements](/docs/self-hosting/getting-started/requirements) before proceeding.
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
<Callout type="warn">
|
|
||||||
**You are responsible for your own network security.** Documenso applies best-effort, non-exhaustive
|
|
||||||
checks to outbound requests such as webhooks, but these are not a complete SSRF mitigation and they
|
|
||||||
fail open. A self-hosted instance can reach internal addresses on your network. Restricting outbound
|
|
||||||
traffic, egress filtering, and blocking access to internal services and cloud metadata endpoints is
|
|
||||||
your responsibility through your firewall and network configuration.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Deployment Options
|
## Deployment Options
|
||||||
|
|||||||
@@ -165,10 +165,10 @@ See [Backups](/docs/self-hosting/maintenance/backups) for automated backup strat
|
|||||||
### Pull the new image
|
### Pull the new image
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker pull documenso/documenso:<version>
|
docker pull documenso/documenso:1.6.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace `<version>` with your target version.
|
Replace `1.6.0` with your target version.
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
<Step>
|
<Step>
|
||||||
@@ -189,7 +189,7 @@ docker run -d \
|
|||||||
-p 3000:3000 \
|
-p 3000:3000 \
|
||||||
--env-file .env \
|
--env-file .env \
|
||||||
-v /path/to/cert.p12:/opt/documenso/cert.p12:ro \
|
-v /path/to/cert.p12:/opt/documenso/cert.p12:ro \
|
||||||
documenso/documenso:<version>
|
documenso/documenso:1.6.0
|
||||||
```
|
```
|
||||||
|
|
||||||
</Step>
|
</Step>
|
||||||
@@ -223,14 +223,14 @@ Edit `compose.yml` or your `.env` file to specify the new version:
|
|||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
documenso:
|
documenso:
|
||||||
image: documenso/documenso:<version>
|
image: documenso/documenso:1.6.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Or if using environment variable substitution:
|
Or if using environment variable substitution:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# In .env
|
# In .env
|
||||||
DOCUMENSO_VERSION=<version>
|
DOCUMENSO_VERSION=1.6.0
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -283,7 +283,7 @@ Edit the deployment directly:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl set image deployment/documenso \
|
kubectl set image deployment/documenso \
|
||||||
documenso=documenso/documenso:<version> \
|
documenso=documenso/documenso:1.6.0 \
|
||||||
-n documenso
|
-n documenso
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -295,7 +295,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: documenso
|
- name: documenso
|
||||||
image: documenso/documenso:<version>
|
image: documenso/documenso:1.6.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Then apply:
|
Then apply:
|
||||||
@@ -421,12 +421,12 @@ To run migrations manually before upgrading:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Pull the new image
|
# Pull the new image
|
||||||
docker pull documenso/documenso:<version>
|
docker pull documenso/documenso:1.6.0
|
||||||
|
|
||||||
# Run migrations only
|
# Run migrations only
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-e NEXT_PRIVATE_DATABASE_URL="postgresql://user:password@host:5432/documenso" \
|
-e NEXT_PRIVATE_DATABASE_URL="postgresql://user:password@host:5432/documenso" \
|
||||||
documenso/documenso:<version> \
|
documenso/documenso:1.6.0 \
|
||||||
npx prisma migrate deploy
|
npx prisma migrate deploy
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -516,7 +516,7 @@ docker run -d \
|
|||||||
-p 3000:3000 \
|
-p 3000:3000 \
|
||||||
--env-file .env \
|
--env-file .env \
|
||||||
-v /path/to/cert.p12:/opt/documenso/cert.p12:ro \
|
-v /path/to/cert.p12:/opt/documenso/cert.p12:ro \
|
||||||
documenso/documenso:<previous-version>
|
documenso/documenso:1.5.0
|
||||||
```
|
```
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { createMDX } from 'fumadocs-mdx/next';
|
import { createMDX } from 'fumadocs-mdx/next';
|
||||||
|
import type { NextConfig } from 'next';
|
||||||
|
|
||||||
const withMDX = createMDX();
|
const withMDX = createMDX();
|
||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
const config: NextConfig = {
|
||||||
const config = {
|
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
|
// biome-ignore lint/suspicious/useAwait: config file
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -13,6 +14,7 @@ const config = {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
// biome-ignore lint/suspicious/useAwait: config file
|
||||||
async redirects() {
|
async redirects() {
|
||||||
return [
|
return [
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"fumadocs-ui": "16.5.0",
|
"fumadocs-ui": "16.5.0",
|
||||||
"lucide-react": "^0.563.0",
|
"lucide-react": "^0.563.0",
|
||||||
"mermaid": "^11.12.2",
|
"mermaid": "^11.12.2",
|
||||||
"next": "16.2.6",
|
"next": "16.2.9",
|
||||||
"next-plausible": "^3.12.5",
|
"next-plausible": "^3.12.5",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"@types/react": "^19.2.10",
|
"@types/react": "^19.2.10",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"postcss": "^8.5.14",
|
"postcss": "^8.5.14",
|
||||||
"tailwindcss": "^4.1.18",
|
"tailwindcss": "^4.3.1",
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
<img src={logoSrc} alt="Documenso" height="28" />
|
<img src={logoSrc} alt="Documenso" style={{ height: 28 }} />
|
||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
color: '#D4D4D8',
|
color: '#D4D4D8',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/prisma": "*",
|
"@documenso/prisma": "*",
|
||||||
"luxon": "^3.7.2",
|
"luxon": "^3.7.2",
|
||||||
"next": "16.2.6"
|
"next": "16.2.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
FROM node:20-alpine AS development-dependencies-env
|
||||||
|
COPY . /app
|
||||||
|
WORKDIR /app
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
FROM node:20-alpine AS production-dependencies-env
|
||||||
|
COPY ./package.json package-lock.json /app/
|
||||||
|
WORKDIR /app
|
||||||
|
RUN npm ci --omit=dev
|
||||||
|
|
||||||
|
FROM node:20-alpine AS build-env
|
||||||
|
COPY . /app/
|
||||||
|
COPY --from=development-dependencies-env /app/node_modules /app/node_modules
|
||||||
|
WORKDIR /app
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM node:20-alpine
|
||||||
|
COPY ./package.json package-lock.json /app/
|
||||||
|
COPY --from=production-dependencies-env /app/node_modules /app/node_modules
|
||||||
|
COPY --from=build-env /app/build /app/build
|
||||||
|
WORKDIR /app
|
||||||
|
CMD ["npm", "run", "start"]
|
||||||
+93
-7
@@ -1,14 +1,100 @@
|
|||||||
# @documenso/remix
|
# Welcome to React Router!
|
||||||
|
|
||||||
The main Documenso web application. Built with [React Router v7](https://reactrouter.com/) and served by a [Hono](https://hono.dev/) server.
|
A modern, production-ready template for building full-stack React applications using React Router.
|
||||||
|
|
||||||
This package is part of the Documenso monorepo and is not meant to be run standalone. Use the root scripts instead.
|
[](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default)
|
||||||
|
|
||||||
- Local development: see the [root README](../../README.md) and the [Local Development docs](https://docs.documenso.com/docs/developers/local-development).
|
## Features
|
||||||
- Self-hosting and deployment: see the [Self-Hosting docs](https://docs.documenso.com/docs/self-hosting).
|
|
||||||
- Architecture overview: see [ARCHITECTURE.md](../../ARCHITECTURE.md).
|
- 🚀 Server-side rendering
|
||||||
|
- ⚡️ Hot Module Replacement (HMR)
|
||||||
|
- 📦 Asset bundling and optimization
|
||||||
|
- 🔄 Data loading and mutations
|
||||||
|
- 🔒 TypeScript by default
|
||||||
|
- 🎉 TailwindCSS for styling
|
||||||
|
- 📖 [React Router docs](https://reactrouter.com/)
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Install the dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
Start the development server with HMR:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# From the monorepo root
|
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Your application will be available at `http://localhost:5173`.
|
||||||
|
|
||||||
|
## Building for Production
|
||||||
|
|
||||||
|
Create a production build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
### Docker Deployment
|
||||||
|
|
||||||
|
This template includes three Dockerfiles optimized for different package managers:
|
||||||
|
|
||||||
|
- `Dockerfile` - for npm
|
||||||
|
- `Dockerfile.pnpm` - for pnpm
|
||||||
|
- `Dockerfile.bun` - for bun
|
||||||
|
|
||||||
|
To build and run using Docker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For npm
|
||||||
|
docker build -t my-app .
|
||||||
|
|
||||||
|
# For pnpm
|
||||||
|
docker build -f Dockerfile.pnpm -t my-app .
|
||||||
|
|
||||||
|
# For bun
|
||||||
|
docker build -f Dockerfile.bun -t my-app .
|
||||||
|
|
||||||
|
# Run the container
|
||||||
|
docker run -p 3000:3000 my-app
|
||||||
|
```
|
||||||
|
|
||||||
|
The containerized application can be deployed to any platform that supports Docker, including:
|
||||||
|
|
||||||
|
- AWS ECS
|
||||||
|
- Google Cloud Run
|
||||||
|
- Azure Container Apps
|
||||||
|
- Digital Ocean App Platform
|
||||||
|
- Fly.io
|
||||||
|
- Railway
|
||||||
|
|
||||||
|
### DIY Deployment
|
||||||
|
|
||||||
|
If you're familiar with deploying Node applications, the built-in app server is production-ready.
|
||||||
|
|
||||||
|
Make sure to deploy the output of `npm run build`
|
||||||
|
|
||||||
|
```
|
||||||
|
├── package.json
|
||||||
|
├── package-lock.json (or pnpm-lock.yaml, or bun.lockb)
|
||||||
|
├── build/
|
||||||
|
│ ├── client/ # Static assets
|
||||||
|
│ └── server/ # Server-side code
|
||||||
|
```
|
||||||
|
|
||||||
|
## Styling
|
||||||
|
|
||||||
|
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Built with ❤️ using React Router.
|
||||||
|
|||||||
+14
-3
@@ -1,5 +1,15 @@
|
|||||||
@import "@documenso/ui/styles/theme.css";
|
@import "@documenso/ui/styles/theme.css";
|
||||||
|
|
||||||
|
/* Content sources: this app plus the shared `ui` and `email` packages it renders. */
|
||||||
|
@source "./**/*.{ts,tsx}";
|
||||||
|
@source "../../../packages/ui/primitives/**/*.{ts,tsx}";
|
||||||
|
@source "../../../packages/ui/components/**/*.{ts,tsx}";
|
||||||
|
@source "../../../packages/ui/icons/**/*.{ts,tsx}";
|
||||||
|
@source "../../../packages/ui/lib/**/*.{ts,tsx}";
|
||||||
|
@source "../../../packages/email/templates/**/*.{ts,tsx}";
|
||||||
|
@source "../../../packages/email/template-components/**/*.{ts,tsx}";
|
||||||
|
@source "../../../packages/email/providers/**/*.{ts,tsx}";
|
||||||
|
|
||||||
/* Inter Variable Fonts */
|
/* Inter Variable Fonts */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Inter";
|
font-family: "Inter";
|
||||||
@@ -64,8 +74,9 @@
|
|||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--font-sans: "Inter";
|
/* Consumed by the `--font-*` theme tokens in @documenso/ui/styles/theme.css. */
|
||||||
--font-signature: "Caveat";
|
--font-family-sans: "Inter";
|
||||||
--font-noto: "Noto Sans", "Noto Sans Korean", "Noto Sans Japanese", "Noto Sans Chinese";
|
--font-family-signature: "Caveat";
|
||||||
|
--font-family-noto: "Noto Sans", "Noto Sans Korean", "Noto Sans Japanese", "Noto Sans Chinese";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export const AccountDeleteDialog = ({ className }: AccountDeleteDialogProps) =>
|
|||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-shrink-0">
|
<div className="shrink-0">
|
||||||
<Dialog onOpenChange={() => setEnteredEmail('')}>
|
<Dialog onOpenChange={() => setEnteredEmail('')}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="destructive">
|
<Button variant="destructive">
|
||||||
@@ -82,7 +82,7 @@ export const AccountDeleteDialog = ({ className }: AccountDeleteDialogProps) =>
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader className="space-y-4">
|
<DialogHeader className="twv3-space-y-4">
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<Trans>Delete Account</Trans>
|
<Trans>Delete Account</Trans>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export const AdminDocumentDeleteDialog = ({ envelopeId }: AdminDocumentDeleteDia
|
|||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-shrink-0">
|
<div className="shrink-0">
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="destructive">
|
<Button variant="destructive">
|
||||||
@@ -80,7 +80,7 @@ export const AdminDocumentDeleteDialog = ({ envelopeId }: AdminDocumentDeleteDia
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader className="space-y-4">
|
<DialogHeader className="twv3-space-y-4">
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<Trans>Delete Document</Trans>
|
<Trans>Delete Document</Trans>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export const AdminOrganisationCreateDialog = ({ trigger, ownerUserId, ...props }
|
|||||||
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button className="flex-shrink-0" variant="secondary">
|
<Button className="shrink-0" variant="secondary">
|
||||||
<Trans>Create organisation</Trans>
|
<Trans>Create organisation</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -109,7 +109,7 @@ export const AdminOrganisationCreateDialog = ({ trigger, ownerUserId, ...props }
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export const AdminOrganisationDeleteDialog = ({
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="organisationName"
|
name="organisationName"
|
||||||
@@ -151,7 +151,7 @@ export const AdminOrganisationDeleteDialog = ({
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="sendEmailToOwner"
|
name="sendEmailToOwner"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
<FormItem className="twv3-space-x-3 twv3-space-y-0 flex flex-row items-start">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="admin-delete-organisation-send-email"
|
id="admin-delete-organisation-send-email"
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const AdminOrganisationMemberDeleteDialog = ({
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader className="space-y-4">
|
<DialogHeader className="twv3-space-y-4">
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<Trans>Remove Organisation Member</Trans>
|
<Trans>Remove Organisation Member</Trans>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|||||||
@@ -110,12 +110,12 @@ export const AdminOrganisationSyncSubscriptionDialog = ({
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="syncClaims"
|
name="syncClaims"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
|
<FormItem className="twv3-space-x-3 twv3-space-y-0 flex flex-row items-center">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="admin-sync-subscription-sync-claims"
|
id="admin-sync-subscription-sync-claims"
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export const AdminSwapSubscriptionDialog = ({
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<fieldset className="flex flex-col space-y-4" disabled={isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex flex-col" disabled={isSubmitting}>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<label className="font-medium text-sm">
|
<label className="font-medium text-sm">
|
||||||
<Trans>Target Organisation</Trans>
|
<Trans>Target Organisation</Trans>
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export const AdminTeamMemberDeleteDialog = ({
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader className="space-y-4">
|
<DialogHeader className="twv3-space-y-4">
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<Trans>Remove Team Member</Trans>
|
<Trans>Remove Team Member</Trans>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export const AdminUserCreateDialog = ({ trigger, ...props }: AdminUserCreateDial
|
|||||||
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button className="flex-shrink-0" variant="secondary">
|
<Button className="shrink-0" variant="secondary">
|
||||||
<Trans>Create User</Trans>
|
<Trans>Create User</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -101,7 +101,7 @@ export const AdminUserCreateDialog = ({ trigger, ...props }: AdminUserCreateDial
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="email"
|
name="email"
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export const AdminUserDeleteDialog = ({ className, user }: AdminUserDeleteDialog
|
|||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-shrink-0">
|
<div className="shrink-0">
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="destructive">
|
<Button variant="destructive">
|
||||||
@@ -86,7 +86,7 @@ export const AdminUserDeleteDialog = ({ className, user }: AdminUserDeleteDialog
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader className="space-y-4">
|
<DialogHeader className="twv3-space-y-4">
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<Trans>Delete Account</Trans>
|
<Trans>Delete Account</Trans>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export const AdminUserDisableDialog = ({ className, userToDisable }: AdminUserDi
|
|||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-shrink-0">
|
<div className="shrink-0">
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="destructive">
|
<Button variant="destructive">
|
||||||
@@ -83,7 +83,7 @@ export const AdminUserDisableDialog = ({ className, userToDisable }: AdminUserDi
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader className="space-y-4">
|
<DialogHeader className="twv3-space-y-4">
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<Trans>Disable Account</Trans>
|
<Trans>Disable Account</Trans>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export const AdminUserEnableDialog = ({ className, userToEnable }: AdminUserEnab
|
|||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-shrink-0">
|
<div className="shrink-0">
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button>
|
<Button>
|
||||||
@@ -83,7 +83,7 @@ export const AdminUserEnableDialog = ({ className, userToEnable }: AdminUserEnab
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader className="space-y-4">
|
<DialogHeader className="twv3-space-y-4">
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<Trans>Enable Account</Trans>
|
<Trans>Enable Account</Trans>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export const AdminUserResetTwoFactorDialog = ({ className, user }: AdminUserRese
|
|||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-shrink-0">
|
<div className="shrink-0">
|
||||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="destructive">
|
<Button variant="destructive">
|
||||||
@@ -99,7 +99,7 @@ export const AdminUserResetTwoFactorDialog = ({ className, user }: AdminUserRese
|
|||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader className="space-y-4">
|
<DialogHeader className="twv3-space-y-4">
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<Trans>Reset Two Factor Authentication</Trans>
|
<Trans>Reset Two Factor Authentication</Trans>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export const AiFeaturesEnableDialog = ({ open, onOpenChange, onEnabled }: AiFeat
|
|||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="twv3-space-y-4">
|
||||||
<p className="text-muted-foreground text-sm">
|
<p className="text-muted-foreground text-sm">
|
||||||
<Trans>
|
<Trans>
|
||||||
Turn on AI detection to automatically find recipients and fields in your documents. AI providers do not
|
Turn on AI detection to automatically find recipients and fields in your documents. AI providers do not
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ export const AiFieldDetectionDialog = ({
|
|||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="twv3-space-y-4">
|
||||||
<p className="text-muted-foreground text-sm">
|
<p className="text-muted-foreground text-sm">
|
||||||
<Trans>
|
<Trans>
|
||||||
We'll scan your document to find form fields like signature lines, text inputs, checkboxes, and more.
|
We'll scan your document to find form fields like signature lines, text inputs, checkboxes, and more.
|
||||||
@@ -166,14 +166,14 @@ export const AiFieldDetectionDialog = ({
|
|||||||
</Trans>
|
</Trans>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Alert className="flex items-center gap-2 space-y-0" variant="neutral">
|
<Alert className="twv3-space-y-0 flex items-center gap-2" variant="neutral">
|
||||||
<ShieldCheckIcon className="h-5 w-5 stroke-green-600" />
|
<ShieldCheckIcon className="h-5 w-5 stroke-green-600" />
|
||||||
<AlertDescription className="mt-0">
|
<AlertDescription className="mt-0">
|
||||||
<Trans>Your document is processed securely using AI services that don't retain your data.</Trans>
|
<Trans>Your document is processed securely using AI services that don't retain your data.</Trans>
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<div className="space-y-1.5">
|
<div className="twv3-space-y-1.5">
|
||||||
<Label htmlFor="context">
|
<Label htmlFor="context">
|
||||||
<Trans>Context</Trans>
|
<Trans>Context</Trans>
|
||||||
</Label>
|
</Label>
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export const AiRecipientDetectionDialog = ({
|
|||||||
</Trans>
|
</Trans>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<Alert className="mt-4 flex items-center gap-2 space-y-0" variant="neutral">
|
<Alert className="twv3-space-y-0 mt-4 flex items-center gap-2" variant="neutral">
|
||||||
<ShieldCheckIcon className="h-5 w-5 stroke-green-600" />
|
<ShieldCheckIcon className="h-5 w-5 stroke-green-600" />
|
||||||
<AlertDescription className="mt-0">
|
<AlertDescription className="mt-0">
|
||||||
<Trans>Your document is processed securely using AI services that don't retain your data.</Trans>
|
<Trans>Your document is processed securely using AI services that don't retain your data.</Trans>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export const ClaimCreateDialog = ({ licenseFlags }: ClaimCreateDialogProps) => {
|
|||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
||||||
<Button className="flex-shrink-0" variant="secondary">
|
<Button className="shrink-0" variant="secondary">
|
||||||
<Trans>Create claim</Trans>
|
<Trans>Create claim</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const ClaimUpdateDialog = ({ claim, trigger, licenseFlags }: ClaimUpdateD
|
|||||||
licenseFlags={licenseFlags}
|
licenseFlags={licenseFlags}
|
||||||
formSubmitTrigger={
|
formSubmitTrigger={
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="twv3-space-x-2 flex items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="backport-email-transport"
|
id="backport-email-transport"
|
||||||
checked={backportEmailTransport}
|
checked={backportEmailTransport}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export const EmailTransportCreateDialog = ({ trigger }: EmailTransportCreateDial
|
|||||||
<Dialog open={open} onOpenChange={(value) => !isPending && setOpen(value)}>
|
<Dialog open={open} onOpenChange={(value) => !isPending && setOpen(value)}>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button className="flex-shrink-0">
|
<Button className="shrink-0">
|
||||||
<Trans>Add transport</Trans>
|
<Trans>Add transport</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export const EmailTransportSendTestDialog = ({ transportId, trigger }: EmailTran
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset disabled={form.formState.isSubmitting} className="space-y-4">
|
<fieldset disabled={form.formState.isSubmitting} className="twv3-space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="to"
|
name="to"
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ export const EnvelopeDistributeDialog = ({
|
|||||||
<fieldset disabled={isSubmitting}>
|
<fieldset disabled={isSubmitting}>
|
||||||
{hasOverlappingEnvelopeFields && (
|
{hasOverlappingEnvelopeFields && (
|
||||||
<Alert variant="warning" className="mb-4 flex flex-row items-start gap-3">
|
<Alert variant="warning" className="mb-4 flex flex-row items-start gap-3">
|
||||||
<AlertTriangleIcon className="mt-0.5 h-5 w-5 flex-shrink-0" />
|
<AlertTriangleIcon className="mt-0.5 h-5 w-5 shrink-0" />
|
||||||
|
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<AlertTitle>
|
<AlertTitle>
|
||||||
|
|||||||
@@ -163,14 +163,14 @@ export const EnvelopeDownloadDialog = ({
|
|||||||
{isLoadingEnvelopeItems
|
{isLoadingEnvelopeItems
|
||||||
? Array.from({ length: 1 }).map((_, index) => (
|
? Array.from({ length: 1 }).map((_, index) => (
|
||||||
<div key={index} className="flex items-center gap-2 rounded-lg border border-border bg-card p-4">
|
<div key={index} className="flex items-center gap-2 rounded-lg border border-border bg-card p-4">
|
||||||
<Skeleton className="h-10 w-10 flex-shrink-0 rounded-lg" />
|
<Skeleton className="h-10 w-10 shrink-0 rounded-lg" />
|
||||||
|
|
||||||
<div className="flex w-full flex-col gap-2">
|
<div className="flex w-full flex-col gap-2">
|
||||||
<Skeleton className="h-4 w-28 rounded-lg" />
|
<Skeleton className="h-4 w-28 rounded-lg" />
|
||||||
<Skeleton className="h-4 w-20 rounded-lg" />
|
<Skeleton className="h-4 w-20 rounded-lg" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Skeleton className="h-10 w-20 flex-shrink-0 rounded-lg" />
|
<Skeleton className="h-10 w-20 shrink-0 rounded-lg" />
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
: envelopeItems.map((item) => (
|
: envelopeItems.map((item) => (
|
||||||
@@ -178,7 +178,7 @@ export const EnvelopeDownloadDialog = ({
|
|||||||
key={item.id}
|
key={item.id}
|
||||||
className="flex items-center gap-4 rounded-lg border border-border bg-card p-4 transition-colors hover:bg-accent/50"
|
className="flex items-center gap-4 rounded-lg border border-border bg-card p-4 transition-colors hover:bg-accent/50"
|
||||||
>
|
>
|
||||||
<div className="flex-shrink-0">
|
<div className="shrink-0">
|
||||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
|
||||||
<FileTextIcon className="h-5 w-5 text-primary" />
|
<FileTextIcon className="h-5 w-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
@@ -194,7 +194,7 @@ export const EnvelopeDownloadDialog = ({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-shrink-0 items-center gap-2">
|
<div className="flex shrink-0 items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@@ -118,12 +118,12 @@ export const EnvelopeDuplicateDialog = ({ envelopeId, envelopeType, trigger }: E
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="twv3-space-y-4">
|
||||||
<Controller
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="includeRecipients"
|
name="includeRecipients"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="twv3-space-x-2 flex items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="envelopeDuplicateIncludeRecipients"
|
id="envelopeDuplicateIncludeRecipients"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
@@ -146,7 +146,7 @@ export const EnvelopeDuplicateDialog = ({ envelopeId, envelopeType, trigger }: E
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="includeFields"
|
name="includeFields"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="twv3-space-x-2 flex items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="envelopeDuplicateIncludeFields"
|
id="envelopeDuplicateIncludeFields"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ export const EnvelopeItemEditDialog = ({
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<fieldset disabled={form.formState.isSubmitting} className="space-y-4">
|
<fieldset disabled={form.formState.isSubmitting} className="twv3-space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="title"
|
name="title"
|
||||||
@@ -253,13 +253,13 @@ export const EnvelopeItemEditDialog = ({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
|
|
||||||
{replacementFile ? (
|
{replacementFile ? (
|
||||||
<div className="mt-1.5 space-y-2">
|
<div className="twv3-space-y-2 mt-1.5">
|
||||||
<div
|
<div
|
||||||
data-testid="envelope-item-edit-selected-file"
|
data-testid="envelope-item-edit-selected-file"
|
||||||
className="flex items-center justify-between rounded-md border border-border bg-muted/50 px-3 py-2"
|
className="flex items-center justify-between rounded-md border border-border bg-muted/50 px-3 py-2"
|
||||||
>
|
>
|
||||||
<div className="flex min-w-0 items-center space-x-2">
|
<div className="twv3-space-x-2 flex min-w-0 items-center">
|
||||||
<FileIcon className="h-4 w-4 flex-shrink-0 text-muted-foreground" />
|
<FileIcon className="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className="truncate font-medium text-sm">{replacementFile.file.name}</p>
|
<p className="truncate font-medium text-sm">{replacementFile.file.name}</p>
|
||||||
<p className="text-muted-foreground text-xs">
|
<p className="text-muted-foreground text-xs">
|
||||||
@@ -309,7 +309,7 @@ export const EnvelopeItemEditDialog = ({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<input {...getInputProps()} />
|
<input {...getInputProps()} />
|
||||||
<div className="flex items-center space-x-2 text-muted-foreground text-sm">
|
<div className="twv3-space-x-2 flex items-center text-muted-foreground text-sm">
|
||||||
<UploadIcon className="h-4 w-4" />
|
<UploadIcon className="h-4 w-4" />
|
||||||
<span>
|
<span>
|
||||||
<Trans>Drop PDF here or click to select</Trans>
|
<Trans>Drop PDF here or click to select</Trans>
|
||||||
|
|||||||
@@ -116,12 +116,12 @@ export const EnvelopeSaveAsTemplateDialog = ({
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="twv3-space-y-4">
|
||||||
<Controller
|
<Controller
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="includeRecipients"
|
name="includeRecipients"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="twv3-space-x-2 flex items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="envelopeIncludeRecipients"
|
id="envelopeIncludeRecipients"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
@@ -144,7 +144,7 @@ export const EnvelopeSaveAsTemplateDialog = ({
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="includeFields"
|
name="includeFields"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="twv3-space-x-2 flex items-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="envelopeIncludeFields"
|
id="envelopeIncludeFields"
|
||||||
checked={field.value}
|
checked={field.value}
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ export const EnvelopesBulkMoveDialog = ({
|
|||||||
</FormLabel>
|
</FormLabel>
|
||||||
|
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="max-h-96 space-y-2 overflow-y-auto">
|
<div className="twv3-space-y-2 max-h-96 overflow-y-auto">
|
||||||
{isFoldersLoading ? (
|
{isFoldersLoading ? (
|
||||||
<div className="flex h-10 items-center justify-center">
|
<div className="flex h-10 items-center justify-center">
|
||||||
<Loader2 className="h-4 w-4 animate-spin" />
|
<Loader2 className="h-4 w-4 animate-spin" />
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export const FolderCreateDialog = ({ type, trigger, parentFolderId, ...props }:
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<fieldset disabled={form.formState.isSubmitting} className="space-y-4">
|
<fieldset disabled={form.formState.isSubmitting} className="twv3-space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export const FolderDeleteDialog = ({ folder, isOpen, onOpenChange }: FolderDelet
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset disabled={form.formState.isSubmitting} className="space-y-4">
|
<fieldset disabled={form.formState.isSubmitting} className="twv3-space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="confirmText"
|
name="confirmText"
|
||||||
|
|||||||
@@ -126,14 +126,14 @@ export const FolderMoveDialog = ({ foldersData, folder, isOpen, onOpenChange }:
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset disabled={form.formState.isSubmitting} className="space-y-4">
|
<fieldset disabled={form.formState.isSubmitting} className="twv3-space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="targetFolderId"
|
name="targetFolderId"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="max-h-96 space-y-2 overflow-y-auto">
|
<div className="twv3-space-y-2 max-h-96 overflow-y-auto">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant={!field.value ? 'default' : 'outline'}
|
variant={!field.value ? 'default' : 'outline'}
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export const FolderUpdateDialog = ({ folder, isOpen, onOpenChange }: FolderUpdat
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)} className="space-y-4">
|
<form onSubmit={form.handleSubmit(onFormSubmit)} className="twv3-space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ export const OrganisationCreateDialog = ({ trigger, ...props }: OrganisationCrea
|
|||||||
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button className="flex-shrink-0" variant="secondary">
|
<Button className="shrink-0" variant="secondary">
|
||||||
<Trans>Create organisation</Trans>
|
<Trans>Create organisation</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -199,7 +199,7 @@ export const OrganisationCreateDialog = ({ trigger, ...props }: OrganisationCrea
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
@@ -305,7 +305,7 @@ const BillingPlanForm = ({ value, onChange, plans, canCreateFreeOrganisation }:
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="twv3-space-y-4">
|
||||||
<Tabs
|
<Tabs
|
||||||
className="flex w-full items-center justify-center"
|
className="flex w-full items-center justify-center"
|
||||||
defaultValue="monthlyPrice"
|
defaultValue="monthlyPrice"
|
||||||
@@ -327,7 +327,7 @@ const BillingPlanForm = ({ value, onChange, plans, canCreateFreeOrganisation }:
|
|||||||
<button
|
<button
|
||||||
onClick={() => onChange('')}
|
onClick={() => onChange('')}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex cursor-pointer items-center space-x-2 rounded-md border p-4 transition-all hover:border-primary hover:shadow-sm',
|
'twv3-space-x-2 flex cursor-pointer items-center rounded-md border p-4 transition-all hover:border-primary hover:shadow-xs',
|
||||||
{
|
{
|
||||||
'border-primary ring-2 ring-primary/10 ring-offset-1': '' === value,
|
'border-primary ring-2 ring-primary/10 ring-offset-1': '' === value,
|
||||||
},
|
},
|
||||||
@@ -360,7 +360,7 @@ const BillingPlanForm = ({ value, onChange, plans, canCreateFreeOrganisation }:
|
|||||||
key={plan[billingPeriod]?.id}
|
key={plan[billingPeriod]?.id}
|
||||||
onClick={() => onChange(plan[billingPeriod]?.id ?? '')}
|
onClick={() => onChange(plan[billingPeriod]?.id ?? '')}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex cursor-pointer items-center space-x-2 rounded-md border p-4 transition-all hover:border-primary hover:shadow-sm',
|
'twv3-space-x-2 flex cursor-pointer items-center rounded-md border p-4 transition-all hover:border-primary hover:shadow-xs',
|
||||||
{
|
{
|
||||||
'border-primary ring-2 ring-primary/10 ring-offset-1': plan[billingPeriod]?.id === value,
|
'border-primary ring-2 ring-primary/10 ring-offset-1': plan[billingPeriod]?.id === value,
|
||||||
},
|
},
|
||||||
@@ -382,7 +382,7 @@ const BillingPlanForm = ({ value, onChange, plans, canCreateFreeOrganisation }:
|
|||||||
<Link
|
<Link
|
||||||
to="https://documen.so/enterprise-cta"
|
to="https://documen.so/enterprise-cta"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
className="flex items-center space-x-2 rounded-md border bg-muted/30 p-4"
|
className="twv3-space-x-2 flex items-center rounded-md border bg-muted/30 p-4"
|
||||||
>
|
>
|
||||||
<div className="flex-1 font-normal">
|
<div className="flex-1 font-normal">
|
||||||
<p className="font-medium text-muted-foreground">
|
<p className="font-medium text-muted-foreground">
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export const OrganisationDeleteDialog = ({ trigger }: OrganisationDeleteDialogPr
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="organisationName"
|
name="organisationName"
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export const OrganisationEmailCreateDialog = ({
|
|||||||
<Dialog {...props} open={open} onOpenChange={(value) => !isPending && setOpen(value)}>
|
<Dialog {...props} open={open} onOpenChange={(value) => !isPending && setOpen(value)}>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button className="flex-shrink-0" variant="secondary">
|
<Button className="shrink-0" variant="secondary">
|
||||||
<Trans>Add Email</Trans>
|
<Trans>Add Email</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -135,7 +135,7 @@ export const OrganisationEmailCreateDialog = ({
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={isPending}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={isPending}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="emailName"
|
name="emailName"
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export const OrganisationEmailDomainCreateDialog = ({ trigger, ...props }: Organ
|
|||||||
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button className="flex-shrink-0" variant="secondary">
|
<Button className="shrink-0" variant="secondary">
|
||||||
<Trans>Add Email Domain</Trans>
|
<Trans>Add Email Domain</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -135,7 +135,7 @@ export const OrganisationEmailDomainCreateDialog = ({ trigger, ...props }: Organ
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="domain"
|
name="domain"
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export const OrganisationEmailDomainDeleteDialog = ({
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset disabled={form.formState.isSubmitting} className="space-y-4">
|
<fieldset disabled={form.formState.isSubmitting} className="twv3-space-y-4">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="confirmText"
|
name="confirmText"
|
||||||
|
|||||||
@@ -59,11 +59,11 @@ export const OrganisationEmailDomainRecordContent = ({ records }: { records: Dom
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="space-y-6">
|
<div className="twv3-space-y-6">
|
||||||
<div className="space-y-4">
|
<div className="twv3-space-y-4">
|
||||||
{records.map((record) => (
|
{records.map((record) => (
|
||||||
<div className="space-y-4 rounded-md border p-4" key={record.name}>
|
<div className="twv3-space-y-4 rounded-md border p-4" key={record.name}>
|
||||||
<div className="space-y-2">
|
<div className="twv3-space-y-2">
|
||||||
<Label>
|
<Label>
|
||||||
<Trans>Record Type</Trans>
|
<Trans>Record Type</Trans>
|
||||||
</Label>
|
</Label>
|
||||||
@@ -79,7 +79,7 @@ export const OrganisationEmailDomainRecordContent = ({ records }: { records: Dom
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="twv3-space-y-2">
|
||||||
<Label>
|
<Label>
|
||||||
<Trans>Record Name</Trans>
|
<Trans>Record Name</Trans>
|
||||||
</Label>
|
</Label>
|
||||||
@@ -95,7 +95,7 @@ export const OrganisationEmailDomainRecordContent = ({ records }: { records: Dom
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="twv3-space-y-2">
|
||||||
<Label>
|
<Label>
|
||||||
<Trans>Record Value</Trans>
|
<Trans>Record Value</Trans>
|
||||||
</Label>
|
</Label>
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ export const OrganisationEmailUpdateDialog = ({
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={isPending}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={isPending}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="emailName"
|
name="emailName"
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export const OrganisationGroupCreateDialog = ({ trigger, ...props }: Organisatio
|
|||||||
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button className="flex-shrink-0" variant="secondary">
|
<Button className="shrink-0" variant="secondary">
|
||||||
<Trans>Create group</Trans>
|
<Trans>Create group</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -127,7 +127,7 @@ export const OrganisationGroupCreateDialog = ({ trigger, ...props }: Organisatio
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|||||||
@@ -313,10 +313,10 @@ export const OrganisationMemberInviteDialog = ({ trigger, ...props }: Organisati
|
|||||||
<TabsContent value="INDIVIDUAL">
|
<TabsContent value="INDIVIDUAL">
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<div className="custom-scrollbar -m-1 max-h-[60vh] space-y-4 overflow-y-auto p-1">
|
<div className="custom-scrollbar twv3-space-y-4 -m-1 max-h-[60vh] overflow-y-auto p-1">
|
||||||
{organisationMemberInvites.map((organisationMemberInvite, index) => (
|
{organisationMemberInvites.map((organisationMemberInvite, index) => (
|
||||||
<div className="flex w-full flex-row space-x-4" key={organisationMemberInvite.id}>
|
<div className="twv3-space-x-4 flex w-full flex-row" key={organisationMemberInvite.id}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={`invitations.${index}.email`}
|
name={`invitations.${index}.email`}
|
||||||
@@ -409,7 +409,7 @@ export const OrganisationMemberInviteDialog = ({ trigger, ...props }: Organisati
|
|||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="BULK">
|
<TabsContent value="BULK">
|
||||||
<div className="mt-4 space-y-4">
|
<div className="twv3-space-y-4 mt-4">
|
||||||
<Card gradient className="h-32">
|
<Card gradient className="h-32">
|
||||||
<CardContent
|
<CardContent
|
||||||
className="flex h-full cursor-pointer flex-col items-center justify-center rounded-lg p-0 text-muted-foreground/80 transition-colors hover:text-muted-foreground/90"
|
className="flex h-full cursor-pointer flex-col items-center justify-center rounded-lg p-0 text-muted-foreground/80 transition-colors hover:text-muted-foreground/90"
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export const PasskeyCreateDialog = ({ trigger, onSuccess, ...props }: PasskeyCre
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="passkeyName"
|
name="passkeyName"
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ export const ManagePublicTemplateDialog = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog {...props} open={isOpen || open} onOpenChange={handleOnOpenChange}>
|
<Dialog {...props} open={isOpen || open} onOpenChange={handleOnOpenChange}>
|
||||||
<fieldset disabled={isLoading} className="relative flex-shrink-0">
|
<fieldset disabled={isLoading} className="relative shrink-0">
|
||||||
<DialogTrigger asChild>{trigger}</DialogTrigger>
|
<DialogTrigger asChild>{trigger}</DialogTrigger>
|
||||||
|
|
||||||
<AnimateGenericFadeInOut motionKey={currentStep}>
|
<AnimateGenericFadeInOut motionKey={currentStep}>
|
||||||
@@ -311,7 +311,7 @@ export const ManagePublicTemplateDialog = ({
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form className="flex h-full flex-col space-y-4" onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form className="twv3-space-y-4 flex h-full flex-col" onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="publicTitle"
|
name="publicTitle"
|
||||||
|
|||||||
@@ -102,8 +102,8 @@ export const SignFieldCheckboxDialog = createCallable<SignFieldCheckboxDialogPro
|
|||||||
call.end(data.values.map((value, i) => (value.checked ? i : null)).filter((value) => value !== null)),
|
call.end(data.values.map((value, i) => (value.checked ? i : null)).filter((value) => value !== null)),
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<ul className="space-y-3">
|
<ul className="twv3-space-y-3">
|
||||||
{(formValues.values || []).map((value, index) => (
|
{(formValues.values || []).map((value, index) => (
|
||||||
<li key={`checkbox-${index}`}>
|
<li key={`checkbox-${index}`}>
|
||||||
<FormField
|
<FormField
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const SignFieldEmailDialog = createCallable<SignFieldEmailDialogProps, st
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit((data) => call.end(data.email))}>
|
<form onSubmit={form.handleSubmit((data) => call.end(data.email))}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="email"
|
name="email"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export const SignFieldInitialsDialog = createCallable<SignFieldInitialsDialogPro
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit((data) => call.end(data.initials))}>
|
<form onSubmit={form.handleSubmit((data) => call.end(data.initials))}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="initials"
|
name="initials"
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export const SignFieldNameDialog = createCallable<SignFieldNameDialogProps, stri
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit((data) => call.end(data.name))}>
|
<form onSubmit={form.handleSubmit((data) => call.end(data.name))}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export const SignFieldNumberDialog = createCallable<SignFieldNumberDialogProps,
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit((data) => call.end(data.number))}>
|
<form onSubmit={form.handleSubmit((data) => call.end(data.number))}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="number"
|
name="number"
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const SignFieldTextDialog = createCallable<SignFieldTextDialogProps, stri
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit((data) => call.end(data.text))}>
|
<form onSubmit={form.handleSubmit((data) => call.end(data.text))}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="text"
|
name="text"
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ export const TeamCreateDialog = ({ trigger, onCreated, ...props }: TeamCreateDia
|
|||||||
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
<Dialog {...props} open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button className="flex-shrink-0" variant="secondary">
|
<Button className="shrink-0" variant="secondary">
|
||||||
<Trans>Create team</Trans>
|
<Trans>Create team</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -195,7 +195,7 @@ export const TeamCreateDialog = ({ trigger, onCreated, ...props }: TeamCreateDia
|
|||||||
{dialogState === 'form' && (
|
{dialogState === 'form' && (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="teamName"
|
name="teamName"
|
||||||
@@ -256,7 +256,7 @@ export const TeamCreateDialog = ({ trigger, onCreated, ...props }: TeamCreateDia
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="inheritMembers"
|
name="inheritMembers"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex items-center space-x-2">
|
<FormItem className="twv3-space-x-2 flex items-center">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Checkbox id="inherit-members" checked={field.value} onCheckedChange={field.onChange} />
|
<Checkbox id="inherit-members" checked={field.value} onCheckedChange={field.onChange} />
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ export const TeamDeleteDialog = ({ trigger, teamId, teamName, redirectTo }: Team
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="teamName"
|
name="teamName"
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export const TeamEmailAddDialog = ({ teamId, trigger, ...props }: TeamEmailAddDi
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export const TeamEmailUpdateDialog = ({ teamEmail, trigger, ...props }: TeamEmai
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
<form onSubmit={form.handleSubmit(onFormSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
|
|||||||
@@ -202,10 +202,10 @@ export const TeamGroupCreateDialog = ({ ...props }: TeamGroupCreateDialogProps)
|
|||||||
|
|
||||||
{step === 'ROLES' && (
|
{step === 'ROLES' && (
|
||||||
<>
|
<>
|
||||||
<div className="custom-scrollbar -m-1 max-h-[60vh] space-y-4 overflow-y-auto p-1">
|
<div className="custom-scrollbar twv3-space-y-4 -m-1 max-h-[60vh] overflow-y-auto p-1">
|
||||||
{form.getValues('groups').map((group, index) => (
|
{form.getValues('groups').map((group, index) => (
|
||||||
<div className="flex w-full flex-row space-x-4" key={index}>
|
<div className="twv3-space-x-4 flex w-full flex-row" key={index}>
|
||||||
<div className="w-full space-y-2">
|
<div className="twv3-space-y-2 w-full">
|
||||||
{index === 0 && (
|
{index === 0 && (
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
<Trans>Group</Trans>
|
<Trans>Group</Trans>
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ export const TeamMemberCreateDialog = ({ trigger, ...props }: TeamMemberCreateDi
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="members"
|
name="members"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="space-y-2">
|
<FormItem className="twv3-space-y-2">
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
<Trans>Members</Trans>
|
<Trans>Members</Trans>
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
@@ -310,7 +310,7 @@ export const TeamMemberCreateDialog = ({ trigger, ...props }: TeamMemberCreateDi
|
|||||||
</FormDescription>
|
</FormDescription>
|
||||||
|
|
||||||
{canInviteOrganisationMembers && (
|
{canInviteOrganisationMembers && (
|
||||||
<Alert variant="neutral" className="mt-2 flex items-center gap-2 space-y-0">
|
<Alert variant="neutral" className="twv3-space-y-0 mt-2 flex items-center gap-2">
|
||||||
<div>
|
<div>
|
||||||
<UserPlusIcon className="h-5 w-5 text-muted-foreground" />
|
<UserPlusIcon className="h-5 w-5 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
@@ -358,10 +358,10 @@ export const TeamMemberCreateDialog = ({ trigger, ...props }: TeamMemberCreateDi
|
|||||||
|
|
||||||
{step === 'MEMBERS' && (
|
{step === 'MEMBERS' && (
|
||||||
<>
|
<>
|
||||||
<div className="custom-scrollbar -m-1 max-h-[60vh] space-y-4 overflow-y-auto p-1">
|
<div className="custom-scrollbar twv3-space-y-4 -m-1 max-h-[60vh] overflow-y-auto p-1">
|
||||||
{form.getValues('members').map((member, index) => (
|
{form.getValues('members').map((member, index) => (
|
||||||
<div className="flex w-full flex-row space-x-4" key={index}>
|
<div className="twv3-space-x-4 flex w-full flex-row" key={index}>
|
||||||
<div className="w-full space-y-2">
|
<div className="twv3-space-y-2 w-full">
|
||||||
{index === 0 && (
|
{index === 0 && (
|
||||||
<FormLabel>
|
<FormLabel>
|
||||||
<Trans>Member</Trans>
|
<Trans>Member</Trans>
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ export const TemplateBulkSendDialog = ({ templateId, recipients, trigger, onSucc
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="sendImmediately"
|
name="sendImmediately"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex items-center space-x-2">
|
<FormItem className="twv3-space-x-2 flex items-center">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Checkbox id="send-immediately" checked={field.value} onCheckedChange={field.onChange} />
|
<Checkbox id="send-immediately" checked={field.value} onCheckedChange={field.onChange} />
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ export const TemplateDirectLinkDialog = ({
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<ul className="mt-4 space-y-4 pl-12">
|
<ul className="twv3-space-y-4 mt-4 pl-12">
|
||||||
{DIRECT_TEMPLATE_DOCUMENTATION.map((step, index) => (
|
{DIRECT_TEMPLATE_DOCUMENTATION.map((step, index) => (
|
||||||
<li className="relative" key={index}>
|
<li className="relative" key={index}>
|
||||||
<div className="absolute -left-12">
|
<div className="absolute -left-12">
|
||||||
@@ -259,7 +259,7 @@ export const TemplateDirectLinkDialog = ({
|
|||||||
.with({ token: P.nullish, currentStep: 'SELECT_RECIPIENT' }, () => (
|
.with({ token: P.nullish, currentStep: 'SELECT_RECIPIENT' }, () => (
|
||||||
<DialogContent className="relative">
|
<DialogContent className="relative">
|
||||||
{isCreatingTemplateDirectLink && validDirectTemplateRecipients.length !== 0 && (
|
{isCreatingTemplateDirectLink && validDirectTemplateRecipients.length !== 0 && (
|
||||||
<div className="absolute inset-0 z-50 flex items-center justify-center rounded bg-white/50 dark:bg-black/50">
|
<div className="absolute inset-0 z-50 flex items-center justify-center rounded-sm bg-white/50 dark:bg-black/50">
|
||||||
<LoaderIcon className="h-6 w-6 animate-spin text-gray-500" />
|
<LoaderIcon className="h-6 w-6 animate-spin text-gray-500" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -405,7 +405,7 @@ export const TemplateDirectLinkDialog = ({
|
|||||||
className="h-8 w-8"
|
className="h-8 w-8"
|
||||||
onClick={() => void onCopyClick(token)}
|
onClick={() => void onCopyClick(token)}
|
||||||
>
|
>
|
||||||
<ClipboardCopyIcon className="h-4 w-4 flex-shrink-0" />
|
<ClipboardCopyIcon className="h-4 w-4 shrink-0" />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -240,9 +240,9 @@ export function TemplateUseDialog({
|
|||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
<fieldset className="flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<div className="custom-scrollbar -m-1 max-h-[60vh] space-y-4 overflow-y-auto p-1">
|
<div className="custom-scrollbar twv3-space-y-4 -m-1 max-h-[60vh] overflow-y-auto p-1">
|
||||||
{formRecipients.map((recipient, index) => (
|
{formRecipients.map((recipient, index) => (
|
||||||
<div className="flex w-full flex-row space-x-4" key={recipient.id}>
|
<div className="twv3-space-x-4 flex w-full flex-row" key={recipient.id}>
|
||||||
{templateSigningOrder === DocumentSigningOrder.SEQUENTIAL && (
|
{templateSigningOrder === DocumentSigningOrder.SEQUENTIAL && (
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
@@ -337,7 +337,7 @@ export function TemplateUseDialog({
|
|||||||
<InfoIcon className="mx-1 h-4 w-4" />
|
<InfoIcon className="mx-1 h-4 w-4" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
|
|
||||||
<TooltipContent className="z-[99999] max-w-md space-y-2 p-4 text-muted-foreground">
|
<TooltipContent className="twv3-space-y-2 z-[99999] max-w-md p-4 text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
<Trans>
|
<Trans>
|
||||||
The document will be immediately sent to recipients if this is checked.
|
The document will be immediately sent to recipients if this is checked.
|
||||||
@@ -362,7 +362,7 @@ export function TemplateUseDialog({
|
|||||||
<TooltipTrigger type="button">
|
<TooltipTrigger type="button">
|
||||||
<InfoIcon className="mx-1 h-4 w-4" />
|
<InfoIcon className="mx-1 h-4 w-4" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent className="z-[99999] max-w-md space-y-2 p-4 text-muted-foreground">
|
<TooltipContent className="twv3-space-y-2 z-[99999] max-w-md p-4 text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
<Trans>Create the document as pending and ready to sign.</Trans>
|
<Trans>Create the document as pending and ready to sign.</Trans>
|
||||||
</p>
|
</p>
|
||||||
@@ -414,7 +414,7 @@ export function TemplateUseDialog({
|
|||||||
<TooltipTrigger type="button">
|
<TooltipTrigger type="button">
|
||||||
<InfoIcon className="mx-1 h-4 w-4" />
|
<InfoIcon className="mx-1 h-4 w-4" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent className="z-[99999] max-w-md space-y-2 p-4 text-muted-foreground">
|
<TooltipContent className="twv3-space-y-2 z-[99999] max-w-md p-4 text-muted-foreground">
|
||||||
<p>
|
<p>
|
||||||
<Trans>
|
<Trans>
|
||||||
Upload a custom document to use instead of the template's default document
|
Upload a custom document to use instead of the template's default document
|
||||||
@@ -429,7 +429,7 @@ export function TemplateUseDialog({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{form.watch('useCustomDocument') && (
|
{form.watch('useCustomDocument') && (
|
||||||
<div className="my-4 space-y-2">
|
<div className="twv3-space-y-2 my-4">
|
||||||
{isLoadingEnvelopeItems ? (
|
{isLoadingEnvelopeItems ? (
|
||||||
<SpinnerBox className="py-16" />
|
<SpinnerBox className="py-16" />
|
||||||
) : (
|
) : (
|
||||||
@@ -445,7 +445,7 @@ export function TemplateUseDialog({
|
|||||||
key={item.id}
|
key={item.id}
|
||||||
className="flex items-center gap-4 rounded-lg border border-border bg-card p-4 transition-colors hover:bg-accent/10"
|
className="flex items-center gap-4 rounded-lg border border-border bg-card p-4 transition-colors hover:bg-accent/10"
|
||||||
>
|
>
|
||||||
<div className="flex-shrink-0">
|
<div className="shrink-0">
|
||||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10">
|
||||||
<FileTextIcon className="h-5 w-5 text-primary" />
|
<FileTextIcon className="h-5 w-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
@@ -464,7 +464,7 @@ export function TemplateUseDialog({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-shrink-0 items-center gap-2">
|
<div className="flex shrink-0 items-center gap-2">
|
||||||
{field.value ? (
|
{field.value ? (
|
||||||
<div className="">
|
<div className="">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export default function TokenDeleteDialog({ token, onDelete, children }: TokenDe
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="tokenName"
|
name="tokenName"
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export const WebhookCreateDialog = ({ trigger, ...props }: WebhookCreateDialogPr
|
|||||||
<Dialog open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)} {...props}>
|
<Dialog open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)} {...props}>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button className="flex-shrink-0">
|
<Button className="shrink-0">
|
||||||
<Trans>Create Webhook</Trans>
|
<Trans>Create Webhook</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -112,7 +112,7 @@ export const WebhookCreateDialog = ({ trigger, ...props }: WebhookCreateDialogPr
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<div className="flex flex-col-reverse gap-4 md:flex-row">
|
<div className="flex flex-col-reverse gap-4 md:flex-row">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export const WebhookDeleteDialog = ({ webhook, children }: WebhookDeleteDialogPr
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="webhookUrl"
|
name="webhookUrl"
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export const WebhookTestDialog = ({ webhook, children }: WebhookTestDialogProps)
|
|||||||
|
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col space-y-4" disabled={form.formState.isSubmitting}>
|
<fieldset className="twv3-space-y-4 flex h-full flex-col" disabled={form.formState.isSubmitting}>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="event"
|
name="event"
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const ConfigureDocumentAdvancedSettings = ({
|
|||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="general" className="mt-0">
|
<TabsContent value="general" className="mt-0">
|
||||||
<div className="flex flex-col space-y-6">
|
<div className="twv3-space-y-6 flex flex-col">
|
||||||
{features.allowConfigureSignatureTypes && (
|
{features.allowConfigureSignatureTypes && (
|
||||||
<FormField
|
<FormField
|
||||||
control={control}
|
control={control}
|
||||||
@@ -215,7 +215,7 @@ export const ConfigureDocumentAdvancedSettings = ({
|
|||||||
|
|
||||||
{features.allowConfigureCommunication && (
|
{features.allowConfigureCommunication && (
|
||||||
<TabsContent value="communication" className="mt-0">
|
<TabsContent value="communication" className="mt-0">
|
||||||
<div className="flex flex-col space-y-6">
|
<div className="twv3-space-y-6 flex flex-col">
|
||||||
<FormField
|
<FormField
|
||||||
control={control}
|
control={control}
|
||||||
name="meta.distributionMethod"
|
name="meta.distributionMethod"
|
||||||
@@ -254,7 +254,7 @@ export const ConfigureDocumentAdvancedSettings = ({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<fieldset
|
<fieldset
|
||||||
className="flex flex-col space-y-6 disabled:cursor-not-allowed disabled:opacity-60"
|
className="twv3-space-y-6 flex flex-col disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
disabled={!isEmailDistribution}
|
disabled={!isEmailDistribution}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ export const ConfigureDocumentRecipients = ({ control, isSubmitting }: Configure
|
|||||||
control={control}
|
control={control}
|
||||||
name="meta.signingOrder"
|
name="meta.signingOrder"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="mb-6 flex flex-row items-center space-x-2 space-y-0">
|
<FormItem className="twv3-space-x-2 twv3-space-y-0 mb-6 flex flex-row items-center">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
{...field}
|
{...field}
|
||||||
@@ -173,7 +173,7 @@ export const ConfigureDocumentRecipients = ({ control, isSubmitting }: Configure
|
|||||||
control={control}
|
control={control}
|
||||||
name="meta.allowDictateNextSigner"
|
name="meta.allowDictateNextSigner"
|
||||||
render={({ field: { value, ...field } }) => (
|
render={({ field: { value, ...field } }) => (
|
||||||
<FormItem className="mb-6 flex flex-row items-center space-x-2 space-y-0">
|
<FormItem className="twv3-space-x-2 twv3-space-y-0 mb-6 flex flex-row items-center">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
{...field}
|
{...field}
|
||||||
@@ -221,7 +221,7 @@ export const ConfigureDocumentRecipients = ({ control, isSubmitting }: Configure
|
|||||||
>
|
>
|
||||||
<Droppable droppableId="signers">
|
<Droppable droppableId="signers">
|
||||||
{(provided) => (
|
{(provided) => (
|
||||||
<div {...provided.droppableProps} ref={provided.innerRef} className="space-y-2">
|
<div {...provided.droppableProps} ref={provided.innerRef} className="twv3-space-y-2">
|
||||||
{signers.map((signer, index) => (
|
{signers.map((signer, index) => (
|
||||||
<Draggable
|
<Draggable
|
||||||
key={signer.id}
|
key={signer.id}
|
||||||
@@ -254,7 +254,7 @@ export const ConfigureDocumentRecipients = ({ control, isSubmitting }: Configure
|
|||||||
'mb-6': errors?.signers?.[index] && !errors?.signers?.[index]?.signingOrder,
|
'mb-6': errors?.signers?.[index] && !errors?.signers?.[index]?.signingOrder,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<GripVertical className="h-5 w-5 flex-shrink-0 opacity-40" />
|
<GripVertical className="h-5 w-5 shrink-0 opacity-40" />
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ export const ConfigureDocumentUpload = ({ isSubmitting = false }: ConfigureDocum
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn('flex flex-col space-y-1', {
|
className={cn('twv3-space-y-1 flex flex-col', {
|
||||||
'text-primary': isDragActive,
|
'text-primary': isDragActive,
|
||||||
'text-muted-foreground': !isDragActive,
|
'text-muted-foreground': !isDragActive,
|
||||||
})}
|
})}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user