Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5c165b22d |
@@ -0,0 +1,38 @@
|
|||||||
|
# Extract and compile translations for all PRs.
|
||||||
|
|
||||||
|
name: 'Extract and compile translations'
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
pull_request:
|
||||||
|
branches: ['main']
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
extract_translations:
|
||||||
|
name: Extract and compile translations
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.ref }}
|
||||||
|
|
||||||
|
- uses: ./.github/actions/node-install
|
||||||
|
|
||||||
|
- name: Extract and compile translations
|
||||||
|
run: |
|
||||||
|
npm run translate:extract
|
||||||
|
npm run translate:compile
|
||||||
|
|
||||||
|
- name: Check and commit any files created
|
||||||
|
run: |
|
||||||
|
git config --global user.name 'github-actions'
|
||||||
|
git config --global user.email 'github-actions@documenso.com'
|
||||||
|
git add packages/lib/translations
|
||||||
|
git diff --staged --quiet --exit-code || (git commit -m "chore: extract translations" && git push)
|
||||||
@@ -25,8 +25,10 @@ jobs:
|
|||||||
|
|
||||||
- uses: ./.github/actions/node-install
|
- uses: ./.github/actions/node-install
|
||||||
|
|
||||||
- name: Extract translations
|
- name: Extract and compile translations
|
||||||
run: npm run translate:extract
|
run: |
|
||||||
|
npm run translate:extract
|
||||||
|
npm run translate:compile
|
||||||
|
|
||||||
- name: Check and commit any files created
|
- name: Check and commit any files created
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -13,4 +13,9 @@ node "$MONOREPO_ROOT/scripts/copy-wellknown.cjs"
|
|||||||
git add "$MONOREPO_ROOT/apps/web/public/"
|
git add "$MONOREPO_ROOT/apps/web/public/"
|
||||||
git add "$MONOREPO_ROOT/apps/marketing/public/"
|
git add "$MONOREPO_ROOT/apps/marketing/public/"
|
||||||
|
|
||||||
|
echo "Extract and compile translations"
|
||||||
|
npm run translate:extract
|
||||||
|
npm run translate:compile
|
||||||
|
git add "$MONOREPO_ROOT/packages/lib/translations/"
|
||||||
|
|
||||||
npx lint-staged
|
npx lint-staged
|
||||||
|
|||||||
@@ -1 +1,36 @@
|
|||||||
# @documenso/documentation
|
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First, run the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
|
# or
|
||||||
|
pnpm dev
|
||||||
|
# or
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3002](http://localhost:3002) with your browser to see the result.
|
||||||
|
|
||||||
|
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
|
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about Next.js, 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.
|
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"index": "Getting Started",
|
"index": "Get Started",
|
||||||
"contributing-translations": "Contributing Translations"
|
"contributing-translations": "Contributing Translations"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"index": "Get Started"
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
title: Angular Integration
|
||||||
|
description: Learn how to use our embedding SDK within your Angular application.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Angular Integration
|
||||||
|
|
||||||
|
Our Angular SDK provides a simple way to embed a signing experience within your Angular application. It supports both direct link templates and signing tokens.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install the SDK, run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @documenso/embed-angular
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To embed a signing experience, you'll need to provide the token for the document you want to embed. This can be done in a few different ways, depending on your use case.
|
||||||
|
|
||||||
|
While the Angular components we provide are configured as standalone components, it can also be used with NgModule. The proceeding examples will assume your project is setup for standalone components.
|
||||||
|
|
||||||
|
### Direct Link Template
|
||||||
|
|
||||||
|
If you have a direct link template, you can simply provide the token for the template to the `EmbedDirectTemplate` component.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { EmbedDirectTemplate } from '@documenso/embed-angular';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'example-component',
|
||||||
|
imports: [EmbedDirectTemplate],
|
||||||
|
template: `
|
||||||
|
<embed-direct-template token="YOUR_TOKEN_HERE" />
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class ExampleComponent {
|
||||||
|
// Component logic.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Props
|
||||||
|
|
||||||
|
| Prop | Type | Description |
|
||||||
|
| ------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
|
||||||
|
| token | string | The token for the document you want to embed |
|
||||||
|
| host | string (optional) | The host to be used for the signing experience, relevant for self-hosters |
|
||||||
|
| name | string (optional) | The name the signer that will be used by default for signing |
|
||||||
|
| lockName | boolean (optional) | Whether or not the name field should be locked disallowing modifications |
|
||||||
|
| email | string (optional) | The email the signer that will be used by default for signing |
|
||||||
|
| lockEmail | boolean (optional) | Whether or not the email field should be locked disallowing modifications |
|
||||||
|
| externalId | string (optional) | The external ID to be used for the document that will be created upon completion |
|
||||||
|
| onDocumentReady | function (optional) | A callback function that will be called when the document is loaded and ready to be signed |
|
||||||
|
| onDocumentCompleted | function (optional) | A callback function that will be called when the document has been completed |
|
||||||
|
| onDocumentError | function (optional) | A callback function that will be called when an error occurs with the document |
|
||||||
|
| onFieldSigned | function (optional) | A callback function that will be called when a field has been signed |
|
||||||
|
| onFieldUnsigned | function (optional) | A callback function that will be called when a field has been unsigned |
|
||||||
|
|
||||||
|
### Signing Token
|
||||||
|
|
||||||
|
If you have a signing token, you can provide it to the `EmbedSignDocument` component.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { EmbedSignDocument } from '@documenso/embed-angular';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
selector: 'example-component',
|
||||||
|
imports: [EmbedSignDocument],
|
||||||
|
template: `
|
||||||
|
<embed-sign-document token="YOUR_TOKEN_HERE" />
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
export class ExampleComponent {
|
||||||
|
// Component logic.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Props
|
||||||
|
|
||||||
|
| Prop | Type | Description |
|
||||||
|
| ------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
|
||||||
|
| token | string | The token for the document you want to embed |
|
||||||
|
| host | string (optional) | The host to be used for the signing experience, relevant for self-hosters |
|
||||||
|
| name | string (optional) | The name the signer that will be used by default for signing |
|
||||||
|
| lockName | boolean (optional) | Whether or not the name field should be locked disallowing modifications |
|
||||||
|
| onDocumentReady | function (optional) | A callback function that will be called when the document is loaded and ready to be signed |
|
||||||
|
| onDocumentCompleted | function (optional) | A callback function that will be called when the document has been completed |
|
||||||
|
| onDocumentError | function (optional) | A callback function that will be called when an error occurs with the document |
|
||||||
@@ -26,13 +26,14 @@ _For most use-cases we recommend using direct templates, however if you have a n
|
|||||||
|
|
||||||
We support embedding across a range of popular JavaScript frameworks, including:
|
We support embedding across a range of popular JavaScript frameworks, including:
|
||||||
|
|
||||||
| Framework | Package |
|
| Framework | Package |
|
||||||
| --------- | -------------------------------------------------------------------------------- |
|
| --------- | ---------------------------------------------------------------------------------- |
|
||||||
| React | [@documenso/embed-react](https://www.npmjs.com/package/@documenso/embed-react) |
|
| Angular | [@documenso/embed-angular](https://www.npmjs.com/package/@documenso/embed-angular) |
|
||||||
| Preact | [@documenso/embed-preact](https://www.npmjs.com/package/@documenso/embed-preact) |
|
| React | [@documenso/embed-react](https://www.npmjs.com/package/@documenso/embed-react) |
|
||||||
| Vue | [@documenso/embed-vue](https://www.npmjs.com/package/@documenso/embed-vue) |
|
| Preact | [@documenso/embed-preact](https://www.npmjs.com/package/@documenso/embed-preact) |
|
||||||
| Svelte | [@documenso/embed-svelte](https://www.npmjs.com/package/@documenso/embed-svelte) |
|
| Vue | [@documenso/embed-vue](https://www.npmjs.com/package/@documenso/embed-vue) |
|
||||||
| Solid | [@documenso/embed-solid](https://www.npmjs.com/package/@documenso/embed-solid) |
|
| Svelte | [@documenso/embed-svelte](https://www.npmjs.com/package/@documenso/embed-svelte) |
|
||||||
|
| Solid | [@documenso/embed-solid](https://www.npmjs.com/package/@documenso/embed-solid) |
|
||||||
|
|
||||||
Additionally, we provide **web components** for more generalized use. However, please note that web components are still in their early stages and haven't been extensively tested.
|
Additionally, we provide **web components** for more generalized use. However, please note that web components are still in their early stages and haven't been extensively tested.
|
||||||
|
|
||||||
|
|||||||
@@ -1,507 +0,0 @@
|
|||||||
---
|
|
||||||
title: API Reference
|
|
||||||
description: Reference documentation for the Documenso public API.
|
|
||||||
---
|
|
||||||
|
|
||||||
import { Callout, Steps } from 'nextra/components';
|
|
||||||
|
|
||||||
# API Reference
|
|
||||||
|
|
||||||
The Swagger UI for the API is available at [/api/v1/openapi](https://app.documenso.com/api/v1/openapi). This page provides detailed information about the API endpoints, request and response formats, and authentication requirements.
|
|
||||||
|
|
||||||
## Upload a Document
|
|
||||||
|
|
||||||
Uploading a document to your Documenso account requires a two-step process.
|
|
||||||
|
|
||||||
<Steps>
|
|
||||||
|
|
||||||
### Create Document
|
|
||||||
|
|
||||||
First, you need to make a `POST` request to the `/api/v1/documents` endpoint, which takes a JSON payload with the following fields:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "string",
|
|
||||||
"externalId": "string",
|
|
||||||
"recipients": [
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
"email": "user@example.com",
|
|
||||||
"role": "SIGNER",
|
|
||||||
"signingOrder": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"meta": {
|
|
||||||
"subject": "string",
|
|
||||||
"message": "string",
|
|
||||||
"timezone": "Etc/UTC",
|
|
||||||
"dateFormat": "yyyy-MM-dd hh:mm a",
|
|
||||||
"redirectUrl": "string",
|
|
||||||
"signingOrder": "PARALLEL"
|
|
||||||
},
|
|
||||||
"authOptions": {
|
|
||||||
"globalAccessAuth": "ACCOUNT",
|
|
||||||
"globalActionAuth": "ACCOUNT"
|
|
||||||
},
|
|
||||||
"formValues": {
|
|
||||||
"additionalProp1": "string",
|
|
||||||
"additionalProp2": "string",
|
|
||||||
"additionalProp3": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- `title` _(required)_ - This represents the document's title.
|
|
||||||
- `externalId` - This is an optional field that you can use to store an external identifier for the document. This can be useful for tracking the document in your system.
|
|
||||||
- `recipients` _(required)_ - This is an array of recipient objects. Each recipient object has the following fields:
|
|
||||||
- `name` - The name of the recipient.
|
|
||||||
- `email` - The email address of the recipient.
|
|
||||||
- `role` - The role of the recipient. See the [available roles](/users/signing-documents#roles).
|
|
||||||
- `signingOrder` - The order in which the recipient should sign the document. This is an integer value starting from 0.
|
|
||||||
- `meta` - This object contains additional metadata for the document. It has the following fields:
|
|
||||||
- `subject` - The subject of the email that will be sent to the recipients.
|
|
||||||
- `message` - The message of the email that will be sent to the recipients.
|
|
||||||
- `timezone` - The timezone in which the document should be signed.
|
|
||||||
- `dateFormat` - The date format that should be used in the document.
|
|
||||||
- `redirectUrl` - The URL to which the user should be redirected after signing the document.
|
|
||||||
- `signingOrder` - The signing order for the document. This can be either `SEQUENTIAL` or `PARALLEL`.
|
|
||||||
- `authOptions` - This object contains authentication options for the document. It has the following fields:
|
|
||||||
- `globalAccessAuth` - The authentication level required to access the document. This can be either `ACCOUNT` or `null`.
|
|
||||||
- If the document is set to `ACCOUNT`, all recipients must authenticate with their Documenso account to access it.
|
|
||||||
- The document can be accessed without a Documenso account if it's set to `null`.
|
|
||||||
- `globalActionAuth` - The authentication level required to perform actions on the document. This can be `ACCOUNT`, `PASSKEY`, `TWO_FACTOR_AUTH`, or `null`.
|
|
||||||
- If the document is set to `ACCOUNT`, all recipients must authenticate with their Documenso account to perform actions on the document.
|
|
||||||
- If it's set to `PASSKEY`, all recipients must have the passkey active to perform actions on the document.
|
|
||||||
- If it's set to `TWO_FACTOR_AUTH`, all recipients must have the two-factor authentication active to perform actions on the document.
|
|
||||||
- If it's set to `null`, all the recipients can perform actions on the document without any authentication.
|
|
||||||
- `formValues` - This object contains additional form values for the document. This property only works with native PDF fields and accepts three types: number, text and boolean.
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
The `globalActionAuth` property is only available for Enterprise accounts.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
Here's an example of the JSON payload for uploading a document:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "my-document.pdf",
|
|
||||||
"externalId": "12345",
|
|
||||||
"recipients": [
|
|
||||||
{
|
|
||||||
"name": "Alex Blake",
|
|
||||||
"email": "alexblake@email.com",
|
|
||||||
"role": "SIGNER",
|
|
||||||
"signingOrder": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ash Drew",
|
|
||||||
"email": "ashdrew@email.com",
|
|
||||||
"role": "SIGNER",
|
|
||||||
"signingOrder": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"meta": {
|
|
||||||
"subject": "Sign the document",
|
|
||||||
"message": "Hey there, please sign this document.",
|
|
||||||
"timezone": "Europe/London",
|
|
||||||
"dateFormat": "Day, Month Year",
|
|
||||||
"redirectUrl": "https://mysite.com/welcome",
|
|
||||||
"signingOrder": "SEQUENTIAL"
|
|
||||||
},
|
|
||||||
"authOptions": {
|
|
||||||
"globalAccessAuth": "ACCOUNT",
|
|
||||||
"globalActionAuth": "PASSKEY"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Upload to S3
|
|
||||||
|
|
||||||
A successful API call to the `/api/v1/documents` endpoint returns a JSON response containing the upload URL, document ID, and recipient information.
|
|
||||||
|
|
||||||
The upload URL is a pre-signed S3 URL that you can use to upload the document to the Documenso (or your) S3 bucket. You need to make a `PUT` request to this URL to upload the document.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"uploadUrl": "https://<url>/<bucket-name>/<id>/my-document.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=<credentials>&X-Amz-Date=<date>&X-Amz-Expires=3600&X-Amz-Signature=<signature>&X-Amz-SignedHeaders=host&x-id=PutObject",
|
|
||||||
"documentId": 51,
|
|
||||||
"recipients": [
|
|
||||||
{
|
|
||||||
"recipientId": 11,
|
|
||||||
"name": "Alex Blake",
|
|
||||||
"email": "alexblake@email.com",
|
|
||||||
"token": "<unique-signer-token>",
|
|
||||||
"role": "SIGNER",
|
|
||||||
"signingOrder": 1,
|
|
||||||
"signingUrl": "https://app.documenso.com/sign/<unique-signer-token>"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"recipientId": 12,
|
|
||||||
"name": "Ash Drew",
|
|
||||||
"email": "ashdrew@email.com",
|
|
||||||
"token": "<unique-signer-token>",
|
|
||||||
"role": "SIGNER",
|
|
||||||
"signingOrder": 0,
|
|
||||||
"signingUrl": "https://app.documenso.com/sign/<unique-signer-token>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
When you make the `PUT` request to the pre-signed URL, you need to include the document file you want to upload. The image below shows how to upload a document to the S3 bucket via Postman.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Here's an example of how to upload a document using cURL:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl --location --request PUT 'https://<url>/<bucket-name>/<id>/my-document.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=<credentials>&X-Amz-Date=<date>&X-Amz-Expires=3600&X-Amz-Signature=<signature>&X-Amz-SignedHeaders=host&x-id=PutObject' \
|
|
||||||
--form '=@"/Users/my-user/Documents/documenso.pdf"'
|
|
||||||
```
|
|
||||||
|
|
||||||
Once the document is successfully uploaded, you can access it in your Documenso account dashboard. The screenshot below shows the document that was uploaded via the API.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Steps>
|
|
||||||
|
|
||||||
## Generate Document From Template
|
|
||||||
|
|
||||||
Documenso allows you to generate documents from templates. This is useful when you have a standard document format you want to reuse.
|
|
||||||
|
|
||||||
The API endpoint for generating a document from a template is `/api/v1/templates/{templateId}/generate-document`, and it takes a JSON payload with the following fields:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "string",
|
|
||||||
"externalId": "string",
|
|
||||||
"recipients": [
|
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"name": "string",
|
|
||||||
"email": "user@example.com",
|
|
||||||
"signingOrder": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"meta": {
|
|
||||||
"subject": "string",
|
|
||||||
"message": "string",
|
|
||||||
"timezone": "string",
|
|
||||||
"dateFormat": "string",
|
|
||||||
"redirectUrl": "string",
|
|
||||||
"signingOrder": "PARALLEL"
|
|
||||||
},
|
|
||||||
"authOptions": {
|
|
||||||
"globalAccessAuth": "ACCOUNT",
|
|
||||||
"globalActionAuth": "ACCOUNT"
|
|
||||||
},
|
|
||||||
"formValues": {
|
|
||||||
"additionalProp1": "string",
|
|
||||||
"additionalProp2": "string",
|
|
||||||
"additionalProp3": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The JSON payload is identical to the payload for uploading a document, so you can read more about the fields in the [Create Document](/developers/public-api/reference#create-document) step. For this API endpoint, the `recipients` property is required.
|
|
||||||
|
|
||||||
<Steps>
|
|
||||||
|
|
||||||
### Grab the Template ID
|
|
||||||
|
|
||||||
The first step is to retrieve the template ID from the Documenso dashboard. You can find the template ID in the URL by navigating to the template details page.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
In this case, the template ID is "99999".
|
|
||||||
|
|
||||||
### Retrieve the Recipient(s) ID(s)
|
|
||||||
|
|
||||||
Once you have the template ID, the next step involves retrieving the ID(s) of the recipient(s) from the template. You can do this by making a GET request to `/api/v1/templates/{template-id}`.
|
|
||||||
|
|
||||||
A successful response looks as follows:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"externalId": "string",
|
|
||||||
"type": "PUBLIC",
|
|
||||||
"title": "string",
|
|
||||||
"userId": 0,
|
|
||||||
"teamId": 0,
|
|
||||||
"templateDocumentDataId": "string",
|
|
||||||
"createdAt": "2024-10-11T08:46:58.247Z",
|
|
||||||
"updatedAt": "2024-10-11T08:46:58.247Z",
|
|
||||||
"templateMeta": {
|
|
||||||
"id": "string",
|
|
||||||
"subject": "string",
|
|
||||||
"message": "string",
|
|
||||||
"timezone": "string",
|
|
||||||
"dateFormat": "string",
|
|
||||||
"templateId": 0,
|
|
||||||
"redirectUrl": "string",
|
|
||||||
"signingOrder": "PARALLEL"
|
|
||||||
},
|
|
||||||
"directLink": {
|
|
||||||
"token": "string",
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
"templateDocumentData": {
|
|
||||||
"id": "string",
|
|
||||||
"type": "S3_PATH",
|
|
||||||
"data": "string"
|
|
||||||
},
|
|
||||||
"Field": [
|
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"recipientId": 0,
|
|
||||||
"type": "SIGNATURE",
|
|
||||||
"page": 0,
|
|
||||||
"positionX": "string",
|
|
||||||
"positionY": "string",
|
|
||||||
"width": "string",
|
|
||||||
"height": "string"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"Recipient": [
|
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"email": "user@example.com",
|
|
||||||
"name": "string",
|
|
||||||
"signingOrder": 0,
|
|
||||||
"authOptions": "string",
|
|
||||||
"role": "CC"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You'll need the recipient(s) ID(s) for the next step.
|
|
||||||
|
|
||||||
### Generate the Document
|
|
||||||
|
|
||||||
To generate a document from the template, you need to make a POST request to the `/api/v1/templates/{template-id}/generate-document` endpoint.
|
|
||||||
|
|
||||||
At the minimum, you must provide the `recipients` array in the JSON payload. Here's an example of the JSON payload:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"recipients": [
|
|
||||||
{
|
|
||||||
"id": 0,
|
|
||||||
"name": "Ash Drew",
|
|
||||||
"email": "ashdrew@email.com",
|
|
||||||
"signingOrder": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Filling the `recipients` array with the corresponding recipient for each template placeholder recipient is recommended. For example, if the template has two placeholders, you should provide at least two recipients in the `recipients` array. Otherwise, the document will be sent to inexistent recipients such as `<recipient.1@documenso.com>`. However, the recipients can always be edited via the API or the web app.
|
|
||||||
|
|
||||||
A successful response will contain the document ID and recipient(s) information.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"documentId": 999,
|
|
||||||
"recipients": [
|
|
||||||
{
|
|
||||||
"recipientId": 0,
|
|
||||||
"name": "Ash Drew",
|
|
||||||
"email": "ashdrew@email.com",
|
|
||||||
"token": "<signing-token>",
|
|
||||||
"role": "SIGNER",
|
|
||||||
"signingOrder": null,
|
|
||||||
"signingUrl": "https://app.documenso.com/sign/<signing-token>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can now access the document in your Documenso account dashboard. The screenshot below shows the document that was generated from the template.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Steps>
|
|
||||||
|
|
||||||
## Add Fields to Document
|
|
||||||
|
|
||||||
The API allows you to add fields to a document via the `/api/v1/documents/{documentId}/fields` endpoint. This is useful when you want to add fields to a document before sending it to recipients.
|
|
||||||
|
|
||||||
To add fields to a document, you need to make a `POST` request with a JSON payload containing the field(s) information.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"recipientId": 0,
|
|
||||||
"type": "SIGNATURE",
|
|
||||||
"pageNumber": 0,
|
|
||||||
"pageX": 0,
|
|
||||||
"pageY": 0,
|
|
||||||
"pageWidth": 0,
|
|
||||||
"pageHeight": 0,
|
|
||||||
"fieldMeta": {
|
|
||||||
"label": "string",
|
|
||||||
"placeholder": "string",
|
|
||||||
"required": true,
|
|
||||||
"readOnly": true,
|
|
||||||
"type": "text",
|
|
||||||
"text": "string",
|
|
||||||
"characterLimit": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// or
|
|
||||||
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"recipientId": 0,
|
|
||||||
"type": "SIGNATURE",
|
|
||||||
"pageNumber": 0,
|
|
||||||
"pageX": 0,
|
|
||||||
"pageY": 0,
|
|
||||||
"pageWidth": 0,
|
|
||||||
"pageHeight": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"recipientId": 0,
|
|
||||||
"type": "TEXT",
|
|
||||||
"pageNumber": 0,
|
|
||||||
"pageX": 0,
|
|
||||||
"pageY": 0,
|
|
||||||
"pageWidth": 0,
|
|
||||||
"pageHeight": 0,
|
|
||||||
"fieldMeta": {
|
|
||||||
"label": "string",
|
|
||||||
"placeholder": "string",
|
|
||||||
"required": true,
|
|
||||||
"readOnly": true,
|
|
||||||
"type": "text",
|
|
||||||
"text": "string",
|
|
||||||
"characterLimit": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="info">This endpoint accepts either one field or an array of fields.</Callout>
|
|
||||||
|
|
||||||
Before adding fields to a document, you need each recipient's ID. If the document already has recipients, you can query the document to retrieve the recipient's details. If the document has no recipients, you need to add a recipient via the UI or API before adding a field.
|
|
||||||
|
|
||||||
<Steps>
|
|
||||||
|
|
||||||
### Retrieve the Recipient(s) ID(s)
|
|
||||||
|
|
||||||
Perform a `GET` request to the `/api/v1/documents/{id}` to retrieve the details of a specific document, including the recipient's information.
|
|
||||||
|
|
||||||
An example response would look like this:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": 137,
|
|
||||||
"externalId": null,
|
|
||||||
"userId": 3,
|
|
||||||
"teamId": null,
|
|
||||||
"title": "documenso.pdf",
|
|
||||||
"status": "DRAFT",
|
|
||||||
"documentDataId": "<document-data-id>",
|
|
||||||
"createdAt": "2024-10-11T12:29:12.725Z",
|
|
||||||
"updatedAt": "2024-10-11T12:29:12.725Z",
|
|
||||||
"completedAt": null,
|
|
||||||
"recipients": [
|
|
||||||
{
|
|
||||||
"id": 55,
|
|
||||||
"documentId": 137,
|
|
||||||
"email": "ashdrew@email.com",
|
|
||||||
"name": "Ash Drew",
|
|
||||||
"role": "SIGNER",
|
|
||||||
"signingOrder": null,
|
|
||||||
"token": "<signing-token>",
|
|
||||||
"signedAt": null,
|
|
||||||
"readStatus": "NOT_OPENED",
|
|
||||||
"signingStatus": "NOT_SIGNED",
|
|
||||||
"sendStatus": "NOT_SENT",
|
|
||||||
"signingUrl": "https://app.documenso.com/sign/<signing-token>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
From this response, you'll only need the recipient ID, which is `55` in this case.
|
|
||||||
|
|
||||||
### (OR) Add a Recipient
|
|
||||||
|
|
||||||
If the document doesn't already have recipient(s), you can add recipient(s) via the API. Make a `POST` request to the `/api/v1/documents/{documentId}/recipients` endpoint with the recipient information. This endpoint takes the following JSON payload:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
"email": "user@example.com",
|
|
||||||
"role": "SIGNER",
|
|
||||||
"signingOrder": 0,
|
|
||||||
"authOptions": {
|
|
||||||
"actionAuth": "ACCOUNT"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="info">The `authOptions` property is only available for Enterprise accounts.</Callout>
|
|
||||||
|
|
||||||
Here's an example of the JSON payload for adding a recipient:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "Ash Drew",
|
|
||||||
"email": "ashdrew@email.com",
|
|
||||||
"role": "SIGNER",
|
|
||||||
"signingOrder": 0
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
A successful request will return a JSON response with the newly added recipient. You can now use the recipient ID to add fields to the document.
|
|
||||||
|
|
||||||
### Add Field(s)
|
|
||||||
|
|
||||||
Now you can make a `POST` request to the `/api/v1/documents/{documentId}/fields` endpoint with the field(s) information. Here's an example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"recipientId": 55,
|
|
||||||
"type": "SIGNATURE",
|
|
||||||
"pageNumber": 1,
|
|
||||||
"pageX": 50,
|
|
||||||
"pageY": 20,
|
|
||||||
"pageWidth": 25,
|
|
||||||
"pageHeight": 5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"recipientId": 55,
|
|
||||||
"type": "TEXT",
|
|
||||||
"pageNumber": 1,
|
|
||||||
"pageX": 20,
|
|
||||||
"pageY": 50,
|
|
||||||
"pageWidth": 30,
|
|
||||||
"pageHeight": 7.5,
|
|
||||||
"fieldMeta": {
|
|
||||||
"label": "Address",
|
|
||||||
"placeholder": "32 New York Street, 41241",
|
|
||||||
"required": true,
|
|
||||||
"readOnly": false,
|
|
||||||
"type": "text",
|
|
||||||
"text": "32 New York Street, 41241",
|
|
||||||
"characterLimit": 40
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
The `text` field represents the default value of the field. If the user doesn't provide any other
|
|
||||||
value, this is the value that will be used to sign the field.
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
A successful request will return a JSON response with the newly added fields. The image below illustrates the fields added to the document via the API.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
</Steps>
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"index": "Getting Started",
|
"index": "Get Started",
|
||||||
"signing-certificate": "Signing Certificate",
|
"signing-certificate": "Signing Certificate",
|
||||||
"how-to": "How To",
|
"how-to": "How To",
|
||||||
"setting-up-oauth-providers": "Setting up OAuth Providers"
|
"setting-up-oauth-providers": "Setting up OAuth Providers"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 65 KiB |
@@ -1,82 +0,0 @@
|
|||||||
---
|
|
||||||
title: Cal.com Chooses Documenso for DPA and BAA Scalability and Compliance
|
|
||||||
description: Learn how Cal.com scales their Data Processing Agreement (DPA) and Business Associate Agreement (BAA) processes with Documenso’s open source platform as they grow.
|
|
||||||
authorName: 'Timur Ercan'
|
|
||||||
authorImage: '/blog/blog-author-timur.jpeg'
|
|
||||||
authorRole: 'Co-Founder'
|
|
||||||
date: 2024-10-11
|
|
||||||
tags:
|
|
||||||
- Customer Story
|
|
||||||
- Open Startup
|
|
||||||
- Open Source
|
|
||||||
---
|
|
||||||
|
|
||||||
<figure>
|
|
||||||
<MdxNextImage
|
|
||||||
src="/blog/cal2.png"
|
|
||||||
width="1260"
|
|
||||||
height="630"
|
|
||||||
alt="Scheduling Infrastructure for Everyone"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<figcaption className="text-center">
|
|
||||||
Scheduling Infrastructure for Everyone.
|
|
||||||
</figcaption>
|
|
||||||
</figure>
|
|
||||||
TL;DR: Cal.com uses Documenso’s template direct links to facilitate low-friction compliance paperwork, enhancing scalability and user experience.
|
|
||||||
|
|
||||||
## Cal.com – The Most Public Private Company
|
|
||||||
|
|
||||||
[Cal.com](Cal.com) is an open source company that needs no introduction. Founded in 2021 by Bailey Pumfleet and Peer Richelsen, it quickly evolved from an open source alternative to the widespread but limited scheduling platform Calendly into the internet’s most beloved scheduling solution. Starting with just two founders, Cal.com has grown into a team of 22, facilitating millions of bookings per year for its ever-growing user and customer base.
|
|
||||||
|
|
||||||
Their commitment to transparency is evident as they follow the [open startup movement](https://cal.com/open), opening up not only their source code but also providing insights into their business operations. Their Commercial Open Source Software (COSS) model, combining a company and an open source project, has inspired a whole cohort of startups joining the space—not least of which is Documenso.
|
|
||||||
|
|
||||||
## The Need
|
|
||||||
|
|
||||||
At this point, Cal.com serves customers of all sizes, from single users to large enterprises. To provide the best product for their customers, they are certified for SOC 2, HIPAA, GDPR, and many other compliance regulations. One challenge that comes with this is the increasing number of waivers that need to be signed when onboarding customers. Business Associate Agreements (BAAs) and Data Processing Agreements (DPAs) are two of the more commonly known examples. To get these signed with minimal effort for both sides, they were looking for a solution to handle these at scale.
|
|
||||||
|
|
||||||
> We love open source.
|
|
||||||
|
|
||||||
— Peer Richelsen, Co-Founder, Cal.com
|
|
||||||
|
|
||||||
Being an open source company, they also prefer open source in their vendors—for both the shared philosophy and the higher level of trust. The goal was to integrate signing into the checkout process as seamlessly as possible.
|
|
||||||
|
|
||||||
## The Solution
|
|
||||||
|
|
||||||
<figure>
|
|
||||||
<MdxNextImage
|
|
||||||
src="/blog/cal.png"
|
|
||||||
width="1260"
|
|
||||||
height="630"
|
|
||||||
alt="Cal.com direct link template to sign a DPA"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<figcaption className="text-center">
|
|
||||||
Sign a DPA with Cal by clicking a link anytime.
|
|
||||||
</figcaption>
|
|
||||||
</figure>
|
|
||||||
|
|
||||||
Documenso offers exactly this solution through direct link templates, enabling Cal.com to:
|
|
||||||
|
|
||||||
- Provide Immediate Access: Customers can access and sign necessary compliance documents through direct links at any time.
|
|
||||||
- Enhance User Experience: Users are immediately forwarded to onboarding after signing.
|
|
||||||
- Ensure Easy Access: The documents are stored within the company’s team account, allowing easy access for anyone who needs them.
|
|
||||||
|
|
||||||
Direct Link templates can also easily be embedded, using the [Documenso widget](https://documen.so/embedded). Embedding anywhere, pre-Filling the templates and notfiying the compliance team at certain point of the flow are a few of the many option the team now has in continously enhanceing their onboard and compliance UX.
|
|
||||||
|
|
||||||
Read more about our direct link templates here: [Direct Link Signing](https://docs.documenso.com/users/direct-links).
|
|
||||||
|
|
||||||
## The Journey
|
|
||||||
|
|
||||||
Initially, Cal.com’s team approached the new solution with skepticism. As Bailey reflected:
|
|
||||||
|
|
||||||
> We were intrigued but skeptical at first, as we put a lot of thought into compliance and doing things right. Documenso’s documentation and support showcased how their direct link templates could meet our needs while being highly compliant.
|
|
||||||
|
|
||||||
This experience highlights Documenso’s trust philosophy. We strive to be transparent in everything we do and let people judge for themselves. It also shows that we neither want nor get trust by default. Doing things right is a conversation worth having, especially in a space as opaque as digital signatures. It goes without saying that the whole team is hyped to have Cal.com on board and yet another open source company joining the open signing movement 🚀
|
|
||||||
|
|
||||||
If you have any questions or comments, please reach out on [Twitter / X](https://twitter.com/eltimuro) (DM open) or [Discord](https://documen.so/discord).
|
|
||||||
|
|
||||||
Thinking about switching to a modern signing platform? Reach out anytime: [https://documen.so/sales](https://documen.so/sales)
|
|
||||||
|
|
||||||
Best from Hamburg\
|
|
||||||
Timur
|
|
||||||
@@ -41,6 +41,8 @@ Mitosis allowed us to quickly target several popular frameworks, including [Reac
|
|||||||
|
|
||||||
I had also hoped to include Angular, but while Mitosis makes it really easy to transpile component, we still have to take care of bundling and packaging the resulting component ourselves. While the above frameworks can all be bundled using Vite.js, Angular still has it's own set of tooling that we would need to learn and use. Given this constraint we opted to put Angular on hold for now while we wait for the newer Vite.js support to mature.
|
I had also hoped to include Angular, but while Mitosis makes it really easy to transpile component, we still have to take care of bundling and packaging the resulting component ourselves. While the above frameworks can all be bundled using Vite.js, Angular still has it's own set of tooling that we would need to learn and use. Given this constraint we opted to put Angular on hold for now while we wait for the newer Vite.js support to mature.
|
||||||
|
|
||||||
|
Update: Angular support is now available! Check out the [Angular integration guide](https://docs.documenso.com/developers/embedding/angular) for more information.
|
||||||
|
|
||||||
### Challenges and Lessons with Mitosis and more
|
### Challenges and Lessons with Mitosis and more
|
||||||
|
|
||||||
While our experience with Mitosis was largely positive, there were some challenges along the way. For instance, certain state properties with the same names as props caused issues during the transpilation process, leading to type errors and unexpected transpilation results with some targets.
|
While our experience with Mitosis was largely positive, there were some challenges along the way. For instance, certain state properties with the same names as props caused issues during the transpilation process, leading to type errors and unexpected transpilation results with some targets.
|
||||||
|
|||||||
@@ -8,61 +8,6 @@ Check out what's new in the latest version and read our thoughts on it. For more
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Documenso v1.7.1: Signing order and document visibility
|
|
||||||
|
|
||||||
We're excited to introduce Documenso v1.7.1, bringing you improved control over your document signing process. Here are the key updates:
|
|
||||||
|
|
||||||
## 🌟 Key New Features
|
|
||||||
|
|
||||||
### 1. Signing Order
|
|
||||||
|
|
||||||
Specify the sequence in which recipients sign your documents. This ensures a structured signing process, particularly useful for complex agreements or hierarchical approvals.
|
|
||||||
|
|
||||||
<video
|
|
||||||
src="/changelog/signing-order-demo.mp4"
|
|
||||||
className="aspect-video w-full"
|
|
||||||
autoPlay
|
|
||||||
loop
|
|
||||||
controls
|
|
||||||
/>
|
|
||||||
|
|
||||||
### 2. Document Visibility
|
|
||||||
|
|
||||||
Manage who can view your documents and when. This feature offers greater privacy and flexibility in your document sharing workflows.
|
|
||||||
|
|
||||||
<video
|
|
||||||
src="/changelog/document-visibility-demo.mp4"
|
|
||||||
className="aspect-video w-full"
|
|
||||||
autoPlay
|
|
||||||
loop
|
|
||||||
controls
|
|
||||||
/>
|
|
||||||
|
|
||||||
## 🔧 Other Improvements
|
|
||||||
|
|
||||||
- Added language switcher for easier language selection
|
|
||||||
- Support for smaller field bounds in documents
|
|
||||||
- Improved select field UX
|
|
||||||
- Enhanced template functionality for advanced fields
|
|
||||||
- Added authOptions to the API
|
|
||||||
- Various UI refinements and bug fixes
|
|
||||||
|
|
||||||
## 💡 Recent Features
|
|
||||||
|
|
||||||
Don't forget about these powerful features from our recent v1.7.0 release:
|
|
||||||
|
|
||||||
- Embedded Signing Experience
|
|
||||||
- Copy and Paste Fields
|
|
||||||
- Customizable Signature Colors
|
|
||||||
|
|
||||||
## 👏 Thank You
|
|
||||||
|
|
||||||
As always, we're grateful for our community's contributions and feedback. Your input continues to shape Documenso into the leading open-source document signing solution.
|
|
||||||
|
|
||||||
We're eager to see how these new features enhance your document workflows. Enjoy exploring Documenso v1.7.1!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# Documenso v1.7.0: Embedded Signing, Copy and Paste, and More
|
# Documenso v1.7.0: Embedded Signing, Copy and Paste, and More
|
||||||
|
|
||||||
We're thrilled to announce the release of Documenso v1.7.0, packed with exciting new features and improvements that enhance document signing flexibility, user experience, and global accessibility.
|
We're thrilled to announce the release of Documenso v1.7.0, packed with exciting new features and improvements that enhance document signing flexibility, user experience, and global accessibility.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/marketing",
|
"name": "@documenso/marketing",
|
||||||
"version": "1.7.2-rc.1",
|
"version": "1.7.1-rc.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 3001",
|
"dev": "next dev -p 3001",
|
||||||
"build": "turbo run translate:extract && turbo run translate:compile && next build",
|
"build": "next build",
|
||||||
"start": "next start -p 3001",
|
"start": "next start -p 3001",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"lint:fix": "next lint --fix",
|
"lint:fix": "next lint --fix",
|
||||||
@@ -56,4 +56,4 @@
|
|||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18"
|
"@types/react-dom": "^18"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 2.2 MiB |
@@ -1,40 +0,0 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.*
|
|
||||||
.yarn/*
|
|
||||||
!.yarn/patches
|
|
||||||
!.yarn/plugins
|
|
||||||
!.yarn/releases
|
|
||||||
!.yarn/versions
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# next.js
|
|
||||||
/.next/
|
|
||||||
/out/
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
*.pem
|
|
||||||
|
|
||||||
# debug
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# env files (can opt-in for commiting if needed)
|
|
||||||
.env*
|
|
||||||
|
|
||||||
# vercel
|
|
||||||
.vercel
|
|
||||||
|
|
||||||
# typescript
|
|
||||||
*.tsbuildinfo
|
|
||||||
next-env.d.ts
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# @documenso/openpage-api
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import type { NextRequest } from 'next/server';
|
|
||||||
|
|
||||||
import cors from '@/lib/cors';
|
|
||||||
|
|
||||||
const paths = [
|
|
||||||
{ path: '/total-prs', description: 'Total GitHub Merged PRs' },
|
|
||||||
{ path: '/total-stars', description: 'Total GitHub Stars' },
|
|
||||||
{ path: '/total-forks', description: 'Total GitHub Forks' },
|
|
||||||
{ path: '/total-issues', description: 'Total GitHub Issues' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function GET(request: NextRequest) {
|
|
||||||
const url = request.nextUrl.toString();
|
|
||||||
const apis = paths.map(({ path, description }) => {
|
|
||||||
return { path: url + path, description };
|
|
||||||
});
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify(apis), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import cors from '@/lib/cors';
|
|
||||||
import { transformRepoStats } from '@/lib/transform-repo-stats';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const res = await fetch('https://stargrazer-live.onrender.com/api/stats');
|
|
||||||
const data = await res.json();
|
|
||||||
const transformedData = transformRepoStats(data, 'forks');
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify(transformedData), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import cors from '@/lib/cors';
|
|
||||||
import { transformRepoStats } from '@/lib/transform-repo-stats';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const res = await fetch('https://stargrazer-live.onrender.com/api/stats');
|
|
||||||
const data = await res.json();
|
|
||||||
const transformedData = transformRepoStats(data, 'openIssues');
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify(transformedData), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import cors from '@/lib/cors';
|
|
||||||
import { transformRepoStats } from '@/lib/transform-repo-stats';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const res = await fetch('https://stargrazer-live.onrender.com/api/stats');
|
|
||||||
const data = await res.json();
|
|
||||||
const transformedData = transformRepoStats(data, 'mergedPRs');
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify(transformedData), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import cors from '@/lib/cors';
|
|
||||||
import { transformRepoStats } from '@/lib/transform-repo-stats';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const res = await fetch('https://stargrazer-live.onrender.com/api/stats');
|
|
||||||
const data = await res.json();
|
|
||||||
const transformedData = transformRepoStats(data, 'stars');
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify(transformedData), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import cors from '@/lib/cors';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const res = await fetch('https://api.github.com/repos/documenso/documenso');
|
|
||||||
const { forks_count } = await res.json();
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify({ data: forks_count }), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import cors from '@/lib/cors';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const res = await fetch(
|
|
||||||
'https://api.github.com/search/issues?q=repo:documenso/documenso+type:issue+state:open&page=0&per_page=1',
|
|
||||||
);
|
|
||||||
const { total_count } = await res.json();
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify({ data: total_count }), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import cors from '@/lib/cors';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const res = await fetch(
|
|
||||||
'https://api.github.com/search/issues?q=repo:documenso/documenso/+is:pr+merged:>=2010-01-01&page=0&per_page=1',
|
|
||||||
);
|
|
||||||
const { total_count } = await res.json();
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify({ data: total_count }), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import type { NextRequest } from 'next/server';
|
|
||||||
|
|
||||||
import cors from '@/lib/cors';
|
|
||||||
|
|
||||||
const paths = [
|
|
||||||
{ path: '/forks', description: 'GitHub Forks' },
|
|
||||||
{ path: '/stars', description: 'GitHub Stars' },
|
|
||||||
{ path: '/issues', description: 'GitHub Merged Issues' },
|
|
||||||
{ path: '/prs', description: 'GitHub Pull Request' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function GET(request: NextRequest) {
|
|
||||||
const url = request.nextUrl.toString();
|
|
||||||
const apis = paths.map(({ path, description }) => {
|
|
||||||
return { path: url + path, description };
|
|
||||||
});
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify(apis), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import cors from '@/lib/cors';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const res = await fetch('https://api.github.com/repos/documenso/documenso');
|
|
||||||
const { stargazers_count } = await res.json();
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify({ data: stargazers_count }), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import cors from '@/lib/cors';
|
|
||||||
import { getUserMonthlyGrowth } from '@/lib/growth/get-user-monthly-growth';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const monthlyUsers = await getUserMonthlyGrowth();
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify(monthlyUsers), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import type { NextRequest } from 'next/server';
|
|
||||||
|
|
||||||
import cors from '@/lib/cors';
|
|
||||||
|
|
||||||
const paths = [
|
|
||||||
{ path: '/total-customers', description: 'Total Customers' },
|
|
||||||
{ path: '/total-users', description: 'Total Users' },
|
|
||||||
{ path: '/new-users', description: 'New Users' },
|
|
||||||
{ path: '/completed-documents', description: 'Completed Documents per Month' },
|
|
||||||
{ path: '/total-completed-documents', description: 'Total Completed Documents' },
|
|
||||||
{ path: '/twitter', description: 'Twitter' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function GET(request: NextRequest) {
|
|
||||||
const url = request.nextUrl.toString();
|
|
||||||
const apis = paths.map(({ path, description }) => {
|
|
||||||
return { path: url + path, description };
|
|
||||||
});
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify(apis), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import cors from '@/lib/cors';
|
|
||||||
import { getUserMonthlyGrowth } from '@/lib/growth/get-user-monthly-growth';
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const totalUsers = await getUserMonthlyGrowth();
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify(totalUsers), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import type { NextRequest } from 'next/server';
|
|
||||||
|
|
||||||
import cors from '@/lib/cors';
|
|
||||||
|
|
||||||
const paths = [
|
|
||||||
{ path: 'github', description: 'GitHub Data' },
|
|
||||||
{ path: 'community', description: 'Community Data' },
|
|
||||||
{ path: 'growth', description: 'Growth Data' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function GET(request: NextRequest) {
|
|
||||||
const url = request.nextUrl.toString();
|
|
||||||
const apis = paths.map(({ path, description }) => {
|
|
||||||
return { path: url + path, description };
|
|
||||||
});
|
|
||||||
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(JSON.stringify(apis), {
|
|
||||||
status: 200,
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OPTIONS(request: Request) {
|
|
||||||
return cors(
|
|
||||||
request,
|
|
||||||
new Response(null, {
|
|
||||||
status: 204,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
/**
|
|
||||||
* Multi purpose CORS lib.
|
|
||||||
* Note: Based on the `cors` package in npm but using only web APIs.
|
|
||||||
* Taken from: https://github.com/vercel/examples/blob/main/edge-functions/cors/lib/cors.ts
|
|
||||||
*/
|
|
||||||
|
|
||||||
type StaticOrigin = boolean | string | RegExp | (boolean | string | RegExp)[];
|
|
||||||
|
|
||||||
type OriginFn = (origin: string | undefined, req: Request) => StaticOrigin | Promise<StaticOrigin>;
|
|
||||||
|
|
||||||
interface CorsOptions {
|
|
||||||
origin?: StaticOrigin | OriginFn;
|
|
||||||
methods?: string | string[];
|
|
||||||
allowedHeaders?: string | string[];
|
|
||||||
exposedHeaders?: string | string[];
|
|
||||||
credentials?: boolean;
|
|
||||||
maxAge?: number;
|
|
||||||
preflightContinue?: boolean;
|
|
||||||
optionsSuccessStatus?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultOptions: CorsOptions = {
|
|
||||||
origin: '*',
|
|
||||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
|
||||||
preflightContinue: false,
|
|
||||||
optionsSuccessStatus: 204,
|
|
||||||
};
|
|
||||||
|
|
||||||
function isOriginAllowed(origin: string, allowed: StaticOrigin): boolean {
|
|
||||||
return Array.isArray(allowed)
|
|
||||||
? allowed.some((o) => isOriginAllowed(origin, o))
|
|
||||||
: typeof allowed === 'string'
|
|
||||||
? origin === allowed
|
|
||||||
: allowed instanceof RegExp
|
|
||||||
? allowed.test(origin)
|
|
||||||
: !!allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOriginHeaders(reqOrigin: string | undefined, origin: StaticOrigin) {
|
|
||||||
const headers = new Headers();
|
|
||||||
|
|
||||||
if (origin === '*') {
|
|
||||||
// Allow any origin
|
|
||||||
headers.set('Access-Control-Allow-Origin', '*');
|
|
||||||
} else if (typeof origin === 'string') {
|
|
||||||
// Fixed origin
|
|
||||||
headers.set('Access-Control-Allow-Origin', origin);
|
|
||||||
headers.append('Vary', 'Origin');
|
|
||||||
} else {
|
|
||||||
const allowed = isOriginAllowed(reqOrigin ?? '', origin);
|
|
||||||
|
|
||||||
if (allowed && reqOrigin) {
|
|
||||||
headers.set('Access-Control-Allow-Origin', reqOrigin);
|
|
||||||
}
|
|
||||||
headers.append('Vary', 'Origin');
|
|
||||||
}
|
|
||||||
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function originHeadersFromReq(req: Request, origin: StaticOrigin | OriginFn) {
|
|
||||||
const reqOrigin = req.headers.get('Origin') || undefined;
|
|
||||||
const value = typeof origin === 'function' ? await origin(reqOrigin, req) : origin;
|
|
||||||
|
|
||||||
if (!value) return;
|
|
||||||
return getOriginHeaders(reqOrigin, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAllowedHeaders(req: Request, allowed?: string | string[]) {
|
|
||||||
const headers = new Headers();
|
|
||||||
|
|
||||||
if (!allowed) {
|
|
||||||
allowed = req.headers.get('Access-Control-Request-Headers')!;
|
|
||||||
headers.append('Vary', 'Access-Control-Request-Headers');
|
|
||||||
} else if (Array.isArray(allowed)) {
|
|
||||||
// If the allowed headers is an array, turn it into a string
|
|
||||||
allowed = allowed.join(',');
|
|
||||||
}
|
|
||||||
if (allowed) {
|
|
||||||
headers.set('Access-Control-Allow-Headers', allowed);
|
|
||||||
}
|
|
||||||
|
|
||||||
return headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function cors(req: Request, res: Response, options?: CorsOptions) {
|
|
||||||
const opts = { ...defaultOptions, ...options };
|
|
||||||
const { headers } = res;
|
|
||||||
const originHeaders = await originHeadersFromReq(req, opts.origin ?? false);
|
|
||||||
const mergeHeaders = (v: string, k: string) => {
|
|
||||||
if (k === 'Vary') headers.append(k, v);
|
|
||||||
else headers.set(k, v);
|
|
||||||
};
|
|
||||||
|
|
||||||
// If there's no origin we won't touch the response
|
|
||||||
if (!originHeaders) return res;
|
|
||||||
|
|
||||||
originHeaders.forEach(mergeHeaders);
|
|
||||||
|
|
||||||
if (opts.credentials) {
|
|
||||||
headers.set('Access-Control-Allow-Credentials', 'true');
|
|
||||||
}
|
|
||||||
|
|
||||||
const exposed = Array.isArray(opts.exposedHeaders)
|
|
||||||
? opts.exposedHeaders.join(',')
|
|
||||||
: opts.exposedHeaders;
|
|
||||||
|
|
||||||
if (exposed) {
|
|
||||||
headers.set('Access-Control-Expose-Headers', exposed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the preflight request
|
|
||||||
if (req.method === 'OPTIONS') {
|
|
||||||
if (opts.methods) {
|
|
||||||
const methods = Array.isArray(opts.methods) ? opts.methods.join(',') : opts.methods;
|
|
||||||
|
|
||||||
headers.set('Access-Control-Allow-Methods', methods);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAllowedHeaders(req, opts.allowedHeaders).forEach(mergeHeaders);
|
|
||||||
|
|
||||||
if (typeof opts.maxAge === 'number') {
|
|
||||||
headers.set('Access-Control-Max-Age', String(opts.maxAge));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.preflightContinue) return res;
|
|
||||||
|
|
||||||
headers.set('Content-Length', '0');
|
|
||||||
return new Response(null, { status: opts.optionsSuccessStatus, headers });
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we got here, it's a normal request
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initCors(options?: CorsOptions) {
|
|
||||||
return async (req: Request, res: Response) => cors(req, res, options);
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import { DateTime } from 'luxon';
|
|
||||||
|
|
||||||
import { kyselyPrisma, sql } from '@documenso/prisma';
|
|
||||||
|
|
||||||
export const getUserMonthlyGrowth = async (type: 'count' | 'cumulative' = 'count') => {
|
|
||||||
const qb = kyselyPrisma.$kysely
|
|
||||||
.selectFrom('User')
|
|
||||||
.select(({ fn }) => [
|
|
||||||
fn<Date>('DATE_TRUNC', [sql.lit('MONTH'), 'User.createdAt']).as('month'),
|
|
||||||
fn.count('id').as('count'),
|
|
||||||
fn
|
|
||||||
.sum(fn.count('id'))
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
|
|
||||||
.over((ob) => ob.orderBy(fn('DATE_TRUNC', [sql.lit('MONTH'), 'User.createdAt']) as any))
|
|
||||||
.as('cume_count'),
|
|
||||||
])
|
|
||||||
.groupBy('month')
|
|
||||||
.orderBy('month', 'desc')
|
|
||||||
.limit(12);
|
|
||||||
|
|
||||||
const result = await qb.execute();
|
|
||||||
|
|
||||||
const transformedData = {
|
|
||||||
labels: result.map((row) => DateTime.fromJSDate(row.month).toFormat('MMM yyyy')).reverse(),
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: type === 'count' ? 'New Users' : 'Total Users',
|
|
||||||
data: result
|
|
||||||
.map((row) => (type === 'count' ? Number(row.count) : Number(row.cume_count)))
|
|
||||||
.reverse(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return transformedData;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetUserMonthlyGrowthResult = Awaited<ReturnType<typeof getUserMonthlyGrowth>>;
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
type RepoStats = {
|
|
||||||
stars: number;
|
|
||||||
forks: number;
|
|
||||||
mergedPRs: number;
|
|
||||||
openIssues: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type DataEntry = {
|
|
||||||
[key: string]: RepoStats;
|
|
||||||
};
|
|
||||||
|
|
||||||
type TransformedData = {
|
|
||||||
labels: string[];
|
|
||||||
datasets: {
|
|
||||||
label: string;
|
|
||||||
data: number[];
|
|
||||||
}[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type MonthNames = {
|
|
||||||
[key: string]: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type MetricKey = keyof RepoStats;
|
|
||||||
|
|
||||||
const FRIENDLY_METRIC_NAMES: { [key in MetricKey]: string } = {
|
|
||||||
stars: 'Stars',
|
|
||||||
forks: 'Forks',
|
|
||||||
mergedPRs: 'Merged PRs',
|
|
||||||
openIssues: 'Open Issues',
|
|
||||||
};
|
|
||||||
|
|
||||||
export function transformRepoStats(data: DataEntry, metric: MetricKey): TransformedData {
|
|
||||||
const sortedEntries = Object.entries(data).sort(([dateA], [dateB]) => {
|
|
||||||
const [yearA, monthA] = dateA.split('-').map(Number);
|
|
||||||
const [yearB, monthB] = dateB.split('-').map(Number);
|
|
||||||
return new Date(yearA, monthA - 1).getTime() - new Date(yearB, monthB - 1).getTime();
|
|
||||||
});
|
|
||||||
|
|
||||||
const monthNames: MonthNames = {
|
|
||||||
'1': 'Jan',
|
|
||||||
'2': 'Feb',
|
|
||||||
'3': 'Mar',
|
|
||||||
'4': 'Apr',
|
|
||||||
'5': 'May',
|
|
||||||
'6': 'Jun',
|
|
||||||
'7': 'Jul',
|
|
||||||
'8': 'Aug',
|
|
||||||
'9': 'Sep',
|
|
||||||
'10': 'Oct',
|
|
||||||
'11': 'Nov',
|
|
||||||
'12': 'Dec',
|
|
||||||
};
|
|
||||||
|
|
||||||
const labels = sortedEntries.map(([date]) => {
|
|
||||||
const [year, month] = date.split('-');
|
|
||||||
const monthIndex = parseInt(month);
|
|
||||||
return `${monthNames[monthIndex.toString()]} ${year}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
labels,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: `Total ${FRIENDLY_METRIC_NAMES[metric]}`,
|
|
||||||
data: sortedEntries.map(([_, stats]) => stats[metric]),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
/** @type {import('next').NextConfig} */
|
|
||||||
const nextConfig = {};
|
|
||||||
|
|
||||||
module.exports = nextConfig;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@documenso/openpage-api",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"dev": "next dev -p 3003",
|
|
||||||
"build": "next build",
|
|
||||||
"start": "next start",
|
|
||||||
"lint:fix": "next lint --fix",
|
|
||||||
"clean": "rimraf .next && rimraf node_modules",
|
|
||||||
"copy:pdfjs": "node ../../scripts/copy-pdfjs.cjs"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@documenso/prisma": "*",
|
|
||||||
"next": "14.2.6"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "20.16.5",
|
|
||||||
"@types/react": "18.3.5",
|
|
||||||
"typescript": "5.5.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2017",
|
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
|
||||||
"allowJs": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"strict": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"incremental": true,
|
|
||||||
"plugins": [
|
|
||||||
{
|
|
||||||
"name": "next"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["./*"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
||||||
"exclude": ["node_modules"]
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/web",
|
"name": "@documenso/web",
|
||||||
"version": "1.7.2-rc.1",
|
"version": "1.7.1-rc.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 3000",
|
"dev": "next dev -p 3000",
|
||||||
"build": "turbo run translate:extract && turbo run translate:compile && next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"e2e:prepare": "next build && next start",
|
"e2e:prepare": "next build && next start",
|
||||||
@@ -74,4 +74,4 @@
|
|||||||
"@types/ua-parser-js": "^0.7.39",
|
"@types/ua-parser-js": "^0.7.39",
|
||||||
"typescript": "5.2.2"
|
"typescript": "5.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,24 +112,6 @@ export const EditDocumentForm = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: updateTypedSignature } =
|
|
||||||
trpc.document.updateTypedSignatureSettings.useMutation({
|
|
||||||
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
|
||||||
onSuccess: (newData) => {
|
|
||||||
utils.document.getDocumentWithDetailsById.setData(
|
|
||||||
{
|
|
||||||
id: initialDocument.id,
|
|
||||||
teamId: team?.id,
|
|
||||||
},
|
|
||||||
(oldData) => ({
|
|
||||||
...(oldData || initialDocument),
|
|
||||||
...newData,
|
|
||||||
id: Number(newData.id),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { mutateAsync: addSigners } = trpc.recipient.addSigners.useMutation({
|
const { mutateAsync: addSigners } = trpc.recipient.addSigners.useMutation({
|
||||||
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||||
onSuccess: (newRecipients) => {
|
onSuccess: (newRecipients) => {
|
||||||
@@ -276,11 +258,6 @@ export const EditDocumentForm = ({
|
|||||||
fields: data.fields,
|
fields: data.fields,
|
||||||
});
|
});
|
||||||
|
|
||||||
await updateTypedSignature({
|
|
||||||
documentId: document.id,
|
|
||||||
typedSignatureEnabled: data.typedSignatureEnabled,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear all field data from localStorage
|
// Clear all field data from localStorage
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
const key = localStorage.key(i);
|
const key = localStorage.key(i);
|
||||||
@@ -410,7 +387,6 @@ export const EditDocumentForm = ({
|
|||||||
fields={fields}
|
fields={fields}
|
||||||
onSubmit={onAddFieldsFormSubmit}
|
onSubmit={onAddFieldsFormSubmit}
|
||||||
isDocumentPdfLoaded={isDocumentPdfLoaded}
|
isDocumentPdfLoaded={isDocumentPdfLoaded}
|
||||||
typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled}
|
|
||||||
teamId={team?.id}
|
teamId={team?.id}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-documen
|
|||||||
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
|
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
|
||||||
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
|
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
|
||||||
|
|
||||||
import { DocumentSearch } from '~/components/(dashboard)/document-search/document-search';
|
|
||||||
import { PeriodSelector } from '~/components/(dashboard)/period-selector/period-selector';
|
import { PeriodSelector } from '~/components/(dashboard)/period-selector/period-selector';
|
||||||
import { isPeriodSelectorValue } from '~/components/(dashboard)/period-selector/types';
|
import { isPeriodSelectorValue } from '~/components/(dashboard)/period-selector/types';
|
||||||
import { DocumentStatus } from '~/components/formatter/document-status';
|
import { DocumentStatus } from '~/components/formatter/document-status';
|
||||||
@@ -26,17 +25,16 @@ import { DataTableSenderFilter } from './data-table-sender-filter';
|
|||||||
import { EmptyDocumentState } from './empty-state';
|
import { EmptyDocumentState } from './empty-state';
|
||||||
import { UploadDocument } from './upload-document';
|
import { UploadDocument } from './upload-document';
|
||||||
|
|
||||||
export interface DocumentsPageViewProps {
|
export type DocumentsPageViewProps = {
|
||||||
searchParams?: {
|
searchParams?: {
|
||||||
status?: ExtendedDocumentStatus;
|
status?: ExtendedDocumentStatus;
|
||||||
period?: PeriodSelectorValue;
|
period?: PeriodSelectorValue;
|
||||||
page?: string;
|
page?: string;
|
||||||
perPage?: string;
|
perPage?: string;
|
||||||
senderIds?: string;
|
senderIds?: string;
|
||||||
search?: string;
|
|
||||||
};
|
};
|
||||||
team?: Team & { teamEmail?: TeamEmail | null } & { currentTeamMember?: { role: TeamMemberRole } };
|
team?: Team & { teamEmail?: TeamEmail | null } & { currentTeamMember?: { role: TeamMemberRole } };
|
||||||
}
|
};
|
||||||
|
|
||||||
export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPageViewProps) => {
|
export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPageViewProps) => {
|
||||||
const { user } = await getRequiredServerComponentSession();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
@@ -46,7 +44,6 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
|
|||||||
const page = Number(searchParams.page) || 1;
|
const page = Number(searchParams.page) || 1;
|
||||||
const perPage = Number(searchParams.perPage) || 20;
|
const perPage = Number(searchParams.perPage) || 20;
|
||||||
const senderIds = parseToIntegerArray(searchParams.senderIds ?? '');
|
const senderIds = parseToIntegerArray(searchParams.senderIds ?? '');
|
||||||
const search = searchParams.search || '';
|
|
||||||
const currentTeam = team
|
const currentTeam = team
|
||||||
? { id: team.id, url: team.url, teamEmail: team.teamEmail?.email }
|
? { id: team.id, url: team.url, teamEmail: team.teamEmail?.email }
|
||||||
: undefined;
|
: undefined;
|
||||||
@@ -55,7 +52,6 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
|
|||||||
const getStatOptions: GetStatsInput = {
|
const getStatOptions: GetStatsInput = {
|
||||||
user,
|
user,
|
||||||
period,
|
period,
|
||||||
search,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (team) {
|
if (team) {
|
||||||
@@ -83,7 +79,6 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
|
|||||||
perPage,
|
perPage,
|
||||||
period,
|
period,
|
||||||
senderIds,
|
senderIds,
|
||||||
search,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const getTabHref = (value: typeof status) => {
|
const getTabHref = (value: typeof status) => {
|
||||||
@@ -153,9 +148,6 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
|
|||||||
<div className="flex w-48 flex-wrap items-center justify-between gap-x-2 gap-y-4">
|
<div className="flex w-48 flex-wrap items-center justify-between gap-x-2 gap-y-4">
|
||||||
<PeriodSelector />
|
<PeriodSelector />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-48 flex-wrap items-center justify-between gap-x-2 gap-y-4">
|
|
||||||
<DocumentSearch initialValue={search} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -144,7 +144,6 @@ export const TemplatesDataTable = ({
|
|||||||
<div className="flex items-center gap-x-4">
|
<div className="flex items-center gap-x-4">
|
||||||
<UseTemplateDialog
|
<UseTemplateDialog
|
||||||
templateId={row.original.id}
|
templateId={row.original.id}
|
||||||
templateSigningOrder={row.original.templateMeta?.signingOrder}
|
|
||||||
recipients={row.original.Recipient}
|
recipients={row.original.Recipient}
|
||||||
documentRootPath={documentRootPath}
|
documentRootPath={documentRootPath}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -434,14 +434,12 @@ export const TemplateDirectLinkDialog = ({
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
loading={isTogglingTemplateAccess}
|
loading={isTogglingTemplateAccess}
|
||||||
onClick={async () => {
|
onClick={async () =>
|
||||||
await toggleTemplateDirectLink({
|
toggleTemplateDirectLink({
|
||||||
templateId: template.id,
|
templateId: template.id,
|
||||||
enabled: isEnabled,
|
enabled: isEnabled,
|
||||||
}).catch((e) => null);
|
})
|
||||||
|
}
|
||||||
onOpenChange(false);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Trans>Save</Trans>
|
<Trans>Save</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ import {
|
|||||||
} from '@documenso/lib/constants/template';
|
} from '@documenso/lib/constants/template';
|
||||||
import { AppError } from '@documenso/lib/errors/app-error';
|
import { AppError } from '@documenso/lib/errors/app-error';
|
||||||
import type { Recipient } from '@documenso/prisma/client';
|
import type { Recipient } from '@documenso/prisma/client';
|
||||||
import { DocumentSigningOrder } from '@documenso/prisma/client';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
import { Checkbox } from '@documenso/ui/primitives/checkbox';
|
import { Checkbox } from '@documenso/ui/primitives/checkbox';
|
||||||
import {
|
import {
|
||||||
@@ -53,7 +51,6 @@ const ZAddRecipientsForNewDocumentSchema = z
|
|||||||
id: z.number(),
|
id: z.number(),
|
||||||
email: z.string().email(),
|
email: z.string().email(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
signingOrder: z.number().optional(),
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
@@ -89,7 +86,6 @@ type TAddRecipientsForNewDocumentSchema = z.infer<typeof ZAddRecipientsForNewDoc
|
|||||||
|
|
||||||
export type UseTemplateDialogProps = {
|
export type UseTemplateDialogProps = {
|
||||||
templateId: number;
|
templateId: number;
|
||||||
templateSigningOrder?: DocumentSigningOrder | null;
|
|
||||||
recipients: Recipient[];
|
recipients: Recipient[];
|
||||||
documentRootPath: string;
|
documentRootPath: string;
|
||||||
};
|
};
|
||||||
@@ -98,7 +94,6 @@ export function UseTemplateDialog({
|
|||||||
recipients,
|
recipients,
|
||||||
documentRootPath,
|
documentRootPath,
|
||||||
templateId,
|
templateId,
|
||||||
templateSigningOrder,
|
|
||||||
}: UseTemplateDialogProps) {
|
}: UseTemplateDialogProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@@ -113,24 +108,21 @@ export function UseTemplateDialog({
|
|||||||
resolver: zodResolver(ZAddRecipientsForNewDocumentSchema),
|
resolver: zodResolver(ZAddRecipientsForNewDocumentSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
sendDocument: false,
|
sendDocument: false,
|
||||||
recipients: recipients
|
recipients: recipients.map((recipient) => {
|
||||||
.sort((a, b) => (a.signingOrder || 0) - (b.signingOrder || 0))
|
const isRecipientEmailPlaceholder = recipient.email.match(
|
||||||
.map((recipient) => {
|
TEMPLATE_RECIPIENT_EMAIL_PLACEHOLDER_REGEX,
|
||||||
const isRecipientEmailPlaceholder = recipient.email.match(
|
);
|
||||||
TEMPLATE_RECIPIENT_EMAIL_PLACEHOLDER_REGEX,
|
|
||||||
);
|
|
||||||
|
|
||||||
const isRecipientNamePlaceholder = recipient.name.match(
|
const isRecipientNamePlaceholder = recipient.name.match(
|
||||||
TEMPLATE_RECIPIENT_NAME_PLACEHOLDER_REGEX,
|
TEMPLATE_RECIPIENT_NAME_PLACEHOLDER_REGEX,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: recipient.id,
|
id: recipient.id,
|
||||||
name: !isRecipientNamePlaceholder ? recipient.name : '',
|
name: !isRecipientNamePlaceholder ? recipient.name : '',
|
||||||
email: !isRecipientEmailPlaceholder ? recipient.email : '',
|
email: !isRecipientEmailPlaceholder ? recipient.email : '',
|
||||||
signingOrder: recipient.signingOrder ?? undefined,
|
};
|
||||||
};
|
}),
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -211,33 +203,6 @@ export function UseTemplateDialog({
|
|||||||
<div className="custom-scrollbar -m-1 max-h-[60vh] space-y-4 overflow-y-auto p-1">
|
<div className="custom-scrollbar -m-1 max-h-[60vh] space-y-4 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="flex w-full flex-row space-x-4" key={recipient.id}>
|
||||||
{templateSigningOrder === DocumentSigningOrder.SEQUENTIAL && (
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name={`recipients.${index}.signingOrder`}
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem
|
|
||||||
className={cn('w-20', {
|
|
||||||
'mt-8': index === 0,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<FormControl>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
disabled
|
|
||||||
className="items-center justify-center"
|
|
||||||
value={
|
|
||||||
field.value?.toString() ||
|
|
||||||
recipients[index]?.signingOrder?.toString()
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={`recipients.${index}.email`}
|
name={`recipients.${index}.email`}
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ import { useSession } from 'next-auth/react';
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
||||||
import type { DocumentAndSender } from '@documenso/lib/server-only/document/get-document-by-token';
|
|
||||||
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
|
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
|
||||||
import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields';
|
import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields';
|
||||||
import { type Field, type Recipient, RecipientRole } from '@documenso/prisma/client';
|
import { type Document, type Field, type Recipient, RecipientRole } from '@documenso/prisma/client';
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
|
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
@@ -26,7 +25,7 @@ import { useRequiredSigningContext } from './provider';
|
|||||||
import { SignDialog } from './sign-dialog';
|
import { SignDialog } from './sign-dialog';
|
||||||
|
|
||||||
export type SigningFormProps = {
|
export type SigningFormProps = {
|
||||||
document: DocumentAndSender;
|
document: Document;
|
||||||
recipient: Recipient;
|
recipient: Recipient;
|
||||||
fields: Field[];
|
fields: Field[];
|
||||||
redirectUrl?: string | null;
|
redirectUrl?: string | null;
|
||||||
@@ -197,7 +196,6 @@ export const SigningForm = ({
|
|||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setSignature(value);
|
setSignature(value);
|
||||||
}}
|
}}
|
||||||
allowTypedSignature={document.documentMeta?.typedSignatureEnabled}
|
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ import { useRequiredSigningContext } from './provider';
|
|||||||
import { SigningFieldContainer } from './signing-field-container';
|
import { SigningFieldContainer } from './signing-field-container';
|
||||||
|
|
||||||
type SignatureFieldState = 'empty' | 'signed-image' | 'signed-text';
|
type SignatureFieldState = 'empty' | 'signed-image' | 'signed-text';
|
||||||
|
|
||||||
export type SignatureFieldProps = {
|
export type SignatureFieldProps = {
|
||||||
field: FieldWithSignature;
|
field: FieldWithSignature;
|
||||||
recipient: Recipient;
|
recipient: Recipient;
|
||||||
onSignField?: (value: TSignFieldWithTokenMutationSchema) => Promise<void> | void;
|
onSignField?: (value: TSignFieldWithTokenMutationSchema) => Promise<void> | void;
|
||||||
onUnsignField?: (value: TRemovedSignedFieldWithTokenMutationSchema) => Promise<void> | void;
|
onUnsignField?: (value: TRemovedSignedFieldWithTokenMutationSchema) => Promise<void> | void;
|
||||||
typedSignatureEnabled?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignatureField = ({
|
export const SignatureField = ({
|
||||||
@@ -44,7 +44,6 @@ export const SignatureField = ({
|
|||||||
recipient,
|
recipient,
|
||||||
onSignField,
|
onSignField,
|
||||||
onUnsignField,
|
onUnsignField,
|
||||||
typedSignatureEnabled,
|
|
||||||
}: SignatureFieldProps) => {
|
}: SignatureFieldProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@@ -93,12 +92,14 @@ export const SignatureField = ({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the user clicks the sign button in the dialog where they enter their signature.
|
* When the user clicks the sign button in the dialog where they enter their signature.
|
||||||
*/
|
*/
|
||||||
const onDialogSignClick = () => {
|
const onDialogSignClick = () => {
|
||||||
setShowSignatureModal(false);
|
setShowSignatureModal(false);
|
||||||
setProvidedSignature(localSignature);
|
setProvidedSignature(localSignature);
|
||||||
|
|
||||||
if (!localSignature) {
|
if (!localSignature) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -108,6 +109,7 @@ export const SignatureField = ({
|
|||||||
actionTarget: field.type,
|
actionTarget: field.type,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSign = async (authOptions?: TRecipientActionAuth, signature?: string) => {
|
const onSign = async (authOptions?: TRecipientActionAuth, signature?: string) => {
|
||||||
try {
|
try {
|
||||||
const value = signature || providedSignature;
|
const value = signature || providedSignature;
|
||||||
@@ -229,11 +231,11 @@ export const SignatureField = ({
|
|||||||
id="signature"
|
id="signature"
|
||||||
className="border-border mt-2 h-44 w-full rounded-md border"
|
className="border-border mt-2 h-44 w-full rounded-md border"
|
||||||
onChange={(value) => setLocalSignature(value)}
|
onChange={(value) => setLocalSignature(value)}
|
||||||
allowTypedSignature={typedSignatureEnabled}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SigningDisclosure />
|
<SigningDisclosure />
|
||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<div className="flex w-full flex-1 flex-nowrap gap-4">
|
<div className="flex w-full flex-1 flex-nowrap gap-4">
|
||||||
<Button
|
<Button
|
||||||
@@ -247,6 +249,7 @@ export const SignatureField = ({
|
|||||||
>
|
>
|
||||||
<Trans>Cancel</Trans>
|
<Trans>Cancel</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex-1"
|
className="flex-1"
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export const SigningFieldContainer = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn('[container-type:size]', { group: type === 'Checkbox' })}>
|
<div className={cn('[container-type:size]', type === 'Checkbox' ? 'group' : '')}>
|
||||||
<FieldRootContainer field={field}>
|
<FieldRootContainer field={field}>
|
||||||
{!field.inserted && !loading && !readOnlyField && (
|
{!field.inserted && !loading && !readOnlyField && (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -112,12 +112,7 @@ export const SigningPageView = ({
|
|||||||
{fields.map((field) =>
|
{fields.map((field) =>
|
||||||
match(field.type)
|
match(field.type)
|
||||||
.with(FieldType.SIGNATURE, () => (
|
.with(FieldType.SIGNATURE, () => (
|
||||||
<SignatureField
|
<SignatureField key={field.id} field={field} recipient={recipient} />
|
||||||
key={field.id}
|
|
||||||
field={field}
|
|
||||||
recipient={recipient}
|
|
||||||
typedSignatureEnabled={documentMeta?.typedSignatureEnabled}
|
|
||||||
/>
|
|
||||||
))
|
))
|
||||||
.with(FieldType.INITIALS, () => (
|
.with(FieldType.INITIALS, () => (
|
||||||
<InitialsField key={field.id} field={field} recipient={recipient} />
|
<InitialsField key={field.id} field={field} recipient={recipient} />
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
import { Trans } from '@lingui/macro';
|
import { Trans } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
|
|
||||||
import { RecipientStatusType, getRecipientType } from '@documenso/lib/client-only/recipient-type';
|
import { getRecipientType } from '@documenso/lib/client-only/recipient-type';
|
||||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||||
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
|
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
|
||||||
import type { DocumentStatus, Recipient } from '@documenso/prisma/client';
|
import type { DocumentStatus, Recipient } from '@documenso/prisma/client';
|
||||||
@@ -31,26 +29,24 @@ export const StackAvatarsWithTooltip = ({
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const waitingRecipients = recipients.filter(
|
const waitingRecipients = recipients.filter(
|
||||||
(recipient) => getRecipientType(recipient) === RecipientStatusType.WAITING,
|
(recipient) => getRecipientType(recipient) === 'waiting',
|
||||||
);
|
);
|
||||||
|
|
||||||
const openedRecipients = recipients.filter(
|
const openedRecipients = recipients.filter(
|
||||||
(recipient) => getRecipientType(recipient) === RecipientStatusType.OPENED,
|
(recipient) => getRecipientType(recipient) === 'opened',
|
||||||
);
|
);
|
||||||
|
|
||||||
const completedRecipients = recipients.filter(
|
const completedRecipients = recipients.filter(
|
||||||
(recipient) => getRecipientType(recipient) === RecipientStatusType.COMPLETED,
|
(recipient) => getRecipientType(recipient) === 'completed',
|
||||||
);
|
);
|
||||||
|
|
||||||
const uncompletedRecipients = recipients.filter(
|
const uncompletedRecipients = recipients.filter(
|
||||||
(recipient) => getRecipientType(recipient) === RecipientStatusType.UNSIGNED,
|
(recipient) => getRecipientType(recipient) === 'unsigned',
|
||||||
);
|
);
|
||||||
|
|
||||||
const sortedRecipients = useMemo(() => recipients.sort((a, b) => a.id - b.id), [recipients]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PopoverHover
|
<PopoverHover
|
||||||
trigger={children || <StackAvatars recipients={sortedRecipients} />}
|
trigger={children || <StackAvatars recipients={recipients} />}
|
||||||
contentProps={{
|
contentProps={{
|
||||||
className: 'flex flex-col gap-y-5 py-2',
|
className: 'flex flex-col gap-y-5 py-2',
|
||||||
side: position,
|
side: position,
|
||||||
@@ -69,7 +65,7 @@ export const StackAvatarsWithTooltip = ({
|
|||||||
type={getRecipientType(recipient)}
|
type={getRecipientType(recipient)}
|
||||||
fallbackText={recipientAbbreviation(recipient)}
|
fallbackText={recipientAbbreviation(recipient)}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div className="">
|
||||||
<p className="text-muted-foreground text-sm">{recipient.email}</p>
|
<p className="text-muted-foreground text-sm">{recipient.email}</p>
|
||||||
<p className="text-muted-foreground/70 text-xs">
|
<p className="text-muted-foreground/70 text-xs">
|
||||||
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {
|
import { getRecipientType } from '@documenso/lib/client-only/recipient-type';
|
||||||
getExtraRecipientsType,
|
|
||||||
getRecipientType,
|
|
||||||
} from '@documenso/lib/client-only/recipient-type';
|
|
||||||
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
|
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
|
||||||
import type { Recipient } from '@documenso/prisma/client';
|
import type { Recipient } from '@documenso/prisma/client';
|
||||||
|
|
||||||
@@ -16,27 +13,20 @@ export function StackAvatars({ recipients }: { recipients: Recipient[] }) {
|
|||||||
const remainingItems = recipients.length - itemsToRender.length;
|
const remainingItems = recipients.length - itemsToRender.length;
|
||||||
|
|
||||||
return itemsToRender.map((recipient: Recipient, index: number) => {
|
return itemsToRender.map((recipient: Recipient, index: number) => {
|
||||||
const first = index === 0;
|
const first = index === 0 ? true : false;
|
||||||
|
|
||||||
if (index === 4 && remainingItems > 0) {
|
const lastItemText =
|
||||||
return (
|
index === itemsToRender.length - 1 && remainingItems > 0
|
||||||
<StackAvatar
|
? `+${remainingItems + 1}`
|
||||||
key="extra-recipient"
|
: undefined;
|
||||||
first={first}
|
|
||||||
zIndex={String(zIndex - index * 10)}
|
|
||||||
type={getExtraRecipientsType(recipients.slice(4))}
|
|
||||||
fallbackText={`+${remainingItems + 1}`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StackAvatar
|
<StackAvatar
|
||||||
key={recipient.id}
|
key={recipient.id}
|
||||||
first={first}
|
first={first}
|
||||||
zIndex={String(zIndex - index * 10)}
|
zIndex={String(zIndex - index * 10)}
|
||||||
type={getRecipientType(recipient)}
|
type={lastItemText && index === 4 ? 'unsigned' : getRecipientType(recipient)}
|
||||||
fallbackText={recipientAbbreviation(recipient)}
|
fallbackText={lastItemText ? lastItemText : recipientAbbreviation(recipient)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
'use client';
|
|
||||||
|
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
import { useRouter, useSearchParams } from 'next/navigation';
|
|
||||||
|
|
||||||
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
|
|
||||||
import { Input } from '@documenso/ui/primitives/input';
|
|
||||||
|
|
||||||
export const DocumentSearch = ({ initialValue = '' }: { initialValue?: string }) => {
|
|
||||||
const router = useRouter();
|
|
||||||
const searchParams = useSearchParams();
|
|
||||||
const [searchTerm, setSearchTerm] = useState(initialValue);
|
|
||||||
const debouncedSearchTerm = useDebouncedValue(searchTerm, 500);
|
|
||||||
|
|
||||||
const handleSearch = useCallback(
|
|
||||||
(term: string) => {
|
|
||||||
const params = new URLSearchParams(searchParams?.toString() ?? '');
|
|
||||||
if (term) {
|
|
||||||
params.set('search', term);
|
|
||||||
} else {
|
|
||||||
params.delete('search');
|
|
||||||
}
|
|
||||||
router.push(`?${params.toString()}`);
|
|
||||||
},
|
|
||||||
[router, searchParams],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
handleSearch(searchTerm);
|
|
||||||
}, [debouncedSearchTerm]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Input
|
|
||||||
type="search"
|
|
||||||
placeholder="Search documents..."
|
|
||||||
value={searchTerm}
|
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -58,8 +58,6 @@ COPY .gitignore .gitignore
|
|||||||
COPY --from=builder /app/out/json/ .
|
COPY --from=builder /app/out/json/ .
|
||||||
COPY --from=builder /app/out/package-lock.json ./package-lock.json
|
COPY --from=builder /app/out/package-lock.json ./package-lock.json
|
||||||
|
|
||||||
COPY --from=builder /app/lingui.config.ts ./lingui.config.ts
|
|
||||||
|
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
# Then copy all the source code (as it changes more often)
|
# Then copy all the source code (as it changes more often)
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/root",
|
"name": "@documenso/root",
|
||||||
"version": "1.7.2-rc.1",
|
"version": "1.7.1-rc.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@documenso/root",
|
"name": "@documenso/root",
|
||||||
"version": "1.7.2-rc.1",
|
"version": "1.7.1-rc.3",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/pdf-sign": "^0.1.0",
|
"@documenso/pdf-sign": "^0.1.0",
|
||||||
"@documenso/prisma": "^0.0.0",
|
|
||||||
"@lingui/core": "^4.11.3",
|
"@lingui/core": "^4.11.3",
|
||||||
"inngest-cli": "^0.29.1",
|
"inngest-cli": "^0.29.1",
|
||||||
"luxon": "^3.5.0",
|
|
||||||
"next-runtime-env": "^3.2.0",
|
"next-runtime-env": "^3.2.0",
|
||||||
"react": "^18"
|
"react": "^18"
|
||||||
},
|
},
|
||||||
@@ -82,7 +80,7 @@
|
|||||||
},
|
},
|
||||||
"apps/marketing": {
|
"apps/marketing": {
|
||||||
"name": "@documenso/marketing",
|
"name": "@documenso/marketing",
|
||||||
"version": "1.7.2-rc.1",
|
"version": "1.7.1-rc.3",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/assets": "*",
|
"@documenso/assets": "*",
|
||||||
@@ -441,60 +439,9 @@
|
|||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"apps/openpage-api": {
|
|
||||||
"name": "@documenso/openpage-api",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@documenso/prisma": "*",
|
|
||||||
"next": "14.2.6"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "20.16.5",
|
|
||||||
"@types/react": "18.3.5",
|
|
||||||
"typescript": "5.5.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apps/openpage-api/node_modules/@types/node": {
|
|
||||||
"version": "20.16.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz",
|
|
||||||
"integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"undici-types": "~6.19.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apps/openpage-api/node_modules/@types/react": {
|
|
||||||
"version": "18.3.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz",
|
|
||||||
"integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/prop-types": "*",
|
|
||||||
"csstype": "^3.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apps/openpage-api/node_modules/typescript": {
|
|
||||||
"version": "5.5.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
|
||||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"tsc": "bin/tsc",
|
|
||||||
"tsserver": "bin/tsserver"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"apps/openpage-api/node_modules/undici-types": {
|
|
||||||
"version": "6.19.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
|
||||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"apps/web": {
|
"apps/web": {
|
||||||
"name": "@documenso/web",
|
"name": "@documenso/web",
|
||||||
"version": "1.7.2-rc.1",
|
"version": "1.7.1-rc.3",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/api": "*",
|
"@documenso/api": "*",
|
||||||
@@ -2724,10 +2671,6 @@
|
|||||||
"nodemailer": "^6.9.3"
|
"nodemailer": "^6.9.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@documenso/openpage-api": {
|
|
||||||
"resolved": "apps/openpage-api",
|
|
||||||
"link": true
|
|
||||||
},
|
|
||||||
"node_modules/@documenso/pdf-sign": {
|
"node_modules/@documenso/pdf-sign": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@documenso/pdf-sign/-/pdf-sign-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@documenso/pdf-sign/-/pdf-sign-0.1.0.tgz",
|
||||||
@@ -22282,9 +22225,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/luxon": {
|
"node_modules/luxon": {
|
||||||
"version": "3.5.0",
|
"version": "3.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz",
|
||||||
"integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==",
|
"integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.7.2-rc.1",
|
"version": "1.7.1-rc.3",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"build:web": "turbo run build --filter=@documenso/web",
|
"build:web": "turbo run build --filter=@documenso/web",
|
||||||
@@ -8,8 +8,7 @@
|
|||||||
"dev:web": "turbo run dev --filter=@documenso/web",
|
"dev:web": "turbo run dev --filter=@documenso/web",
|
||||||
"dev:marketing": "turbo run dev --filter=@documenso/marketing",
|
"dev:marketing": "turbo run dev --filter=@documenso/marketing",
|
||||||
"dev:docs": "turbo run dev --filter=@documenso/documentation",
|
"dev:docs": "turbo run dev --filter=@documenso/documentation",
|
||||||
"dev:openpage-api": "turbo run dev --filter=@documenso/openpage-api",
|
"start": "turbo run start --filter=@documenso/web --filter=@documenso/marketing --filter=@documenso/documentation",
|
||||||
"start": "turbo run start --filter=@documenso/web --filter=@documenso/marketing --filter=@documenso/documentation --filter=@documenso/openpage-api",
|
|
||||||
"lint": "turbo run lint",
|
"lint": "turbo run lint",
|
||||||
"lint:fix": "turbo run lint:fix",
|
"lint:fix": "turbo run lint:fix",
|
||||||
"format": "prettier --write \"**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts,mdx}\"",
|
"format": "prettier --write \"**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts,mdx}\"",
|
||||||
@@ -64,10 +63,8 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/pdf-sign": "^0.1.0",
|
"@documenso/pdf-sign": "^0.1.0",
|
||||||
"@documenso/prisma": "^0.0.0",
|
|
||||||
"@lingui/core": "^4.11.3",
|
"@lingui/core": "^4.11.3",
|
||||||
"inngest-cli": "^0.29.1",
|
"inngest-cli": "^0.29.1",
|
||||||
"luxon": "^3.5.0",
|
|
||||||
"next-runtime-env": "^3.2.0",
|
"next-runtime-env": "^3.2.0",
|
||||||
"react": "^18"
|
"react": "^18"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,12 +12,10 @@ import {
|
|||||||
ZDeleteFieldMutationSchema,
|
ZDeleteFieldMutationSchema,
|
||||||
ZDeleteRecipientMutationSchema,
|
ZDeleteRecipientMutationSchema,
|
||||||
ZDownloadDocumentSuccessfulSchema,
|
ZDownloadDocumentSuccessfulSchema,
|
||||||
ZFindTeamMembersResponseSchema,
|
|
||||||
ZGenerateDocumentFromTemplateMutationResponseSchema,
|
ZGenerateDocumentFromTemplateMutationResponseSchema,
|
||||||
ZGenerateDocumentFromTemplateMutationSchema,
|
ZGenerateDocumentFromTemplateMutationSchema,
|
||||||
ZGetDocumentsQuerySchema,
|
ZGetDocumentsQuerySchema,
|
||||||
ZGetTemplatesQuerySchema,
|
ZGetTemplatesQuerySchema,
|
||||||
ZInviteTeamMemberMutationSchema,
|
|
||||||
ZNoBodyMutationSchema,
|
ZNoBodyMutationSchema,
|
||||||
ZResendDocumentForSigningMutationSchema,
|
ZResendDocumentForSigningMutationSchema,
|
||||||
ZSendDocumentForSigningMutationSchema,
|
ZSendDocumentForSigningMutationSchema,
|
||||||
@@ -28,17 +26,13 @@ import {
|
|||||||
ZSuccessfulGetDocumentResponseSchema,
|
ZSuccessfulGetDocumentResponseSchema,
|
||||||
ZSuccessfulGetTemplateResponseSchema,
|
ZSuccessfulGetTemplateResponseSchema,
|
||||||
ZSuccessfulGetTemplatesResponseSchema,
|
ZSuccessfulGetTemplatesResponseSchema,
|
||||||
ZSuccessfulInviteTeamMemberResponseSchema,
|
|
||||||
ZSuccessfulRecipientResponseSchema,
|
ZSuccessfulRecipientResponseSchema,
|
||||||
ZSuccessfulRemoveTeamMemberResponseSchema,
|
|
||||||
ZSuccessfulResendDocumentResponseSchema,
|
ZSuccessfulResendDocumentResponseSchema,
|
||||||
ZSuccessfulResponseSchema,
|
ZSuccessfulResponseSchema,
|
||||||
ZSuccessfulSigningResponseSchema,
|
ZSuccessfulSigningResponseSchema,
|
||||||
ZSuccessfulUpdateTeamMemberResponseSchema,
|
|
||||||
ZUnsuccessfulResponseSchema,
|
ZUnsuccessfulResponseSchema,
|
||||||
ZUpdateFieldMutationSchema,
|
ZUpdateFieldMutationSchema,
|
||||||
ZUpdateRecipientMutationSchema,
|
ZUpdateRecipientMutationSchema,
|
||||||
ZUpdateTeamMemberMutationSchema,
|
|
||||||
} from './schema';
|
} from './schema';
|
||||||
|
|
||||||
const c = initContract();
|
const c = initContract();
|
||||||
@@ -279,61 +273,6 @@ export const ApiContractV1 = c.router(
|
|||||||
},
|
},
|
||||||
summary: 'Delete a field from a document',
|
summary: 'Delete a field from a document',
|
||||||
},
|
},
|
||||||
|
|
||||||
findTeamMembers: {
|
|
||||||
method: 'GET',
|
|
||||||
path: '/api/v1/team/:id/members',
|
|
||||||
responses: {
|
|
||||||
200: ZFindTeamMembersResponseSchema,
|
|
||||||
400: ZUnsuccessfulResponseSchema,
|
|
||||||
401: ZUnsuccessfulResponseSchema,
|
|
||||||
404: ZUnsuccessfulResponseSchema,
|
|
||||||
500: ZUnsuccessfulResponseSchema,
|
|
||||||
},
|
|
||||||
summary: 'List team members',
|
|
||||||
},
|
|
||||||
|
|
||||||
inviteTeamMember: {
|
|
||||||
method: 'POST',
|
|
||||||
path: '/api/v1/team/:id/members/invite',
|
|
||||||
body: ZInviteTeamMemberMutationSchema,
|
|
||||||
responses: {
|
|
||||||
200: ZSuccessfulInviteTeamMemberResponseSchema,
|
|
||||||
400: ZUnsuccessfulResponseSchema,
|
|
||||||
401: ZUnsuccessfulResponseSchema,
|
|
||||||
404: ZUnsuccessfulResponseSchema,
|
|
||||||
500: ZUnsuccessfulResponseSchema,
|
|
||||||
},
|
|
||||||
summary: 'Invite a member to a team',
|
|
||||||
},
|
|
||||||
|
|
||||||
updateTeamMember: {
|
|
||||||
method: 'PUT',
|
|
||||||
path: '/api/v1/team/:id/members/:memberId',
|
|
||||||
body: ZUpdateTeamMemberMutationSchema,
|
|
||||||
responses: {
|
|
||||||
200: ZSuccessfulUpdateTeamMemberResponseSchema,
|
|
||||||
400: ZUnsuccessfulResponseSchema,
|
|
||||||
401: ZUnsuccessfulResponseSchema,
|
|
||||||
404: ZUnsuccessfulResponseSchema,
|
|
||||||
500: ZUnsuccessfulResponseSchema,
|
|
||||||
},
|
|
||||||
summary: 'Update a team member',
|
|
||||||
},
|
|
||||||
|
|
||||||
removeTeamMember: {
|
|
||||||
method: 'DELETE',
|
|
||||||
path: '/api/v1/team/:id/members/:memberId',
|
|
||||||
body: null,
|
|
||||||
responses: {
|
|
||||||
200: ZSuccessfulRemoveTeamMemberResponseSchema,
|
|
||||||
400: ZUnsuccessfulResponseSchema,
|
|
||||||
401: ZUnsuccessfulResponseSchema,
|
|
||||||
404: ZUnsuccessfulResponseSchema,
|
|
||||||
500: ZUnsuccessfulResponseSchema,
|
|
||||||
},
|
|
||||||
summary: 'Remove a member from a team',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
baseHeaders: ZAuthorizationHeadersSchema,
|
baseHeaders: ZAuthorizationHeadersSchema,
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ import { getRecipientById } from '@documenso/lib/server-only/recipient/get-recip
|
|||||||
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
|
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
|
||||||
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
|
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
|
||||||
import { updateRecipient } from '@documenso/lib/server-only/recipient/update-recipient';
|
import { updateRecipient } from '@documenso/lib/server-only/recipient/update-recipient';
|
||||||
import { createTeamMemberInvites } from '@documenso/lib/server-only/team/create-team-member-invites';
|
|
||||||
import { deleteTeamMembers } from '@documenso/lib/server-only/team/delete-team-members';
|
|
||||||
import type { CreateDocumentFromTemplateResponse } from '@documenso/lib/server-only/template/create-document-from-template';
|
import type { CreateDocumentFromTemplateResponse } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||||
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
|
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||||
import { createDocumentFromTemplateLegacy } from '@documenso/lib/server-only/template/create-document-from-template-legacy';
|
import { createDocumentFromTemplateLegacy } from '@documenso/lib/server-only/template/create-document-from-template-legacy';
|
||||||
@@ -51,12 +49,7 @@ import {
|
|||||||
} from '@documenso/lib/universal/upload/server-actions';
|
} from '@documenso/lib/universal/upload/server-actions';
|
||||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import {
|
import { DocumentDataType, DocumentStatus, SigningStatus } from '@documenso/prisma/client';
|
||||||
DocumentDataType,
|
|
||||||
DocumentStatus,
|
|
||||||
SigningStatus,
|
|
||||||
TeamMemberRole,
|
|
||||||
} from '@documenso/prisma/client';
|
|
||||||
|
|
||||||
import { ApiContractV1 } from './contract';
|
import { ApiContractV1 } from './contract';
|
||||||
import { authenticatedMiddleware } from './middleware/authenticated';
|
import { authenticatedMiddleware } from './middleware/authenticated';
|
||||||
@@ -1286,270 +1279,4 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
findTeamMembers: authenticatedMiddleware(async (args, user, team) => {
|
|
||||||
const { id: teamId } = args.params;
|
|
||||||
|
|
||||||
if (team?.id !== Number(teamId)) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
body: {
|
|
||||||
message: 'You are not authorized to perform actions against this team.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const self = await prisma.teamMember.findFirst({
|
|
||||||
where: {
|
|
||||||
userId: user.id,
|
|
||||||
teamId: team.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (self?.role !== TeamMemberRole.ADMIN) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
body: {
|
|
||||||
message: 'You are not authorized to perform actions against this team.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const members = await prisma.teamMember.findMany({
|
|
||||||
where: {
|
|
||||||
teamId: team.id,
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
user: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: {
|
|
||||||
members: members.map((member) => ({
|
|
||||||
id: member.id,
|
|
||||||
email: member.user.email,
|
|
||||||
role: member.role,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
|
|
||||||
inviteTeamMember: authenticatedMiddleware(async (args, user, team) => {
|
|
||||||
const { id: teamId } = args.params;
|
|
||||||
|
|
||||||
const { email, role } = args.body;
|
|
||||||
|
|
||||||
if (team?.id !== Number(teamId)) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
body: {
|
|
||||||
message: 'You are not authorized to perform actions against this team.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const self = await prisma.teamMember.findFirst({
|
|
||||||
where: {
|
|
||||||
userId: user.id,
|
|
||||||
teamId: team.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (self?.role !== TeamMemberRole.ADMIN) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
body: {
|
|
||||||
message: 'You are not authorized to perform actions against this team.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasAlreadyBeenInvited = await prisma.teamMember.findFirst({
|
|
||||||
where: {
|
|
||||||
teamId: team.id,
|
|
||||||
user: {
|
|
||||||
email,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hasAlreadyBeenInvited) {
|
|
||||||
return {
|
|
||||||
status: 400,
|
|
||||||
body: {
|
|
||||||
message: 'This user has already been invited to the team',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
await createTeamMemberInvites({
|
|
||||||
userId: user.id,
|
|
||||||
userName: user.name ?? '',
|
|
||||||
teamId: team.id,
|
|
||||||
invitations: [
|
|
||||||
{
|
|
||||||
email,
|
|
||||||
role,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: {
|
|
||||||
message: 'An invite has been sent to the member',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
|
|
||||||
updateTeamMember: authenticatedMiddleware(async (args, user, team) => {
|
|
||||||
const { id: teamId, memberId } = args.params;
|
|
||||||
|
|
||||||
const { role } = args.body;
|
|
||||||
|
|
||||||
if (team?.id !== Number(teamId)) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
body: {
|
|
||||||
message: 'You are not authorized to perform actions against this team.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const self = await prisma.teamMember.findFirst({
|
|
||||||
where: {
|
|
||||||
userId: user.id,
|
|
||||||
teamId: team.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (self?.role !== TeamMemberRole.ADMIN) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
body: {
|
|
||||||
message: 'You are not authorized to perform actions against this team.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const member = await prisma.teamMember.findFirst({
|
|
||||||
where: {
|
|
||||||
id: Number(memberId),
|
|
||||||
teamId: team.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!member) {
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
body: {
|
|
||||||
message: 'The provided member id does not exist.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedMember = await prisma.teamMember.update({
|
|
||||||
where: {
|
|
||||||
id: member.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
role,
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
user: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: {
|
|
||||||
id: updatedMember.id,
|
|
||||||
email: updatedMember.user.email,
|
|
||||||
role: updatedMember.role,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
|
|
||||||
removeTeamMember: authenticatedMiddleware(async (args, user, team) => {
|
|
||||||
const { id: teamId, memberId } = args.params;
|
|
||||||
|
|
||||||
if (team?.id !== Number(teamId)) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
body: {
|
|
||||||
message: 'You are not authorized to perform actions against this team.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const self = await prisma.teamMember.findFirst({
|
|
||||||
where: {
|
|
||||||
userId: user.id,
|
|
||||||
teamId: team.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (self?.role !== TeamMemberRole.ADMIN) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
body: {
|
|
||||||
message: 'You are not authorized to perform actions against this team.',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const member = await prisma.teamMember.findFirst({
|
|
||||||
where: {
|
|
||||||
id: Number(memberId),
|
|
||||||
teamId: Number(teamId),
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
user: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!member) {
|
|
||||||
return {
|
|
||||||
status: 404,
|
|
||||||
body: {
|
|
||||||
message: 'Member not found',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (team.ownerUserId === member.userId) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
body: {
|
|
||||||
message: 'You cannot remove the owner of the team',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (member.userId === user.id) {
|
|
||||||
return {
|
|
||||||
status: 403,
|
|
||||||
body: {
|
|
||||||
message: 'You cannot remove yourself from the team',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
await deleteTeamMembers({
|
|
||||||
userId: user.id,
|
|
||||||
teamId: team.id,
|
|
||||||
teamMemberIds: [member.id],
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
body: {
|
|
||||||
id: member.id,
|
|
||||||
email: member.user.email,
|
|
||||||
role: member.role,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
RecipientRole,
|
RecipientRole,
|
||||||
SendStatus,
|
SendStatus,
|
||||||
SigningStatus,
|
SigningStatus,
|
||||||
TeamMemberRole,
|
|
||||||
TemplateType,
|
TemplateType,
|
||||||
} from '@documenso/prisma/client';
|
} from '@documenso/prisma/client';
|
||||||
|
|
||||||
@@ -533,41 +532,3 @@ export const ZGetTemplatesQuerySchema = z.object({
|
|||||||
page: z.coerce.number().min(1).optional().default(1),
|
page: z.coerce.number().min(1).optional().default(1),
|
||||||
perPage: z.coerce.number().min(1).optional().default(1),
|
perPage: z.coerce.number().min(1).optional().default(1),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ZFindTeamMembersResponseSchema = z.object({
|
|
||||||
members: z.array(
|
|
||||||
z.object({
|
|
||||||
id: z.number(),
|
|
||||||
email: z.string().email(),
|
|
||||||
role: z.nativeEnum(TeamMemberRole),
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZInviteTeamMemberMutationSchema = z.object({
|
|
||||||
email: z
|
|
||||||
.string()
|
|
||||||
.email()
|
|
||||||
.transform((email) => email.toLowerCase()),
|
|
||||||
role: z.nativeEnum(TeamMemberRole).optional().default(TeamMemberRole.MEMBER),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZSuccessfulInviteTeamMemberResponseSchema = z.object({
|
|
||||||
message: z.string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZUpdateTeamMemberMutationSchema = z.object({
|
|
||||||
role: z.nativeEnum(TeamMemberRole),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZSuccessfulUpdateTeamMemberResponseSchema = z.object({
|
|
||||||
id: z.number(),
|
|
||||||
email: z.string().email(),
|
|
||||||
role: z.nativeEnum(TeamMemberRole),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ZSuccessfulRemoveTeamMemberResponseSchema = z.object({
|
|
||||||
id: z.number(),
|
|
||||||
email: z.string().email(),
|
|
||||||
role: z.nativeEnum(TeamMemberRole),
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,278 +0,0 @@
|
|||||||
import { expect, test } from '@playwright/test';
|
|
||||||
|
|
||||||
import {
|
|
||||||
ZFindTeamMembersResponseSchema,
|
|
||||||
ZSuccessfulInviteTeamMemberResponseSchema,
|
|
||||||
ZSuccessfulRemoveTeamMemberResponseSchema,
|
|
||||||
ZSuccessfulUpdateTeamMemberResponseSchema,
|
|
||||||
ZUnsuccessfulResponseSchema,
|
|
||||||
} from '@documenso/api/v1/schema';
|
|
||||||
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
|
||||||
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
|
||||||
import { TeamMemberRole } from '@documenso/prisma/client';
|
|
||||||
import { seedTeam } from '@documenso/prisma/seed/teams';
|
|
||||||
import { seedUser } from '@documenso/prisma/seed/users';
|
|
||||||
|
|
||||||
test.describe('Team API', () => {
|
|
||||||
test('findTeamMembers: should list team members', async ({ request }) => {
|
|
||||||
const team = await seedTeam({
|
|
||||||
createTeamMembers: 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
const ownerMember = team.members.find((member) => member.userId === team.owner.id)!;
|
|
||||||
|
|
||||||
// Should not be undefined
|
|
||||||
expect(ownerMember).toBeTruthy();
|
|
||||||
|
|
||||||
const { token } = await createApiToken({
|
|
||||||
userId: team.owner.id,
|
|
||||||
teamId: team.id,
|
|
||||||
tokenName: 'test',
|
|
||||||
expiresIn: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await request.get(`${WEBAPP_BASE_URL}/api/v1/team/${team.id}/members`, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(response.ok()).toBeTruthy();
|
|
||||||
expect(response.status()).toBe(200);
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
const parsed = ZFindTeamMembersResponseSchema.safeParse(data);
|
|
||||||
|
|
||||||
const safeData = parsed.success ? parsed.data : null;
|
|
||||||
|
|
||||||
expect(parsed.success).toBeTruthy();
|
|
||||||
|
|
||||||
expect(safeData!.members).toHaveLength(4); // Owner + 3 members
|
|
||||||
expect(safeData!.members[0]).toHaveProperty('id');
|
|
||||||
expect(safeData!.members[0]).toHaveProperty('email');
|
|
||||||
expect(safeData!.members[0]).toHaveProperty('role');
|
|
||||||
|
|
||||||
expect(safeData!.members).toContainEqual({
|
|
||||||
id: ownerMember.id,
|
|
||||||
email: ownerMember.user.email,
|
|
||||||
role: ownerMember.role,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('inviteTeamMember: should invite a new team member', async ({ request }) => {
|
|
||||||
const team = await seedTeam();
|
|
||||||
|
|
||||||
const { token } = await createApiToken({
|
|
||||||
userId: team.owner.id,
|
|
||||||
teamId: team.id,
|
|
||||||
tokenName: 'test',
|
|
||||||
expiresIn: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const newUser = await seedUser();
|
|
||||||
|
|
||||||
const response = await request.post(
|
|
||||||
`${WEBAPP_BASE_URL}/api/v1/team/${team.id}/members/invite`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
email: newUser.email,
|
|
||||||
role: TeamMemberRole.MEMBER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(response.ok()).toBeTruthy();
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
const parsed = ZSuccessfulInviteTeamMemberResponseSchema.safeParse(data);
|
|
||||||
|
|
||||||
const safeData = parsed.success ? parsed.data : null;
|
|
||||||
|
|
||||||
expect(parsed.success).toBeTruthy();
|
|
||||||
expect(safeData!.message).toBe('An invite has been sent to the member');
|
|
||||||
|
|
||||||
const invite = await prisma.teamMemberInvite.findFirst({
|
|
||||||
where: {
|
|
||||||
email: newUser.email,
|
|
||||||
teamId: team.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(invite).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('updateTeamMember: should update a team member role', async ({ request }) => {
|
|
||||||
const team = await seedTeam({
|
|
||||||
createTeamMembers: 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { token } = await createApiToken({
|
|
||||||
userId: team.owner.id,
|
|
||||||
teamId: team.id,
|
|
||||||
tokenName: 'test',
|
|
||||||
expiresIn: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const member = team.members.find((member) => member.role === TeamMemberRole.MEMBER)!;
|
|
||||||
|
|
||||||
// Should not be undefined
|
|
||||||
expect(member).toBeTruthy();
|
|
||||||
|
|
||||||
const response = await request.put(
|
|
||||||
`${WEBAPP_BASE_URL}/api/v1/team/${team.id}/members/${member.id}`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
role: TeamMemberRole.ADMIN,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(response.ok()).toBeTruthy();
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
const parsed = ZSuccessfulUpdateTeamMemberResponseSchema.safeParse(data);
|
|
||||||
|
|
||||||
const safeData = parsed.success ? parsed.data : null;
|
|
||||||
|
|
||||||
expect(parsed.success).toBeTruthy();
|
|
||||||
|
|
||||||
expect(safeData!.id).toBe(member.id);
|
|
||||||
expect(safeData!.email).toBe(member.user.email);
|
|
||||||
expect(safeData!.role).toBe(TeamMemberRole.ADMIN);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('removeTeamMember: should remove a team member', async ({ request }) => {
|
|
||||||
const team = await seedTeam({
|
|
||||||
createTeamMembers: 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { token } = await createApiToken({
|
|
||||||
userId: team.owner.id,
|
|
||||||
teamId: team.id,
|
|
||||||
tokenName: 'test',
|
|
||||||
expiresIn: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const member = team.members.find((member) => member.role === TeamMemberRole.MEMBER)!;
|
|
||||||
|
|
||||||
// Should not be undefined
|
|
||||||
expect(member).toBeTruthy();
|
|
||||||
|
|
||||||
const response = await request.delete(
|
|
||||||
`${WEBAPP_BASE_URL}/api/v1/team/${team.id}/members/${member.id}`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(response.status()).toBe(200);
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
const parsed = ZSuccessfulRemoveTeamMemberResponseSchema.safeParse(data);
|
|
||||||
|
|
||||||
const safeData = parsed.success ? parsed.data : null;
|
|
||||||
|
|
||||||
expect(parsed.success).toBeTruthy();
|
|
||||||
|
|
||||||
expect(safeData!.id).toBe(member.id);
|
|
||||||
expect(safeData!.email).toBe(member.user.email);
|
|
||||||
expect(safeData!.role).toBe(member.role);
|
|
||||||
|
|
||||||
const removedMemberCount = await prisma.teamMember.count({
|
|
||||||
where: {
|
|
||||||
id: member.id,
|
|
||||||
teamId: team.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(removedMemberCount).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('removeTeamMember: should not remove team owner', async ({ request }) => {
|
|
||||||
const team = await seedTeam({
|
|
||||||
createTeamMembers: 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { token } = await createApiToken({
|
|
||||||
userId: team.owner.id,
|
|
||||||
teamId: team.id,
|
|
||||||
tokenName: 'test',
|
|
||||||
expiresIn: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const ownerMember = team.members.find((member) => member.userId === team.owner.id)!;
|
|
||||||
|
|
||||||
// Should not be undefined
|
|
||||||
expect(ownerMember).toBeTruthy();
|
|
||||||
|
|
||||||
const response = await request.delete(
|
|
||||||
`${WEBAPP_BASE_URL}/api/v1/team/${team.id}/members/${ownerMember.id}`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(response.status()).toBe(403);
|
|
||||||
|
|
||||||
const parsed = ZUnsuccessfulResponseSchema.safeParse(await response.json());
|
|
||||||
|
|
||||||
expect(parsed.success).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('removeTeamMember: should not remove self', async ({ request }) => {
|
|
||||||
const team = await seedTeam({
|
|
||||||
createTeamMembers: 3,
|
|
||||||
});
|
|
||||||
|
|
||||||
const member = team.members.find((member) => member.role === TeamMemberRole.MEMBER)!;
|
|
||||||
|
|
||||||
// Make our non-owner member an admin
|
|
||||||
await prisma.teamMember.update({
|
|
||||||
where: {
|
|
||||||
id: member.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
role: TeamMemberRole.ADMIN,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const { token } = await createApiToken({
|
|
||||||
userId: member.userId,
|
|
||||||
teamId: team.id,
|
|
||||||
tokenName: 'test',
|
|
||||||
expiresIn: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await request.delete(
|
|
||||||
`${WEBAPP_BASE_URL}/api/v1/team/${team.id}/members/${member.id}`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(response.status()).toBe(403);
|
|
||||||
|
|
||||||
const parsed = ZUnsuccessfulResponseSchema.safeParse(await response.json());
|
|
||||||
|
|
||||||
expect(parsed.success).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
import { expect, test } from '@playwright/test';
|
|
||||||
|
|
||||||
import { DocumentStatus, TeamMemberRole } from '@documenso/prisma/client';
|
|
||||||
import { seedDocuments, seedTeamDocuments } from '@documenso/prisma/seed/documents';
|
|
||||||
import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams';
|
|
||||||
import { seedUser } from '@documenso/prisma/seed/users';
|
|
||||||
|
|
||||||
import { apiSignin, apiSignout } from '../fixtures/authentication';
|
|
||||||
import { checkDocumentTabCount } from '../fixtures/documents';
|
|
||||||
|
|
||||||
test('[TEAMS]: search respects team document visibility', async ({ page }) => {
|
|
||||||
const team = await seedTeam();
|
|
||||||
const adminUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.ADMIN });
|
|
||||||
const managerUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MANAGER });
|
|
||||||
const memberUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MEMBER });
|
|
||||||
|
|
||||||
await seedDocuments([
|
|
||||||
{
|
|
||||||
sender: team.owner,
|
|
||||||
recipients: [],
|
|
||||||
type: DocumentStatus.COMPLETED,
|
|
||||||
documentOptions: {
|
|
||||||
teamId: team.id,
|
|
||||||
visibility: 'EVERYONE',
|
|
||||||
title: 'Searchable Document for Everyone',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sender: team.owner,
|
|
||||||
recipients: [],
|
|
||||||
type: DocumentStatus.COMPLETED,
|
|
||||||
documentOptions: {
|
|
||||||
teamId: team.id,
|
|
||||||
visibility: 'MANAGER_AND_ABOVE',
|
|
||||||
title: 'Searchable Document for Managers',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sender: team.owner,
|
|
||||||
recipients: [],
|
|
||||||
type: DocumentStatus.COMPLETED,
|
|
||||||
documentOptions: {
|
|
||||||
teamId: team.id,
|
|
||||||
visibility: 'ADMIN',
|
|
||||||
title: 'Searchable Document for Admins',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const testCases = [
|
|
||||||
{ user: adminUser, visibleDocs: 3 },
|
|
||||||
{ user: managerUser, visibleDocs: 2 },
|
|
||||||
{ user: memberUser, visibleDocs: 1 },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const { user, visibleDocs } of testCases) {
|
|
||||||
await apiSignin({
|
|
||||||
page,
|
|
||||||
email: user.email,
|
|
||||||
redirectPath: `/t/${team.url}/documents`,
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.getByPlaceholder('Search documents...').fill('Searchable');
|
|
||||||
await page.waitForURL(/search=Searchable/);
|
|
||||||
|
|
||||||
await checkDocumentTabCount(page, 'All', visibleDocs);
|
|
||||||
|
|
||||||
await apiSignout({ page });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[TEAMS]: search does not reveal documents from other teams', async ({ page }) => {
|
|
||||||
const { team: teamA, teamMember2: teamAMember } = await seedTeamDocuments();
|
|
||||||
const { team: teamB } = await seedTeamDocuments();
|
|
||||||
|
|
||||||
await seedDocuments([
|
|
||||||
{
|
|
||||||
sender: teamA.owner,
|
|
||||||
recipients: [],
|
|
||||||
type: DocumentStatus.COMPLETED,
|
|
||||||
documentOptions: {
|
|
||||||
teamId: teamA.id,
|
|
||||||
visibility: 'EVERYONE',
|
|
||||||
title: 'Unique Team A Document',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sender: teamB.owner,
|
|
||||||
recipients: [],
|
|
||||||
type: DocumentStatus.COMPLETED,
|
|
||||||
documentOptions: {
|
|
||||||
teamId: teamB.id,
|
|
||||||
visibility: 'EVERYONE',
|
|
||||||
title: 'Unique Team B Document',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
await apiSignin({
|
|
||||||
page,
|
|
||||||
email: teamAMember.email,
|
|
||||||
redirectPath: `/t/${teamA.url}/documents`,
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.getByPlaceholder('Search documents...').fill('Unique');
|
|
||||||
await page.waitForURL(/search=Unique/);
|
|
||||||
|
|
||||||
await checkDocumentTabCount(page, 'All', 1);
|
|
||||||
await expect(page.getByRole('link', { name: 'Unique Team A Document' })).toBeVisible();
|
|
||||||
await expect(page.getByRole('link', { name: 'Unique Team B Document' })).not.toBeVisible();
|
|
||||||
|
|
||||||
await apiSignout({ page });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[PERSONAL]: search does not reveal team documents in personal account', async ({ page }) => {
|
|
||||||
const { team, teamMember2 } = await seedTeamDocuments();
|
|
||||||
|
|
||||||
await seedDocuments([
|
|
||||||
{
|
|
||||||
sender: teamMember2,
|
|
||||||
recipients: [],
|
|
||||||
type: DocumentStatus.COMPLETED,
|
|
||||||
documentOptions: {
|
|
||||||
teamId: null,
|
|
||||||
title: 'Personal Unique Document',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sender: team.owner,
|
|
||||||
recipients: [],
|
|
||||||
type: DocumentStatus.COMPLETED,
|
|
||||||
documentOptions: {
|
|
||||||
teamId: team.id,
|
|
||||||
visibility: 'EVERYONE',
|
|
||||||
title: 'Team Unique Document',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
await apiSignin({
|
|
||||||
page,
|
|
||||||
email: teamMember2.email,
|
|
||||||
redirectPath: '/documents',
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.getByPlaceholder('Search documents...').fill('Unique');
|
|
||||||
await page.waitForURL(/search=Unique/);
|
|
||||||
|
|
||||||
await checkDocumentTabCount(page, 'All', 1);
|
|
||||||
await expect(page.getByRole('link', { name: 'Personal Unique Document' })).toBeVisible();
|
|
||||||
await expect(page.getByRole('link', { name: 'Team Unique Document' })).not.toBeVisible();
|
|
||||||
|
|
||||||
await apiSignout({ page });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[TEAMS]: search respects recipient visibility regardless of team visibility', async ({
|
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
const team = await seedTeam();
|
|
||||||
const memberUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MEMBER });
|
|
||||||
|
|
||||||
await seedDocuments([
|
|
||||||
{
|
|
||||||
sender: team.owner,
|
|
||||||
recipients: [memberUser],
|
|
||||||
type: DocumentStatus.COMPLETED,
|
|
||||||
documentOptions: {
|
|
||||||
teamId: team.id,
|
|
||||||
visibility: 'ADMIN',
|
|
||||||
title: 'Admin Document with Member Recipient',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
await apiSignin({
|
|
||||||
page,
|
|
||||||
email: memberUser.email,
|
|
||||||
redirectPath: `/t/${team.url}/documents`,
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.getByPlaceholder('Search documents...').fill('Admin Document');
|
|
||||||
await page.waitForURL(/search=Admin(%20|\+|\s)Document/);
|
|
||||||
|
|
||||||
await checkDocumentTabCount(page, 'All', 1);
|
|
||||||
await expect(
|
|
||||||
page.getByRole('link', { name: 'Admin Document with Member Recipient' }),
|
|
||||||
).toBeVisible();
|
|
||||||
|
|
||||||
await apiSignout({ page });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('[TEAMS]: search by recipient name respects visibility', async ({ page }) => {
|
|
||||||
const team = await seedTeam();
|
|
||||||
const adminUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.ADMIN });
|
|
||||||
const memberUser = await seedTeamMember({
|
|
||||||
teamId: team.id,
|
|
||||||
role: TeamMemberRole.MEMBER,
|
|
||||||
name: 'Team Member',
|
|
||||||
});
|
|
||||||
|
|
||||||
const uniqueRecipient = await seedUser();
|
|
||||||
|
|
||||||
await seedDocuments([
|
|
||||||
{
|
|
||||||
sender: team.owner,
|
|
||||||
recipients: [uniqueRecipient],
|
|
||||||
type: DocumentStatus.COMPLETED,
|
|
||||||
documentOptions: {
|
|
||||||
teamId: team.id,
|
|
||||||
visibility: 'ADMIN',
|
|
||||||
title: 'Admin Document for Unique Recipient',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Admin should see the document when searching by recipient name
|
|
||||||
await apiSignin({
|
|
||||||
page,
|
|
||||||
email: adminUser.email,
|
|
||||||
redirectPath: `/t/${team.url}/documents`,
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.getByPlaceholder('Search documents...').fill('Unique Recipient');
|
|
||||||
await page.waitForURL(/search=Unique(%20|\+|\s)Recipient/);
|
|
||||||
|
|
||||||
await checkDocumentTabCount(page, 'All', 1);
|
|
||||||
await expect(
|
|
||||||
page.getByRole('link', { name: 'Admin Document for Unique Recipient' }),
|
|
||||||
).toBeVisible();
|
|
||||||
|
|
||||||
await apiSignout({ page });
|
|
||||||
|
|
||||||
// Member should not see the document when searching by recipient name
|
|
||||||
await apiSignin({
|
|
||||||
page,
|
|
||||||
email: memberUser.email,
|
|
||||||
redirectPath: `/t/${team.url}/documents`,
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.getByPlaceholder('Search documents...').fill('Unique Recipient');
|
|
||||||
await page.waitForURL(/search=Unique(%20|\+|\s)Recipient/);
|
|
||||||
|
|
||||||
await checkDocumentTabCount(page, 'All', 0);
|
|
||||||
await expect(
|
|
||||||
page.getByRole('link', { name: 'Admin Document for Unique Recipient' }),
|
|
||||||
).not.toBeVisible();
|
|
||||||
|
|
||||||
await apiSignout({ page });
|
|
||||||
});
|
|
||||||
@@ -112,6 +112,7 @@ test('[DIRECT_TEMPLATES]: toggle direct template link', async ({ page }) => {
|
|||||||
await page.getByRole('switch').click();
|
await page.getByRole('switch').click();
|
||||||
await page.getByRole('button', { name: 'Save' }).click();
|
await page.getByRole('button', { name: 'Save' }).click();
|
||||||
await expect(page.getByText('Direct link signing has been').first()).toBeVisible();
|
await expect(page.getByText('Direct link signing has been').first()).toBeVisible();
|
||||||
|
await page.getByLabel('Direct Link Signing', { exact: true }).press('Escape');
|
||||||
|
|
||||||
// Check that the direct template link is no longer accessible.
|
// Check that the direct template link is no longer accessible.
|
||||||
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test:dev": "NODE_OPTIONS=--experimental-require-module playwright test",
|
"test:dev": "playwright test",
|
||||||
"test-ui:dev": "NODE_OPTIONS=--experimental-require-module playwright test --ui",
|
"test-ui:dev": "playwright test --ui",
|
||||||
"test:e2e": "NODE_OPTIONS=--experimental-require-module start-server-and-test \"npm run start -w @documenso/web\" http://localhost:3000 \"playwright test\""
|
"test:e2e": "start-server-and-test \"npm run start -w @documenso/web\" http://localhost:3000 \"playwright test\""
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import { checkboxValidationSigns } from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
|
import { checkboxValidationSigns } from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
|
||||||
|
|
||||||
import type { TCheckboxFieldMeta } from '../types/field-meta';
|
interface CheckboxFieldMeta {
|
||||||
|
readOnly?: boolean;
|
||||||
|
required?: boolean;
|
||||||
|
validationRule?: string;
|
||||||
|
validationLength?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export const validateCheckboxField = (
|
export const validateCheckboxField = (
|
||||||
values: string[],
|
values: string[],
|
||||||
fieldMeta: TCheckboxFieldMeta,
|
fieldMeta: CheckboxFieldMeta,
|
||||||
isSigningPage: boolean = false,
|
isSigningPage: boolean = false,
|
||||||
): string[] => {
|
): string[] => {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import type { TDropdownFieldMeta as DropdownFieldMeta } from '../types/field-meta';
|
interface DropdownFieldMeta {
|
||||||
|
readOnly?: boolean;
|
||||||
|
required?: boolean;
|
||||||
|
values?: { value: string }[];
|
||||||
|
defaultValue?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const validateDropdownField = (
|
export const validateDropdownField = (
|
||||||
value: string | undefined,
|
value: string | undefined,
|
||||||
fieldMeta: DropdownFieldMeta,
|
fieldMeta: DropdownFieldMeta,
|
||||||
isSigningPage: boolean = false,
|
isSigningPage: boolean = false,
|
||||||
fontSize?: number,
|
|
||||||
): string[] => {
|
): string[] => {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
@@ -46,9 +50,5 @@ export const validateDropdownField = (
|
|||||||
errors.push('Duplicate values are not allowed');
|
errors.push('Duplicate values are not allowed');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fontSize && (fontSize < 8 || fontSize > 96)) {
|
|
||||||
errors.push('Font size must be between 8 and 96.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import type {
|
|
||||||
TDateFieldMeta as DateFieldMeta,
|
|
||||||
TEmailFieldMeta as EmailFieldMeta,
|
|
||||||
TInitialsFieldMeta as InitialsFieldMeta,
|
|
||||||
TNameFieldMeta as NameFieldMeta,
|
|
||||||
} from '../types/field-meta';
|
|
||||||
|
|
||||||
export const validateFields = (
|
|
||||||
fieldMeta: DateFieldMeta | EmailFieldMeta | InitialsFieldMeta | NameFieldMeta,
|
|
||||||
): string[] => {
|
|
||||||
const errors = [];
|
|
||||||
const { fontSize } = fieldMeta;
|
|
||||||
|
|
||||||
if (fontSize && (fontSize < 8 || fontSize > 96)) {
|
|
||||||
errors.push('Font size must be between 8 and 96.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors;
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
// import { numberFormatValues } from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
|
// import { numberFormatValues } from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
|
||||||
import type { TNumberFieldMeta as NumberFieldMeta } from '../types/field-meta';
|
|
||||||
|
interface NumberFieldMeta {
|
||||||
|
minValue?: number;
|
||||||
|
maxValue?: number;
|
||||||
|
readOnly?: boolean;
|
||||||
|
required?: boolean;
|
||||||
|
numberFormat?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const validateNumberField = (
|
export const validateNumberField = (
|
||||||
value: string,
|
value: string,
|
||||||
@@ -8,7 +15,7 @@ export const validateNumberField = (
|
|||||||
): string[] => {
|
): string[] => {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
const { minValue, maxValue, readOnly, required, numberFormat, fontSize } = fieldMeta || {};
|
const { minValue, maxValue, readOnly, required, numberFormat } = fieldMeta || {};
|
||||||
|
|
||||||
const formatRegex: { [key: string]: RegExp } = {
|
const formatRegex: { [key: string]: RegExp } = {
|
||||||
'123,456,789.00': /^(?:\d{1,3}(?:,\d{3})*|\d+)(?:\.\d{1,2})?$/,
|
'123,456,789.00': /^(?:\d{1,3}(?:,\d{3})*|\d+)(?:\.\d{1,2})?$/,
|
||||||
@@ -56,9 +63,5 @@ export const validateNumberField = (
|
|||||||
errors.push('A field cannot be both read-only and required');
|
errors.push('A field cannot be both read-only and required');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fontSize && (fontSize < 8 || fontSize > 96)) {
|
|
||||||
errors.push('Font size must be between 8 and 96.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import type { TRadioFieldMeta as RadioFieldMeta } from '../types/field-meta';
|
interface RadioFieldMeta {
|
||||||
|
readOnly?: boolean;
|
||||||
|
required?: boolean;
|
||||||
|
values?: { checked: boolean; value: string }[];
|
||||||
|
}
|
||||||
|
|
||||||
export const validateRadioField = (
|
export const validateRadioField = (
|
||||||
value: string | undefined,
|
value: string | undefined,
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import type { TTextFieldMeta as TextFieldMeta } from '../types/field-meta';
|
interface TextFieldMeta {
|
||||||
|
characterLimit?: number;
|
||||||
|
readOnly?: boolean;
|
||||||
|
required?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const validateTextField = (
|
export const validateTextField = (
|
||||||
value: string,
|
value: string,
|
||||||
@@ -7,7 +11,7 @@ export const validateTextField = (
|
|||||||
): string[] => {
|
): string[] => {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
const { characterLimit, readOnly, required, fontSize } = fieldMeta;
|
const { characterLimit, readOnly, required } = fieldMeta;
|
||||||
|
|
||||||
if (required && !value && isSigningPage) {
|
if (required && !value && isSigningPage) {
|
||||||
errors.push('Value is required');
|
errors.push('Value is required');
|
||||||
@@ -25,9 +29,5 @@ export const validateTextField = (
|
|||||||
errors.push('A field cannot be both read-only and required');
|
errors.push('A field cannot be both read-only and required');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fontSize && (fontSize < 8 || fontSize > 96)) {
|
|
||||||
errors.push('Font size must be between 8 and 96.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
|||||||
|
|
||||||
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
|
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
|
||||||
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
|
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
|
||||||
import type { Field } from '@documenso/prisma/client';
|
import { Field } from '@documenso/prisma/client';
|
||||||
|
|
||||||
export const useFieldPageCoords = (field: Field) => {
|
export const useFieldPageCoords = (field: Field) => {
|
||||||
const [coords, setCoords] = useState({
|
const [coords, setCoords] = useState({
|
||||||
|
|||||||
@@ -15,10 +15,9 @@ type SupportedLanguages = (typeof SUPPORTED_LANGUAGE_CODES)[number];
|
|||||||
async function loadCatalog(lang: SupportedLanguages): Promise<{
|
async function loadCatalog(lang: SupportedLanguages): Promise<{
|
||||||
[k: string]: Messages;
|
[k: string]: Messages;
|
||||||
}> {
|
}> {
|
||||||
const extension = process.env.NODE_ENV === 'development' ? 'po' : 'js';
|
const { messages } = await import(
|
||||||
const context = IS_APP_WEB ? 'web' : 'marketing';
|
`../../translations/${lang}/${IS_APP_WEB ? 'web' : 'marketing'}.js`
|
||||||
|
);
|
||||||
const { messages } = await import(`../../translations/${lang}/${context}.${extension}`);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[lang]: messages,
|
[lang]: messages,
|
||||||
|
|||||||
@@ -1,19 +1,12 @@
|
|||||||
import type { Recipient } from '@documenso/prisma/client';
|
import type { Recipient } from '@documenso/prisma/client';
|
||||||
import { ReadStatus, RecipientRole, SendStatus, SigningStatus } from '@documenso/prisma/client';
|
import { ReadStatus, RecipientRole, SendStatus, SigningStatus } from '@documenso/prisma/client';
|
||||||
|
|
||||||
export enum RecipientStatusType {
|
|
||||||
COMPLETED = 'completed',
|
|
||||||
OPENED = 'opened',
|
|
||||||
WAITING = 'waiting',
|
|
||||||
UNSIGNED = 'unsigned',
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getRecipientType = (recipient: Recipient) => {
|
export const getRecipientType = (recipient: Recipient) => {
|
||||||
if (
|
if (
|
||||||
recipient.role === RecipientRole.CC ||
|
recipient.role === RecipientRole.CC ||
|
||||||
(recipient.sendStatus === SendStatus.SENT && recipient.signingStatus === SigningStatus.SIGNED)
|
(recipient.sendStatus === SendStatus.SENT && recipient.signingStatus === SigningStatus.SIGNED)
|
||||||
) {
|
) {
|
||||||
return RecipientStatusType.COMPLETED;
|
return 'completed';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -21,33 +14,12 @@ export const getRecipientType = (recipient: Recipient) => {
|
|||||||
recipient.readStatus === ReadStatus.OPENED &&
|
recipient.readStatus === ReadStatus.OPENED &&
|
||||||
recipient.signingStatus === SigningStatus.NOT_SIGNED
|
recipient.signingStatus === SigningStatus.NOT_SIGNED
|
||||||
) {
|
) {
|
||||||
return RecipientStatusType.OPENED;
|
return 'opened';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (recipient.sendStatus === 'SENT' && recipient.signingStatus === 'NOT_SIGNED') {
|
||||||
recipient.sendStatus === SendStatus.SENT &&
|
return 'waiting';
|
||||||
recipient.signingStatus === SigningStatus.NOT_SIGNED
|
|
||||||
) {
|
|
||||||
return RecipientStatusType.WAITING;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return RecipientStatusType.UNSIGNED;
|
return 'unsigned';
|
||||||
};
|
|
||||||
|
|
||||||
export const getExtraRecipientsType = (extraRecipients: Recipient[]) => {
|
|
||||||
const types = extraRecipients.map((r) => getRecipientType(r));
|
|
||||||
|
|
||||||
if (types.includes(RecipientStatusType.UNSIGNED)) {
|
|
||||||
return RecipientStatusType.UNSIGNED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (types.includes(RecipientStatusType.OPENED)) {
|
|
||||||
return RecipientStatusType.OPENED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (types.includes(RecipientStatusType.WAITING)) {
|
|
||||||
return RecipientStatusType.WAITING;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RecipientStatusType.COMPLETED;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
export const SUPPORTED_LANGUAGE_CODES = ['de', 'en', 'fr', 'es'] as const;
|
export const SUPPORTED_LANGUAGE_CODES = ['de', 'en', 'fr'] as const;
|
||||||
|
|
||||||
export const ZSupportedLanguageCodeSchema = z.enum(SUPPORTED_LANGUAGE_CODES).catch('en');
|
export const ZSupportedLanguageCodeSchema = z.enum(SUPPORTED_LANGUAGE_CODES).catch('en');
|
||||||
|
|
||||||
@@ -42,8 +42,4 @@ export const SUPPORTED_LANGUAGES: Record<string, SupportedLanguage> = {
|
|||||||
full: 'French',
|
full: 'French',
|
||||||
short: 'fr',
|
short: 'fr',
|
||||||
},
|
},
|
||||||
es: {
|
|
||||||
full: 'Spanish',
|
|
||||||
short: 'es',
|
|
||||||
},
|
|
||||||
} satisfies Record<SupportedLanguageCodes, SupportedLanguage>;
|
} satisfies Record<SupportedLanguageCodes, SupportedLanguage>;
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export type CreateDocumentMetaOptions = {
|
|||||||
dateFormat?: string;
|
dateFormat?: string;
|
||||||
redirectUrl?: string;
|
redirectUrl?: string;
|
||||||
signingOrder?: DocumentSigningOrder;
|
signingOrder?: DocumentSigningOrder;
|
||||||
typedSignatureEnabled?: boolean;
|
|
||||||
userId: number;
|
userId: number;
|
||||||
requestMetadata: RequestMetadata;
|
requestMetadata: RequestMetadata;
|
||||||
};
|
};
|
||||||
@@ -33,7 +32,6 @@ export const upsertDocumentMeta = async ({
|
|||||||
userId,
|
userId,
|
||||||
redirectUrl,
|
redirectUrl,
|
||||||
signingOrder,
|
signingOrder,
|
||||||
typedSignatureEnabled,
|
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
}: CreateDocumentMetaOptions) => {
|
}: CreateDocumentMetaOptions) => {
|
||||||
const user = await prisma.user.findFirstOrThrow({
|
const user = await prisma.user.findFirstOrThrow({
|
||||||
@@ -84,7 +82,6 @@ export const upsertDocumentMeta = async ({
|
|||||||
documentId,
|
documentId,
|
||||||
redirectUrl,
|
redirectUrl,
|
||||||
signingOrder,
|
signingOrder,
|
||||||
typedSignatureEnabled,
|
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
subject,
|
subject,
|
||||||
@@ -94,7 +91,6 @@ export const upsertDocumentMeta = async ({
|
|||||||
timezone,
|
timezone,
|
||||||
redirectUrl,
|
redirectUrl,
|
||||||
signingOrder,
|
signingOrder,
|
||||||
typedSignatureEnabled,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export type FindDocumentsOptions = {
|
|||||||
};
|
};
|
||||||
period?: PeriodSelectorValue;
|
period?: PeriodSelectorValue;
|
||||||
senderIds?: number[];
|
senderIds?: number[];
|
||||||
search?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const findDocuments = async ({
|
export const findDocuments = async ({
|
||||||
@@ -38,7 +37,6 @@ export const findDocuments = async ({
|
|||||||
orderBy,
|
orderBy,
|
||||||
period,
|
period,
|
||||||
senderIds,
|
senderIds,
|
||||||
search,
|
|
||||||
}: FindDocumentsOptions) => {
|
}: FindDocumentsOptions) => {
|
||||||
const { user, team } = await prisma.$transaction(async (tx) => {
|
const { user, team } = await prisma.$transaction(async (tx) => {
|
||||||
const user = await tx.user.findFirstOrThrow({
|
const user = await tx.user.findFirstOrThrow({
|
||||||
@@ -94,14 +92,6 @@ export const findDocuments = async ({
|
|||||||
})
|
})
|
||||||
.otherwise(() => undefined);
|
.otherwise(() => undefined);
|
||||||
|
|
||||||
const searchFilter: Prisma.DocumentWhereInput = {
|
|
||||||
OR: [
|
|
||||||
{ title: { contains: search, mode: 'insensitive' } },
|
|
||||||
{ Recipient: { some: { name: { contains: search, mode: 'insensitive' } } } },
|
|
||||||
{ Recipient: { some: { email: { contains: search, mode: 'insensitive' } } } },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const visibilityFilters = [
|
const visibilityFilters = [
|
||||||
match(teamMemberRole)
|
match(teamMemberRole)
|
||||||
.with(TeamMemberRole.ADMIN, () => ({
|
.with(TeamMemberRole.ADMIN, () => ({
|
||||||
@@ -198,7 +188,7 @@ export const findDocuments = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const whereClause: Prisma.DocumentWhereInput = {
|
const whereClause: Prisma.DocumentWhereInput = {
|
||||||
AND: [{ ...termFilters }, { ...filters }, { ...deletedFilter }, { ...searchFilter }],
|
AND: [{ ...termFilters }, { ...filters }, { ...deletedFilter }],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (period) {
|
if (period) {
|
||||||
|
|||||||
@@ -15,10 +15,9 @@ export type GetStatsInput = {
|
|||||||
user: User;
|
user: User;
|
||||||
team?: Omit<GetTeamCountsOption, 'createdAt'>;
|
team?: Omit<GetTeamCountsOption, 'createdAt'>;
|
||||||
period?: PeriodSelectorValue;
|
period?: PeriodSelectorValue;
|
||||||
search?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getStats = async ({ user, period, search, ...options }: GetStatsInput) => {
|
export const getStats = async ({ user, period, ...options }: GetStatsInput) => {
|
||||||
let createdAt: Prisma.DocumentWhereInput['createdAt'];
|
let createdAt: Prisma.DocumentWhereInput['createdAt'];
|
||||||
|
|
||||||
if (period) {
|
if (period) {
|
||||||
@@ -32,14 +31,8 @@ export const getStats = async ({ user, period, search, ...options }: GetStatsInp
|
|||||||
}
|
}
|
||||||
|
|
||||||
const [ownerCounts, notSignedCounts, hasSignedCounts] = await (options.team
|
const [ownerCounts, notSignedCounts, hasSignedCounts] = await (options.team
|
||||||
? getTeamCounts({
|
? getTeamCounts({ ...options.team, createdAt, currentUserEmail: user.email, userId: user.id })
|
||||||
...options.team,
|
: getCounts({ user, createdAt }));
|
||||||
createdAt,
|
|
||||||
currentUserEmail: user.email,
|
|
||||||
userId: user.id,
|
|
||||||
search,
|
|
||||||
})
|
|
||||||
: getCounts({ user, createdAt, search }));
|
|
||||||
|
|
||||||
const stats: Record<ExtendedDocumentStatus, number> = {
|
const stats: Record<ExtendedDocumentStatus, number> = {
|
||||||
[ExtendedDocumentStatus.DRAFT]: 0,
|
[ExtendedDocumentStatus.DRAFT]: 0,
|
||||||
@@ -79,18 +72,9 @@ export const getStats = async ({ user, period, search, ...options }: GetStatsInp
|
|||||||
type GetCountsOption = {
|
type GetCountsOption = {
|
||||||
user: User;
|
user: User;
|
||||||
createdAt: Prisma.DocumentWhereInput['createdAt'];
|
createdAt: Prisma.DocumentWhereInput['createdAt'];
|
||||||
search?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
|
const getCounts = async ({ user, createdAt }: GetCountsOption) => {
|
||||||
const searchFilter: Prisma.DocumentWhereInput = {
|
|
||||||
OR: [
|
|
||||||
{ title: { contains: search, mode: 'insensitive' } },
|
|
||||||
{ Recipient: { some: { name: { contains: search, mode: 'insensitive' } } } },
|
|
||||||
{ Recipient: { some: { email: { contains: search, mode: 'insensitive' } } } },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
// Owner counts.
|
// Owner counts.
|
||||||
prisma.document.groupBy({
|
prisma.document.groupBy({
|
||||||
@@ -103,7 +87,6 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
|
|||||||
createdAt,
|
createdAt,
|
||||||
teamId: null,
|
teamId: null,
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
AND: [searchFilter],
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
// Not signed counts.
|
// Not signed counts.
|
||||||
@@ -122,7 +105,6 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
createdAt,
|
createdAt,
|
||||||
AND: [searchFilter],
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
// Has signed counts.
|
// Has signed counts.
|
||||||
@@ -160,7 +142,6 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
AND: [searchFilter],
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
@@ -174,7 +155,6 @@ type GetTeamCountsOption = {
|
|||||||
userId: number;
|
userId: number;
|
||||||
createdAt: Prisma.DocumentWhereInput['createdAt'];
|
createdAt: Prisma.DocumentWhereInput['createdAt'];
|
||||||
currentTeamMemberRole?: TeamMemberRole;
|
currentTeamMemberRole?: TeamMemberRole;
|
||||||
search?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTeamCounts = async (options: GetTeamCountsOption) => {
|
const getTeamCounts = async (options: GetTeamCountsOption) => {
|
||||||
@@ -189,14 +169,6 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
|
|||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const searchFilter: Prisma.DocumentWhereInput = {
|
|
||||||
OR: [
|
|
||||||
{ title: { contains: options.search, mode: 'insensitive' } },
|
|
||||||
{ Recipient: { some: { name: { contains: options.search, mode: 'insensitive' } } } },
|
|
||||||
{ Recipient: { some: { email: { contains: options.search, mode: 'insensitive' } } } },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let ownerCountsWhereInput: Prisma.DocumentWhereInput = {
|
let ownerCountsWhereInput: Prisma.DocumentWhereInput = {
|
||||||
userId: userIdWhereClause,
|
userId: userIdWhereClause,
|
||||||
createdAt,
|
createdAt,
|
||||||
@@ -248,7 +220,6 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
...searchFilter,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (teamEmail) {
|
if (teamEmail) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// https://github.com/Hopding/pdf-lib/issues/20#issuecomment-412852821
|
// https://github.com/Hopding/pdf-lib/issues/20#issuecomment-412852821
|
||||||
import fontkit from '@pdf-lib/fontkit';
|
import fontkit from '@pdf-lib/fontkit';
|
||||||
import type { PDFDocument } from 'pdf-lib';
|
import { PDFDocument, RotationTypes, degrees, radiansToDegrees } from 'pdf-lib';
|
||||||
import { RotationTypes, degrees, radiansToDegrees } from 'pdf-lib';
|
|
||||||
import { P, match } from 'ts-pattern';
|
import { P, match } from 'ts-pattern';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -14,16 +13,7 @@ import { FieldType } from '@documenso/prisma/client';
|
|||||||
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
|
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
|
||||||
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
|
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
|
||||||
|
|
||||||
import {
|
import { ZCheckboxFieldMeta, ZRadioFieldMeta } from '../../types/field-meta';
|
||||||
ZCheckboxFieldMeta,
|
|
||||||
ZDateFieldMeta,
|
|
||||||
ZEmailFieldMeta,
|
|
||||||
ZInitialsFieldMeta,
|
|
||||||
ZNameFieldMeta,
|
|
||||||
ZNumberFieldMeta,
|
|
||||||
ZRadioFieldMeta,
|
|
||||||
ZTextFieldMeta,
|
|
||||||
} from '../../types/field-meta';
|
|
||||||
|
|
||||||
export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignature) => {
|
export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignature) => {
|
||||||
const fontCaveat = await fetch(process.env.FONT_CAVEAT_URI).then(async (res) =>
|
const fontCaveat = await fetch(process.env.FONT_CAVEAT_URI).then(async (res) =>
|
||||||
@@ -42,6 +32,7 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
|||||||
|
|
||||||
const minFontSize = isSignatureField ? MIN_HANDWRITING_FONT_SIZE : MIN_STANDARD_FONT_SIZE;
|
const minFontSize = isSignatureField ? MIN_HANDWRITING_FONT_SIZE : MIN_STANDARD_FONT_SIZE;
|
||||||
const maxFontSize = isSignatureField ? DEFAULT_HANDWRITING_FONT_SIZE : DEFAULT_STANDARD_FONT_SIZE;
|
const maxFontSize = isSignatureField ? DEFAULT_HANDWRITING_FONT_SIZE : DEFAULT_STANDARD_FONT_SIZE;
|
||||||
|
let fontSize = maxFontSize;
|
||||||
|
|
||||||
const page = pages.at(field.page - 1);
|
const page = pages.at(field.page - 1);
|
||||||
|
|
||||||
@@ -216,33 +207,16 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.otherwise((field) => {
|
.otherwise((field) => {
|
||||||
const fieldMetaParsers = {
|
|
||||||
[FieldType.TEXT]: ZTextFieldMeta,
|
|
||||||
[FieldType.NUMBER]: ZNumberFieldMeta,
|
|
||||||
[FieldType.DATE]: ZDateFieldMeta,
|
|
||||||
[FieldType.EMAIL]: ZEmailFieldMeta,
|
|
||||||
[FieldType.NAME]: ZNameFieldMeta,
|
|
||||||
[FieldType.INITIALS]: ZInitialsFieldMeta,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
const Parser = fieldMetaParsers[field.type as keyof typeof fieldMetaParsers];
|
|
||||||
const meta = Parser ? Parser.safeParse(field.fieldMeta) : null;
|
|
||||||
|
|
||||||
const customFontSize = meta?.success && meta.data.fontSize ? meta.data.fontSize : null;
|
|
||||||
const longestLineInTextForWidth = field.customText
|
const longestLineInTextForWidth = field.customText
|
||||||
.split('\n')
|
.split('\n')
|
||||||
.sort((a, b) => b.length - a.length)[0];
|
.sort((a, b) => b.length - a.length)[0];
|
||||||
|
|
||||||
let fontSize = customFontSize || maxFontSize;
|
|
||||||
let textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
|
let textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
|
||||||
const textHeight = font.heightAtSize(fontSize);
|
const textHeight = font.heightAtSize(fontSize);
|
||||||
|
|
||||||
if (!customFontSize) {
|
const scalingFactor = Math.min(fieldWidth / textWidth, fieldHeight / textHeight, 1);
|
||||||
const scalingFactor = Math.min(fieldWidth / textWidth, fieldHeight / textHeight, 1);
|
|
||||||
fontSize = Math.max(Math.min(fontSize * scalingFactor, maxFontSize), minFontSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fontSize = Math.max(Math.min(fontSize * scalingFactor, maxFontSize), minFontSize);
|
||||||
textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
|
textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
|
||||||
|
|
||||||
let textX = fieldX + (fieldWidth - textWidth) / 2;
|
let textX = fieldX + (fieldWidth - textWidth) / 2;
|
||||||
@@ -276,6 +250,17 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
|||||||
return pdf;
|
return pdf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const insertFieldInPDFBytes = async (
|
||||||
|
pdf: ArrayBuffer | Uint8Array | string,
|
||||||
|
field: FieldWithSignature,
|
||||||
|
) => {
|
||||||
|
const pdfDoc = await PDFDocument.load(pdf);
|
||||||
|
|
||||||
|
await insertFieldInPDF(pdfDoc, field);
|
||||||
|
|
||||||
|
return await pdfDoc.save();
|
||||||
|
};
|
||||||
|
|
||||||
const adjustPositionForRotation = (
|
const adjustPositionForRotation = (
|
||||||
pageWidth: number,
|
pageWidth: number,
|
||||||
pageHeight: number,
|
pageHeight: number,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ export async function insertTextInPDF(
|
|||||||
positionY: number,
|
positionY: number,
|
||||||
page = 0,
|
page = 0,
|
||||||
useHandwritingFont = true,
|
useHandwritingFont = true,
|
||||||
customFontSize?: number,
|
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
// Fetch the font file from the public URL.
|
// Fetch the font file from the public URL.
|
||||||
const fontResponse = await fetch(CAVEAT_FONT_PATH());
|
const fontResponse = await fetch(CAVEAT_FONT_PATH());
|
||||||
@@ -25,7 +24,7 @@ export async function insertTextInPDF(
|
|||||||
const pages = pdfDoc.getPages();
|
const pages = pdfDoc.getPages();
|
||||||
const pdfPage = pages[page];
|
const pdfPage = pages[page];
|
||||||
|
|
||||||
const textSize = customFontSize || (useHandwritingFont ? 50 : 15);
|
const textSize = useHandwritingFont ? 50 : 15;
|
||||||
const textWidth = font.widthOfTextAtSize(text, textSize);
|
const textWidth = font.widthOfTextAtSize(text, textSize);
|
||||||
const textHeight = font.heightAtSize(textSize);
|
const textHeight = font.heightAtSize(textSize);
|
||||||
const fieldSize = { width: 250, height: 64 };
|
const fieldSize = { width: 250, height: 64 };
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const createApiToken = async ({
|
|||||||
name: tokenName,
|
name: tokenName,
|
||||||
token: hashedToken,
|
token: hashedToken,
|
||||||
expires: expiresIn ? DateTime.now().plus(timeConstantsRecords[expiresIn]).toJSDate() : null,
|
expires: expiresIn ? DateTime.now().plus(timeConstantsRecords[expiresIn]).toJSDate() : null,
|
||||||
userId,
|
userId: teamId ? null : userId,
|
||||||
teamId,
|
teamId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ export const deleteTokenById = async ({ id, userId, teamId }: DeleteTokenByIdOpt
|
|||||||
return await prisma.apiToken.delete({
|
return await prisma.apiToken.delete({
|
||||||
where: {
|
where: {
|
||||||
id,
|
id,
|
||||||
teamId: teamId ?? null,
|
userId: teamId ? null : userId,
|
||||||
|
teamId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ export const getUserTokens = async ({ userId }: GetUserTokensOptions) => {
|
|||||||
return await prisma.apiToken.findMany({
|
return await prisma.apiToken.findMany({
|
||||||
where: {
|
where: {
|
||||||
userId,
|
userId,
|
||||||
teamId: null,
|
|
||||||
},
|
},
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ export const getApiTokenByToken = async ({ token }: { token: string }) => {
|
|||||||
throw new Error('Expired token');
|
throw new Error('Expired token');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle a silly choice from many moons ago
|
if (apiToken.team) {
|
||||||
if (apiToken.team && !apiToken.user) {
|
|
||||||
apiToken.user = await prisma.user.findFirst({
|
apiToken.user = await prisma.user.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: apiToken.team.ownerUserId,
|
id: apiToken.team.ownerUserId,
|
||||||
@@ -34,13 +33,9 @@ export const getApiTokenByToken = async ({ token }: { token: string }) => {
|
|||||||
|
|
||||||
const { user } = apiToken;
|
const { user } = apiToken;
|
||||||
|
|
||||||
// This will never happen but we need to narrow types
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new Error('Invalid token');
|
throw new Error('Invalid token');
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return { ...apiToken, user };
|
||||||
...apiToken,
|
|
||||||
user,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
RecipientRole,
|
RecipientRole,
|
||||||
SendStatus,
|
SendStatus,
|
||||||
SigningStatus,
|
SigningStatus,
|
||||||
WebhookTriggerEvents,
|
|
||||||
} from '@documenso/prisma/client';
|
} from '@documenso/prisma/client';
|
||||||
import type { TSignFieldWithTokenMutationSchema } from '@documenso/trpc/server/field-router/schema';
|
import type { TSignFieldWithTokenMutationSchema } from '@documenso/trpc/server/field-router/schema';
|
||||||
|
|
||||||
@@ -40,7 +39,6 @@ import {
|
|||||||
import { formatDocumentsPath } from '../../utils/teams';
|
import { formatDocumentsPath } from '../../utils/teams';
|
||||||
import { sendDocument } from '../document/send-document';
|
import { sendDocument } from '../document/send-document';
|
||||||
import { validateFieldAuth } from '../document/validate-field-auth';
|
import { validateFieldAuth } from '../document/validate-field-auth';
|
||||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
|
||||||
|
|
||||||
export type CreateDocumentFromDirectTemplateOptions = {
|
export type CreateDocumentFromDirectTemplateOptions = {
|
||||||
directRecipientName?: string;
|
directRecipientName?: string;
|
||||||
@@ -555,23 +553,6 @@ export const createDocumentFromDirectTemplate = async ({
|
|||||||
teamId: template.teamId || undefined,
|
teamId: template.teamId || undefined,
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
});
|
});
|
||||||
|
|
||||||
const updatedDocument = await prisma.document.findFirstOrThrow({
|
|
||||||
where: {
|
|
||||||
id: documentId,
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
documentData: true,
|
|
||||||
Recipient: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await triggerWebhook({
|
|
||||||
event: WebhookTriggerEvents.DOCUMENT_SIGNED,
|
|
||||||
data: updatedDocument,
|
|
||||||
userId: updatedDocument.userId,
|
|
||||||
teamId: updatedDocument.teamId ?? undefined,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[CREATE_DOCUMENT_FROM_DIRECT_TEMPLATE]:', err);
|
console.error('[CREATE_DOCUMENT_FROM_DIRECT_TEMPLATE]:', err);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { nanoid } from '@documenso/lib/universal/id';
|
import { nanoid } from '@documenso/lib/universal/id';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
|
import type { DocumentSigningOrder, Field } from '@documenso/prisma/client';
|
||||||
import {
|
import {
|
||||||
DocumentSigningOrder,
|
|
||||||
DocumentSource,
|
DocumentSource,
|
||||||
type Field,
|
|
||||||
type Recipient,
|
type Recipient,
|
||||||
RecipientRole,
|
RecipientRole,
|
||||||
SendStatus,
|
SendStatus,
|
||||||
@@ -154,7 +153,7 @@ export const createDocumentFromTemplate = async ({
|
|||||||
const document = await tx.document.create({
|
const document = await tx.document.create({
|
||||||
data: {
|
data: {
|
||||||
source: DocumentSource.TEMPLATE,
|
source: DocumentSource.TEMPLATE,
|
||||||
externalId: externalId || template.externalId,
|
externalId,
|
||||||
templateId: template.id,
|
templateId: template.id,
|
||||||
userId,
|
userId,
|
||||||
teamId: template.teamId,
|
teamId: template.teamId,
|
||||||
@@ -173,9 +172,7 @@ export const createDocumentFromTemplate = async ({
|
|||||||
dateFormat: override?.dateFormat || template.templateMeta?.dateFormat,
|
dateFormat: override?.dateFormat || template.templateMeta?.dateFormat,
|
||||||
redirectUrl: override?.redirectUrl || template.templateMeta?.redirectUrl,
|
redirectUrl: override?.redirectUrl || template.templateMeta?.redirectUrl,
|
||||||
signingOrder:
|
signingOrder:
|
||||||
override?.signingOrder ||
|
override?.signingOrder || template.templateMeta?.signingOrder || undefined,
|
||||||
template.templateMeta?.signingOrder ||
|
|
||||||
DocumentSigningOrder.PARALLEL,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Recipient: {
|
Recipient: {
|
||||||
|
|||||||
@@ -51,11 +51,6 @@ export const findTemplates = async ({
|
|||||||
},
|
},
|
||||||
Field: true,
|
Field: true,
|
||||||
Recipient: true,
|
Recipient: true,
|
||||||
templateMeta: {
|
|
||||||
select: {
|
|
||||||
signingOrder: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
directLink: {
|
directLink: {
|
||||||
select: {
|
select: {
|
||||||
token: true,
|
token: true,
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export const updateTemplateSettings = async ({
|
|||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
title: data.title,
|
title: data.title,
|
||||||
externalId: data.externalId,
|
externalId: data.externalId || null,
|
||||||
type: data.type,
|
type: data.type,
|
||||||
publicDescription: data.publicDescription,
|
publicDescription: data.publicDescription,
|
||||||
publicTitle: data.publicTitle,
|
publicTitle: data.publicTitle,
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# Compiled translations.
|
|
||||||
*.js
|
|
||||||
@@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
"Project-Id-Version: documenso-app\n"
|
"Project-Id-Version: documenso-app\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2024-10-18 04:04\n"
|
"PO-Revision-Date: 2024-09-16 16:03\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
@@ -73,11 +73,11 @@ msgstr "Fügen Sie dem Dokument eine externe ID hinzu. Diese kann verwendet werd
|
|||||||
msgid "Add an external ID to the template. This can be used to identify in external systems."
|
msgid "Add an external ID to the template. This can be used to identify in external systems."
|
||||||
msgstr "Fügen Sie der Vorlage eine externe ID hinzu. Diese kann zur Identifizierung in externen Systemen verwendet werden."
|
msgstr "Fügen Sie der Vorlage eine externe ID hinzu. Diese kann zur Identifizierung in externen Systemen verwendet werden."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:187
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:177
|
||||||
msgid "Add another option"
|
msgid "Add another option"
|
||||||
msgstr "Weitere Option hinzufügen"
|
msgstr "Weitere Option hinzufügen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:232
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:230
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:167
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:167
|
||||||
msgid "Add another value"
|
msgid "Add another value"
|
||||||
msgstr "Weiteren Wert hinzufügen"
|
msgstr "Weiteren Wert hinzufügen"
|
||||||
@@ -98,11 +98,11 @@ msgstr "Platzhalterempfänger hinzufügen"
|
|||||||
msgid "Add Signer"
|
msgid "Add Signer"
|
||||||
msgstr "Unterzeichner hinzufügen"
|
msgstr "Unterzeichner hinzufügen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:73
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:70
|
||||||
msgid "Add text"
|
msgid "Add text"
|
||||||
msgstr "Text hinzufügen"
|
msgstr "Text hinzufügen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:78
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:75
|
||||||
msgid "Add text to the field"
|
msgid "Add text to the field"
|
||||||
msgstr "Text zum Feld hinzufügen"
|
msgstr "Text zum Feld hinzufügen"
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ msgstr "Admin"
|
|||||||
msgid "Advanced Options"
|
msgid "Advanced Options"
|
||||||
msgstr "Erweiterte Optionen"
|
msgstr "Erweiterte Optionen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:570
|
#: packages/ui/primitives/document-flow/add-fields.tsx:527
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:402
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:402
|
||||||
msgid "Advanced settings"
|
msgid "Advanced settings"
|
||||||
msgstr "Erweiterte Einstellungen"
|
msgstr "Erweiterte Einstellungen"
|
||||||
@@ -140,15 +140,15 @@ msgstr "Genehmiger"
|
|||||||
msgid "Approving"
|
msgid "Approving"
|
||||||
msgstr "Genehmigung"
|
msgstr "Genehmigung"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:377
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:276
|
||||||
msgid "Black"
|
msgid "Black"
|
||||||
msgstr "Schwarz"
|
msgstr "Schwarz"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:391
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:290
|
||||||
msgid "Blue"
|
msgid "Blue"
|
||||||
msgstr "Blau"
|
msgstr "Blau"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:356
|
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:287
|
||||||
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:58
|
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:58
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Abbrechen"
|
msgstr "Abbrechen"
|
||||||
@@ -159,7 +159,7 @@ msgstr "Unterzeichner kann nicht entfernt werden"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:221
|
#: packages/ui/primitives/document-flow/add-signers.tsx:221
|
||||||
#~ msgid "Cannot update signer because they have already signed a field"
|
#~ msgid "Cannot update signer because they have already signed a field"
|
||||||
#~ msgstr "Cannot update signer because they have already signed a field"
|
#~ msgstr ""
|
||||||
|
|
||||||
#: packages/lib/constants/recipient-roles.ts:17
|
#: packages/lib/constants/recipient-roles.ts:17
|
||||||
msgid "Cc"
|
msgid "Cc"
|
||||||
@@ -174,16 +174,16 @@ msgstr "CC"
|
|||||||
msgid "CC'd"
|
msgid "CC'd"
|
||||||
msgstr "CC'd"
|
msgstr "CC'd"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:86
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:83
|
||||||
msgid "Character Limit"
|
msgid "Character Limit"
|
||||||
msgstr "Zeichenbeschränkung"
|
msgstr "Zeichenbeschränkung"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1026
|
#: packages/ui/primitives/document-flow/add-fields.tsx:950
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:788
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:788
|
||||||
msgid "Checkbox"
|
msgid "Checkbox"
|
||||||
msgstr "Checkbox"
|
msgstr "Checkbox"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:197
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:195
|
||||||
msgid "Checkbox values"
|
msgid "Checkbox values"
|
||||||
msgstr "Checkbox-Werte"
|
msgstr "Checkbox-Werte"
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ msgstr "Checkbox-Werte"
|
|||||||
msgid "Clear filters"
|
msgid "Clear filters"
|
||||||
msgstr "Filter löschen"
|
msgstr "Filter löschen"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:411
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:310
|
||||||
msgid "Clear Signature"
|
msgid "Clear Signature"
|
||||||
msgstr "Unterschrift löschen"
|
msgstr "Unterschrift löschen"
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ msgstr "Schließen"
|
|||||||
msgid "Configure Direct Recipient"
|
msgid "Configure Direct Recipient"
|
||||||
msgstr "Direkten Empfänger konfigurieren"
|
msgstr "Direkten Empfänger konfigurieren"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:571
|
#: packages/ui/primitives/document-flow/add-fields.tsx:528
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:403
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:403
|
||||||
msgid "Configure the {0} field"
|
msgid "Configure the {0} field"
|
||||||
msgstr "Konfigurieren Sie das Feld {0}"
|
msgstr "Konfigurieren Sie das Feld {0}"
|
||||||
@@ -224,7 +224,7 @@ msgstr "In die Zwischenablage kopiert"
|
|||||||
msgid "Custom Text"
|
msgid "Custom Text"
|
||||||
msgstr "Benutzerdefinierter Text"
|
msgstr "Benutzerdefinierter Text"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:922
|
#: packages/ui/primitives/document-flow/add-fields.tsx:846
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:684
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:684
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Datum"
|
msgstr "Datum"
|
||||||
@@ -256,16 +256,16 @@ msgstr "Herunterladen"
|
|||||||
msgid "Drag & drop your PDF here."
|
msgid "Drag & drop your PDF here."
|
||||||
msgstr "Ziehen Sie Ihr PDF hierher."
|
msgstr "Ziehen Sie Ihr PDF hierher."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1052
|
#: packages/ui/primitives/document-flow/add-fields.tsx:976
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:814
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:814
|
||||||
msgid "Dropdown"
|
msgid "Dropdown"
|
||||||
msgstr "Dropdown"
|
msgstr "Dropdown"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:158
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:148
|
||||||
msgid "Dropdown options"
|
msgid "Dropdown options"
|
||||||
msgstr "Dropdown-Optionen"
|
msgstr "Dropdown-Optionen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:870
|
#: packages/ui/primitives/document-flow/add-fields.tsx:794
|
||||||
#: packages/ui/primitives/document-flow/add-signature.tsx:272
|
#: packages/ui/primitives/document-flow/add-signature.tsx:272
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:500
|
#: packages/ui/primitives/document-flow/add-signers.tsx:500
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
|
||||||
@@ -278,10 +278,6 @@ msgstr "E-Mail"
|
|||||||
msgid "Email Options"
|
msgid "Email Options"
|
||||||
msgstr "E-Mail-Optionen"
|
msgstr "E-Mail-Optionen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1117
|
|
||||||
msgid "Empty field"
|
|
||||||
msgstr "Leeres Feld"
|
|
||||||
|
|
||||||
#: packages/lib/constants/template.ts:8
|
#: packages/lib/constants/template.ts:8
|
||||||
msgid "Enable Direct Link Signing"
|
msgid "Enable Direct Link Signing"
|
||||||
msgstr "Direktlink-Signierung aktivieren"
|
msgstr "Direktlink-Signierung aktivieren"
|
||||||
@@ -291,15 +287,11 @@ msgstr "Direktlink-Signierung aktivieren"
|
|||||||
msgid "Enable signing order"
|
msgid "Enable signing order"
|
||||||
msgstr "Aktiviere die Signaturreihenfolge"
|
msgstr "Aktiviere die Signaturreihenfolge"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:790
|
|
||||||
msgid "Enable Typed Signatures"
|
|
||||||
msgstr "Aktivieren Sie getippte Unterschriften"
|
|
||||||
|
|
||||||
#: packages/ui/primitives/document-password-dialog.tsx:84
|
#: packages/ui/primitives/document-password-dialog.tsx:84
|
||||||
msgid "Enter password"
|
msgid "Enter password"
|
||||||
msgstr "Passwort eingeben"
|
msgstr "Passwort eingeben"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:257
|
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:216
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr "Fehler"
|
msgstr "Fehler"
|
||||||
|
|
||||||
@@ -308,44 +300,26 @@ msgstr "Fehler"
|
|||||||
msgid "External ID"
|
msgid "External ID"
|
||||||
msgstr "Externe ID"
|
msgstr "Externe ID"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:258
|
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:217
|
||||||
msgid "Failed to save settings."
|
msgid "Failed to save settings."
|
||||||
msgstr "Einstellungen konnten nicht gespeichert werden."
|
msgstr "Einstellungen konnten nicht gespeichert werden."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:93
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:90
|
||||||
msgid "Field character limit"
|
msgid "Field character limit"
|
||||||
msgstr "Zeichenbeschränkung des Feldes"
|
msgstr "Zeichenbeschränkung des Feldes"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:62
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:107
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:44
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:44
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/name-field.tsx:44
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:130
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:107
|
|
||||||
msgid "Field font size"
|
|
||||||
msgstr "Feldschriftgröße"
|
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:110
|
|
||||||
msgid "Field format"
|
msgid "Field format"
|
||||||
msgstr "Feldformat"
|
msgstr "Feldformat"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:53
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:50
|
||||||
msgid "Field label"
|
msgid "Field label"
|
||||||
msgstr "Feldbeschriftung"
|
msgstr "Feldbeschriftung"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:65
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:62
|
||||||
msgid "Field placeholder"
|
msgid "Field placeholder"
|
||||||
msgstr "Feldplatzhalter"
|
msgstr "Feldplatzhalter"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:56
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:38
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:38
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/name-field.tsx:38
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:124
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:101
|
|
||||||
msgid "Font Size"
|
|
||||||
msgstr "Schriftgröße"
|
|
||||||
|
|
||||||
#: packages/ui/components/document/document-global-auth-action-select.tsx:64
|
#: packages/ui/components/document/document-global-auth-action-select.tsx:64
|
||||||
msgid "Global recipient action authentication"
|
msgid "Global recipient action authentication"
|
||||||
msgstr "Globale Empfängerauthentifizierung"
|
msgstr "Globale Empfängerauthentifizierung"
|
||||||
@@ -354,7 +328,7 @@ msgstr "Globale Empfängerauthentifizierung"
|
|||||||
msgid "Go Back"
|
msgid "Go Back"
|
||||||
msgstr "Zurück"
|
msgstr "Zurück"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:398
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:297
|
||||||
msgid "Green"
|
msgid "Green"
|
||||||
msgstr "Grün"
|
msgstr "Grün"
|
||||||
|
|
||||||
@@ -383,9 +357,9 @@ msgstr "Ich bin verpflichtet, eine Kopie dieses Dokuments zu erhalten"
|
|||||||
msgid "Inherit authentication method"
|
msgid "Inherit authentication method"
|
||||||
msgstr "Authentifizierungsmethode erben"
|
msgstr "Authentifizierungsmethode erben"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:67
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:64
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:72
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:69
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:48
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:45
|
||||||
msgid "Label"
|
msgid "Label"
|
||||||
msgstr "Beschriftung"
|
msgstr "Beschriftung"
|
||||||
|
|
||||||
@@ -393,7 +367,7 @@ msgstr "Beschriftung"
|
|||||||
msgid "Manager"
|
msgid "Manager"
|
||||||
msgstr "Manager"
|
msgstr "Manager"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:188
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:168
|
||||||
msgid "Max"
|
msgid "Max"
|
||||||
msgstr "Max"
|
msgstr "Max"
|
||||||
|
|
||||||
@@ -406,11 +380,11 @@ msgstr "Mitglied"
|
|||||||
msgid "Message <0>(Optional)</0>"
|
msgid "Message <0>(Optional)</0>"
|
||||||
msgstr "Nachricht <0>(Optional)</0>"
|
msgstr "Nachricht <0>(Optional)</0>"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:176
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:156
|
||||||
msgid "Min"
|
msgid "Min"
|
||||||
msgstr "Min"
|
msgstr "Min"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:896
|
#: packages/ui/primitives/document-flow/add-fields.tsx:820
|
||||||
#: packages/ui/primitives/document-flow/add-signature.tsx:298
|
#: packages/ui/primitives/document-flow/add-signature.tsx:298
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:535
|
#: packages/ui/primitives/document-flow/add-signers.tsx:535
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:541
|
#: packages/ui/primitives/document-flow/add-signers.tsx:541
|
||||||
@@ -432,12 +406,12 @@ msgstr "Muss unterzeichnen"
|
|||||||
msgid "Needs to view"
|
msgid "Needs to view"
|
||||||
msgstr "Muss sehen"
|
msgstr "Muss sehen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:680
|
#: packages/ui/primitives/document-flow/add-fields.tsx:631
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:497
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:497
|
||||||
msgid "No recipient matching this description was found."
|
msgid "No recipient matching this description was found."
|
||||||
msgstr "Kein passender Empfänger mit dieser Beschreibung gefunden."
|
msgstr "Kein passender Empfänger mit dieser Beschreibung gefunden."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:696
|
#: packages/ui/primitives/document-flow/add-fields.tsx:647
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:513
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:513
|
||||||
msgid "No recipients with this role"
|
msgid "No recipients with this role"
|
||||||
msgstr "Keine Empfänger mit dieser Rolle"
|
msgstr "Keine Empfänger mit dieser Rolle"
|
||||||
@@ -462,12 +436,12 @@ msgstr "Kein Unterschriftsfeld gefunden"
|
|||||||
msgid "No value found."
|
msgid "No value found."
|
||||||
msgstr "Kein Wert gefunden."
|
msgstr "Kein Wert gefunden."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:974
|
#: packages/ui/primitives/document-flow/add-fields.tsx:898
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:736
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:736
|
||||||
msgid "Number"
|
msgid "Number"
|
||||||
msgstr "Nummer"
|
msgstr "Nummer"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:103
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:100
|
||||||
msgid "Number format"
|
msgid "Number format"
|
||||||
msgstr "Zahlenformat"
|
msgstr "Zahlenformat"
|
||||||
|
|
||||||
@@ -487,17 +461,17 @@ msgstr "Seite {0} von {1}"
|
|||||||
msgid "Password Required"
|
msgid "Password Required"
|
||||||
msgstr "Passwort erforderlich"
|
msgstr "Passwort erforderlich"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:156
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:154
|
||||||
msgid "Pick a number"
|
msgid "Pick a number"
|
||||||
msgstr "Wählen Sie eine Zahl"
|
msgstr "Wählen Sie eine Zahl"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:79
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:76
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:84
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:81
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:60
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:57
|
||||||
msgid "Placeholder"
|
msgid "Placeholder"
|
||||||
msgstr "Platzhalter"
|
msgstr "Platzhalter"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1000
|
#: packages/ui/primitives/document-flow/add-fields.tsx:924
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:762
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:762
|
||||||
msgid "Radio"
|
msgid "Radio"
|
||||||
msgstr "Radio"
|
msgstr "Radio"
|
||||||
@@ -506,11 +480,11 @@ msgstr "Radio"
|
|||||||
msgid "Radio values"
|
msgid "Radio values"
|
||||||
msgstr "Radio-Werte"
|
msgstr "Radio-Werte"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:186
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:184
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:147
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:137
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:156
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:136
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:122
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:122
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:133
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:114
|
||||||
msgid "Read only"
|
msgid "Read only"
|
||||||
msgstr "Nur lesen"
|
msgstr "Nur lesen"
|
||||||
|
|
||||||
@@ -524,7 +498,7 @@ msgstr "Erhält Kopie"
|
|||||||
msgid "Recipient action authentication"
|
msgid "Recipient action authentication"
|
||||||
msgstr "Empfängeraktion Authentifizierung"
|
msgstr "Empfängeraktion Authentifizierung"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:384
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:283
|
||||||
msgid "Red"
|
msgid "Red"
|
||||||
msgstr "Rot"
|
msgstr "Rot"
|
||||||
|
|
||||||
@@ -533,15 +507,15 @@ msgstr "Rot"
|
|||||||
msgid "Redirect URL"
|
msgid "Redirect URL"
|
||||||
msgstr "Weiterleitungs-URL"
|
msgstr "Weiterleitungs-URL"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1104
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1027
|
||||||
msgid "Remove"
|
msgid "Remove"
|
||||||
msgstr "Entfernen"
|
msgstr "Entfernen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:176
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:174
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:137
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:127
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:146
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:126
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:112
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:112
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:123
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:104
|
||||||
msgid "Required field"
|
msgid "Required field"
|
||||||
msgstr "Pflichtfeld"
|
msgstr "Pflichtfeld"
|
||||||
|
|
||||||
@@ -549,7 +523,7 @@ msgstr "Pflichtfeld"
|
|||||||
msgid "Rows per page"
|
msgid "Rows per page"
|
||||||
msgstr "Zeilen pro Seite"
|
msgstr "Zeilen pro Seite"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:355
|
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:286
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Speichern"
|
msgstr "Speichern"
|
||||||
|
|
||||||
@@ -561,7 +535,7 @@ msgstr "Vorlage speichern"
|
|||||||
msgid "Search languages..."
|
msgid "Search languages..."
|
||||||
msgstr "Sprachen suchen..."
|
msgstr "Sprachen suchen..."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:115
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:105
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr "Auswählen"
|
msgstr "Auswählen"
|
||||||
|
|
||||||
@@ -569,11 +543,11 @@ msgstr "Auswählen"
|
|||||||
msgid "Select an option"
|
msgid "Select an option"
|
||||||
msgstr "Option auswählen"
|
msgstr "Option auswählen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:139
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:137
|
||||||
msgid "Select at least"
|
msgid "Select at least"
|
||||||
msgstr "Wählen Sie mindestens"
|
msgstr "Wählen Sie mindestens"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:105
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:95
|
||||||
msgid "Select default option"
|
msgid "Select default option"
|
||||||
msgstr "Standardoption auswählen"
|
msgstr "Standardoption auswählen"
|
||||||
|
|
||||||
@@ -604,7 +578,7 @@ msgstr "Erweiterte Einstellungen anzeigen"
|
|||||||
msgid "Sign"
|
msgid "Sign"
|
||||||
msgstr "Unterschreiben"
|
msgstr "Unterschreiben"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:818
|
#: packages/ui/primitives/document-flow/add-fields.tsx:742
|
||||||
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
||||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:580
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:580
|
||||||
@@ -652,7 +626,7 @@ msgstr "Einreichen"
|
|||||||
msgid "Template title"
|
msgid "Template title"
|
||||||
msgstr "Vorlagentitel"
|
msgstr "Vorlagentitel"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:948
|
#: packages/ui/primitives/document-flow/add-fields.tsx:872
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:710
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:710
|
||||||
msgid "Text"
|
msgid "Text"
|
||||||
msgstr "Text"
|
msgstr "Text"
|
||||||
@@ -713,7 +687,7 @@ msgstr "Der Name des Unterzeichners"
|
|||||||
msgid "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
|
msgid "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
|
||||||
msgstr "Dies kann überschrieben werden, indem die Authentifizierungsanforderungen im nächsten Schritt direkt für jeden Empfänger festgelegt werden."
|
msgstr "Dies kann überschrieben werden, indem die Authentifizierungsanforderungen im nächsten Schritt direkt für jeden Empfänger festgelegt werden."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:752
|
#: packages/ui/primitives/document-flow/add-fields.tsx:703
|
||||||
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
|
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
|
||||||
msgstr "Dieses Dokument wurde bereits an diesen Empfänger gesendet. Sie können diesen Empfänger nicht mehr bearbeiten."
|
msgstr "Dieses Dokument wurde bereits an diesen Empfänger gesendet. Sie können diesen Empfänger nicht mehr bearbeiten."
|
||||||
|
|
||||||
@@ -725,17 +699,17 @@ msgstr "Dieses Dokument ist durch ein Passwort geschützt. Bitte geben Sie das P
|
|||||||
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
||||||
msgstr "Dieses Feld kann nicht geändert oder gelöscht werden. Wenn Sie den direkten Link dieser Vorlage teilen oder zu Ihrem öffentlichen Profil hinzufügen, kann jeder, der darauf zugreift, seinen Namen und seine E-Mail-Adresse eingeben und die ihm zugewiesenen Felder ausfüllen."
|
msgstr "Dieses Feld kann nicht geändert oder gelöscht werden. Wenn Sie den direkten Link dieser Vorlage teilen oder zu Ihrem öffentlichen Profil hinzufügen, kann jeder, der darauf zugreift, seinen Namen und seine E-Mail-Adresse eingeben und die ihm zugewiesenen Felder ausfüllen."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1084
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1007
|
||||||
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
|
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
|
||||||
msgstr "Dieser Empfänger kann nicht mehr bearbeitet werden, da er ein Feld unterschrieben oder das Dokument abgeschlossen hat."
|
msgstr ""
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:165
|
#: packages/ui/primitives/document-flow/add-signers.tsx:195
|
||||||
#~ msgid "This signer has already received the document."
|
#~ msgid "This signer has already received the document."
|
||||||
#~ msgstr "This signer has already received the document."
|
#~ msgstr "Dieser Unterzeichner hat das Dokument bereits erhalten."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:194
|
#: packages/ui/primitives/document-flow/add-signers.tsx:194
|
||||||
msgid "This signer has already signed the document."
|
msgid "This signer has already signed the document."
|
||||||
msgstr "Dieser Unterzeichner hat das Dokument bereits unterschrieben."
|
msgstr ""
|
||||||
|
|
||||||
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:48
|
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:48
|
||||||
msgid "This will override any global settings."
|
msgid "This will override any global settings."
|
||||||
@@ -750,7 +724,7 @@ msgstr "Zeitzone"
|
|||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1067
|
#: packages/ui/primitives/document-flow/add-fields.tsx:990
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:828
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:828
|
||||||
msgid "To proceed further, please set at least one value for the {0} field."
|
msgid "To proceed further, please set at least one value for the {0} field."
|
||||||
msgstr "Um fortzufahren, legen Sie bitte mindestens einen Wert für das Feld {0} fest."
|
msgstr "Um fortzufahren, legen Sie bitte mindestens einen Wert für das Feld {0} fest."
|
||||||
@@ -771,13 +745,13 @@ msgstr "Upgrade"
|
|||||||
msgid "Upload Template Document"
|
msgid "Upload Template Document"
|
||||||
msgstr "Vorlagendokument hochladen"
|
msgstr "Vorlagendokument hochladen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:132
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:130
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:167
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:147
|
||||||
msgid "Validation"
|
msgid "Validation"
|
||||||
msgstr "Validierung"
|
msgstr "Validierung"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:91
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:88
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:96
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:93
|
||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Wert"
|
msgstr "Wert"
|
||||||
|
|
||||||
@@ -816,4 +790,3 @@ msgstr "Sie können derzeit keine Dokumente hochladen."
|
|||||||
#: packages/ui/primitives/document-dropzone.tsx:69
|
#: packages/ui/primitives/document-dropzone.tsx:69
|
||||||
msgid "You have reached your document limit."
|
msgid "You have reached your document limit."
|
||||||
msgstr "Sie haben Ihr Dokumentenlimit erreicht."
|
msgstr "Sie haben Ihr Dokumentenlimit erreicht."
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
"Project-Id-Version: documenso-app\n"
|
"Project-Id-Version: documenso-app\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2024-10-18 04:04\n"
|
"PO-Revision-Date: 2024-09-16 14:04\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
@@ -387,7 +387,7 @@ msgstr "Unsere selbstgehostete Option ist ideal für kleine Teams und Einzelpers
|
|||||||
|
|
||||||
#: apps/marketing/src/app/(marketing)/open/data.ts:25
|
#: apps/marketing/src/app/(marketing)/open/data.ts:25
|
||||||
#~ msgid "Part-Time"
|
#~ msgid "Part-Time"
|
||||||
#~ msgstr "Part-Time"
|
#~ msgstr "Teilzeit"
|
||||||
|
|
||||||
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:151
|
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:151
|
||||||
msgid "Premium Profile Name"
|
msgid "Premium Profile Name"
|
||||||
@@ -618,4 +618,3 @@ msgstr "Sie können Documenso kostenlos selbst hosten oder unsere sofort einsatz
|
|||||||
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
||||||
msgid "Your browser does not support the video tag."
|
msgid "Your browser does not support the video tag."
|
||||||
msgstr "Ihr Browser unterstützt das Video-Tag nicht."
|
msgstr "Ihr Browser unterstützt das Video-Tag nicht."
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ msgstr ""
|
|||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
"Project-Id-Version: documenso-app\n"
|
"Project-Id-Version: documenso-app\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"PO-Revision-Date: 2024-10-18 04:04\n"
|
"PO-Revision-Date: 2024-09-16 16:03\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language-Team: German\n"
|
"Language-Team: German\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
@@ -220,7 +220,7 @@ msgstr "Aktive Abonnements"
|
|||||||
msgid "Add"
|
msgid "Add"
|
||||||
msgstr "Hinzufügen"
|
msgstr "Hinzufügen"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:175
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:157
|
||||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:87
|
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:87
|
||||||
msgid "Add all relevant fields for each recipient."
|
msgid "Add all relevant fields for each recipient."
|
||||||
msgstr "Fügen Sie alle relevanten Felder für jeden Empfänger hinzu."
|
msgstr "Fügen Sie alle relevanten Felder für jeden Empfänger hinzu."
|
||||||
@@ -241,7 +241,7 @@ msgstr "Fügen Sie einen Authenticator hinzu, um als sekundäre Authentifizierun
|
|||||||
msgid "Add email"
|
msgid "Add email"
|
||||||
msgstr "E-Mail hinzufügen"
|
msgstr "E-Mail hinzufügen"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:174
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:156
|
||||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:86
|
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:86
|
||||||
msgid "Add Fields"
|
msgid "Add Fields"
|
||||||
msgstr "Felder hinzufügen"
|
msgstr "Felder hinzufügen"
|
||||||
@@ -263,11 +263,11 @@ msgstr "Passkey hinzufügen"
|
|||||||
msgid "Add Placeholders"
|
msgid "Add Placeholders"
|
||||||
msgstr "Platzhalter hinzufügen"
|
msgstr "Platzhalter hinzufügen"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:169
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:151
|
||||||
msgid "Add Signers"
|
msgid "Add Signers"
|
||||||
msgstr "Unterzeichner hinzufügen"
|
msgstr "Unterzeichner hinzufügen"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:179
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:161
|
||||||
msgid "Add Subject"
|
msgid "Add Subject"
|
||||||
msgstr "Betreff hinzufügen"
|
msgstr "Betreff hinzufügen"
|
||||||
|
|
||||||
@@ -283,7 +283,7 @@ msgstr "Team-E-Mail hinzufügen"
|
|||||||
#~ msgid "Add Text"
|
#~ msgid "Add Text"
|
||||||
#~ msgstr "Add Text"
|
#~ msgstr "Add Text"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:170
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:152
|
||||||
msgid "Add the people who will sign the document."
|
msgid "Add the people who will sign the document."
|
||||||
msgstr "Fügen Sie die Personen hinzu, die das Dokument unterschreiben werden."
|
msgstr "Fügen Sie die Personen hinzu, die das Dokument unterschreiben werden."
|
||||||
|
|
||||||
@@ -291,7 +291,7 @@ msgstr "Fügen Sie die Personen hinzu, die das Dokument unterschreiben werden."
|
|||||||
msgid "Add the recipients to create the document with"
|
msgid "Add the recipients to create the document with"
|
||||||
msgstr "Fügen Sie die Empfänger hinzu, um das Dokument zu erstellen"
|
msgstr "Fügen Sie die Empfänger hinzu, um das Dokument zu erstellen"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:180
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:162
|
||||||
msgid "Add the subject and message you wish to send to signers."
|
msgid "Add the subject and message you wish to send to signers."
|
||||||
msgstr "Fügen Sie den Betreff und die Nachricht hinzu, die Sie den Unterzeichnern senden möchten."
|
msgstr "Fügen Sie den Betreff und die Nachricht hinzu, die Sie den Unterzeichnern senden möchten."
|
||||||
|
|
||||||
@@ -370,13 +370,13 @@ msgstr "Eine E-Mail, in der die Übertragung dieses Teams angefordert wird, wurd
|
|||||||
msgid "An error occurred"
|
msgid "An error occurred"
|
||||||
msgstr "Ein Fehler ist aufgetreten"
|
msgstr "Ein Fehler ist aufgetreten"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:266
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:248
|
||||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:197
|
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:197
|
||||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:231
|
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:231
|
||||||
msgid "An error occurred while adding signers."
|
msgid "An error occurred while adding signers."
|
||||||
msgstr "Ein Fehler ist aufgetreten, während Unterzeichner hinzugefügt wurden."
|
msgstr "Ein Fehler ist aufgetreten, während Unterzeichner hinzugefügt wurden."
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:301
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:278
|
||||||
msgid "An error occurred while adding the fields."
|
msgid "An error occurred while adding the fields."
|
||||||
msgstr "Ein Fehler ist aufgetreten, während die Felder hinzugefügt wurden."
|
msgstr "Ein Fehler ist aufgetreten, während die Felder hinzugefügt wurden."
|
||||||
|
|
||||||
@@ -426,7 +426,7 @@ msgstr "Ein Fehler ist aufgetreten, während die Vorlage verschoben wurde."
|
|||||||
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:148
|
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:148
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/number-field.tsx:195
|
#: apps/web/src/app/(signing)/sign/[token]/number-field.tsx:195
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/radio-field.tsx:129
|
#: apps/web/src/app/(signing)/sign/[token]/radio-field.tsx:129
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:173
|
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:175
|
||||||
msgid "An error occurred while removing the signature."
|
msgid "An error occurred while removing the signature."
|
||||||
msgstr "Ein Fehler ist aufgetreten, während die Unterschrift entfernt wurde."
|
msgstr "Ein Fehler ist aufgetreten, während die Unterschrift entfernt wurde."
|
||||||
|
|
||||||
@@ -434,7 +434,7 @@ msgstr "Ein Fehler ist aufgetreten, während die Unterschrift entfernt wurde."
|
|||||||
msgid "An error occurred while removing the text."
|
msgid "An error occurred while removing the text."
|
||||||
msgstr "Ein Fehler ist aufgetreten, während der Text entfernt wurde."
|
msgstr "Ein Fehler ist aufgetreten, während der Text entfernt wurde."
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:332
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:309
|
||||||
msgid "An error occurred while sending the document."
|
msgid "An error occurred while sending the document."
|
||||||
msgstr "Ein Fehler ist aufgetreten, während das Dokument gesendet wurde."
|
msgstr "Ein Fehler ist aufgetreten, während das Dokument gesendet wurde."
|
||||||
|
|
||||||
@@ -449,7 +449,7 @@ msgstr "Beim Senden Ihrer Bestätigungs-E-Mail ist ein Fehler aufgetreten"
|
|||||||
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:122
|
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:122
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/number-field.tsx:150
|
#: apps/web/src/app/(signing)/sign/[token]/number-field.tsx:150
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/radio-field.tsx:102
|
#: apps/web/src/app/(signing)/sign/[token]/radio-field.tsx:102
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:147
|
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:149
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:168
|
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:168
|
||||||
msgid "An error occurred while signing the document."
|
msgid "An error occurred while signing the document."
|
||||||
msgstr "Ein Fehler ist aufgetreten, während das Dokument unterzeichnet wurde."
|
msgstr "Ein Fehler ist aufgetreten, während das Dokument unterzeichnet wurde."
|
||||||
@@ -458,7 +458,7 @@ msgstr "Ein Fehler ist aufgetreten, während das Dokument unterzeichnet wurde."
|
|||||||
msgid "An error occurred while trying to create a checkout session."
|
msgid "An error occurred while trying to create a checkout session."
|
||||||
msgstr "Ein Fehler ist aufgetreten, während versucht wurde, eine Checkout-Sitzung zu erstellen."
|
msgstr "Ein Fehler ist aufgetreten, während versucht wurde, eine Checkout-Sitzung zu erstellen."
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:232
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:214
|
||||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:166
|
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:166
|
||||||
msgid "An error occurred while updating the document settings."
|
msgid "An error occurred while updating the document settings."
|
||||||
msgstr "Ein Fehler ist aufgetreten, während die Dokumenteinstellungen aktualisiert wurden."
|
msgstr "Ein Fehler ist aufgetreten, während die Dokumenteinstellungen aktualisiert wurden."
|
||||||
@@ -665,11 +665,11 @@ msgstr "Durch die Aktivierung von 2FA müssen Sie jedes Mal, wenn Sie sich anmel
|
|||||||
#: apps/web/src/app/(signing)/sign/[token]/document-action-auth-account.tsx:71
|
#: apps/web/src/app/(signing)/sign/[token]/document-action-auth-account.tsx:71
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/document-action-auth-passkey.tsx:164
|
#: apps/web/src/app/(signing)/sign/[token]/document-action-auth-passkey.tsx:164
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/document-action-auth-passkey.tsx:189
|
#: apps/web/src/app/(signing)/sign/[token]/document-action-auth-passkey.tsx:189
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/form.tsx:151
|
#: apps/web/src/app/(signing)/sign/[token]/form.tsx:150
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:215
|
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:215
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/number-field.tsx:327
|
#: apps/web/src/app/(signing)/sign/[token]/number-field.tsx:327
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx:113
|
#: apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx:113
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:248
|
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:250
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:333
|
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:333
|
||||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/team-transfer-status.tsx:121
|
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/team-transfer-status.tsx:121
|
||||||
#: apps/web/src/components/(dashboard)/settings/token/delete-token-dialog.tsx:176
|
#: apps/web/src/components/(dashboard)/settings/token/delete-token-dialog.tsx:176
|
||||||
@@ -752,7 +752,7 @@ msgid "Click to copy signing link for sending to recipient"
|
|||||||
msgstr "Klicken Sie, um den Signatur-Link zu kopieren, um ihn an den Empfänger zu senden"
|
msgstr "Klicken Sie, um den Signatur-Link zu kopieren, um ihn an den Empfänger zu senden"
|
||||||
|
|
||||||
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:175
|
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:175
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/form.tsx:115
|
#: apps/web/src/app/(signing)/sign/[token]/form.tsx:114
|
||||||
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:435
|
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:435
|
||||||
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:314
|
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:314
|
||||||
msgid "Click to insert field"
|
msgid "Click to insert field"
|
||||||
@@ -789,7 +789,7 @@ msgstr "Unterzeichnung abschließen"
|
|||||||
msgid "Complete Viewing"
|
msgid "Complete Viewing"
|
||||||
msgstr "Betrachten abschließen"
|
msgstr "Betrachten abschließen"
|
||||||
|
|
||||||
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:62
|
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:58
|
||||||
#: apps/web/src/components/formatter/document-status.tsx:28
|
#: apps/web/src/components/formatter/document-status.tsx:28
|
||||||
msgid "Completed"
|
msgid "Completed"
|
||||||
msgstr "Abgeschlossen"
|
msgstr "Abgeschlossen"
|
||||||
@@ -802,7 +802,7 @@ msgstr "Abgeschlossene Dokumente"
|
|||||||
msgid "Completed Documents"
|
msgid "Completed Documents"
|
||||||
msgstr "Abgeschlossene Dokumente"
|
msgstr "Abgeschlossene Dokumente"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:165
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:147
|
||||||
msgid "Configure general settings for the document."
|
msgid "Configure general settings for the document."
|
||||||
msgstr "Konfigurieren Sie die allgemeinen Einstellungen für das Dokument."
|
msgstr "Konfigurieren Sie die allgemeinen Einstellungen für das Dokument."
|
||||||
|
|
||||||
@@ -1274,7 +1274,7 @@ msgstr "Dokument erneut gesendet"
|
|||||||
msgid "Document resealed"
|
msgid "Document resealed"
|
||||||
msgstr "Dokument wieder versiegelt"
|
msgstr "Dokument wieder versiegelt"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:321
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:298
|
||||||
msgid "Document sent"
|
msgid "Document sent"
|
||||||
msgstr "Dokument gesendet"
|
msgstr "Dokument gesendet"
|
||||||
|
|
||||||
@@ -1316,7 +1316,7 @@ msgstr "Dokument wird dauerhaft gelöscht"
|
|||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx:109
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx:109
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/loading.tsx:16
|
#: apps/web/src/app/(dashboard)/documents/[id]/loading.tsx:16
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/sent/page.tsx:15
|
#: apps/web/src/app/(dashboard)/documents/[id]/sent/page.tsx:15
|
||||||
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:119
|
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:114
|
||||||
#: apps/web/src/app/(profile)/p/[url]/page.tsx:166
|
#: apps/web/src/app/(profile)/p/[url]/page.tsx:166
|
||||||
#: apps/web/src/app/not-found.tsx:21
|
#: apps/web/src/app/not-found.tsx:21
|
||||||
#: apps/web/src/components/(dashboard)/common/command-menu.tsx:205
|
#: apps/web/src/components/(dashboard)/common/command-menu.tsx:205
|
||||||
@@ -1369,10 +1369,6 @@ msgstr "Entwurfdokumente"
|
|||||||
msgid "Drafted Documents"
|
msgid "Drafted Documents"
|
||||||
msgstr "Entwurfte Dokumente"
|
msgstr "Entwurfte Dokumente"
|
||||||
|
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:245
|
|
||||||
#~ msgid "Draw"
|
|
||||||
#~ msgstr "Draw"
|
|
||||||
|
|
||||||
#: apps/web/src/app/(teams)/t/[teamUrl]/layout-billing-banner.tsx:121
|
#: apps/web/src/app/(teams)/t/[teamUrl]/layout-billing-banner.tsx:121
|
||||||
msgid "Due to an unpaid invoice, your team has been restricted. Please settle the payment to restore full access to your team."
|
msgid "Due to an unpaid invoice, your team has been restricted. Please settle the payment to restore full access to your team."
|
||||||
msgstr "Aufgrund einer unbezahlten Rechnung wurde Ihrem Team der Zugriff eingeschränkt. Bitte begleichen Sie die Zahlung, um den vollumfänglichen Zugang zu Ihrem Team wiederherzustellen."
|
msgstr "Aufgrund einer unbezahlten Rechnung wurde Ihrem Team der Zugriff eingeschränkt. Bitte begleichen Sie die Zahlung, um den vollumfänglichen Zugang zu Ihrem Team wiederherzustellen."
|
||||||
@@ -1495,10 +1491,10 @@ msgstr "Geben Sie hier Ihren Text ein"
|
|||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/admin/documents/[id]/admin-actions.tsx:41
|
#: apps/web/src/app/(dashboard)/admin/documents/[id]/admin-actions.tsx:41
|
||||||
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:78
|
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:78
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:231
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:213
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:265
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:247
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:300
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:277
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:331
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:308
|
||||||
#: apps/web/src/app/(dashboard)/documents/move-document-dialog.tsx:57
|
#: apps/web/src/app/(dashboard)/documents/move-document-dialog.tsx:57
|
||||||
#: apps/web/src/app/(dashboard)/documents/upload-document.tsx:106
|
#: apps/web/src/app/(dashboard)/documents/upload-document.tsx:106
|
||||||
#: apps/web/src/app/(dashboard)/documents/upload-document.tsx:112
|
#: apps/web/src/app/(dashboard)/documents/upload-document.tsx:112
|
||||||
@@ -1523,8 +1519,8 @@ msgstr "Geben Sie hier Ihren Text ein"
|
|||||||
#: apps/web/src/app/(signing)/sign/[token]/number-field.tsx:194
|
#: apps/web/src/app/(signing)/sign/[token]/number-field.tsx:194
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/radio-field.tsx:101
|
#: apps/web/src/app/(signing)/sign/[token]/radio-field.tsx:101
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/radio-field.tsx:128
|
#: apps/web/src/app/(signing)/sign/[token]/radio-field.tsx:128
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:146
|
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:148
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:172
|
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:174
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:167
|
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:167
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:195
|
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:195
|
||||||
#: apps/web/src/components/(dashboard)/layout/verify-email-banner.tsx:54
|
#: apps/web/src/components/(dashboard)/layout/verify-email-banner.tsx:54
|
||||||
@@ -1601,7 +1597,7 @@ msgstr "Haben Sie Ihr Passwort vergessen?"
|
|||||||
msgid "Full Name"
|
msgid "Full Name"
|
||||||
msgstr "Vollständiger Name"
|
msgstr "Vollständiger Name"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:164
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:146
|
||||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:76
|
#: apps/web/src/app/(dashboard)/templates/[id]/edit-template.tsx:76
|
||||||
#: apps/web/src/app/(recipient)/d/[token]/direct-template.tsx:60
|
#: apps/web/src/app/(recipient)/d/[token]/direct-template.tsx:60
|
||||||
#: apps/web/src/components/(teams)/settings/layout/desktop-nav.tsx:43
|
#: apps/web/src/components/(teams)/settings/layout/desktop-nav.tsx:43
|
||||||
@@ -2147,7 +2143,7 @@ msgstr "Sobald Sie den QR-Code gescannt oder den Code manuell eingegeben haben,
|
|||||||
msgid "Oops! Something went wrong."
|
msgid "Oops! Something went wrong."
|
||||||
msgstr "Hoppla! Etwas ist schief gelaufen."
|
msgstr "Hoppla! Etwas ist schief gelaufen."
|
||||||
|
|
||||||
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:101
|
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:97
|
||||||
msgid "Opened"
|
msgid "Opened"
|
||||||
msgstr "Geöffnet"
|
msgstr "Geöffnet"
|
||||||
|
|
||||||
@@ -2298,7 +2294,7 @@ msgstr "Bitte kontaktieren Sie den Support, wenn Sie diese Aktion rückgängig m
|
|||||||
msgid "Please enter a meaningful name for your token. This will help you identify it later."
|
msgid "Please enter a meaningful name for your token. This will help you identify it later."
|
||||||
msgstr "Bitte geben Sie einen aussagekräftigen Namen für Ihr Token ein. Dies wird Ihnen helfen, es später zu identifizieren."
|
msgstr "Bitte geben Sie einen aussagekräftigen Namen für Ihr Token ein. Dies wird Ihnen helfen, es später zu identifizieren."
|
||||||
|
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/form.tsx:135
|
#: apps/web/src/app/(signing)/sign/[token]/form.tsx:134
|
||||||
msgid "Please mark as viewed to complete"
|
msgid "Please mark as viewed to complete"
|
||||||
msgstr "Bitte als angesehen markieren, um abzuschließen"
|
msgstr "Bitte als angesehen markieren, um abzuschließen"
|
||||||
|
|
||||||
@@ -2734,13 +2730,13 @@ msgstr "Vorlagen in Ihrem Team-Öffentliches Profil anzeigen, damit Ihre Zielgru
|
|||||||
#: apps/web/src/app/(signing)/sign/[token]/document-action-auth-2fa.tsx:182
|
#: apps/web/src/app/(signing)/sign/[token]/document-action-auth-2fa.tsx:182
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:224
|
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:224
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx:124
|
#: apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx:124
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:256
|
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:259
|
||||||
#: apps/web/src/components/ui/user-profile-skeleton.tsx:75
|
#: apps/web/src/components/ui/user-profile-skeleton.tsx:75
|
||||||
#: apps/web/src/components/ui/user-profile-timur.tsx:81
|
#: apps/web/src/components/ui/user-profile-timur.tsx:81
|
||||||
msgid "Sign"
|
msgid "Sign"
|
||||||
msgstr "Unterzeichnen"
|
msgstr "Unterzeichnen"
|
||||||
|
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:217
|
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:219
|
||||||
msgid "Sign as {0} <0>({1})</0>"
|
msgid "Sign as {0} <0>({1})</0>"
|
||||||
msgstr "Unterzeichnen als {0} <0>({1})</0>"
|
msgstr "Unterzeichnen als {0} <0>({1})</0>"
|
||||||
|
|
||||||
@@ -2806,8 +2802,8 @@ msgstr "Registrieren mit OIDC"
|
|||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:88
|
#: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:88
|
||||||
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:338
|
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:338
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:195
|
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:197
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:225
|
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:227
|
||||||
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:391
|
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:391
|
||||||
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:270
|
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:270
|
||||||
#: apps/web/src/components/forms/profile.tsx:132
|
#: apps/web/src/components/forms/profile.tsx:132
|
||||||
@@ -3556,10 +3552,6 @@ msgstr "Type 'delete' to confirm"
|
|||||||
msgid "Type a command or search..."
|
msgid "Type a command or search..."
|
||||||
msgstr "Type a command or search..."
|
msgstr "Type a command or search..."
|
||||||
|
|
||||||
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:275
|
|
||||||
#~ msgid "Typed Signature"
|
|
||||||
#~ msgstr "Typed Signature"
|
|
||||||
|
|
||||||
#: apps/web/src/app/(unauthenticated)/verify-email/page.tsx:26
|
#: apps/web/src/app/(unauthenticated)/verify-email/page.tsx:26
|
||||||
msgid "Uh oh! Looks like you're missing a token"
|
msgid "Uh oh! Looks like you're missing a token"
|
||||||
msgstr "Uh oh! Looks like you're missing a token"
|
msgstr "Uh oh! Looks like you're missing a token"
|
||||||
@@ -3644,7 +3636,7 @@ msgstr "Unable to sign in"
|
|||||||
msgid "Unauthorized"
|
msgid "Unauthorized"
|
||||||
msgstr "Unauthorized"
|
msgstr "Unauthorized"
|
||||||
|
|
||||||
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:116
|
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:112
|
||||||
msgid "Uncompleted"
|
msgid "Uncompleted"
|
||||||
msgstr "Uncompleted"
|
msgstr "Uncompleted"
|
||||||
|
|
||||||
@@ -3856,7 +3848,7 @@ msgstr "Teams ansehen"
|
|||||||
msgid "Viewed"
|
msgid "Viewed"
|
||||||
msgstr "Angesehen"
|
msgstr "Angesehen"
|
||||||
|
|
||||||
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:86
|
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:82
|
||||||
msgid "Waiting"
|
msgid "Waiting"
|
||||||
msgstr "Warten"
|
msgstr "Warten"
|
||||||
|
|
||||||
@@ -4390,7 +4382,7 @@ msgstr "Ihr Dokument wurde erfolgreich aus der Vorlage erstellt."
|
|||||||
msgid "Your document has been re-sent successfully."
|
msgid "Your document has been re-sent successfully."
|
||||||
msgstr "Ihr Dokument wurde erfolgreich erneut gesendet."
|
msgstr "Ihr Dokument wurde erfolgreich erneut gesendet."
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:322
|
#: apps/web/src/app/(dashboard)/documents/[id]/edit-document.tsx:299
|
||||||
msgid "Your document has been sent successfully."
|
msgid "Your document has been sent successfully."
|
||||||
msgstr "Ihr Dokument wurde erfolgreich gesendet."
|
msgstr "Ihr Dokument wurde erfolgreich gesendet."
|
||||||
|
|
||||||
@@ -4497,4 +4489,3 @@ msgstr "Ihr Token wurde erfolgreich erstellt! Stellen Sie sicher, dass Sie es ko
|
|||||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/tokens/page.tsx:86
|
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/tokens/page.tsx:86
|
||||||
msgid "Your tokens will be shown here once you create them."
|
msgid "Your tokens will be shown here once you create them."
|
||||||
msgstr "Ihre Tokens werden hier angezeigt, sobald Sie sie erstellt haben."
|
msgstr "Ihre Tokens werden hier angezeigt, sobald Sie sie erstellt haben."
|
||||||
|
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ msgstr "Add an external ID to the document. This can be used to identify the doc
|
|||||||
msgid "Add an external ID to the template. This can be used to identify in external systems."
|
msgid "Add an external ID to the template. This can be used to identify in external systems."
|
||||||
msgstr "Add an external ID to the template. This can be used to identify in external systems."
|
msgstr "Add an external ID to the template. This can be used to identify in external systems."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:187
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:177
|
||||||
msgid "Add another option"
|
msgid "Add another option"
|
||||||
msgstr "Add another option"
|
msgstr "Add another option"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:232
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:230
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:167
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:167
|
||||||
msgid "Add another value"
|
msgid "Add another value"
|
||||||
msgstr "Add another value"
|
msgstr "Add another value"
|
||||||
@@ -93,11 +93,11 @@ msgstr "Add Placeholder Recipient"
|
|||||||
msgid "Add Signer"
|
msgid "Add Signer"
|
||||||
msgstr "Add Signer"
|
msgstr "Add Signer"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:73
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:70
|
||||||
msgid "Add text"
|
msgid "Add text"
|
||||||
msgstr "Add text"
|
msgstr "Add text"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:78
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:75
|
||||||
msgid "Add text to the field"
|
msgid "Add text to the field"
|
||||||
msgstr "Add text to the field"
|
msgstr "Add text to the field"
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ msgstr "Admin"
|
|||||||
msgid "Advanced Options"
|
msgid "Advanced Options"
|
||||||
msgstr "Advanced Options"
|
msgstr "Advanced Options"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:570
|
#: packages/ui/primitives/document-flow/add-fields.tsx:527
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:402
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:402
|
||||||
msgid "Advanced settings"
|
msgid "Advanced settings"
|
||||||
msgstr "Advanced settings"
|
msgstr "Advanced settings"
|
||||||
@@ -135,15 +135,15 @@ msgstr "Approver"
|
|||||||
msgid "Approving"
|
msgid "Approving"
|
||||||
msgstr "Approving"
|
msgstr "Approving"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:377
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:276
|
||||||
msgid "Black"
|
msgid "Black"
|
||||||
msgstr "Black"
|
msgstr "Black"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:391
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:290
|
||||||
msgid "Blue"
|
msgid "Blue"
|
||||||
msgstr "Blue"
|
msgstr "Blue"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:356
|
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:287
|
||||||
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:58
|
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:58
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr "Cancel"
|
msgstr "Cancel"
|
||||||
@@ -169,16 +169,16 @@ msgstr "CC"
|
|||||||
msgid "CC'd"
|
msgid "CC'd"
|
||||||
msgstr "CC'd"
|
msgstr "CC'd"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:86
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:83
|
||||||
msgid "Character Limit"
|
msgid "Character Limit"
|
||||||
msgstr "Character Limit"
|
msgstr "Character Limit"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1026
|
#: packages/ui/primitives/document-flow/add-fields.tsx:950
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:788
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:788
|
||||||
msgid "Checkbox"
|
msgid "Checkbox"
|
||||||
msgstr "Checkbox"
|
msgstr "Checkbox"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:197
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:195
|
||||||
msgid "Checkbox values"
|
msgid "Checkbox values"
|
||||||
msgstr "Checkbox values"
|
msgstr "Checkbox values"
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ msgstr "Checkbox values"
|
|||||||
msgid "Clear filters"
|
msgid "Clear filters"
|
||||||
msgstr "Clear filters"
|
msgstr "Clear filters"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:411
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:310
|
||||||
msgid "Clear Signature"
|
msgid "Clear Signature"
|
||||||
msgstr "Clear Signature"
|
msgstr "Clear Signature"
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ msgstr "Close"
|
|||||||
msgid "Configure Direct Recipient"
|
msgid "Configure Direct Recipient"
|
||||||
msgstr "Configure Direct Recipient"
|
msgstr "Configure Direct Recipient"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:571
|
#: packages/ui/primitives/document-flow/add-fields.tsx:528
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:403
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:403
|
||||||
msgid "Configure the {0} field"
|
msgid "Configure the {0} field"
|
||||||
msgstr "Configure the {0} field"
|
msgstr "Configure the {0} field"
|
||||||
@@ -219,7 +219,7 @@ msgstr "Copied to clipboard"
|
|||||||
msgid "Custom Text"
|
msgid "Custom Text"
|
||||||
msgstr "Custom Text"
|
msgstr "Custom Text"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:922
|
#: packages/ui/primitives/document-flow/add-fields.tsx:846
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:684
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:684
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Date"
|
msgstr "Date"
|
||||||
@@ -251,16 +251,16 @@ msgstr "Download"
|
|||||||
msgid "Drag & drop your PDF here."
|
msgid "Drag & drop your PDF here."
|
||||||
msgstr "Drag & drop your PDF here."
|
msgstr "Drag & drop your PDF here."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1052
|
#: packages/ui/primitives/document-flow/add-fields.tsx:976
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:814
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:814
|
||||||
msgid "Dropdown"
|
msgid "Dropdown"
|
||||||
msgstr "Dropdown"
|
msgstr "Dropdown"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:158
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:148
|
||||||
msgid "Dropdown options"
|
msgid "Dropdown options"
|
||||||
msgstr "Dropdown options"
|
msgstr "Dropdown options"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:870
|
#: packages/ui/primitives/document-flow/add-fields.tsx:794
|
||||||
#: packages/ui/primitives/document-flow/add-signature.tsx:272
|
#: packages/ui/primitives/document-flow/add-signature.tsx:272
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:500
|
#: packages/ui/primitives/document-flow/add-signers.tsx:500
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
|
||||||
@@ -273,10 +273,6 @@ msgstr "Email"
|
|||||||
msgid "Email Options"
|
msgid "Email Options"
|
||||||
msgstr "Email Options"
|
msgstr "Email Options"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1117
|
|
||||||
msgid "Empty field"
|
|
||||||
msgstr "Empty field"
|
|
||||||
|
|
||||||
#: packages/lib/constants/template.ts:8
|
#: packages/lib/constants/template.ts:8
|
||||||
msgid "Enable Direct Link Signing"
|
msgid "Enable Direct Link Signing"
|
||||||
msgstr "Enable Direct Link Signing"
|
msgstr "Enable Direct Link Signing"
|
||||||
@@ -286,15 +282,11 @@ msgstr "Enable Direct Link Signing"
|
|||||||
msgid "Enable signing order"
|
msgid "Enable signing order"
|
||||||
msgstr "Enable signing order"
|
msgstr "Enable signing order"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:790
|
|
||||||
msgid "Enable Typed Signatures"
|
|
||||||
msgstr "Enable Typed Signatures"
|
|
||||||
|
|
||||||
#: packages/ui/primitives/document-password-dialog.tsx:84
|
#: packages/ui/primitives/document-password-dialog.tsx:84
|
||||||
msgid "Enter password"
|
msgid "Enter password"
|
||||||
msgstr "Enter password"
|
msgstr "Enter password"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:257
|
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:216
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
msgstr "Error"
|
msgstr "Error"
|
||||||
|
|
||||||
@@ -303,44 +295,26 @@ msgstr "Error"
|
|||||||
msgid "External ID"
|
msgid "External ID"
|
||||||
msgstr "External ID"
|
msgstr "External ID"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:258
|
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:217
|
||||||
msgid "Failed to save settings."
|
msgid "Failed to save settings."
|
||||||
msgstr "Failed to save settings."
|
msgstr "Failed to save settings."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:93
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:90
|
||||||
msgid "Field character limit"
|
msgid "Field character limit"
|
||||||
msgstr "Field character limit"
|
msgstr "Field character limit"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:62
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:107
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:44
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:44
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/name-field.tsx:44
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:130
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:107
|
|
||||||
msgid "Field font size"
|
|
||||||
msgstr "Field font size"
|
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:110
|
|
||||||
msgid "Field format"
|
msgid "Field format"
|
||||||
msgstr "Field format"
|
msgstr "Field format"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:53
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:50
|
||||||
msgid "Field label"
|
msgid "Field label"
|
||||||
msgstr "Field label"
|
msgstr "Field label"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:65
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:62
|
||||||
msgid "Field placeholder"
|
msgid "Field placeholder"
|
||||||
msgstr "Field placeholder"
|
msgstr "Field placeholder"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/date-field.tsx:56
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/email-field.tsx:38
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/initials-field.tsx:38
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/name-field.tsx:38
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:124
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:101
|
|
||||||
msgid "Font Size"
|
|
||||||
msgstr "Font Size"
|
|
||||||
|
|
||||||
#: packages/ui/components/document/document-global-auth-action-select.tsx:64
|
#: packages/ui/components/document/document-global-auth-action-select.tsx:64
|
||||||
msgid "Global recipient action authentication"
|
msgid "Global recipient action authentication"
|
||||||
msgstr "Global recipient action authentication"
|
msgstr "Global recipient action authentication"
|
||||||
@@ -349,7 +323,7 @@ msgstr "Global recipient action authentication"
|
|||||||
msgid "Go Back"
|
msgid "Go Back"
|
||||||
msgstr "Go Back"
|
msgstr "Go Back"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:398
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:297
|
||||||
msgid "Green"
|
msgid "Green"
|
||||||
msgstr "Green"
|
msgstr "Green"
|
||||||
|
|
||||||
@@ -378,9 +352,9 @@ msgstr "I am required to receive a copy of this document"
|
|||||||
msgid "Inherit authentication method"
|
msgid "Inherit authentication method"
|
||||||
msgstr "Inherit authentication method"
|
msgstr "Inherit authentication method"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:67
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:64
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:72
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:69
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:48
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:45
|
||||||
msgid "Label"
|
msgid "Label"
|
||||||
msgstr "Label"
|
msgstr "Label"
|
||||||
|
|
||||||
@@ -388,7 +362,7 @@ msgstr "Label"
|
|||||||
msgid "Manager"
|
msgid "Manager"
|
||||||
msgstr "Manager"
|
msgstr "Manager"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:188
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:168
|
||||||
msgid "Max"
|
msgid "Max"
|
||||||
msgstr "Max"
|
msgstr "Max"
|
||||||
|
|
||||||
@@ -401,11 +375,11 @@ msgstr "Member"
|
|||||||
msgid "Message <0>(Optional)</0>"
|
msgid "Message <0>(Optional)</0>"
|
||||||
msgstr "Message <0>(Optional)</0>"
|
msgstr "Message <0>(Optional)</0>"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:176
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:156
|
||||||
msgid "Min"
|
msgid "Min"
|
||||||
msgstr "Min"
|
msgstr "Min"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:896
|
#: packages/ui/primitives/document-flow/add-fields.tsx:820
|
||||||
#: packages/ui/primitives/document-flow/add-signature.tsx:298
|
#: packages/ui/primitives/document-flow/add-signature.tsx:298
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:535
|
#: packages/ui/primitives/document-flow/add-signers.tsx:535
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:541
|
#: packages/ui/primitives/document-flow/add-signers.tsx:541
|
||||||
@@ -427,12 +401,12 @@ msgstr "Needs to sign"
|
|||||||
msgid "Needs to view"
|
msgid "Needs to view"
|
||||||
msgstr "Needs to view"
|
msgstr "Needs to view"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:680
|
#: packages/ui/primitives/document-flow/add-fields.tsx:631
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:497
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:497
|
||||||
msgid "No recipient matching this description was found."
|
msgid "No recipient matching this description was found."
|
||||||
msgstr "No recipient matching this description was found."
|
msgstr "No recipient matching this description was found."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:696
|
#: packages/ui/primitives/document-flow/add-fields.tsx:647
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:513
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:513
|
||||||
msgid "No recipients with this role"
|
msgid "No recipients with this role"
|
||||||
msgstr "No recipients with this role"
|
msgstr "No recipients with this role"
|
||||||
@@ -457,12 +431,12 @@ msgstr "No signature field found"
|
|||||||
msgid "No value found."
|
msgid "No value found."
|
||||||
msgstr "No value found."
|
msgstr "No value found."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:974
|
#: packages/ui/primitives/document-flow/add-fields.tsx:898
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:736
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:736
|
||||||
msgid "Number"
|
msgid "Number"
|
||||||
msgstr "Number"
|
msgstr "Number"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:103
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:100
|
||||||
msgid "Number format"
|
msgid "Number format"
|
||||||
msgstr "Number format"
|
msgstr "Number format"
|
||||||
|
|
||||||
@@ -482,17 +456,17 @@ msgstr "Page {0} of {1}"
|
|||||||
msgid "Password Required"
|
msgid "Password Required"
|
||||||
msgstr "Password Required"
|
msgstr "Password Required"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:156
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:154
|
||||||
msgid "Pick a number"
|
msgid "Pick a number"
|
||||||
msgstr "Pick a number"
|
msgstr "Pick a number"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:79
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:76
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:84
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:81
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:60
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:57
|
||||||
msgid "Placeholder"
|
msgid "Placeholder"
|
||||||
msgstr "Placeholder"
|
msgstr "Placeholder"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1000
|
#: packages/ui/primitives/document-flow/add-fields.tsx:924
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:762
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:762
|
||||||
msgid "Radio"
|
msgid "Radio"
|
||||||
msgstr "Radio"
|
msgstr "Radio"
|
||||||
@@ -501,11 +475,11 @@ msgstr "Radio"
|
|||||||
msgid "Radio values"
|
msgid "Radio values"
|
||||||
msgstr "Radio values"
|
msgstr "Radio values"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:186
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:184
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:147
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:137
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:156
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:136
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:122
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:122
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:133
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:114
|
||||||
msgid "Read only"
|
msgid "Read only"
|
||||||
msgstr "Read only"
|
msgstr "Read only"
|
||||||
|
|
||||||
@@ -519,7 +493,7 @@ msgstr "Receives copy"
|
|||||||
msgid "Recipient action authentication"
|
msgid "Recipient action authentication"
|
||||||
msgstr "Recipient action authentication"
|
msgstr "Recipient action authentication"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:384
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:283
|
||||||
msgid "Red"
|
msgid "Red"
|
||||||
msgstr "Red"
|
msgstr "Red"
|
||||||
|
|
||||||
@@ -528,15 +502,15 @@ msgstr "Red"
|
|||||||
msgid "Redirect URL"
|
msgid "Redirect URL"
|
||||||
msgstr "Redirect URL"
|
msgstr "Redirect URL"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1104
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1027
|
||||||
msgid "Remove"
|
msgid "Remove"
|
||||||
msgstr "Remove"
|
msgstr "Remove"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:176
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:174
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:137
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:127
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:146
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:126
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:112
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx:112
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:123
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx:104
|
||||||
msgid "Required field"
|
msgid "Required field"
|
||||||
msgstr "Required field"
|
msgstr "Required field"
|
||||||
|
|
||||||
@@ -544,7 +518,7 @@ msgstr "Required field"
|
|||||||
msgid "Rows per page"
|
msgid "Rows per page"
|
||||||
msgstr "Rows per page"
|
msgstr "Rows per page"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:355
|
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:286
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Save"
|
msgstr "Save"
|
||||||
|
|
||||||
@@ -556,7 +530,7 @@ msgstr "Save Template"
|
|||||||
msgid "Search languages..."
|
msgid "Search languages..."
|
||||||
msgstr "Search languages..."
|
msgstr "Search languages..."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:115
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:105
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr "Select"
|
msgstr "Select"
|
||||||
|
|
||||||
@@ -564,11 +538,11 @@ msgstr "Select"
|
|||||||
msgid "Select an option"
|
msgid "Select an option"
|
||||||
msgstr "Select an option"
|
msgstr "Select an option"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:139
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:137
|
||||||
msgid "Select at least"
|
msgid "Select at least"
|
||||||
msgstr "Select at least"
|
msgstr "Select at least"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:105
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx:95
|
||||||
msgid "Select default option"
|
msgid "Select default option"
|
||||||
msgstr "Select default option"
|
msgstr "Select default option"
|
||||||
|
|
||||||
@@ -599,7 +573,7 @@ msgstr "Show advanced settings"
|
|||||||
msgid "Sign"
|
msgid "Sign"
|
||||||
msgstr "Sign"
|
msgstr "Sign"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:818
|
#: packages/ui/primitives/document-flow/add-fields.tsx:742
|
||||||
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
||||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:580
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:580
|
||||||
@@ -647,7 +621,7 @@ msgstr "Submit"
|
|||||||
msgid "Template title"
|
msgid "Template title"
|
||||||
msgstr "Template title"
|
msgstr "Template title"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:948
|
#: packages/ui/primitives/document-flow/add-fields.tsx:872
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:710
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:710
|
||||||
msgid "Text"
|
msgid "Text"
|
||||||
msgstr "Text"
|
msgstr "Text"
|
||||||
@@ -708,7 +682,7 @@ msgstr "The signer's name"
|
|||||||
msgid "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
|
msgid "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
|
||||||
msgstr "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
|
msgstr "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:752
|
#: packages/ui/primitives/document-flow/add-fields.tsx:703
|
||||||
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
|
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
|
||||||
msgstr "This document has already been sent to this recipient. You can no longer edit this recipient."
|
msgstr "This document has already been sent to this recipient. You can no longer edit this recipient."
|
||||||
|
|
||||||
@@ -720,7 +694,7 @@ msgstr "This document is password protected. Please enter the password to view t
|
|||||||
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
||||||
msgstr "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
msgstr "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1084
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1007
|
||||||
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
|
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
|
||||||
msgstr "This recipient can no longer be modified as they have signed a field, or completed the document."
|
msgstr "This recipient can no longer be modified as they have signed a field, or completed the document."
|
||||||
|
|
||||||
@@ -745,7 +719,7 @@ msgstr "Time Zone"
|
|||||||
msgid "Title"
|
msgid "Title"
|
||||||
msgstr "Title"
|
msgstr "Title"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1067
|
#: packages/ui/primitives/document-flow/add-fields.tsx:990
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:828
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:828
|
||||||
msgid "To proceed further, please set at least one value for the {0} field."
|
msgid "To proceed further, please set at least one value for the {0} field."
|
||||||
msgstr "To proceed further, please set at least one value for the {0} field."
|
msgstr "To proceed further, please set at least one value for the {0} field."
|
||||||
@@ -766,13 +740,13 @@ msgstr "Upgrade"
|
|||||||
msgid "Upload Template Document"
|
msgid "Upload Template Document"
|
||||||
msgstr "Upload Template Document"
|
msgstr "Upload Template Document"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:132
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx:130
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:167
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:147
|
||||||
msgid "Validation"
|
msgid "Validation"
|
||||||
msgstr "Validation"
|
msgstr "Validation"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:91
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:88
|
||||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:96
|
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx:93
|
||||||
msgid "Value"
|
msgid "Value"
|
||||||
msgstr "Value"
|
msgstr "Value"
|
||||||
|
|
||||||
|
|||||||