Compare commits

...

15 Commits

Author SHA1 Message Date
b3ccb3d26f v1.8.1-rc.9 2024-12-05 13:53:35 +11:00
b17370c153 chore: reword some german translations to increase clarity (#1507) 2024-12-05 09:42:10 +11:00
0c53f5b061 v1.8.1-rc.8 2024-12-04 23:29:15 +11:00
ed6157de80 feat: upload signature as img (#1496)
Allow users to upload their signature as an image.

https://github.com/user-attachments/assets/375faad2-f0db-4f44-83d2-d969c5ab4442
2024-12-04 23:22:18 +11:00
5e08d0cffb v1.8.1-rc.7 2024-12-04 22:43:41 +11:00
5565aff7a3 fix: docs content (#1495) 2024-12-04 19:49:44 +09:00
428acf4ac3 fix: dateformat api bug (#1506) 2024-12-04 19:48:44 +09:00
f4b1e5104e feat: add platform plan pricing (#1505)
Add platform plan to the billing page.
2024-12-04 15:42:03 +09:00
a687064a42 v1.8.1-rc.6 2024-12-04 14:58:29 +11:00
8ec69388a5 fix: add document rejection webhook
Adds the document rejection webhook since it was missing.

Additionally, normalises and standardises the webhook body.
2024-12-04 14:35:20 +11:00
f3da11b3e7 fix: e2e tests failing due to same-site cookies 2024-12-04 14:33:21 +11:00
fc84ee8ec2 fix: use default nextauth logic for secure cookies 2024-12-03 21:35:09 +11:00
4282a96ee7 v1.8.1-rc.5 2024-12-03 15:44:10 +11:00
2aae7435f8 fix: auth cookies across iframes (#1501) 2024-12-03 15:28:30 +11:00
bdd33bd335 feat: signing volume (#1358)
adds a signing volume and leaderboard section to the admin panel
2024-12-03 11:27:22 +11:00
49 changed files with 1382 additions and 200 deletions

View File

@ -500,8 +500,35 @@ Now you can make a `POST` request to the `/api/v1/documents/{documentId}/fields`
value, this is the value that will be used to sign the field. value, this is the value that will be used to sign the field.
</Callout> </Callout>
<Callout type="warning">
It's important to pass the `type` in the `fieldMeta` property for the advanced fields. [Read more
here](#a-note-on-advanced-fields)
</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. 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.
![A screenshot of the document in the Documenso editor](/api-reference/fields-added-via-api.webp) ![A screenshot of the document in the Documenso editor](/api-reference/fields-added-via-api.webp)
</Steps> </Steps>
#### A Note on Advanced Fields
The advanced fields are: text, checkbox, radio, number, and select. Whenever you append any of these advanced fields to a document, you need to pass the `type` in the `fieldMeta` property:
```json
...
"fieldMeta": {
"type": "text",
}
...
```
Replace the `text` value with the corresponding field type:
- For the `TEXT` field it should be `text`.
- For the `CHECKBOX` field it should be `checkbox`.
- For the `RADIO` field it should be `radio`.
- For the `NUMBER` field it should be `number`.
- For the `SELECT` field it should be `select`. (check this before merge)
You must pass this property at all times, even if you don't need to set any other properties. If you don't, the endpoint will throw an error.

View File

@ -20,6 +20,7 @@ Documenso supports Webhooks and allows you to subscribe to the following events:
- `document.opened` - `document.opened`
- `document.signed` - `document.signed`
- `document.completed` - `document.completed`
- `document.rejected`
## Create a webhook subscription ## Create a webhook subscription
@ -36,7 +37,7 @@ Clicking on the "**Create Webhook**" button opens a modal to create a new webhoo
To create a new webhook subscription, you need to provide the following information: To create a new webhook subscription, you need to provide the following information:
- Enter the webhook URL that will receive the event payload. - Enter the webhook URL that will receive the event payload.
- Select the event(s) you want to subscribe to: `document.created`, `document.sent`, `document.opened`, `document.signed`, `document.completed`. - Select the event(s) you want to subscribe to: `document.created`, `document.sent`, `document.opened`, `document.signed`, `document.completed`, `document.rejected`.
- Optionally, you can provide a secret key that will be used to sign the payload. This key will be included in the `X-Documenso-Secret` header of the request. - Optionally, you can provide a secret key that will be used to sign the payload. This key will be included in the `X-Documenso-Secret` header of the request.
![A screenshot of the Create Webhook modal that shows the URL input field and the event checkboxes](/webhook-images/webhooks-page-create-webhook-modal.webp) ![A screenshot of the Create Webhook modal that shows the URL input field and the event checkboxes](/webhook-images/webhooks-page-create-webhook-modal.webp)
@ -53,45 +54,55 @@ You can edit or delete your webhook subscriptions by clicking the "**Edit**" or
The payload sent to the webhook URL contains the following fields: The payload sent to the webhook URL contains the following fields:
| Field | Type | Description | | Field | Type | Description |
| -------------------------------------------- | --------- | ---------------------------------------------------- | | -------------------------------------------- | --------- | ----------------------------------------------------- |
| `event` | string | The type of event that triggered the webhook. | | `event` | string | The type of event that triggered the webhook. |
| `payload.id` | number | The id of the document. | | `payload.id` | number | The id of the document. |
| `payload.userId` | number | The id of the user who owns the document. | | `payload.externalId` | string? | External identifier for the document. |
| `payload.authOptions` | json? | Authentication options for the document. | | `payload.userId` | number | The id of the user who owns the document. |
| `payload.formValues` | json? | Form values for the document. | | `payload.authOptions` | json? | Authentication options for the document. |
| `payload.title` | string | The name of the document. | | `payload.formValues` | json? | Form values for the document. |
| `payload.status` | string | The current status of the document. | | `payload.visibility` | string | Document visibility (e.g., EVERYONE). |
| `payload.documentDataId` | string | The identifier for the document data. | | `payload.title` | string | The title of the document. |
| `payload.createdAt` | datetime | The creation date and time of the document. | | `payload.status` | string | The current status of the document. |
| `payload.updatedAt` | datetime | The last update date and time of the document. | | `payload.documentDataId` | string | The identifier for the document data. |
| `payload.completedAt` | datetime? | The completion date and time of the document. | | `payload.createdAt` | datetime | The creation date and time of the document. |
| `payload.deletedAt` | datetime? | The deletion date and time of the document. | | `payload.updatedAt` | datetime | The last update date and time of the document. |
| `payload.teamId` | number? | The id of the team. | | `payload.completedAt` | datetime? | The completion date and time of the document. |
| `payload.documentData.id` | string | The id of the document data. | | `payload.deletedAt` | datetime? | The deletion date and time of the document. |
| `payload.documentData.type` | string | The type of the document data. | | `payload.teamId` | number? | The id of the team if document belongs to a team. |
| `payload.documentData.data` | string | The data of the document. | | `payload.templateId` | number? | The id of the template if created from template. |
| `payload.documentData.initialData` | string | The initial data of the document. | | `payload.source` | string | The source of the document (e.g., DOCUMENT, TEMPLATE) |
| `payload.Recipient[].id` | number | The id of the recipient. | | `payload.documentMeta.id` | string | The id of the document metadata. |
| `payload.Recipient[].documentId` | number? | The id the document associated with the recipient. | | `payload.documentMeta.subject` | string? | The subject of the document. |
| `payload.Recipient[].templateId` | number? | The template identifier for the recipient. | | `payload.documentMeta.message` | string? | The message associated with the document. |
| `payload.Recipient[].email` | string | The email address of the recipient. | | `payload.documentMeta.timezone` | string | The timezone setting for the document. |
| `payload.Recipient[].name` | string | The name of the recipient. | | `payload.documentMeta.password` | string? | The password protection if set. |
| `payload.Recipient[].token` | string | The token associated with the recipient. | | `payload.documentMeta.dateFormat` | string | The date format used in the document. |
| `payload.Recipient[].expired` | datetime? | The expiration status of the recipient. | | `payload.documentMeta.redirectUrl` | string? | The URL to redirect after signing. |
| `payload.Recipient[].signedAt` | datetime? | The date and time the recipient signed the document. | | `payload.documentMeta.signingOrder` | string | The signing order (e.g., PARALLEL, SEQUENTIAL). |
| `payload.Recipient[].authOptions.accessAuth` | json? | Access authentication options. | | `payload.documentMeta.typedSignatureEnabled` | boolean | Whether typed signatures are enabled. |
| `payload.Recipient[].authOptions.actionAuth` | json? | Action authentication options. | | `payload.documentMeta.language` | string | The language of the document. |
| `payload.Recipient[].role` | string | The role of the recipient. | | `payload.documentMeta.distributionMethod` | string | The method of distributing the document. |
| `payload.Recipient[].readStatus` | string | The read status of the document by the recipient. | | `payload.documentMeta.emailSettings` | json? | Email notification settings. |
| `payload.Recipient[].signingStatus` | string | The signing status of the recipient. | | `payload.Recipient[].id` | number | The id of the recipient. |
| `payload.Recipient[].sendStatus` | string | The send status of the document to the recipient. | | `payload.Recipient[].documentId` | number? | The id of the document for this recipient. |
| `createdAt` | datetime | The creation date and time of the webhook event. | | `payload.Recipient[].templateId` | number? | The template id if from a template. |
| `webhookEndpoint` | string | The endpoint URL where the webhook is sent. | | `payload.Recipient[].email` | string | The email address of the recipient. |
| `payload.Recipient[].name` | string | The name of the recipient. |
## Webhook event payload example | `payload.Recipient[].token` | string | The unique token for this recipient. |
| `payload.Recipient[].documentDeletedAt` | datetime? | When the document was deleted for this recipient. |
When an event that you have subscribed to occurs, Documenso will send a POST request to the specified webhook URL with a payload containing information about the event. | `payload.Recipient[].expired` | datetime? | When the recipient's access expired. |
| `payload.Recipient[].signedAt` | datetime? | When the recipient signed the document. |
| `payload.Recipient[].authOptions` | json? | Authentication options for this recipient. |
| `payload.Recipient[].signingOrder` | number? | The order in which this recipient should sign. |
| `payload.Recipient[].rejectionReason` | string? | The reason if the recipient rejected the document. |
| `payload.Recipient[].role` | string | The role of the recipient (e.g., SIGNER, VIEWER). |
| `payload.Recipient[].readStatus` | string | Whether the recipient has read the document. |
| `payload.Recipient[].signingStatus` | string | The signing status of this recipient. |
| `payload.Recipient[].sendStatus` | string | The sending status for this recipient. |
| `createdAt` | datetime | The creation date and time of the webhook event. |
| `webhookEndpoint` | string | The endpoint URL where the webhook is sent. |
## Example payloads ## Example payloads
@ -104,9 +115,11 @@ Example payload for the `document.created` event:
"event": "DOCUMENT_CREATED", "event": "DOCUMENT_CREATED",
"payload": { "payload": {
"id": 10, "id": 10,
"externalId": null,
"userId": 1, "userId": 1,
"authOptions": null, "authOptions": null,
"formValues": null, "formValues": null,
"visibility": "EVERYONE",
"title": "documenso.pdf", "title": "documenso.pdf",
"status": "DRAFT", "status": "DRAFT",
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0", "documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
@ -114,7 +127,43 @@ Example payload for the `document.created` event:
"updatedAt": "2024-04-22T11:44:43.341Z", "updatedAt": "2024-04-22T11:44:43.341Z",
"completedAt": null, "completedAt": null,
"deletedAt": null, "deletedAt": null,
"teamId": null "teamId": null,
"templateId": null,
"source": "DOCUMENT",
"documentMeta": {
"id": "doc_meta_123",
"subject": "Please sign this document",
"message": "Hello, please review and sign this document.",
"timezone": "UTC",
"password": null,
"dateFormat": "MM/DD/YYYY",
"redirectUrl": null,
"signingOrder": "PARALLEL",
"typedSignatureEnabled": true,
"language": "en",
"distributionMethod": "EMAIL",
"emailSettings": null
},
"Recipient": [
{
"id": 52,
"documentId": 10,
"templateId": null,
"email": "signer@documenso.com",
"name": "John Doe",
"token": "vbT8hi3jKQmrFP_LN1WcS",
"documentDeletedAt": null,
"expired": null,
"signedAt": null,
"authOptions": null,
"signingOrder": 1,
"rejectionReason": null,
"role": "SIGNER",
"readStatus": "NOT_OPENED",
"signingStatus": "NOT_SIGNED",
"sendStatus": "NOT_SENT"
}
]
}, },
"createdAt": "2024-04-22T11:44:44.779Z", "createdAt": "2024-04-22T11:44:44.779Z",
"webhookEndpoint": "https://mywebhooksite.com/mywebhook" "webhookEndpoint": "https://mywebhooksite.com/mywebhook"
@ -128,9 +177,11 @@ Example payload for the `document.sent` event:
"event": "DOCUMENT_SENT", "event": "DOCUMENT_SENT",
"payload": { "payload": {
"id": 10, "id": 10,
"externalId": null,
"userId": 1, "userId": 1,
"authOptions": null, "authOptions": null,
"formValues": null, "formValues": null,
"visibility": "EVERYONE",
"title": "documenso.pdf", "title": "documenso.pdf",
"status": "PENDING", "status": "PENDING",
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0", "documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
@ -139,6 +190,22 @@ Example payload for the `document.sent` event:
"completedAt": null, "completedAt": null,
"deletedAt": null, "deletedAt": null,
"teamId": null, "teamId": null,
"templateId": null,
"source": "DOCUMENT",
"documentMeta": {
"id": "doc_meta_123",
"subject": "Please sign this document",
"message": "Hello, please review and sign this document.",
"timezone": "UTC",
"password": null,
"dateFormat": "MM/DD/YYYY",
"redirectUrl": null,
"signingOrder": "PARALLEL",
"typedSignatureEnabled": true,
"language": "en",
"distributionMethod": "EMAIL",
"emailSettings": null
},
"Recipient": [ "Recipient": [
{ {
"id": 52, "id": 52,
@ -147,12 +214,12 @@ Example payload for the `document.sent` event:
"email": "signer2@documenso.com", "email": "signer2@documenso.com",
"name": "Signer 2", "name": "Signer 2",
"token": "vbT8hi3jKQmrFP_LN1WcS", "token": "vbT8hi3jKQmrFP_LN1WcS",
"documentDeletedAt": null,
"expired": null, "expired": null,
"signedAt": null, "signedAt": null,
"authOptions": { "authOptions": null,
"accessAuth": null, "signingOrder": 1,
"actionAuth": null "rejectionReason": null,
},
"role": "VIEWER", "role": "VIEWER",
"readStatus": "NOT_OPENED", "readStatus": "NOT_OPENED",
"signingStatus": "NOT_SIGNED", "signingStatus": "NOT_SIGNED",
@ -165,12 +232,12 @@ Example payload for the `document.sent` event:
"email": "signer1@documenso.com", "email": "signer1@documenso.com",
"name": "Signer 1", "name": "Signer 1",
"token": "HkrptwS42ZBXdRKj1TyUo", "token": "HkrptwS42ZBXdRKj1TyUo",
"documentDeletedAt": null,
"expired": null, "expired": null,
"signedAt": null, "signedAt": null,
"authOptions": { "authOptions": null,
"accessAuth": null, "signingOrder": 2,
"actionAuth": null "rejectionReason": null,
},
"role": "SIGNER", "role": "SIGNER",
"readStatus": "NOT_OPENED", "readStatus": "NOT_OPENED",
"signingStatus": "NOT_SIGNED", "signingStatus": "NOT_SIGNED",
@ -190,9 +257,11 @@ Example payload for the `document.opened` event:
"event": "DOCUMENT_OPENED", "event": "DOCUMENT_OPENED",
"payload": { "payload": {
"id": 10, "id": 10,
"externalId": null,
"userId": 1, "userId": 1,
"authOptions": null, "authOptions": null,
"formValues": null, "formValues": null,
"visibility": "EVERYONE",
"title": "documenso.pdf", "title": "documenso.pdf",
"status": "PENDING", "status": "PENDING",
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0", "documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
@ -201,6 +270,22 @@ Example payload for the `document.opened` event:
"completedAt": null, "completedAt": null,
"deletedAt": null, "deletedAt": null,
"teamId": null, "teamId": null,
"templateId": null,
"source": "DOCUMENT",
"documentMeta": {
"id": "doc_meta_123",
"subject": "Please sign this document",
"message": "Hello, please review and sign this document.",
"timezone": "UTC",
"password": null,
"dateFormat": "MM/DD/YYYY",
"redirectUrl": null,
"signingOrder": "PARALLEL",
"typedSignatureEnabled": true,
"language": "en",
"distributionMethod": "EMAIL",
"emailSettings": null
},
"Recipient": [ "Recipient": [
{ {
"id": 52, "id": 52,
@ -209,24 +294,18 @@ Example payload for the `document.opened` event:
"email": "signer2@documenso.com", "email": "signer2@documenso.com",
"name": "Signer 2", "name": "Signer 2",
"token": "vbT8hi3jKQmrFP_LN1WcS", "token": "vbT8hi3jKQmrFP_LN1WcS",
"documentDeletedAt": null,
"expired": null, "expired": null,
"signedAt": null, "signedAt": null,
"authOptions": { "authOptions": null,
"accessAuth": null, "signingOrder": 1,
"actionAuth": null "rejectionReason": null,
},
"role": "VIEWER", "role": "VIEWER",
"readStatus": "OPENED", "readStatus": "OPENED",
"signingStatus": "NOT_SIGNED", "signingStatus": "NOT_SIGNED",
"sendStatus": "SENT" "sendStatus": "SENT"
} }
], ]
"documentData": {
"id": "hs8qz1ktr9204jn7mg6c5dxy0",
"type": "S3_PATH",
"data": "9753/xzqrshtlpokm/documenso.pdf",
"initialData": "9753/xzqrshtlpokm/documenso.pdf"
}
}, },
"createdAt": "2024-04-22T11:50:26.174Z", "createdAt": "2024-04-22T11:50:26.174Z",
"webhookEndpoint": "https://mywebhooksite.com/mywebhook" "webhookEndpoint": "https://mywebhooksite.com/mywebhook"
@ -240,9 +319,11 @@ Example payload for the `document.signed` event:
"event": "DOCUMENT_SIGNED", "event": "DOCUMENT_SIGNED",
"payload": { "payload": {
"id": 10, "id": 10,
"externalId": null,
"userId": 1, "userId": 1,
"authOptions": null, "authOptions": null,
"formValues": null, "formValues": null,
"visibility": "EVERYONE",
"title": "documenso.pdf", "title": "documenso.pdf",
"status": "COMPLETED", "status": "COMPLETED",
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0", "documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
@ -251,6 +332,22 @@ Example payload for the `document.signed` event:
"completedAt": "2024-04-22T11:52:05.707Z", "completedAt": "2024-04-22T11:52:05.707Z",
"deletedAt": null, "deletedAt": null,
"teamId": null, "teamId": null,
"templateId": null,
"source": "DOCUMENT",
"documentMeta": {
"id": "doc_meta_123",
"subject": "Please sign this document",
"message": "Hello, please review and sign this document.",
"timezone": "UTC",
"password": null,
"dateFormat": "MM/DD/YYYY",
"redirectUrl": null,
"signingOrder": "PARALLEL",
"typedSignatureEnabled": true,
"language": "en",
"distributionMethod": "EMAIL",
"emailSettings": null
},
"Recipient": [ "Recipient": [
{ {
"id": 51, "id": 51,
@ -259,12 +356,15 @@ Example payload for the `document.signed` event:
"email": "signer1@documenso.com", "email": "signer1@documenso.com",
"name": "Signer 1", "name": "Signer 1",
"token": "HkrptwS42ZBXdRKj1TyUo", "token": "HkrptwS42ZBXdRKj1TyUo",
"documentDeletedAt": null,
"expired": null, "expired": null,
"signedAt": "2024-04-22T11:52:05.688Z", "signedAt": "2024-04-22T11:52:05.688Z",
"authOptions": { "authOptions": {
"accessAuth": null, "accessAuth": null,
"actionAuth": null "actionAuth": null
}, },
"signingOrder": 1,
"rejectionReason": null,
"role": "SIGNER", "role": "SIGNER",
"readStatus": "OPENED", "readStatus": "OPENED",
"signingStatus": "SIGNED", "signingStatus": "SIGNED",
@ -284,9 +384,11 @@ Example payload for the `document.completed` event:
"event": "DOCUMENT_COMPLETED", "event": "DOCUMENT_COMPLETED",
"payload": { "payload": {
"id": 10, "id": 10,
"externalId": null,
"userId": 1, "userId": 1,
"authOptions": null, "authOptions": null,
"formValues": null, "formValues": null,
"visibility": "EVERYONE",
"title": "documenso.pdf", "title": "documenso.pdf",
"status": "COMPLETED", "status": "COMPLETED",
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0", "documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
@ -295,11 +397,21 @@ Example payload for the `document.completed` event:
"completedAt": "2024-04-22T11:52:05.707Z", "completedAt": "2024-04-22T11:52:05.707Z",
"deletedAt": null, "deletedAt": null,
"teamId": null, "teamId": null,
"documentData": { "templateId": null,
"id": "hs8qz1ktr9204jn7mg6c5dxy0", "source": "DOCUMENT",
"type": "S3_PATH", "documentMeta": {
"data": "bk9p1h7x0s3m/documenso-signed.pdf", "id": "doc_meta_123",
"initialData": "9753/xzqrshtlpokm/documenso.pdf" "subject": "Please sign this document",
"message": "Hello, please review and sign this document.",
"timezone": "UTC",
"password": null,
"dateFormat": "MM/DD/YYYY",
"redirectUrl": null,
"signingOrder": "PARALLEL",
"typedSignatureEnabled": true,
"language": "en",
"distributionMethod": "EMAIL",
"emailSettings": null
}, },
"Recipient": [ "Recipient": [
{ {
@ -309,12 +421,15 @@ Example payload for the `document.completed` event:
"email": "signer2@documenso.com", "email": "signer2@documenso.com",
"name": "Signer 2", "name": "Signer 2",
"token": "vbT8hi3jKQmrFP_LN1WcS", "token": "vbT8hi3jKQmrFP_LN1WcS",
"documentDeletedAt": null,
"expired": null, "expired": null,
"signedAt": "2024-04-22T11:51:10.055Z", "signedAt": "2024-04-22T11:51:10.055Z",
"authOptions": { "authOptions": {
"accessAuth": null, "accessAuth": null,
"actionAuth": null "actionAuth": null
}, },
"signingOrder": 1,
"rejectionReason": null,
"role": "VIEWER", "role": "VIEWER",
"readStatus": "OPENED", "readStatus": "OPENED",
"signingStatus": "SIGNED", "signingStatus": "SIGNED",
@ -327,12 +442,15 @@ Example payload for the `document.completed` event:
"email": "signer1@documenso.com", "email": "signer1@documenso.com",
"name": "Signer 1", "name": "Signer 1",
"token": "HkrptwS42ZBXdRKj1TyUo", "token": "HkrptwS42ZBXdRKj1TyUo",
"documentDeletedAt": null,
"expired": null, "expired": null,
"signedAt": "2024-04-22T11:52:05.688Z", "signedAt": "2024-04-22T11:52:05.688Z",
"authOptions": { "authOptions": {
"accessAuth": null, "accessAuth": null,
"actionAuth": null "actionAuth": null
}, },
"signingOrder": 2,
"rejectionReason": null,
"role": "SIGNER", "role": "SIGNER",
"readStatus": "OPENED", "readStatus": "OPENED",
"signingStatus": "SIGNED", "signingStatus": "SIGNED",
@ -345,6 +463,71 @@ Example payload for the `document.completed` event:
} }
``` ```
Example payload for the `document.rejected` event:
```json
{
"event": "DOCUMENT_REJECTED",
"payload": {
"id": 10,
"externalId": null,
"userId": 1,
"authOptions": null,
"formValues": null,
"visibility": "EVERYONE",
"title": "documenso.pdf",
"status": "PENDING",
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
"createdAt": "2024-04-22T11:44:43.341Z",
"updatedAt": "2024-04-22T11:48:07.569Z",
"completedAt": null,
"deletedAt": null,
"teamId": null,
"templateId": null,
"source": "DOCUMENT",
"documentMeta": {
"id": "doc_meta_123",
"subject": "Please sign this document",
"message": "Hello, please review and sign this document.",
"timezone": "UTC",
"password": null,
"dateFormat": "MM/DD/YYYY",
"redirectUrl": null,
"signingOrder": "PARALLEL",
"typedSignatureEnabled": true,
"language": "en",
"distributionMethod": "EMAIL",
"emailSettings": null
},
"Recipient": [
{
"id": 52,
"documentId": 10,
"templateId": null,
"email": "signer@documenso.com",
"name": "Signer",
"token": "vbT8hi3jKQmrFP_LN1WcS",
"documentDeletedAt": null,
"expired": null,
"signedAt": "2024-04-22T11:48:07.569Z",
"authOptions": {
"accessAuth": null,
"actionAuth": null
},
"signingOrder": 1,
"rejectionReason": "I do not agree with the terms",
"role": "SIGNER",
"readStatus": "OPENED",
"signingStatus": "REJECTED",
"sendStatus": "SENT"
}
]
},
"createdAt": "2024-04-22T11:48:07.945Z",
"webhookEndpoint": "https://mywebhooksite.com/mywebhook"
}
```
## Availability ## Availability
Webhooks are available to individual users and teams. Webhooks are available to individual users and teams.

View File

@ -10,7 +10,6 @@
"signing-documents": "Signing Documents", "signing-documents": "Signing Documents",
"templates": "Templates", "templates": "Templates",
"direct-links": "Direct Signing Links", "direct-links": "Direct Signing Links",
"document-visibility": "Document Visibility",
"teams": "Teams", "teams": "Teams",
"-- Legal Overview": { "-- Legal Overview": {
"type": "separator", "type": "separator",

View File

@ -1,5 +1,6 @@
{ {
"general-settings": "General Settings", "preferences": "Preferences",
"document-visibility": "Document Visibility", "document-visibility": "Document Visibility",
"sender-details": "Email Sender Details" "sender-details": "Email Sender Details",
"branding-preferences": "Branding Preferences"
} }

View File

@ -0,0 +1,16 @@
---
title: Branding Preferences
description: Learn how to set the branding preferences for your team account.
---
# Branding Preferences
You can set the branding preferences for your team account by going to the **Branding Preferences** tab in the team's settings dashboard.
![A screenshot of the team's branding preferences page](/teams/team-branding-preferences.webp)
On this page, you can:
- **Upload a Logo** - Upload your team's logo to be displayed instead of the default Documenso logo.
- **Set the Brand Website** - Enter the URL of your team's website to be displayed in the email communications sent by the team.
- **Add Additional Brand Details** - You can add additional information to display at the bottom of the emails sent by the team. This can include contact information, social media links, and other relevant details.

View File

@ -13,9 +13,9 @@ The default document visibility option allows you to control who can view and ac
- **Managers and above** - The document is visible to team members with the role of _Manager or above_ and _Admin_. - **Managers and above** - The document is visible to team members with the role of _Manager or above_ and _Admin_.
- **Admin only** - The document is only visible to the team's admins. - **Admin only** - The document is only visible to the team's admins.
![A screenshot of the document visibility selector from the team's general settings page](/teams/team-general-settings-document-visibility-select.webp) ![A screenshot of the document visibility selector from the team's global preferences page](/teams/team-preferences-document-visibility.webp)
The default document visibility is set to "_EVERYONE_" by default. You can change this setting by going to the [team's general settings page](/users/teams/general-settings) and selecting a different visibility option. The default document visibility is set to "_EVERYONE_" by default. You can change this setting by going to the [team's general preferences page](/users/teams/preferences) and selecting a different visibility option.
<Callout type="warning"> <Callout type="warning">
If the team member uploading the document has a role lower than the default document visibility, If the team member uploading the document has a role lower than the default document visibility,

View File

@ -1,15 +0,0 @@
---
title: General Settings
description: Learn how to manage your team's General settings.
---
# General Settings
You can manage your team's general settings by clicking on the **General Settings** tab in the team's settings dashboard.
![A screenshot of team's General settings page](/teams/team-general-settings.webp)
The general settings page allows you to update the following settings:
- **Document Visibility** - Set the default visibility of the documents created by team members. Learn more about [document visibility](/users/teams/document-visibility).
- **Sender Details** - Set whether the sender's name should be included in the emails sent by the team. Learn more about [sender details](/users/teams/sender-details).

View File

@ -0,0 +1,19 @@
---
title: Preferences
description: Learn how to manage your team's global preferences.
---
# Preferences
You can manage your team's global preferences by clicking on the **Preferences** tab in the team's settings dashboard.
![A screenshot of the team's global preferences page](/teams/team-preferences.webp)
The preferences page allows you to update the following settings:
- **Document Visibility** - Set the default visibility of the documents created by team members. Learn more about [document visibility](/users/teams/document-visibility).
- **Default Document Language** - This setting allows you to set the default language for the documents uploaded in the team account. The default language is used as the default language in the email communications with the document recipients. You can change the language for individual documents when uploading them.
- **Sender Details** - Set whether the sender's name should be included in the emails sent by the team. Learn more about [sender details](/users/teams/sender-details).
- **Typed Signature** - It controls whether the document recipients can sign the documents with a typed signature or not. If enabled, the recipients can sign the document using either a drawn or a typed signature. If disabled, the recipients can only sign the documents usign a drawn signature. This setting can also be changed for individual documents when uploading them.
- **Include the Signing Certificate** - This setting controls whether the signing certificate should be included in the signed documents. If enabled, the signing certificate is included in the signed documents. If disabled, the signing certificate is not included in the signed documents. Regardless of this setting, the signing certificate is always available in the document's audit log page.
- **Branding Preferences** - Set the branding preferences and defaults for the team account. Learn more about [branding preferences](/users/teams/branding-preferences).

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

View File

@ -1,6 +1,6 @@
{ {
"name": "@documenso/marketing", "name": "@documenso/marketing",
"version": "1.8.1-rc.4", "version": "1.8.1-rc.9",
"private": true, "private": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@documenso/web", "name": "@documenso/web",
"version": "1.8.1-rc.4", "version": "1.8.1-rc.9",
"private": true, "private": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {

View File

@ -0,0 +1,169 @@
'use client';
import { useEffect, useMemo, useState, useTransition } from 'react';
import { msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { ChevronDownIcon as CaretSortIcon, Loader } from 'lucide-react';
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
import { DataTable } from '@documenso/ui/primitives/data-table';
import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination';
import { Input } from '@documenso/ui/primitives/input';
export type SigningVolume = {
id: number;
name: string;
signingVolume: number;
createdAt: Date;
planId: string;
};
type LeaderboardTableProps = {
signingVolume: SigningVolume[];
totalPages: number;
perPage: number;
page: number;
sortBy: 'name' | 'createdAt' | 'signingVolume';
sortOrder: 'asc' | 'desc';
};
export const LeaderboardTable = ({
signingVolume,
totalPages,
perPage,
page,
sortBy,
sortOrder,
}: LeaderboardTableProps) => {
const { _, i18n } = useLingui();
const [isPending, startTransition] = useTransition();
const updateSearchParams = useUpdateSearchParams();
const [searchString, setSearchString] = useState('');
const debouncedSearchString = useDebouncedValue(searchString, 1000);
const columns = useMemo(() => {
return [
{
header: () => (
<div
className="flex cursor-pointer items-center"
onClick={() => handleColumnSort('name')}
>
{_(msg`Name`)}
<CaretSortIcon className="ml-2 h-4 w-4" />
</div>
),
accessorKey: 'name',
cell: ({ row }) => {
return (
<div>
<a
className="text-primary underline"
href={`https://dashboard.stripe.com/subscriptions/${row.original.planId}`}
target="_blank"
>
{row.getValue('name')}
</a>
</div>
);
},
size: 250,
},
{
header: () => (
<div
className="flex cursor-pointer items-center"
onClick={() => handleColumnSort('signingVolume')}
>
{_(msg`Signing Volume`)}
<CaretSortIcon className="ml-2 h-4 w-4" />
</div>
),
accessorKey: 'signingVolume',
cell: ({ row }) => <div>{Number(row.getValue('signingVolume'))}</div>,
},
{
header: () => {
return (
<div
className="flex cursor-pointer items-center"
onClick={() => handleColumnSort('createdAt')}
>
{_(msg`Created`)}
<CaretSortIcon className="ml-2 h-4 w-4" />
</div>
);
},
accessorKey: 'createdAt',
cell: ({ row }) => i18n.date(row.original.createdAt),
},
] satisfies DataTableColumnDef<SigningVolume>[];
}, [sortOrder]);
useEffect(() => {
startTransition(() => {
updateSearchParams({
search: debouncedSearchString,
page: 1,
perPage,
sortBy,
sortOrder,
});
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedSearchString]);
const onPaginationChange = (page: number, perPage: number) => {
startTransition(() => {
updateSearchParams({
page,
perPage,
});
});
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setSearchString(e.target.value);
};
const handleColumnSort = (column: 'name' | 'createdAt' | 'signingVolume') => {
startTransition(() => {
updateSearchParams({
sortBy: column,
sortOrder: sortOrder === 'asc' ? 'desc' : 'asc',
});
});
};
return (
<div className="relative">
<Input
className="my-6 flex flex-row gap-4"
type="text"
placeholder={_(msg`Search by name or email`)}
value={searchString}
onChange={handleChange}
/>
<DataTable
columns={columns}
data={signingVolume}
perPage={perPage}
currentPage={page}
totalPages={totalPages}
onPaginationChange={onPaginationChange}
>
{(table) => <DataTablePagination additionalInformation="VisibleCount" table={table} />}
</DataTable>
{isPending && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" />
</div>
)}
</div>
);
};

View File

@ -0,0 +1,25 @@
'use server';
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
import { getSigningVolume } from '@documenso/lib/server-only/admin/get-signing-volume';
type SearchOptions = {
search: string;
page: number;
perPage: number;
sortBy: 'name' | 'createdAt' | 'signingVolume';
sortOrder: 'asc' | 'desc';
};
export async function search({ search, page, perPage, sortBy, sortOrder }: SearchOptions) {
const { user } = await getRequiredServerComponentSession();
if (!isAdmin(user)) {
throw new Error('Unauthorized');
}
const results = await getSigningVolume({ search, page, perPage, sortBy, sortOrder });
return results;
}

View File

@ -0,0 +1,60 @@
import { Trans } from '@lingui/macro';
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
import { LeaderboardTable } from './data-table-leaderboard';
import { search } from './fetch-leaderboard.actions';
type AdminLeaderboardProps = {
searchParams?: {
search?: string;
page?: number;
perPage?: number;
sortBy?: 'name' | 'createdAt' | 'signingVolume';
sortOrder?: 'asc' | 'desc';
};
};
export default async function Leaderboard({ searchParams = {} }: AdminLeaderboardProps) {
await setupI18nSSR();
const { user } = await getRequiredServerComponentSession();
if (!isAdmin(user)) {
throw new Error('Unauthorized');
}
const page = Number(searchParams.page) || 1;
const perPage = Number(searchParams.perPage) || 10;
const searchString = searchParams.search || '';
const sortBy = searchParams.sortBy || 'signingVolume';
const sortOrder = searchParams.sortOrder || 'desc';
const { leaderboard: signingVolume, totalPages } = await search({
search: searchString,
page,
perPage,
sortBy,
sortOrder,
});
return (
<div>
<h2 className="text-4xl font-semibold">
<Trans>Signing Volume</Trans>
</h2>
<div className="mt-8">
<LeaderboardTable
signingVolume={signingVolume}
totalPages={totalPages}
page={page}
perPage={perPage}
sortBy={sortBy}
sortOrder={sortOrder}
/>
</div>
</div>
);
}

View File

@ -6,7 +6,7 @@ import Link from 'next/link';
import { usePathname } from 'next/navigation'; import { usePathname } from 'next/navigation';
import { Trans } from '@lingui/macro'; import { Trans } from '@lingui/macro';
import { BarChart3, FileStack, Settings, Users, Wallet2 } from 'lucide-react'; import { BarChart3, FileStack, Settings, Trophy, Users, Wallet2 } from 'lucide-react';
import { cn } from '@documenso/ui/lib/utils'; import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
@ -80,6 +80,20 @@ export const AdminNav = ({ className, ...props }: AdminNavProps) => {
</Link> </Link>
</Button> </Button>
<Button
variant="ghost"
className={cn(
'justify-start md:w-full',
pathname?.startsWith('/admin/leaderboard') && 'bg-secondary',
)}
asChild
>
<Link href="/admin/leaderboard">
<Trophy className="mr-2 h-5 w-5" />
<Trans>Leaderboard</Trans>
</Link>
</Button>
<Button <Button
variant="ghost" variant="ghost"
className={cn( className={cn(

View File

@ -41,7 +41,7 @@ export default async function BillingSettingsPage() {
const [subscriptions, prices, primaryAccountPlanPrices] = await Promise.all([ const [subscriptions, prices, primaryAccountPlanPrices] = await Promise.all([
getSubscriptionsByUserId({ userId: user.id }), getSubscriptionsByUserId({ userId: user.id }),
getPricesByInterval({ plan: STRIPE_PLAN_TYPE.REGULAR }), getPricesByInterval({ plans: [STRIPE_PLAN_TYPE.REGULAR, STRIPE_PLAN_TYPE.PLATFORM] }),
getPrimaryAccountPlanPrices(), getPrimaryAccountPlanPrices(),
]); ]);

View File

@ -80,7 +80,7 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen
return ( return (
<EmbedAuthenticateView <EmbedAuthenticateView
email={user?.email || recipient.email} email={user?.email || recipient.email}
returnTo={`/embed/direct/${token}`} returnTo={`/embed/sign/${token}`}
/> />
); );
} }

View File

@ -78,13 +78,14 @@ async function middleware(req: NextRequest): Promise<NextResponse> {
if (req.nextUrl.pathname.startsWith('/embed')) { if (req.nextUrl.pathname.startsWith('/embed')) {
const res = NextResponse.next(); const res = NextResponse.next();
const origin = req.headers.get('Origin') ?? '*';
// Allow third parties to iframe the document. // Allow third parties to iframe the document.
res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.headers.set('Access-Control-Allow-Origin', '*'); res.headers.set('Access-Control-Allow-Origin', origin);
res.headers.set('Content-Security-Policy', 'frame-ancestors *'); res.headers.set('Content-Security-Policy', `frame-ancestors ${origin}`);
res.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin'); res.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
res.headers.set('X-Content-Type-Options', 'nosniff'); res.headers.set('X-Content-Type-Options', 'nosniff');
res.headers.set('X-Frame-Options', 'ALLOW-ALL');
return res; return res;
} }

8
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@documenso/root", "name": "@documenso/root",
"version": "1.8.1-rc.4", "version": "1.8.1-rc.9",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@documenso/root", "name": "@documenso/root",
"version": "1.8.1-rc.4", "version": "1.8.1-rc.9",
"workspaces": [ "workspaces": [
"apps/*", "apps/*",
"packages/*" "packages/*"
@ -77,7 +77,7 @@
}, },
"apps/marketing": { "apps/marketing": {
"name": "@documenso/marketing", "name": "@documenso/marketing",
"version": "1.8.1-rc.4", "version": "1.8.1-rc.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@documenso/assets": "*", "@documenso/assets": "*",
@ -438,7 +438,7 @@
}, },
"apps/web": { "apps/web": {
"name": "@documenso/web", "name": "@documenso/web",
"version": "1.8.1-rc.4", "version": "1.8.1-rc.9",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@documenso/api": "*", "@documenso/api": "*",

View File

@ -1,6 +1,6 @@
{ {
"private": true, "private": true,
"version": "1.8.1-rc.4", "version": "1.8.1-rc.9",
"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",

View File

@ -244,18 +244,10 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
} }
const dateFormat = body.meta.dateFormat const dateFormat = body.meta.dateFormat
? DATE_FORMATS.find((format) => format.label === body.meta.dateFormat) ? DATE_FORMATS.find((format) => format.value === body.meta.dateFormat)
: DATE_FORMATS.find((format) => format.value === DEFAULT_DOCUMENT_DATE_FORMAT); : DATE_FORMATS.find((format) => format.value === DEFAULT_DOCUMENT_DATE_FORMAT);
const timezone = body.meta.timezone
? TIME_ZONES.find((tz) => tz === body.meta.timezone)
: DEFAULT_DOCUMENT_TIME_ZONE;
const isDateFormatValid = body.meta.dateFormat if (body.meta.dateFormat && !dateFormat) {
? DATE_FORMATS.some((format) => format.label === dateFormat?.label)
: true;
const isTimeZoneValid = body.meta.timezone ? TIME_ZONES.includes(String(timezone)) : true;
if (!isDateFormatValid) {
return { return {
status: 400, status: 400,
body: { body: {
@ -264,6 +256,12 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
}; };
} }
const timezone = body.meta.timezone
? TIME_ZONES.find((tz) => tz === body.meta.timezone)
: DEFAULT_DOCUMENT_TIME_ZONE;
const isTimeZoneValid = body.meta.timezone ? TIME_ZONES.includes(String(timezone)) : true;
if (!isTimeZoneValid) { if (!isTimeZoneValid) {
return { return {
status: 400, status: 400,

View File

@ -12,10 +12,10 @@ export type GetPricesByIntervalOptions = {
/** /**
* Filter products by their meta 'plan' attribute. * Filter products by their meta 'plan' attribute.
*/ */
plan?: STRIPE_PLAN_TYPE.COMMUNITY | STRIPE_PLAN_TYPE.REGULAR; plans?: STRIPE_PLAN_TYPE[];
}; };
export const getPricesByInterval = async ({ plan }: GetPricesByIntervalOptions = {}) => { export const getPricesByInterval = async ({ plans }: GetPricesByIntervalOptions = {}) => {
let { data: prices } = await stripe.prices.search({ let { data: prices } = await stripe.prices.search({
query: `active:'true' type:'recurring'`, query: `active:'true' type:'recurring'`,
expand: ['data.product'], expand: ['data.product'],
@ -27,7 +27,8 @@ export const getPricesByInterval = async ({ plan }: GetPricesByIntervalOptions =
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const product = price.product as Stripe.Product; const product = price.product as Stripe.Product;
const filter = !plan || product.metadata?.plan === plan; // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const filter = !plans || plans.includes(product.metadata?.plan as STRIPE_PLAN_TYPE);
// Filter out prices for products that are not active. // Filter out prices for products that are not active.
return product.active && filter; return product.active && filter;

View File

@ -21,6 +21,7 @@ import { insertFieldInPDF } from '../../../server-only/pdf/insert-field-in-pdf';
import { normalizeSignatureAppearances } from '../../../server-only/pdf/normalize-signature-appearances'; import { normalizeSignatureAppearances } from '../../../server-only/pdf/normalize-signature-appearances';
import { triggerWebhook } from '../../../server-only/webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../../../server-only/webhooks/trigger/trigger-webhook';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
import { ZWebhookDocumentSchema } from '../../../types/webhook-payload';
import { ZRequestMetadataSchema } from '../../../universal/extract-request-metadata'; import { ZRequestMetadataSchema } from '../../../universal/extract-request-metadata';
import { getFile } from '../../../universal/upload/get-file'; import { getFile } from '../../../universal/upload/get-file';
import { putPdfFile } from '../../../universal/upload/put-file'; import { putPdfFile } from '../../../universal/upload/put-file';
@ -249,13 +250,14 @@ export const SEAL_DOCUMENT_JOB_DEFINITION = {
}, },
include: { include: {
documentData: true, documentData: true,
documentMeta: true,
Recipient: true, Recipient: true,
}, },
}); });
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_COMPLETED, event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
data: updatedDocument, data: ZWebhookDocumentSchema.parse(updatedDocument),
userId: updatedDocument.userId, userId: updatedDocument.userId,
teamId: updatedDocument.teamId ?? undefined, teamId: updatedDocument.teamId ?? undefined,
}); });

View File

@ -26,6 +26,10 @@ import { extractNextAuthRequestMetadata } from '../universal/extract-request-met
import { getAuthenticatorOptions } from '../utils/authenticator'; import { getAuthenticatorOptions } from '../utils/authenticator';
import { ErrorCode } from './error-codes'; import { ErrorCode } from './error-codes';
const useSecureCookies =
process.env.NODE_ENV === 'production' && String(process.env.NEXTAUTH_URL).startsWith('https://');
const cookiePrefix = useSecureCookies ? '__Secure-' : '';
export const NEXT_AUTH_OPTIONS: AuthOptions = { export const NEXT_AUTH_OPTIONS: AuthOptions = {
adapter: PrismaAdapter(prisma), adapter: PrismaAdapter(prisma),
secret: process.env.NEXTAUTH_SECRET ?? 'secret', secret: process.env.NEXTAUTH_SECRET ?? 'secret',
@ -431,5 +435,53 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
return true; return true;
}, },
}, },
cookies: {
sessionToken: {
name: `${cookiePrefix}next-auth.session-token`,
options: {
httpOnly: true,
sameSite: useSecureCookies ? 'none' : 'lax',
path: '/',
secure: useSecureCookies,
},
},
callbackUrl: {
name: `${cookiePrefix}next-auth.callback-url`,
options: {
sameSite: useSecureCookies ? 'none' : 'lax',
path: '/',
secure: useSecureCookies,
},
},
csrfToken: {
// Default to __Host- for CSRF token for additional protection if using useSecureCookies
// NB: The `__Host-` prefix is stricter than the `__Secure-` prefix.
name: `${cookiePrefix}next-auth.csrf-token`,
options: {
httpOnly: true,
sameSite: useSecureCookies ? 'none' : 'lax',
path: '/',
secure: useSecureCookies,
},
},
pkceCodeVerifier: {
name: `${cookiePrefix}next-auth.pkce.code_verifier`,
options: {
httpOnly: true,
sameSite: useSecureCookies ? 'none' : 'lax',
path: '/',
secure: useSecureCookies,
},
},
state: {
name: `${cookiePrefix}next-auth.state`,
options: {
httpOnly: true,
sameSite: useSecureCookies ? 'none' : 'lax',
path: '/',
secure: useSecureCookies,
},
},
},
// Note: `events` are handled in `apps/web/src/pages/api/auth/[...nextauth].ts` to allow access to the request. // Note: `events` are handled in `apps/web/src/pages/api/auth/[...nextauth].ts` to allow access to the request.
}; };

View File

@ -0,0 +1,148 @@
import { prisma } from '@documenso/prisma';
import { Prisma } from '@documenso/prisma/client';
export type SigningVolume = {
id: number;
name: string;
signingVolume: number;
createdAt: Date;
planId: string;
};
export type GetSigningVolumeOptions = {
search?: string;
page?: number;
perPage?: number;
sortBy?: 'name' | 'createdAt' | 'signingVolume';
sortOrder?: 'asc' | 'desc';
};
export async function getSigningVolume({
search = '',
page = 1,
perPage = 10,
sortBy = 'signingVolume',
sortOrder = 'desc',
}: GetSigningVolumeOptions) {
const whereClause = Prisma.validator<Prisma.SubscriptionWhereInput>()({
status: 'ACTIVE',
OR: [
{
User: {
OR: [
{ name: { contains: search, mode: 'insensitive' } },
{ email: { contains: search, mode: 'insensitive' } },
],
},
},
{
team: {
name: { contains: search, mode: 'insensitive' },
},
},
],
});
const orderByClause = getOrderByClause({ sortBy, sortOrder });
const [subscriptions, totalCount] = await Promise.all([
prisma.subscription.findMany({
where: whereClause,
include: {
User: {
include: {
Document: {
where: {
status: 'COMPLETED',
deletedAt: null,
},
},
},
},
team: {
include: {
document: {
where: {
status: 'COMPLETED',
deletedAt: null,
},
},
},
},
},
orderBy: orderByClause,
skip: Math.max(page - 1, 0) * perPage,
take: perPage,
}),
prisma.subscription.count({
where: whereClause,
}),
]);
const leaderboardWithVolume: SigningVolume[] = subscriptions.map((subscription) => {
const name =
subscription.User?.name || subscription.team?.name || subscription.User?.email || 'Unknown';
const userSignedDocs = subscription.User?.Document?.length || 0;
const teamSignedDocs = subscription.team?.document?.length || 0;
return {
id: subscription.id,
name,
signingVolume: userSignedDocs + teamSignedDocs,
createdAt: subscription.createdAt,
planId: subscription.planId,
};
});
return {
leaderboard: leaderboardWithVolume,
totalPages: Math.ceil(totalCount / perPage),
};
}
function getOrderByClause(options: {
sortBy: string;
sortOrder: 'asc' | 'desc';
}): Prisma.SubscriptionOrderByWithRelationInput | Prisma.SubscriptionOrderByWithRelationInput[] {
const { sortBy, sortOrder } = options;
if (sortBy === 'name') {
return [
{
User: {
name: sortOrder,
},
},
{
team: {
name: sortOrder,
},
},
];
}
if (sortBy === 'createdAt') {
return {
createdAt: sortOrder,
};
}
// Default: sort by signing volume
return [
{
User: {
Document: {
_count: sortOrder,
},
},
},
{
team: {
document: {
_count: sortOrder,
},
},
},
];
}

View File

@ -13,6 +13,7 @@ import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { jobs } from '../../jobs/client'; import { jobs } from '../../jobs/client';
import type { TRecipientActionAuth } from '../../types/document-auth'; import type { TRecipientActionAuth } from '../../types/document-auth';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import { getIsRecipientsTurnToSign } from '../recipient/get-is-recipient-turn'; import { getIsRecipientsTurnToSign } from '../recipient/get-is-recipient-turn';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
import { sendPendingEmail } from './send-pending-email'; import { sendPendingEmail } from './send-pending-email';
@ -203,11 +204,19 @@ export const completeDocumentWithToken = async ({
}); });
} }
const updatedDocument = await getDocument({ token, documentId }); const updatedDocument = await prisma.document.findFirstOrThrow({
where: {
id: document.id,
},
include: {
documentMeta: true,
Recipient: true,
},
});
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_SIGNED, event: WebhookTriggerEvents.DOCUMENT_SIGNED,
data: updatedDocument, data: ZWebhookDocumentSchema.parse(updatedDocument),
userId: updatedDocument.userId, userId: updatedDocument.userId,
teamId: updatedDocument.teamId ?? undefined, teamId: updatedDocument.teamId ?? undefined,
}); });

View File

@ -9,6 +9,7 @@ import { DocumentSource, DocumentVisibility, WebhookTriggerEvents } from '@docum
import type { Team, TeamGlobalSettings } from '@documenso/prisma/client'; import type { Team, TeamGlobalSettings } from '@documenso/prisma/client';
import { TeamMemberRole } from '@documenso/prisma/client'; import { TeamMemberRole } from '@documenso/prisma/client';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type CreateDocumentOptions = { export type CreateDocumentOptions = {
@ -135,13 +136,27 @@ export const createDocument = async ({
}), }),
}); });
const createdDocument = await tx.document.findFirst({
where: {
id: document.id,
},
include: {
documentMeta: true,
Recipient: true,
},
});
if (!createdDocument) {
throw new Error('Document not found');
}
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_CREATED, event: WebhookTriggerEvents.DOCUMENT_CREATED,
data: document, data: ZWebhookDocumentSchema.parse(createdDocument),
userId, userId,
teamId, teamId,
}); });
return document; return createdDocument;
}); });
}; };

View File

@ -3,10 +3,13 @@ import { TRPCError } from '@trpc/server';
import { jobs } from '@documenso/lib/jobs/client'; import { jobs } from '@documenso/lib/jobs/client';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type RejectDocumentWithTokenOptions = { export type RejectDocumentWithTokenOptions = {
token: string; token: string;
@ -31,6 +34,8 @@ export async function rejectDocumentWithToken({
Document: { Document: {
include: { include: {
User: true, User: true,
Recipient: true,
documentMeta: true,
}, },
}, },
}, },
@ -45,8 +50,6 @@ export async function rejectDocumentWithToken({
}); });
} }
// Add the audit log entry before updating the recipient
// Update the recipient status to rejected // Update the recipient status to rejected
const [updatedRecipient] = await prisma.$transaction([ const [updatedRecipient] = await prisma.$transaction([
prisma.recipient.update({ prisma.recipient.update({
@ -88,5 +91,28 @@ export async function rejectDocumentWithToken({
}, },
}); });
// Get the updated document with all recipients
const updatedDocument = await prisma.document.findFirst({
where: {
id: document.id,
},
include: {
Recipient: true,
documentMeta: true,
},
});
if (!updatedDocument) {
throw new Error('Document not found after update');
}
// Trigger webhook for document rejection
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_REJECTED,
data: ZWebhookDocumentSchema.parse(updatedDocument),
userId: document.userId,
teamId: document.teamId ?? undefined,
});
return updatedRecipient; return updatedRecipient;
} }

View File

@ -10,6 +10,7 @@ import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/
import { WebhookTriggerEvents } from '@documenso/prisma/client'; import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { signPdf } from '@documenso/signing'; import { signPdf } from '@documenso/signing';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { getFile } from '../../universal/upload/get-file'; import { getFile } from '../../universal/upload/get-file';
import { putPdfFile } from '../../universal/upload/put-file'; import { putPdfFile } from '../../universal/upload/put-file';
@ -199,13 +200,14 @@ export const sealDocument = async ({
}, },
include: { include: {
documentData: true, documentData: true,
documentMeta: true,
Recipient: true, Recipient: true,
}, },
}); });
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_COMPLETED, event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
data: updatedDocument, data: ZWebhookDocumentSchema.parse(updatedDocument),
userId: document.userId, userId: document.userId,
teamId: document.teamId ?? undefined, teamId: document.teamId ?? undefined,
}); });

View File

@ -14,6 +14,7 @@ import { WebhookTriggerEvents } from '@documenso/prisma/client';
import { jobs } from '../../jobs/client'; import { jobs } from '../../jobs/client';
import { extractDerivedDocumentEmailSettings } from '../../types/document-email'; import { extractDerivedDocumentEmailSettings } from '../../types/document-email';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import { getFile } from '../../universal/upload/get-file'; import { getFile } from '../../universal/upload/get-file';
import { insertFormValuesInPdf } from '../pdf/insert-form-values-in-pdf'; import { insertFormValuesInPdf } from '../pdf/insert-form-values-in-pdf';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
@ -236,6 +237,7 @@ export const sendDocument = async ({
status: DocumentStatus.PENDING, status: DocumentStatus.PENDING,
}, },
include: { include: {
documentMeta: true,
Recipient: true, Recipient: true,
}, },
}); });
@ -243,7 +245,7 @@ export const sendDocument = async ({
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_SENT, event: WebhookTriggerEvents.DOCUMENT_SENT,
data: updatedDocument, data: ZWebhookDocumentSchema.parse(updatedDocument),
userId, userId,
teamId, teamId,
}); });

View File

@ -6,8 +6,8 @@ import { ReadStatus } from '@documenso/prisma/client';
import { WebhookTriggerEvents } from '@documenso/prisma/client'; import { WebhookTriggerEvents } from '@documenso/prisma/client';
import type { TDocumentAccessAuthTypes } from '../../types/document-auth'; import type { TDocumentAccessAuthTypes } from '../../types/document-auth';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
import { getDocumentAndRecipientByToken } from './get-document-by-token';
export type ViewedDocumentOptions = { export type ViewedDocumentOptions = {
token: string; token: string;
@ -63,11 +63,23 @@ export const viewedDocument = async ({
}); });
}); });
const document = await getDocumentAndRecipientByToken({ token, requireAccessAuth: false }); const document = await prisma.document.findFirst({
where: {
id: documentId,
},
include: {
documentMeta: true,
Recipient: true,
},
});
if (!document) {
throw new Error('Document not found');
}
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_OPENED, event: WebhookTriggerEvents.DOCUMENT_OPENED,
data: document, data: ZWebhookDocumentSchema.parse(document),
userId: document.userId, userId: document.userId,
teamId: document.teamId ?? undefined, teamId: document.teamId ?? undefined,
}); });

View File

@ -31,6 +31,7 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { TRecipientActionAuthTypes } from '../../types/document-auth'; import type { TRecipientActionAuthTypes } from '../../types/document-auth';
import { DocumentAccessAuth, ZRecipientAuthOptionsSchema } from '../../types/document-auth'; import { DocumentAccessAuth, ZRecipientAuthOptionsSchema } from '../../types/document-auth';
import { ZFieldMetaSchema } from '../../types/field-meta'; import { ZFieldMetaSchema } from '../../types/field-meta';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import type { CreateDocumentAuditLogDataResponse } from '../../utils/document-audit-logs'; import type { CreateDocumentAuditLogDataResponse } from '../../utils/document-audit-logs';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
@ -591,7 +592,7 @@ export const createDocumentFromDirectTemplate = async ({
requestMetadata, requestMetadata,
}); });
const updatedDocument = await prisma.document.findFirstOrThrow({ const createdDocument = await prisma.document.findFirstOrThrow({
where: { where: {
id: documentId, id: documentId,
}, },
@ -603,9 +604,9 @@ export const createDocumentFromDirectTemplate = async ({
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_SIGNED, event: WebhookTriggerEvents.DOCUMENT_SIGNED,
data: updatedDocument, data: ZWebhookDocumentSchema.parse(createdDocument),
userId: updatedDocument.userId, userId: template.userId,
teamId: updatedDocument.teamId ?? undefined, teamId: template.teamId ?? undefined,
}); });
} catch (err) { } catch (err) {
console.error('[CREATE_DOCUMENT_FROM_DIRECT_TEMPLATE]:', err); console.error('[CREATE_DOCUMENT_FROM_DIRECT_TEMPLATE]:', err);

View File

@ -18,6 +18,7 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import { ZRecipientAuthOptionsSchema } from '../../types/document-auth'; import { ZRecipientAuthOptionsSchema } from '../../types/document-auth';
import type { TDocumentEmailSettings } from '../../types/document-email'; import type { TDocumentEmailSettings } from '../../types/document-email';
import { ZFieldMetaSchema } from '../../types/field-meta'; import { ZFieldMetaSchema } from '../../types/field-meta';
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import { import {
@ -291,9 +292,23 @@ export const createDocumentFromTemplate = async ({
}), }),
}); });
const createdDocument = await tx.document.findFirst({
where: {
id: document.id,
},
include: {
documentMeta: true,
Recipient: true,
},
});
if (!createdDocument) {
throw new Error('Document not found');
}
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_CREATED, event: WebhookTriggerEvents.DOCUMENT_CREATED,
data: document, data: ZWebhookDocumentSchema.parse(createdDocument),
userId, userId,
teamId, teamId,
}); });

View File

@ -60,14 +60,19 @@ msgstr "{0} von {1} Zeile(n) ausgewählt."
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:136 #: packages/lib/jobs/definitions/emails/send-signing-email.ts:136
#: packages/lib/server-only/document/resend-document.tsx:137 #: packages/lib/server-only/document/resend-document.tsx:137
msgid "{0} on behalf of {1} has invited you to {recipientActionVerb} the document \"{2}\"." msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
msgstr "{0} hat dich im Namen von {1} eingeladen, das Dokument \"{2}\" {recipientActionVerb}." msgstr ""
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:136
#: packages/lib/server-only/document/resend-document.tsx:137
#~ msgid "{0} on behalf of {1} has invited you to {recipientActionVerb} the document \"{2}\"."
#~ msgstr "{0} hat dich im Namen von {1} eingeladen, das Dokument \"{2}\" {recipientActionVerb}."
#: packages/email/template-components/template-document-invite.tsx:51 #: packages/email/template-components/template-document-invite.tsx:51
#~ msgid "{0}<0/>\"{documentName}\"" #~ msgid "{0}<0/>\"{documentName}\""
#~ msgstr "{0}<0/>\"{documentName}\"" #~ msgstr "{0}<0/>\"{documentName}\""
#: packages/email/templates/document-invite.tsx:94 #: packages/email/templates/document-invite.tsx:95
msgid "{inviterName} <0>({inviterEmail})</0>" msgid "{inviterName} <0>({inviterEmail})</0>"
msgstr "{inviterName} <0>({inviterEmail})</0>" msgstr "{inviterName} <0>({inviterEmail})</0>"
@ -87,7 +92,7 @@ msgstr "{inviterName} hat dich eingeladen, {0}<0/>\"{documentName}\""
msgid "{inviterName} has invited you to {action} {documentName}" msgid "{inviterName} has invited you to {action} {documentName}"
msgstr "{inviterName} hat dich eingeladen, {action} {documentName}" msgstr "{inviterName} hat dich eingeladen, {action} {documentName}"
#: packages/email/templates/document-invite.tsx:106 #: packages/email/templates/document-invite.tsx:108
msgid "{inviterName} has invited you to {action} the document \"{documentName}\"." msgid "{inviterName} has invited you to {action} the document \"{documentName}\"."
msgstr "{inviterName} hat Sie eingeladen, das Dokument \"{documentName}\" {action}." msgstr "{inviterName} hat Sie eingeladen, das Dokument \"{documentName}\" {action}."
@ -100,16 +105,24 @@ msgid "{inviterName} has removed you from the document<0/>\"{documentName}\""
msgstr "{inviterName} hat dich aus dem Dokument<0/>\"{documentName}\" entfernt" msgstr "{inviterName} hat dich aus dem Dokument<0/>\"{documentName}\" entfernt"
#: packages/email/template-components/template-document-invite.tsx:63 #: packages/email/template-components/template-document-invite.tsx:63
msgid "{inviterName} on behalf of {teamName} has invited you to {0}" msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {0}"
msgstr "{inviterName} im Namen von {teamName} hat Sie eingeladen, {0}" msgstr ""
#: packages/email/templates/document-invite.tsx:45
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
msgstr ""
#: packages/email/template-components/template-document-invite.tsx:63
#~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}"
#~ msgstr "{inviterName} im Namen von {teamName} hat Sie eingeladen, {0}"
#: packages/email/template-components/template-document-invite.tsx:49 #: packages/email/template-components/template-document-invite.tsx:49
#~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\"" #~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\""
#~ msgstr "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\"" #~ msgstr "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\""
#: packages/email/templates/document-invite.tsx:45 #: packages/email/templates/document-invite.tsx:45
msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}" #~ msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}"
msgstr "{inviterName} hat dich im Namen von {teamName} eingeladen, {action} {documentName}" #~ msgstr "{inviterName} hat dich im Namen von {teamName} eingeladen, {action} {documentName}"
#: packages/email/templates/team-join.tsx:67 #: packages/email/templates/team-join.tsx:67
msgid "{memberEmail} joined the following team" msgid "{memberEmail} joined the following team"
@ -568,7 +581,7 @@ msgstr "Unterschrift löschen"
#: packages/ui/primitives/document-flow/add-signature.tsx:394 #: packages/ui/primitives/document-flow/add-signature.tsx:394
msgid "Click to insert field" msgid "Click to insert field"
msgstr "Klicken, um das Feld einzufügen" msgstr "Klicken, um das Feld auszufüllen"
#: packages/ui/primitives/document-flow/missing-signature-field-dialog.tsx:44 #: packages/ui/primitives/document-flow/missing-signature-field-dialog.tsx:44
msgid "Close" msgid "Close"
@ -711,7 +724,7 @@ msgid "Document created"
msgstr "Dokument erstellt" msgstr "Dokument erstellt"
#: packages/email/templates/document-created-from-direct-template.tsx:32 #: packages/email/templates/document-created-from-direct-template.tsx:32
#: packages/lib/server-only/template/create-document-from-direct-template.ts:573 #: packages/lib/server-only/template/create-document-from-direct-template.ts:574
msgid "Document created from direct template" msgid "Document created from direct template"
msgstr "Dokument erstellt aus direkter Vorlage" msgstr "Dokument erstellt aus direkter Vorlage"

View File

@ -51,16 +51,16 @@ msgstr "\"{placeholderEmail}\" im Namen von \"{0}\" hat Sie eingeladen, \"Beispi
#~ msgstr "\"{teamUrl}\" hat Sie eingeladen, \"Beispieldokument\" zu unterschreiben." #~ msgstr "\"{teamUrl}\" hat Sie eingeladen, \"Beispieldokument\" zu unterschreiben."
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83
msgid "({0}) has invited you to approve this document" #~ msgid "({0}) has invited you to approve this document"
msgstr "({0}) hat dich eingeladen, dieses Dokument zu genehmigen" #~ msgstr "({0}) hat dich eingeladen, dieses Dokument zu genehmigen"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80
msgid "({0}) has invited you to sign this document" #~ msgid "({0}) has invited you to sign this document"
msgstr "({0}) hat dich eingeladen, dieses Dokument zu unterzeichnen" #~ msgstr "({0}) hat dich eingeladen, dieses Dokument zu unterzeichnen"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77
msgid "({0}) has invited you to view this document" #~ msgid "({0}) has invited you to view this document"
msgstr "({0}) hat dich eingeladen, dieses Dokument zu betrachten" #~ msgstr "({0}) hat dich eingeladen, dieses Dokument zu betrachten"
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313 #: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313
msgid "{0, plural, one {(1 character over)} other {(# characters over)}}" msgid "{0, plural, one {(1 character over)} other {(# characters over)}}"
@ -944,7 +944,7 @@ msgstr "Klicken Sie, um den Signatur-Link zu kopieren, um ihn an den Empfänger
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:456 #: apps/web/src/app/embed/direct/[[...url]]/client.tsx:456
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:335 #: apps/web/src/app/embed/sign/[[...url]]/client.tsx:335
msgid "Click to insert field" msgid "Click to insert field"
msgstr "Klicken Sie, um das Feld einzufügen" msgstr "Klicken Sie, um das Feld auszufüllen"
#: apps/web/src/app/(dashboard)/templates/new-template-dialog.tsx:126 #: apps/web/src/app/(dashboard)/templates/new-template-dialog.tsx:126
#: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:389 #: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:389
@ -964,7 +964,7 @@ msgstr "Schließen"
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:325 #: apps/web/src/app/embed/sign/[[...url]]/client.tsx:325
#: apps/web/src/components/forms/v2/signup.tsx:534 #: apps/web/src/components/forms/v2/signup.tsx:534
msgid "Complete" msgid "Complete"
msgstr "Vollständig" msgstr "Abschließen"
#: apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx:69 #: apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx:69
msgid "Complete Approval" msgid "Complete Approval"
@ -1206,6 +1206,7 @@ msgid "Create your account and start using state-of-the-art document signing. Op
msgstr "Erstellen Sie Ihr Konto und beginnen Sie mit dem modernen Dokumentensignieren. Offenes und schönes Signieren liegt in Ihrer Reichweite." msgstr "Erstellen Sie Ihr Konto und beginnen Sie mit dem modernen Dokumentensignieren. Offenes und schönes Signieren liegt in Ihrer Reichweite."
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62 #: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:96
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35 #: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:54 #: apps/web/src/app/(dashboard)/documents/data-table.tsx:54
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65 #: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65
@ -1245,6 +1246,11 @@ msgstr "Aktuelles Passwort"
msgid "Current plan: {0}" msgid "Current plan: {0}"
msgstr "Aktueller Plan: {0}" msgstr "Aktueller Plan: {0}"
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:94
#: apps/web/src/app/(dashboard)/admin/leaderboard/leaderboard-table.tsx:94
#~ msgid "Customer Type"
#~ msgstr ""
#: apps/web/src/app/(dashboard)/settings/billing/billing-plans.tsx:28 #: apps/web/src/app/(dashboard)/settings/billing/billing-plans.tsx:28
msgid "Daily" msgid "Daily"
msgstr "Täglich" msgstr "Täglich"
@ -1988,6 +1994,18 @@ msgstr "Zum Eigentümer gehen"
msgid "Go to your <0>public profile settings</0> to add documents." msgid "Go to your <0>public profile settings</0> to add documents."
msgstr "Gehen Sie zu Ihren <0>öffentlichen Profileinstellungen</0>, um Dokumente hinzuzufügen." msgstr "Gehen Sie zu Ihren <0>öffentlichen Profileinstellungen</0>, um Dokumente hinzuzufügen."
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:107
msgid "has invited you to approve this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:98
msgid "has invited you to sign this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:89
msgid "has invited you to view this document"
msgstr ""
#: apps/web/src/app/(dashboard)/settings/profile/page.tsx:29 #: apps/web/src/app/(dashboard)/settings/profile/page.tsx:29
msgid "Here you can edit your personal details." msgid "Here you can edit your personal details."
msgstr "Hier können Sie Ihre persönlichen Daten bearbeiten." msgstr "Hier können Sie Ihre persönlichen Daten bearbeiten."
@ -2204,6 +2222,10 @@ msgstr "Zuletzt aktualisiert am"
msgid "Last used" msgid "Last used"
msgstr "Zuletzt verwendet" msgstr "Zuletzt verwendet"
#: apps/web/src/app/(dashboard)/admin/nav.tsx:93
msgid "Leaderboard"
msgstr ""
#: apps/web/src/components/(teams)/dialogs/leave-team-dialog.tsx:111 #: apps/web/src/components/(teams)/dialogs/leave-team-dialog.tsx:111
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:117 #: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:117
msgid "Leave" msgid "Leave"
@ -2411,6 +2433,7 @@ msgid "My templates"
msgstr "Meine Vorlagen" msgstr "Meine Vorlagen"
#: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148 #: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:56
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99 #: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66 #: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144 #: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144
@ -2524,6 +2547,18 @@ msgstr "Nichts zu tun"
msgid "Number" msgid "Number"
msgstr "Nummer" msgstr "Nummer"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:103
msgid "on behalf of \"{0}\" has invited you to approve this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:94
msgid "on behalf of \"{0}\" has invited you to sign this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:85
msgid "on behalf of \"{0}\" has invited you to view this document"
msgstr ""
#: apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx:128 #: apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx:128
msgid "On this page, you can create a new webhook." msgid "On this page, you can create a new webhook."
msgstr "Auf dieser Seite können Sie einen neuen Webhook erstellen." msgstr "Auf dieser Seite können Sie einen neuen Webhook erstellen."
@ -3105,6 +3140,7 @@ msgstr "Suchen"
msgid "Search by document title" msgid "Search by document title"
msgstr "Nach Dokumenttitel suchen" msgstr "Nach Dokumenttitel suchen"
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:147
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144 #: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144
msgid "Search by name or email" msgid "Search by name or email"
msgstr "Nach Name oder E-Mail suchen" msgstr "Nach Name oder E-Mail suchen"
@ -3369,6 +3405,11 @@ msgstr "Unterzeichnungslinks wurden für dieses Dokument erstellt."
msgid "Signing up..." msgid "Signing up..."
msgstr "Registrierung..." msgstr "Registrierung..."
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:82
#: apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx:46
msgid "Signing Volume"
msgstr ""
#: apps/web/src/app/(profile)/p/[url]/page.tsx:109 #: apps/web/src/app/(profile)/p/[url]/page.tsx:109
msgid "Since {0}" msgid "Since {0}"
msgstr "Seit {0}" msgstr "Seit {0}"
@ -3377,7 +3418,7 @@ msgstr "Seit {0}"
msgid "Site Banner" msgid "Site Banner"
msgstr "Website Banner" msgstr "Website Banner"
#: apps/web/src/app/(dashboard)/admin/nav.tsx:93 #: apps/web/src/app/(dashboard)/admin/nav.tsx:107
#: apps/web/src/app/(dashboard)/admin/site-settings/page.tsx:26 #: apps/web/src/app/(dashboard)/admin/site-settings/page.tsx:26
msgid "Site Settings" msgid "Site Settings"
msgstr "Website Einstellungen" msgstr "Website Einstellungen"

View File

@ -55,14 +55,19 @@ msgstr "{0} of {1} row(s) selected."
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:136 #: packages/lib/jobs/definitions/emails/send-signing-email.ts:136
#: packages/lib/server-only/document/resend-document.tsx:137 #: packages/lib/server-only/document/resend-document.tsx:137
msgid "{0} on behalf of {1} has invited you to {recipientActionVerb} the document \"{2}\"." msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
msgstr "{0} on behalf of {1} has invited you to {recipientActionVerb} the document \"{2}\"." msgstr "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:136
#: packages/lib/server-only/document/resend-document.tsx:137
#~ msgid "{0} on behalf of {1} has invited you to {recipientActionVerb} the document \"{2}\"."
#~ msgstr "{0} on behalf of {1} has invited you to {recipientActionVerb} the document \"{2}\"."
#: packages/email/template-components/template-document-invite.tsx:51 #: packages/email/template-components/template-document-invite.tsx:51
#~ msgid "{0}<0/>\"{documentName}\"" #~ msgid "{0}<0/>\"{documentName}\""
#~ msgstr "{0}<0/>\"{documentName}\"" #~ msgstr "{0}<0/>\"{documentName}\""
#: packages/email/templates/document-invite.tsx:94 #: packages/email/templates/document-invite.tsx:95
msgid "{inviterName} <0>({inviterEmail})</0>" msgid "{inviterName} <0>({inviterEmail})</0>"
msgstr "{inviterName} <0>({inviterEmail})</0>" msgstr "{inviterName} <0>({inviterEmail})</0>"
@ -82,7 +87,7 @@ msgstr "{inviterName} has invited you to {0}<0/>\"{documentName}\""
msgid "{inviterName} has invited you to {action} {documentName}" msgid "{inviterName} has invited you to {action} {documentName}"
msgstr "{inviterName} has invited you to {action} {documentName}" msgstr "{inviterName} has invited you to {action} {documentName}"
#: packages/email/templates/document-invite.tsx:106 #: packages/email/templates/document-invite.tsx:108
msgid "{inviterName} has invited you to {action} the document \"{documentName}\"." msgid "{inviterName} has invited you to {action} the document \"{documentName}\"."
msgstr "{inviterName} has invited you to {action} the document \"{documentName}\"." msgstr "{inviterName} has invited you to {action} the document \"{documentName}\"."
@ -95,16 +100,24 @@ msgid "{inviterName} has removed you from the document<0/>\"{documentName}\""
msgstr "{inviterName} has removed you from the document<0/>\"{documentName}\"" msgstr "{inviterName} has removed you from the document<0/>\"{documentName}\""
#: packages/email/template-components/template-document-invite.tsx:63 #: packages/email/template-components/template-document-invite.tsx:63
msgid "{inviterName} on behalf of {teamName} has invited you to {0}" msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {0}"
msgstr "{inviterName} on behalf of {teamName} has invited you to {0}" msgstr "{inviterName} on behalf of \"{teamName}\" has invited you to {0}"
#: packages/email/templates/document-invite.tsx:45
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
msgstr "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
#: packages/email/template-components/template-document-invite.tsx:63
#~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}"
#~ msgstr "{inviterName} on behalf of {teamName} has invited you to {0}"
#: packages/email/template-components/template-document-invite.tsx:49 #: packages/email/template-components/template-document-invite.tsx:49
#~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\"" #~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\""
#~ msgstr "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\"" #~ msgstr "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\""
#: packages/email/templates/document-invite.tsx:45 #: packages/email/templates/document-invite.tsx:45
msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}" #~ msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}"
msgstr "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}" #~ msgstr "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}"
#: packages/email/templates/team-join.tsx:67 #: packages/email/templates/team-join.tsx:67
msgid "{memberEmail} joined the following team" msgid "{memberEmail} joined the following team"
@ -706,7 +719,7 @@ msgid "Document created"
msgstr "Document created" msgstr "Document created"
#: packages/email/templates/document-created-from-direct-template.tsx:32 #: packages/email/templates/document-created-from-direct-template.tsx:32
#: packages/lib/server-only/template/create-document-from-direct-template.ts:573 #: packages/lib/server-only/template/create-document-from-direct-template.ts:574
msgid "Document created from direct template" msgid "Document created from direct template"
msgstr "Document created from direct template" msgstr "Document created from direct template"

View File

@ -46,16 +46,16 @@ msgstr "\"{placeholderEmail}\" on behalf of \"{0}\" has invited you to sign \"ex
#~ msgstr "\"{teamUrl}\" has invited you to sign \"example document\"." #~ msgstr "\"{teamUrl}\" has invited you to sign \"example document\"."
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83
msgid "({0}) has invited you to approve this document" #~ msgid "({0}) has invited you to approve this document"
msgstr "({0}) has invited you to approve this document" #~ msgstr "({0}) has invited you to approve this document"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80
msgid "({0}) has invited you to sign this document" #~ msgid "({0}) has invited you to sign this document"
msgstr "({0}) has invited you to sign this document" #~ msgstr "({0}) has invited you to sign this document"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77
msgid "({0}) has invited you to view this document" #~ msgid "({0}) has invited you to view this document"
msgstr "({0}) has invited you to view this document" #~ msgstr "({0}) has invited you to view this document"
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313 #: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313
msgid "{0, plural, one {(1 character over)} other {(# characters over)}}" msgid "{0, plural, one {(1 character over)} other {(# characters over)}}"
@ -1201,6 +1201,7 @@ msgid "Create your account and start using state-of-the-art document signing. Op
msgstr "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp." msgstr "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62 #: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:96
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35 #: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:54 #: apps/web/src/app/(dashboard)/documents/data-table.tsx:54
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65 #: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65
@ -1240,6 +1241,11 @@ msgstr "Current Password"
msgid "Current plan: {0}" msgid "Current plan: {0}"
msgstr "Current plan: {0}" msgstr "Current plan: {0}"
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:94
#: apps/web/src/app/(dashboard)/admin/leaderboard/leaderboard-table.tsx:94
#~ msgid "Customer Type"
#~ msgstr "Customer Type"
#: apps/web/src/app/(dashboard)/settings/billing/billing-plans.tsx:28 #: apps/web/src/app/(dashboard)/settings/billing/billing-plans.tsx:28
msgid "Daily" msgid "Daily"
msgstr "Daily" msgstr "Daily"
@ -1983,6 +1989,18 @@ msgstr "Go to owner"
msgid "Go to your <0>public profile settings</0> to add documents." msgid "Go to your <0>public profile settings</0> to add documents."
msgstr "Go to your <0>public profile settings</0> to add documents." msgstr "Go to your <0>public profile settings</0> to add documents."
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:107
msgid "has invited you to approve this document"
msgstr "has invited you to approve this document"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:98
msgid "has invited you to sign this document"
msgstr "has invited you to sign this document"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:89
msgid "has invited you to view this document"
msgstr "has invited you to view this document"
#: apps/web/src/app/(dashboard)/settings/profile/page.tsx:29 #: apps/web/src/app/(dashboard)/settings/profile/page.tsx:29
msgid "Here you can edit your personal details." msgid "Here you can edit your personal details."
msgstr "Here you can edit your personal details." msgstr "Here you can edit your personal details."
@ -2199,6 +2217,10 @@ msgstr "Last updated at"
msgid "Last used" msgid "Last used"
msgstr "Last used" msgstr "Last used"
#: apps/web/src/app/(dashboard)/admin/nav.tsx:93
msgid "Leaderboard"
msgstr "Leaderboard"
#: apps/web/src/components/(teams)/dialogs/leave-team-dialog.tsx:111 #: apps/web/src/components/(teams)/dialogs/leave-team-dialog.tsx:111
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:117 #: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:117
msgid "Leave" msgid "Leave"
@ -2406,6 +2428,7 @@ msgid "My templates"
msgstr "My templates" msgstr "My templates"
#: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148 #: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:56
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99 #: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66 #: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144 #: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144
@ -2519,6 +2542,18 @@ msgstr "Nothing to do"
msgid "Number" msgid "Number"
msgstr "Number" msgstr "Number"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:103
msgid "on behalf of \"{0}\" has invited you to approve this document"
msgstr "on behalf of \"{0}\" has invited you to approve this document"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:94
msgid "on behalf of \"{0}\" has invited you to sign this document"
msgstr "on behalf of \"{0}\" has invited you to sign this document"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:85
msgid "on behalf of \"{0}\" has invited you to view this document"
msgstr "on behalf of \"{0}\" has invited you to view this document"
#: apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx:128 #: apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx:128
msgid "On this page, you can create a new webhook." msgid "On this page, you can create a new webhook."
msgstr "On this page, you can create a new webhook." msgstr "On this page, you can create a new webhook."
@ -3100,6 +3135,7 @@ msgstr "Search"
msgid "Search by document title" msgid "Search by document title"
msgstr "Search by document title" msgstr "Search by document title"
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:147
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144 #: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144
msgid "Search by name or email" msgid "Search by name or email"
msgstr "Search by name or email" msgstr "Search by name or email"
@ -3364,6 +3400,11 @@ msgstr "Signing links have been generated for this document."
msgid "Signing up..." msgid "Signing up..."
msgstr "Signing up..." msgstr "Signing up..."
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:82
#: apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx:46
msgid "Signing Volume"
msgstr "Signing Volume"
#: apps/web/src/app/(profile)/p/[url]/page.tsx:109 #: apps/web/src/app/(profile)/p/[url]/page.tsx:109
msgid "Since {0}" msgid "Since {0}"
msgstr "Since {0}" msgstr "Since {0}"
@ -3372,7 +3413,7 @@ msgstr "Since {0}"
msgid "Site Banner" msgid "Site Banner"
msgstr "Site Banner" msgstr "Site Banner"
#: apps/web/src/app/(dashboard)/admin/nav.tsx:93 #: apps/web/src/app/(dashboard)/admin/nav.tsx:107
#: apps/web/src/app/(dashboard)/admin/site-settings/page.tsx:26 #: apps/web/src/app/(dashboard)/admin/site-settings/page.tsx:26
msgid "Site Settings" msgid "Site Settings"
msgstr "Site Settings" msgstr "Site Settings"

View File

@ -60,14 +60,19 @@ msgstr "{0} de {1} fila(s) seleccionada."
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:136 #: packages/lib/jobs/definitions/emails/send-signing-email.ts:136
#: packages/lib/server-only/document/resend-document.tsx:137 #: packages/lib/server-only/document/resend-document.tsx:137
msgid "{0} on behalf of {1} has invited you to {recipientActionVerb} the document \"{2}\"." msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
msgstr "{0} en nombre de {1} te ha invitado a {recipientActionVerb} el documento \"{2}\"." msgstr ""
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:136
#: packages/lib/server-only/document/resend-document.tsx:137
#~ msgid "{0} on behalf of {1} has invited you to {recipientActionVerb} the document \"{2}\"."
#~ msgstr "{0} en nombre de {1} te ha invitado a {recipientActionVerb} el documento \"{2}\"."
#: packages/email/template-components/template-document-invite.tsx:51 #: packages/email/template-components/template-document-invite.tsx:51
#~ msgid "{0}<0/>\"{documentName}\"" #~ msgid "{0}<0/>\"{documentName}\""
#~ msgstr "{0}<0/>\"{documentName}\"" #~ msgstr "{0}<0/>\"{documentName}\""
#: packages/email/templates/document-invite.tsx:94 #: packages/email/templates/document-invite.tsx:95
msgid "{inviterName} <0>({inviterEmail})</0>" msgid "{inviterName} <0>({inviterEmail})</0>"
msgstr "{inviterName} <0>({inviterEmail})</0>" msgstr "{inviterName} <0>({inviterEmail})</0>"
@ -87,7 +92,7 @@ msgstr "{inviterName} te ha invitado a {0}<0/>\"{documentName}\""
msgid "{inviterName} has invited you to {action} {documentName}" msgid "{inviterName} has invited you to {action} {documentName}"
msgstr "{inviterName} te ha invitado a {action} {documentName}" msgstr "{inviterName} te ha invitado a {action} {documentName}"
#: packages/email/templates/document-invite.tsx:106 #: packages/email/templates/document-invite.tsx:108
msgid "{inviterName} has invited you to {action} the document \"{documentName}\"." msgid "{inviterName} has invited you to {action} the document \"{documentName}\"."
msgstr "{inviterName} te ha invitado a {action} el documento \"{documentName}\"." msgstr "{inviterName} te ha invitado a {action} el documento \"{documentName}\"."
@ -100,16 +105,24 @@ msgid "{inviterName} has removed you from the document<0/>\"{documentName}\""
msgstr "{inviterName} te ha eliminado del documento<0/>\"{documentName}\"" msgstr "{inviterName} te ha eliminado del documento<0/>\"{documentName}\""
#: packages/email/template-components/template-document-invite.tsx:63 #: packages/email/template-components/template-document-invite.tsx:63
msgid "{inviterName} on behalf of {teamName} has invited you to {0}" msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {0}"
msgstr "{inviterName} en nombre de {teamName} te ha invitado a {0}" msgstr ""
#: packages/email/templates/document-invite.tsx:45
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
msgstr ""
#: packages/email/template-components/template-document-invite.tsx:63
#~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}"
#~ msgstr "{inviterName} en nombre de {teamName} te ha invitado a {0}"
#: packages/email/template-components/template-document-invite.tsx:49 #: packages/email/template-components/template-document-invite.tsx:49
#~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\"" #~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\""
#~ msgstr "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\"" #~ msgstr "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\""
#: packages/email/templates/document-invite.tsx:45 #: packages/email/templates/document-invite.tsx:45
msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}" #~ msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}"
msgstr "{inviterName} en nombre de {teamName} te ha invitado a {action} {documentName}" #~ msgstr "{inviterName} en nombre de {teamName} te ha invitado a {action} {documentName}"
#: packages/email/templates/team-join.tsx:67 #: packages/email/templates/team-join.tsx:67
msgid "{memberEmail} joined the following team" msgid "{memberEmail} joined the following team"
@ -711,7 +724,7 @@ msgid "Document created"
msgstr "Documento creado" msgstr "Documento creado"
#: packages/email/templates/document-created-from-direct-template.tsx:32 #: packages/email/templates/document-created-from-direct-template.tsx:32
#: packages/lib/server-only/template/create-document-from-direct-template.ts:573 #: packages/lib/server-only/template/create-document-from-direct-template.ts:574
msgid "Document created from direct template" msgid "Document created from direct template"
msgstr "Documento creado a partir de plantilla directa" msgstr "Documento creado a partir de plantilla directa"

View File

@ -51,16 +51,16 @@ msgstr "\"{placeholderEmail}\" en nombre de \"{0}\" te ha invitado a firmar \"do
#~ msgstr "\"{teamUrl}\" te ha invitado a firmar \"ejemplo de documento\"." #~ msgstr "\"{teamUrl}\" te ha invitado a firmar \"ejemplo de documento\"."
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83
msgid "({0}) has invited you to approve this document" #~ msgid "({0}) has invited you to approve this document"
msgstr "({0}) te ha invitado a aprobar este documento" #~ msgstr "({0}) te ha invitado a aprobar este documento"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80
msgid "({0}) has invited you to sign this document" #~ msgid "({0}) has invited you to sign this document"
msgstr "({0}) te ha invitado a firmar este documento" #~ msgstr "({0}) te ha invitado a firmar este documento"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77
msgid "({0}) has invited you to view this document" #~ msgid "({0}) has invited you to view this document"
msgstr "({0}) te ha invitado a ver este documento" #~ msgstr "({0}) te ha invitado a ver este documento"
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313 #: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313
msgid "{0, plural, one {(1 character over)} other {(# characters over)}}" msgid "{0, plural, one {(1 character over)} other {(# characters over)}}"
@ -1206,6 +1206,7 @@ msgid "Create your account and start using state-of-the-art document signing. Op
msgstr "Crea tu cuenta y comienza a utilizar la firma de documentos de última generación. La firma abierta y hermosa está al alcance de tu mano." msgstr "Crea tu cuenta y comienza a utilizar la firma de documentos de última generación. La firma abierta y hermosa está al alcance de tu mano."
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62 #: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:96
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35 #: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:54 #: apps/web/src/app/(dashboard)/documents/data-table.tsx:54
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65 #: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65
@ -1988,6 +1989,18 @@ msgstr "Ir al propietario"
msgid "Go to your <0>public profile settings</0> to add documents." msgid "Go to your <0>public profile settings</0> to add documents."
msgstr "Ve a tu <0>configuración de perfil público</0> para agregar documentos." msgstr "Ve a tu <0>configuración de perfil público</0> para agregar documentos."
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:107
msgid "has invited you to approve this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:98
msgid "has invited you to sign this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:89
msgid "has invited you to view this document"
msgstr ""
#: apps/web/src/app/(dashboard)/settings/profile/page.tsx:29 #: apps/web/src/app/(dashboard)/settings/profile/page.tsx:29
msgid "Here you can edit your personal details." msgid "Here you can edit your personal details."
msgstr "Aquí puedes editar tus datos personales." msgstr "Aquí puedes editar tus datos personales."
@ -2204,6 +2217,10 @@ msgstr "Última actualización el"
msgid "Last used" msgid "Last used"
msgstr "Último uso" msgstr "Último uso"
#: apps/web/src/app/(dashboard)/admin/nav.tsx:93
msgid "Leaderboard"
msgstr ""
#: apps/web/src/components/(teams)/dialogs/leave-team-dialog.tsx:111 #: apps/web/src/components/(teams)/dialogs/leave-team-dialog.tsx:111
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:117 #: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:117
msgid "Leave" msgid "Leave"
@ -2411,6 +2428,7 @@ msgid "My templates"
msgstr "Mis plantillas" msgstr "Mis plantillas"
#: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148 #: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:56
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99 #: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66 #: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144 #: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144
@ -2524,6 +2542,18 @@ msgstr "Nada que hacer"
msgid "Number" msgid "Number"
msgstr "Número" msgstr "Número"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:103
msgid "on behalf of \"{0}\" has invited you to approve this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:94
msgid "on behalf of \"{0}\" has invited you to sign this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:85
msgid "on behalf of \"{0}\" has invited you to view this document"
msgstr ""
#: apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx:128 #: apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx:128
msgid "On this page, you can create a new webhook." msgid "On this page, you can create a new webhook."
msgstr "En esta página, puedes crear un nuevo webhook." msgstr "En esta página, puedes crear un nuevo webhook."
@ -3105,6 +3135,7 @@ msgstr "Buscar"
msgid "Search by document title" msgid "Search by document title"
msgstr "Buscar por título del documento" msgstr "Buscar por título del documento"
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:147
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144 #: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144
msgid "Search by name or email" msgid "Search by name or email"
msgstr "Buscar por nombre o correo electrónico" msgstr "Buscar por nombre o correo electrónico"
@ -3369,6 +3400,11 @@ msgstr "Se han generado enlaces de firma para este documento."
msgid "Signing up..." msgid "Signing up..."
msgstr "Registrándose..." msgstr "Registrándose..."
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:82
#: apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx:46
msgid "Signing Volume"
msgstr ""
#: apps/web/src/app/(profile)/p/[url]/page.tsx:109 #: apps/web/src/app/(profile)/p/[url]/page.tsx:109
msgid "Since {0}" msgid "Since {0}"
msgstr "Desde {0}" msgstr "Desde {0}"
@ -3377,7 +3413,7 @@ msgstr "Desde {0}"
msgid "Site Banner" msgid "Site Banner"
msgstr "Banner del sitio" msgstr "Banner del sitio"
#: apps/web/src/app/(dashboard)/admin/nav.tsx:93 #: apps/web/src/app/(dashboard)/admin/nav.tsx:107
#: apps/web/src/app/(dashboard)/admin/site-settings/page.tsx:26 #: apps/web/src/app/(dashboard)/admin/site-settings/page.tsx:26
msgid "Site Settings" msgid "Site Settings"
msgstr "Configuraciones del sitio" msgstr "Configuraciones del sitio"

View File

@ -60,14 +60,19 @@ msgstr "{0} sur {1} ligne(s) sélectionnée(s)."
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:136 #: packages/lib/jobs/definitions/emails/send-signing-email.ts:136
#: packages/lib/server-only/document/resend-document.tsx:137 #: packages/lib/server-only/document/resend-document.tsx:137
msgid "{0} on behalf of {1} has invited you to {recipientActionVerb} the document \"{2}\"." msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
msgstr "{0} au nom de {1} vous a invité à {recipientActionVerb} le document \"{2}\"." msgstr ""
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:136
#: packages/lib/server-only/document/resend-document.tsx:137
#~ msgid "{0} on behalf of {1} has invited you to {recipientActionVerb} the document \"{2}\"."
#~ msgstr "{0} au nom de {1} vous a invité à {recipientActionVerb} le document \"{2}\"."
#: packages/email/template-components/template-document-invite.tsx:51 #: packages/email/template-components/template-document-invite.tsx:51
#~ msgid "{0}<0/>\"{documentName}\"" #~ msgid "{0}<0/>\"{documentName}\""
#~ msgstr "{0}<0/>\"{documentName}\"" #~ msgstr "{0}<0/>\"{documentName}\""
#: packages/email/templates/document-invite.tsx:94 #: packages/email/templates/document-invite.tsx:95
msgid "{inviterName} <0>({inviterEmail})</0>" msgid "{inviterName} <0>({inviterEmail})</0>"
msgstr "{inviterName} <0>({inviterEmail})</0>" msgstr "{inviterName} <0>({inviterEmail})</0>"
@ -87,7 +92,7 @@ msgstr "{inviterName} vous a invité à {0}<0/>\"{documentName}\""
msgid "{inviterName} has invited you to {action} {documentName}" msgid "{inviterName} has invited you to {action} {documentName}"
msgstr "{inviterName} vous a invité à {action} {documentName}" msgstr "{inviterName} vous a invité à {action} {documentName}"
#: packages/email/templates/document-invite.tsx:106 #: packages/email/templates/document-invite.tsx:108
msgid "{inviterName} has invited you to {action} the document \"{documentName}\"." msgid "{inviterName} has invited you to {action} the document \"{documentName}\"."
msgstr "{inviterName} vous a invité à {action} le document \"{documentName}\"." msgstr "{inviterName} vous a invité à {action} le document \"{documentName}\"."
@ -100,16 +105,24 @@ msgid "{inviterName} has removed you from the document<0/>\"{documentName}\""
msgstr "{inviterName} vous a retiré du document<0/>\"{documentName}\"" msgstr "{inviterName} vous a retiré du document<0/>\"{documentName}\""
#: packages/email/template-components/template-document-invite.tsx:63 #: packages/email/template-components/template-document-invite.tsx:63
msgid "{inviterName} on behalf of {teamName} has invited you to {0}" msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {0}"
msgstr "{inviterName} au nom de {teamName} vous a invité à {0}" msgstr ""
#: packages/email/templates/document-invite.tsx:45
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
msgstr ""
#: packages/email/template-components/template-document-invite.tsx:63
#~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}"
#~ msgstr "{inviterName} au nom de {teamName} vous a invité à {0}"
#: packages/email/template-components/template-document-invite.tsx:49 #: packages/email/template-components/template-document-invite.tsx:49
#~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\"" #~ msgid "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\""
#~ msgstr "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\"" #~ msgstr "{inviterName} on behalf of {teamName} has invited you to {0}<0/>\"{documentName}\""
#: packages/email/templates/document-invite.tsx:45 #: packages/email/templates/document-invite.tsx:45
msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}" #~ msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}"
msgstr "{inviterName} au nom de {teamName} vous a invité à {action} {documentName}" #~ msgstr "{inviterName} au nom de {teamName} vous a invité à {action} {documentName}"
#: packages/email/templates/team-join.tsx:67 #: packages/email/templates/team-join.tsx:67
msgid "{memberEmail} joined the following team" msgid "{memberEmail} joined the following team"
@ -711,7 +724,7 @@ msgid "Document created"
msgstr "Document créé" msgstr "Document créé"
#: packages/email/templates/document-created-from-direct-template.tsx:32 #: packages/email/templates/document-created-from-direct-template.tsx:32
#: packages/lib/server-only/template/create-document-from-direct-template.ts:573 #: packages/lib/server-only/template/create-document-from-direct-template.ts:574
msgid "Document created from direct template" msgid "Document created from direct template"
msgstr "Document créé à partir d'un modèle direct" msgstr "Document créé à partir d'un modèle direct"

View File

@ -51,16 +51,16 @@ msgstr "\"{placeholderEmail}\" au nom de \"{0}\" vous a invité à signer \"exem
#~ msgstr "\"{teamUrl}\" vous a invité à signer \"example document\"." #~ msgstr "\"{teamUrl}\" vous a invité à signer \"example document\"."
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83
msgid "({0}) has invited you to approve this document" #~ msgid "({0}) has invited you to approve this document"
msgstr "({0}) vous a invité à approuver ce document" #~ msgstr "({0}) vous a invité à approuver ce document"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80
msgid "({0}) has invited you to sign this document" #~ msgid "({0}) has invited you to sign this document"
msgstr "({0}) vous a invité à signer ce document" #~ msgstr "({0}) vous a invité à signer ce document"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77 #: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77
msgid "({0}) has invited you to view this document" #~ msgid "({0}) has invited you to view this document"
msgstr "({0}) vous a invité à consulter ce document" #~ msgstr "({0}) vous a invité à consulter ce document"
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313 #: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313
msgid "{0, plural, one {(1 character over)} other {(# characters over)}}" msgid "{0, plural, one {(1 character over)} other {(# characters over)}}"
@ -1206,6 +1206,7 @@ msgid "Create your account and start using state-of-the-art document signing. Op
msgstr "Créez votre compte et commencez à utiliser la signature de documents à la pointe de la technologie. Une signature ouverte et magnifique est à votre portée." msgstr "Créez votre compte et commencez à utiliser la signature de documents à la pointe de la technologie. Une signature ouverte et magnifique est à votre portée."
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62 #: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:96
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35 #: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:54 #: apps/web/src/app/(dashboard)/documents/data-table.tsx:54
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65 #: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65
@ -1988,6 +1989,18 @@ msgstr "Aller au propriétaire"
msgid "Go to your <0>public profile settings</0> to add documents." msgid "Go to your <0>public profile settings</0> to add documents."
msgstr "Allez à vos <0>paramètres de profil public</0> pour ajouter des documents." msgstr "Allez à vos <0>paramètres de profil public</0> pour ajouter des documents."
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:107
msgid "has invited you to approve this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:98
msgid "has invited you to sign this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:89
msgid "has invited you to view this document"
msgstr ""
#: apps/web/src/app/(dashboard)/settings/profile/page.tsx:29 #: apps/web/src/app/(dashboard)/settings/profile/page.tsx:29
msgid "Here you can edit your personal details." msgid "Here you can edit your personal details."
msgstr "Ici, vous pouvez modifier vos coordonnées personnelles." msgstr "Ici, vous pouvez modifier vos coordonnées personnelles."
@ -2204,6 +2217,10 @@ msgstr "Dernière mise à jour à"
msgid "Last used" msgid "Last used"
msgstr "Dernière utilisation" msgstr "Dernière utilisation"
#: apps/web/src/app/(dashboard)/admin/nav.tsx:93
msgid "Leaderboard"
msgstr ""
#: apps/web/src/components/(teams)/dialogs/leave-team-dialog.tsx:111 #: apps/web/src/components/(teams)/dialogs/leave-team-dialog.tsx:111
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:117 #: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:117
msgid "Leave" msgid "Leave"
@ -2411,6 +2428,7 @@ msgid "My templates"
msgstr "Mes modèles" msgstr "Mes modèles"
#: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148 #: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:56
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99 #: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66 #: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144 #: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144
@ -2524,6 +2542,18 @@ msgstr "Rien à faire"
msgid "Number" msgid "Number"
msgstr "Numéro" msgstr "Numéro"
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:103
msgid "on behalf of \"{0}\" has invited you to approve this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:94
msgid "on behalf of \"{0}\" has invited you to sign this document"
msgstr ""
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:85
msgid "on behalf of \"{0}\" has invited you to view this document"
msgstr ""
#: apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx:128 #: apps/web/src/components/(dashboard)/settings/webhooks/create-webhook-dialog.tsx:128
msgid "On this page, you can create a new webhook." msgid "On this page, you can create a new webhook."
msgstr "Sur cette page, vous pouvez créer un nouveau webhook." msgstr "Sur cette page, vous pouvez créer un nouveau webhook."
@ -3105,6 +3135,7 @@ msgstr "Recherche"
msgid "Search by document title" msgid "Search by document title"
msgstr "Recherche par titre de document" msgstr "Recherche par titre de document"
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:147
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144 #: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144
msgid "Search by name or email" msgid "Search by name or email"
msgstr "Recherche par nom ou e-mail" msgstr "Recherche par nom ou e-mail"
@ -3369,6 +3400,11 @@ msgstr "Des liens de signature ont été générés pour ce document."
msgid "Signing up..." msgid "Signing up..."
msgstr "Inscription en cours..." msgstr "Inscription en cours..."
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:82
#: apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx:46
msgid "Signing Volume"
msgstr ""
#: apps/web/src/app/(profile)/p/[url]/page.tsx:109 #: apps/web/src/app/(profile)/p/[url]/page.tsx:109
msgid "Since {0}" msgid "Since {0}"
msgstr "Depuis {0}" msgstr "Depuis {0}"
@ -3377,7 +3413,7 @@ msgstr "Depuis {0}"
msgid "Site Banner" msgid "Site Banner"
msgstr "Bannière du site" msgstr "Bannière du site"
#: apps/web/src/app/(dashboard)/admin/nav.tsx:93 #: apps/web/src/app/(dashboard)/admin/nav.tsx:107
#: apps/web/src/app/(dashboard)/admin/site-settings/page.tsx:26 #: apps/web/src/app/(dashboard)/admin/site-settings/page.tsx:26
msgid "Site Settings" msgid "Site Settings"
msgstr "Paramètres du site" msgstr "Paramètres du site"

View File

@ -0,0 +1,80 @@
import { z } from 'zod';
import {
DocumentDistributionMethod,
DocumentSigningOrder,
DocumentSource,
DocumentStatus,
DocumentVisibility,
ReadStatus,
RecipientRole,
SendStatus,
SigningStatus,
} from '@documenso/prisma/client';
/**
* Schema for recipient data in webhook payloads.
*/
export const ZWebhookRecipientSchema = z.object({
id: z.number(),
documentId: z.number().nullable(),
templateId: z.number().nullable(),
email: z.string(),
name: z.string(),
token: z.string(),
documentDeletedAt: z.date().nullable(),
expired: z.date().nullable(),
signedAt: z.date().nullable(),
authOptions: z.any().nullable(),
signingOrder: z.number().nullable(),
rejectionReason: z.string().nullable(),
role: z.nativeEnum(RecipientRole),
readStatus: z.nativeEnum(ReadStatus),
signingStatus: z.nativeEnum(SigningStatus),
sendStatus: z.nativeEnum(SendStatus),
});
/**
* Schema for document meta in webhook payloads.
*/
export const ZWebhookDocumentMetaSchema = z.object({
id: z.string(),
subject: z.string().nullable(),
message: z.string().nullable(),
timezone: z.string(),
password: z.string().nullable(),
dateFormat: z.string(),
redirectUrl: z.string().nullable(),
signingOrder: z.nativeEnum(DocumentSigningOrder),
typedSignatureEnabled: z.boolean(),
language: z.string(),
distributionMethod: z.nativeEnum(DocumentDistributionMethod),
emailSettings: z.any().nullable(),
});
/**
* Schema for document data in webhook payloads.
*/
export const ZWebhookDocumentSchema = z.object({
id: z.number(),
externalId: z.string().nullable(),
userId: z.number(),
authOptions: z.any().nullable(),
formValues: z.any().nullable(),
visibility: z.nativeEnum(DocumentVisibility),
title: z.string(),
status: z.nativeEnum(DocumentStatus),
documentDataId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
completedAt: z.date().nullable(),
deletedAt: z.date().nullable(),
teamId: z.number().nullable(),
templateId: z.number().nullable(),
source: z.nativeEnum(DocumentSource),
documentMeta: ZWebhookDocumentMetaSchema.nullable(),
Recipient: z.array(ZWebhookRecipientSchema),
});
export type TWebhookRecipient = z.infer<typeof ZWebhookRecipientSchema>;
export type TWebhookDocument = z.infer<typeof ZWebhookDocumentSchema>;

View File

@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "WebhookTriggerEvents" ADD VALUE 'DOCUMENT_REJECTED';

View File

@ -162,6 +162,7 @@ enum WebhookTriggerEvents {
DOCUMENT_OPENED DOCUMENT_OPENED
DOCUMENT_SIGNED DOCUMENT_SIGNED
DOCUMENT_COMPLETED DOCUMENT_COMPLETED
DOCUMENT_REJECTED
} }
model Webhook { model Webhook {

View File

@ -6,7 +6,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
import { Caveat } from 'next/font/google'; import { Caveat } from 'next/font/google';
import { Trans } from '@lingui/macro'; import { Trans } from '@lingui/macro';
import { Undo2 } from 'lucide-react'; import { Undo2, Upload } from 'lucide-react';
import type { StrokeOptions } from 'perfect-freehand'; import type { StrokeOptions } from 'perfect-freehand';
import { getStroke } from 'perfect-freehand'; import { getStroke } from 'perfect-freehand';
@ -33,6 +33,64 @@ const fontCaveat = Caveat({
const DPI = 2; const DPI = 2;
const isBase64Image = (value: string) => value.startsWith('data:image/png;base64,');
const loadImage = async (file: File | undefined): Promise<HTMLImageElement> => {
if (!file) {
throw new Error('No file selected');
}
if (!file.type.startsWith('image/')) {
throw new Error('Invalid file type');
}
if (file.size > 5 * 1024 * 1024) {
throw new Error('Image size should be less than 5MB');
}
return new Promise((resolve, reject) => {
const img = new Image();
const objectUrl = URL.createObjectURL(file);
img.onload = () => {
URL.revokeObjectURL(objectUrl);
resolve(img);
};
img.onerror = () => {
URL.revokeObjectURL(objectUrl);
reject(new Error('Failed to load image'));
};
img.src = objectUrl;
});
};
const loadImageOntoCanvas = (
image: HTMLImageElement,
canvas: HTMLCanvasElement,
ctx: CanvasRenderingContext2D,
): ImageData => {
const scale = Math.min((canvas.width * 0.8) / image.width, (canvas.height * 0.8) / image.height);
const x = (canvas.width - image.width * scale) / 2;
const y = (canvas.height - image.height * scale) / 2;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(image, x, y, image.width * scale, image.height * scale);
ctx.restore();
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
return imageData;
};
export type SignaturePadProps = Omit<HTMLAttributes<HTMLCanvasElement>, 'onChange'> & { export type SignaturePadProps = Omit<HTMLAttributes<HTMLCanvasElement>, 'onChange'> & {
onChange?: (_signatureDataUrl: string | null) => void; onChange?: (_signatureDataUrl: string | null) => void;
containerClassName?: string; containerClassName?: string;
@ -52,12 +110,15 @@ export const SignaturePad = ({
}: SignaturePadProps) => { }: SignaturePadProps) => {
const $el = useRef<HTMLCanvasElement>(null); const $el = useRef<HTMLCanvasElement>(null);
const $imageData = useRef<ImageData | null>(null); const $imageData = useRef<ImageData | null>(null);
const $fileInput = useRef<HTMLInputElement>(null);
const [isPressed, setIsPressed] = useState(false); const [isPressed, setIsPressed] = useState(false);
const [lines, setLines] = useState<Point[][]>([]); const [lines, setLines] = useState<Point[][]>([]);
const [currentLine, setCurrentLine] = useState<Point[]>([]); const [currentLine, setCurrentLine] = useState<Point[]>([]);
const [selectedColor, setSelectedColor] = useState('black'); const [selectedColor, setSelectedColor] = useState('black');
const [typedSignature, setTypedSignature] = useState(defaultValue ?? ''); const [typedSignature, setTypedSignature] = useState(
defaultValue && !isBase64Image(defaultValue) ? defaultValue : '',
);
const perfectFreehandOptions = useMemo(() => { const perfectFreehandOptions = useMemo(() => {
const size = $el.current ? Math.min($el.current.height, $el.current.width) * 0.03 : 10; const size = $el.current ? Math.min($el.current.height, $el.current.width) * 0.03 : 10;
@ -80,6 +141,14 @@ export const SignaturePad = ({
setIsPressed(true); setIsPressed(true);
if (typedSignature) {
setTypedSignature('');
if ($el.current) {
const ctx = $el.current.getContext('2d');
ctx?.clearRect(0, 0, $el.current.width, $el.current.height);
}
}
const point = Point.fromEvent(event, DPI, $el.current); const point = Point.fromEvent(event, DPI, $el.current);
setCurrentLine([point]); setCurrentLine([point]);
@ -193,6 +262,10 @@ export const SignaturePad = ({
$imageData.current = null; $imageData.current = null;
} }
if ($fileInput.current) {
$fileInput.current.value = '';
}
onChange?.(null); onChange?.(null);
setTypedSignature(''); setTypedSignature('');
@ -255,12 +328,30 @@ export const SignaturePad = ({
} }
}; };
const handleImageUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
try {
const img = await loadImage(event.target.files?.[0]);
if (!$el.current) return;
const ctx = $el.current.getContext('2d');
if (!ctx) return;
$imageData.current = loadImageOntoCanvas(img, $el.current, ctx);
onChange?.($el.current.toDataURL());
setLines([]);
setCurrentLine([]);
setTypedSignature('');
} catch (error) {
console.error(error);
}
};
useEffect(() => { useEffect(() => {
if (typedSignature.trim() !== '') { if (typedSignature.trim() !== '' && !isBase64Image(typedSignature)) {
renderTypedSignature(); renderTypedSignature();
onChange?.(typedSignature); onChange?.(typedSignature);
} else {
onClearClick();
} }
}, [typedSignature, selectedColor]); }, [typedSignature, selectedColor]);
@ -370,6 +461,26 @@ export const SignaturePad = ({
</div> </div>
)} )}
<div className="text-foreground absolute left-3 top-3 filter">
<div
className="focus-visible:ring-ring ring-offset-background text-muted-foreground/60 hover:text-muted-foreground flex cursor-pointer flex-row gap-2 rounded-full p-0 text-[0.688rem] focus-visible:outline-none focus-visible:ring-2"
onClick={() => $fileInput.current?.click()}
>
<Input
ref={$fileInput}
type="file"
accept="image/*"
className="hidden"
onChange={handleImageUpload}
disabled={disabled}
/>
<Upload className="h-4 w-4" />
<span>
<Trans>Upload Signature</Trans>
</span>
</div>
</div>
<div className="text-foreground absolute right-2 top-2 filter"> <div className="text-foreground absolute right-2 top-2 filter">
<Select defaultValue={selectedColor} onValueChange={(value) => setSelectedColor(value)}> <Select defaultValue={selectedColor} onValueChange={(value) => setSelectedColor(value)}>
<SelectTrigger className="h-auto w-auto border-none p-0.5"> <SelectTrigger className="h-auto w-auto border-none p-0.5">