mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
Compare commits
15 Commits
v1.8.1-rc.
...
v1.8.1-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
| b3ccb3d26f | |||
| b17370c153 | |||
| 0c53f5b061 | |||
| ed6157de80 | |||
| 5e08d0cffb | |||
| 5565aff7a3 | |||
| 428acf4ac3 | |||
| f4b1e5104e | |||
| a687064a42 | |||
| 8ec69388a5 | |||
| f3da11b3e7 | |||
| fc84ee8ec2 | |||
| 4282a96ee7 | |||
| 2aae7435f8 | |||
| bdd33bd335 |
@ -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.
|
||||
</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.
|
||||
|
||||

|
||||
|
||||
</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.
|
||||
|
||||
@ -20,6 +20,7 @@ Documenso supports Webhooks and allows you to subscribe to the following events:
|
||||
- `document.opened`
|
||||
- `document.signed`
|
||||
- `document.completed`
|
||||
- `document.rejected`
|
||||
|
||||
## 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:
|
||||
|
||||
- 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.
|
||||
|
||||

|
||||
@ -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:
|
||||
|
||||
| Field | Type | Description |
|
||||
| -------------------------------------------- | --------- | ---------------------------------------------------- |
|
||||
| `event` | string | The type of event that triggered the webhook. |
|
||||
| `payload.id` | number | The id of the document. |
|
||||
| `payload.userId` | number | The id of the user who owns the document. |
|
||||
| `payload.authOptions` | json? | Authentication options for the document. |
|
||||
| `payload.formValues` | json? | Form values for the document. |
|
||||
| `payload.title` | string | The name of the document. |
|
||||
| `payload.status` | string | The current status of the document. |
|
||||
| `payload.documentDataId` | string | The identifier for the document data. |
|
||||
| `payload.createdAt` | datetime | The creation date and time of the document. |
|
||||
| `payload.updatedAt` | datetime | The last update date and time of the document. |
|
||||
| `payload.completedAt` | datetime? | The completion date and time of the document. |
|
||||
| `payload.deletedAt` | datetime? | The deletion date and time of the document. |
|
||||
| `payload.teamId` | number? | The id of the team. |
|
||||
| `payload.documentData.id` | string | The id of the document data. |
|
||||
| `payload.documentData.type` | string | The type of the document data. |
|
||||
| `payload.documentData.data` | string | The data of the document. |
|
||||
| `payload.documentData.initialData` | string | The initial data of the document. |
|
||||
| `payload.Recipient[].id` | number | The id of the recipient. |
|
||||
| `payload.Recipient[].documentId` | number? | The id the document associated with the recipient. |
|
||||
| `payload.Recipient[].templateId` | number? | The template identifier for the recipient. |
|
||||
| `payload.Recipient[].email` | string | The email address of the recipient. |
|
||||
| `payload.Recipient[].name` | string | The name of the recipient. |
|
||||
| `payload.Recipient[].token` | string | The token associated with the recipient. |
|
||||
| `payload.Recipient[].expired` | datetime? | The expiration status of the recipient. |
|
||||
| `payload.Recipient[].signedAt` | datetime? | The date and time the recipient signed the document. |
|
||||
| `payload.Recipient[].authOptions.accessAuth` | json? | Access authentication options. |
|
||||
| `payload.Recipient[].authOptions.actionAuth` | json? | Action authentication options. |
|
||||
| `payload.Recipient[].role` | string | The role of the recipient. |
|
||||
| `payload.Recipient[].readStatus` | string | The read status of the document by the recipient. |
|
||||
| `payload.Recipient[].signingStatus` | string | The signing status of the recipient. |
|
||||
| `payload.Recipient[].sendStatus` | string | The send status of the document to the recipient. |
|
||||
| `createdAt` | datetime | The creation date and time of the webhook event. |
|
||||
| `webhookEndpoint` | string | The endpoint URL where the webhook is sent. |
|
||||
|
||||
## Webhook event payload example
|
||||
|
||||
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.
|
||||
| Field | Type | Description |
|
||||
| -------------------------------------------- | --------- | ----------------------------------------------------- |
|
||||
| `event` | string | The type of event that triggered the webhook. |
|
||||
| `payload.id` | number | The id of the document. |
|
||||
| `payload.externalId` | string? | External identifier for the document. |
|
||||
| `payload.userId` | number | The id of the user who owns the document. |
|
||||
| `payload.authOptions` | json? | Authentication options for the document. |
|
||||
| `payload.formValues` | json? | Form values for the document. |
|
||||
| `payload.visibility` | string | Document visibility (e.g., EVERYONE). |
|
||||
| `payload.title` | string | The title of the document. |
|
||||
| `payload.status` | string | The current status of the document. |
|
||||
| `payload.documentDataId` | string | The identifier for the document data. |
|
||||
| `payload.createdAt` | datetime | The creation date and time of the document. |
|
||||
| `payload.updatedAt` | datetime | The last update date and time of the document. |
|
||||
| `payload.completedAt` | datetime? | The completion date and time of the document. |
|
||||
| `payload.deletedAt` | datetime? | The deletion date and time of the document. |
|
||||
| `payload.teamId` | number? | The id of the team if document belongs to a team. |
|
||||
| `payload.templateId` | number? | The id of the template if created from template. |
|
||||
| `payload.source` | string | The source of the document (e.g., DOCUMENT, TEMPLATE) |
|
||||
| `payload.documentMeta.id` | string | The id of the document metadata. |
|
||||
| `payload.documentMeta.subject` | string? | The subject of the document. |
|
||||
| `payload.documentMeta.message` | string? | The message associated with the document. |
|
||||
| `payload.documentMeta.timezone` | string | The timezone setting for the document. |
|
||||
| `payload.documentMeta.password` | string? | The password protection if set. |
|
||||
| `payload.documentMeta.dateFormat` | string | The date format used in the document. |
|
||||
| `payload.documentMeta.redirectUrl` | string? | The URL to redirect after signing. |
|
||||
| `payload.documentMeta.signingOrder` | string | The signing order (e.g., PARALLEL, SEQUENTIAL). |
|
||||
| `payload.documentMeta.typedSignatureEnabled` | boolean | Whether typed signatures are enabled. |
|
||||
| `payload.documentMeta.language` | string | The language of the document. |
|
||||
| `payload.documentMeta.distributionMethod` | string | The method of distributing the document. |
|
||||
| `payload.documentMeta.emailSettings` | json? | Email notification settings. |
|
||||
| `payload.Recipient[].id` | number | The id of the recipient. |
|
||||
| `payload.Recipient[].documentId` | number? | The id of the document for this recipient. |
|
||||
| `payload.Recipient[].templateId` | number? | The template id if from a template. |
|
||||
| `payload.Recipient[].email` | string | The email address of the recipient. |
|
||||
| `payload.Recipient[].name` | string | The name of the recipient. |
|
||||
| `payload.Recipient[].token` | string | The unique token for this recipient. |
|
||||
| `payload.Recipient[].documentDeletedAt` | datetime? | When the document was deleted for this recipient. |
|
||||
| `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
|
||||
|
||||
@ -104,9 +115,11 @@ Example payload for the `document.created` event:
|
||||
"event": "DOCUMENT_CREATED",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "documenso.pdf",
|
||||
"status": "DRAFT",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
@ -114,7 +127,43 @@ Example payload for the `document.created` event:
|
||||
"updatedAt": "2024-04-22T11:44:43.341Z",
|
||||
"completedAt": 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",
|
||||
"webhookEndpoint": "https://mywebhooksite.com/mywebhook"
|
||||
@ -128,9 +177,11 @@ Example payload for the `document.sent` event:
|
||||
"event": "DOCUMENT_SENT",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "documenso.pdf",
|
||||
"status": "PENDING",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
@ -139,6 +190,22 @@ Example payload for the `document.sent` event:
|
||||
"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,
|
||||
@ -147,12 +214,12 @@ Example payload for the `document.sent` event:
|
||||
"email": "signer2@documenso.com",
|
||||
"name": "Signer 2",
|
||||
"token": "vbT8hi3jKQmrFP_LN1WcS",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": null,
|
||||
"authOptions": {
|
||||
"accessAuth": null,
|
||||
"actionAuth": null
|
||||
},
|
||||
"authOptions": null,
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": null,
|
||||
"role": "VIEWER",
|
||||
"readStatus": "NOT_OPENED",
|
||||
"signingStatus": "NOT_SIGNED",
|
||||
@ -165,12 +232,12 @@ Example payload for the `document.sent` event:
|
||||
"email": "signer1@documenso.com",
|
||||
"name": "Signer 1",
|
||||
"token": "HkrptwS42ZBXdRKj1TyUo",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": null,
|
||||
"authOptions": {
|
||||
"accessAuth": null,
|
||||
"actionAuth": null
|
||||
},
|
||||
"authOptions": null,
|
||||
"signingOrder": 2,
|
||||
"rejectionReason": null,
|
||||
"role": "SIGNER",
|
||||
"readStatus": "NOT_OPENED",
|
||||
"signingStatus": "NOT_SIGNED",
|
||||
@ -190,9 +257,11 @@ Example payload for the `document.opened` event:
|
||||
"event": "DOCUMENT_OPENED",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "documenso.pdf",
|
||||
"status": "PENDING",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
@ -201,6 +270,22 @@ Example payload for the `document.opened` event:
|
||||
"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,
|
||||
@ -209,24 +294,18 @@ Example payload for the `document.opened` event:
|
||||
"email": "signer2@documenso.com",
|
||||
"name": "Signer 2",
|
||||
"token": "vbT8hi3jKQmrFP_LN1WcS",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": null,
|
||||
"authOptions": {
|
||||
"accessAuth": null,
|
||||
"actionAuth": null
|
||||
},
|
||||
"authOptions": null,
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": null,
|
||||
"role": "VIEWER",
|
||||
"readStatus": "OPENED",
|
||||
"signingStatus": "NOT_SIGNED",
|
||||
"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",
|
||||
"webhookEndpoint": "https://mywebhooksite.com/mywebhook"
|
||||
@ -240,9 +319,11 @@ Example payload for the `document.signed` event:
|
||||
"event": "DOCUMENT_SIGNED",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "documenso.pdf",
|
||||
"status": "COMPLETED",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
@ -251,6 +332,22 @@ Example payload for the `document.signed` event:
|
||||
"completedAt": "2024-04-22T11:52:05.707Z",
|
||||
"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": 51,
|
||||
@ -259,12 +356,15 @@ Example payload for the `document.signed` event:
|
||||
"email": "signer1@documenso.com",
|
||||
"name": "Signer 1",
|
||||
"token": "HkrptwS42ZBXdRKj1TyUo",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": "2024-04-22T11:52:05.688Z",
|
||||
"authOptions": {
|
||||
"accessAuth": null,
|
||||
"actionAuth": null
|
||||
},
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": null,
|
||||
"role": "SIGNER",
|
||||
"readStatus": "OPENED",
|
||||
"signingStatus": "SIGNED",
|
||||
@ -284,9 +384,11 @@ Example payload for the `document.completed` event:
|
||||
"event": "DOCUMENT_COMPLETED",
|
||||
"payload": {
|
||||
"id": 10,
|
||||
"externalId": null,
|
||||
"userId": 1,
|
||||
"authOptions": null,
|
||||
"formValues": null,
|
||||
"visibility": "EVERYONE",
|
||||
"title": "documenso.pdf",
|
||||
"status": "COMPLETED",
|
||||
"documentDataId": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
@ -295,11 +397,21 @@ Example payload for the `document.completed` event:
|
||||
"completedAt": "2024-04-22T11:52:05.707Z",
|
||||
"deletedAt": null,
|
||||
"teamId": null,
|
||||
"documentData": {
|
||||
"id": "hs8qz1ktr9204jn7mg6c5dxy0",
|
||||
"type": "S3_PATH",
|
||||
"data": "bk9p1h7x0s3m/documenso-signed.pdf",
|
||||
"initialData": "9753/xzqrshtlpokm/documenso.pdf"
|
||||
"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": [
|
||||
{
|
||||
@ -309,12 +421,15 @@ Example payload for the `document.completed` event:
|
||||
"email": "signer2@documenso.com",
|
||||
"name": "Signer 2",
|
||||
"token": "vbT8hi3jKQmrFP_LN1WcS",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": "2024-04-22T11:51:10.055Z",
|
||||
"authOptions": {
|
||||
"accessAuth": null,
|
||||
"actionAuth": null
|
||||
},
|
||||
"signingOrder": 1,
|
||||
"rejectionReason": null,
|
||||
"role": "VIEWER",
|
||||
"readStatus": "OPENED",
|
||||
"signingStatus": "SIGNED",
|
||||
@ -327,12 +442,15 @@ Example payload for the `document.completed` event:
|
||||
"email": "signer1@documenso.com",
|
||||
"name": "Signer 1",
|
||||
"token": "HkrptwS42ZBXdRKj1TyUo",
|
||||
"documentDeletedAt": null,
|
||||
"expired": null,
|
||||
"signedAt": "2024-04-22T11:52:05.688Z",
|
||||
"authOptions": {
|
||||
"accessAuth": null,
|
||||
"actionAuth": null
|
||||
},
|
||||
"signingOrder": 2,
|
||||
"rejectionReason": null,
|
||||
"role": "SIGNER",
|
||||
"readStatus": "OPENED",
|
||||
"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
|
||||
|
||||
Webhooks are available to individual users and teams.
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
"signing-documents": "Signing Documents",
|
||||
"templates": "Templates",
|
||||
"direct-links": "Direct Signing Links",
|
||||
"document-visibility": "Document Visibility",
|
||||
"teams": "Teams",
|
||||
"-- Legal Overview": {
|
||||
"type": "separator",
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"general-settings": "General Settings",
|
||||
"preferences": "Preferences",
|
||||
"document-visibility": "Document Visibility",
|
||||
"sender-details": "Email Sender Details"
|
||||
"sender-details": "Email Sender Details",
|
||||
"branding-preferences": "Branding Preferences"
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||

|
||||
|
||||
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.
|
||||
@ -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_.
|
||||
- **Admin only** - The document is only visible to the team's admins.
|
||||
|
||||

|
||||

|
||||
|
||||
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">
|
||||
If the team member uploading the document has a role lower than the default document visibility,
|
||||
|
||||
@ -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.
|
||||
|
||||

|
||||
|
||||
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).
|
||||
19
apps/documentation/pages/users/teams/preferences.mdx
Normal file
19
apps/documentation/pages/users/teams/preferences.mdx
Normal 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.
|
||||
|
||||

|
||||
|
||||
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).
|
||||
BIN
apps/documentation/public/teams/team-branding-preferences.webp
Normal file
BIN
apps/documentation/public/teams/team-branding-preferences.webp
Normal file
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 |
BIN
apps/documentation/public/teams/team-preferences.webp
Normal file
BIN
apps/documentation/public/teams/team-preferences.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@documenso/marketing",
|
||||
"version": "1.8.1-rc.4",
|
||||
"version": "1.8.1-rc.9",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@documenso/web",
|
||||
"version": "1.8.1-rc.4",
|
||||
"version": "1.8.1-rc.9",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
60
apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx
Normal file
60
apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@ -6,7 +6,7 @@ import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
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 { Button } from '@documenso/ui/primitives/button';
|
||||
@ -80,6 +80,20 @@ export const AdminNav = ({ className, ...props }: AdminNavProps) => {
|
||||
</Link>
|
||||
</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
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
|
||||
@ -41,7 +41,7 @@ export default async function BillingSettingsPage() {
|
||||
|
||||
const [subscriptions, prices, primaryAccountPlanPrices] = await Promise.all([
|
||||
getSubscriptionsByUserId({ userId: user.id }),
|
||||
getPricesByInterval({ plan: STRIPE_PLAN_TYPE.REGULAR }),
|
||||
getPricesByInterval({ plans: [STRIPE_PLAN_TYPE.REGULAR, STRIPE_PLAN_TYPE.PLATFORM] }),
|
||||
getPrimaryAccountPlanPrices(),
|
||||
]);
|
||||
|
||||
|
||||
@ -80,7 +80,7 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen
|
||||
return (
|
||||
<EmbedAuthenticateView
|
||||
email={user?.email || recipient.email}
|
||||
returnTo={`/embed/direct/${token}`}
|
||||
returnTo={`/embed/sign/${token}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -78,13 +78,14 @@ async function middleware(req: NextRequest): Promise<NextResponse> {
|
||||
if (req.nextUrl.pathname.startsWith('/embed')) {
|
||||
const res = NextResponse.next();
|
||||
|
||||
const origin = req.headers.get('Origin') ?? '*';
|
||||
|
||||
// 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-Origin', '*');
|
||||
res.headers.set('Content-Security-Policy', 'frame-ancestors *');
|
||||
res.headers.set('Access-Control-Allow-Origin', origin);
|
||||
res.headers.set('Content-Security-Policy', `frame-ancestors ${origin}`);
|
||||
res.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
res.headers.set('X-Content-Type-Options', 'nosniff');
|
||||
res.headers.set('X-Frame-Options', 'ALLOW-ALL');
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@documenso/root",
|
||||
"version": "1.8.1-rc.4",
|
||||
"version": "1.8.1-rc.9",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@documenso/root",
|
||||
"version": "1.8.1-rc.4",
|
||||
"version": "1.8.1-rc.9",
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
"packages/*"
|
||||
@ -77,7 +77,7 @@
|
||||
},
|
||||
"apps/marketing": {
|
||||
"name": "@documenso/marketing",
|
||||
"version": "1.8.1-rc.4",
|
||||
"version": "1.8.1-rc.9",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@documenso/assets": "*",
|
||||
@ -438,7 +438,7 @@
|
||||
},
|
||||
"apps/web": {
|
||||
"name": "@documenso/web",
|
||||
"version": "1.8.1-rc.4",
|
||||
"version": "1.8.1-rc.9",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@documenso/api": "*",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "1.8.1-rc.4",
|
||||
"version": "1.8.1-rc.9",
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"build:web": "turbo run build --filter=@documenso/web",
|
||||
|
||||
@ -244,18 +244,10 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
||||
}
|
||||
|
||||
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);
|
||||
const timezone = body.meta.timezone
|
||||
? TIME_ZONES.find((tz) => tz === body.meta.timezone)
|
||||
: DEFAULT_DOCUMENT_TIME_ZONE;
|
||||
|
||||
const isDateFormatValid = body.meta.dateFormat
|
||||
? DATE_FORMATS.some((format) => format.label === dateFormat?.label)
|
||||
: true;
|
||||
const isTimeZoneValid = body.meta.timezone ? TIME_ZONES.includes(String(timezone)) : true;
|
||||
|
||||
if (!isDateFormatValid) {
|
||||
if (body.meta.dateFormat && !dateFormat) {
|
||||
return {
|
||||
status: 400,
|
||||
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) {
|
||||
return {
|
||||
status: 400,
|
||||
|
||||
@ -12,10 +12,10 @@ export type GetPricesByIntervalOptions = {
|
||||
/**
|
||||
* 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({
|
||||
query: `active:'true' type:'recurring'`,
|
||||
expand: ['data.product'],
|
||||
@ -27,7 +27,8 @@ export const getPricesByInterval = async ({ plan }: GetPricesByIntervalOptions =
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
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.
|
||||
return product.active && filter;
|
||||
|
||||
@ -21,6 +21,7 @@ import { insertFieldInPDF } from '../../../server-only/pdf/insert-field-in-pdf';
|
||||
import { normalizeSignatureAppearances } from '../../../server-only/pdf/normalize-signature-appearances';
|
||||
import { triggerWebhook } from '../../../server-only/webhooks/trigger/trigger-webhook';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
|
||||
import { ZWebhookDocumentSchema } from '../../../types/webhook-payload';
|
||||
import { ZRequestMetadataSchema } from '../../../universal/extract-request-metadata';
|
||||
import { getFile } from '../../../universal/upload/get-file';
|
||||
import { putPdfFile } from '../../../universal/upload/put-file';
|
||||
@ -249,13 +250,14 @@ export const SEAL_DOCUMENT_JOB_DEFINITION = {
|
||||
},
|
||||
include: {
|
||||
documentData: true,
|
||||
documentMeta: true,
|
||||
Recipient: true,
|
||||
},
|
||||
});
|
||||
|
||||
await triggerWebhook({
|
||||
event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
|
||||
data: updatedDocument,
|
||||
data: ZWebhookDocumentSchema.parse(updatedDocument),
|
||||
userId: updatedDocument.userId,
|
||||
teamId: updatedDocument.teamId ?? undefined,
|
||||
});
|
||||
|
||||
@ -26,6 +26,10 @@ import { extractNextAuthRequestMetadata } from '../universal/extract-request-met
|
||||
import { getAuthenticatorOptions } from '../utils/authenticator';
|
||||
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 = {
|
||||
adapter: PrismaAdapter(prisma),
|
||||
secret: process.env.NEXTAUTH_SECRET ?? 'secret',
|
||||
@ -431,5 +435,53 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
|
||||
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.
|
||||
};
|
||||
|
||||
148
packages/lib/server-only/admin/get-signing-volume.ts
Normal file
148
packages/lib/server-only/admin/get-signing-volume.ts
Normal 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,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -13,6 +13,7 @@ import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||
|
||||
import { jobs } from '../../jobs/client';
|
||||
import type { TRecipientActionAuth } from '../../types/document-auth';
|
||||
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
|
||||
import { getIsRecipientsTurnToSign } from '../recipient/get-is-recipient-turn';
|
||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||
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({
|
||||
event: WebhookTriggerEvents.DOCUMENT_SIGNED,
|
||||
data: updatedDocument,
|
||||
data: ZWebhookDocumentSchema.parse(updatedDocument),
|
||||
userId: updatedDocument.userId,
|
||||
teamId: updatedDocument.teamId ?? undefined,
|
||||
});
|
||||
|
||||
@ -9,6 +9,7 @@ import { DocumentSource, DocumentVisibility, WebhookTriggerEvents } from '@docum
|
||||
import type { Team, TeamGlobalSettings } from '@documenso/prisma/client';
|
||||
import { TeamMemberRole } from '@documenso/prisma/client';
|
||||
|
||||
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
|
||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||
|
||||
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({
|
||||
event: WebhookTriggerEvents.DOCUMENT_CREATED,
|
||||
data: document,
|
||||
data: ZWebhookDocumentSchema.parse(createdDocument),
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
|
||||
return document;
|
||||
return createdDocument;
|
||||
});
|
||||
};
|
||||
|
||||
@ -3,10 +3,13 @@ import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { jobs } from '@documenso/lib/jobs/client';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||
|
||||
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 { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||
|
||||
export type RejectDocumentWithTokenOptions = {
|
||||
token: string;
|
||||
@ -31,6 +34,8 @@ export async function rejectDocumentWithToken({
|
||||
Document: {
|
||||
include: {
|
||||
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
|
||||
const [updatedRecipient] = await prisma.$transaction([
|
||||
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;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/
|
||||
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||
import { signPdf } from '@documenso/signing';
|
||||
|
||||
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
|
||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||
import { getFile } from '../../universal/upload/get-file';
|
||||
import { putPdfFile } from '../../universal/upload/put-file';
|
||||
@ -199,13 +200,14 @@ export const sealDocument = async ({
|
||||
},
|
||||
include: {
|
||||
documentData: true,
|
||||
documentMeta: true,
|
||||
Recipient: true,
|
||||
},
|
||||
});
|
||||
|
||||
await triggerWebhook({
|
||||
event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
|
||||
data: updatedDocument,
|
||||
data: ZWebhookDocumentSchema.parse(updatedDocument),
|
||||
userId: document.userId,
|
||||
teamId: document.teamId ?? undefined,
|
||||
});
|
||||
|
||||
@ -14,6 +14,7 @@ import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||
|
||||
import { jobs } from '../../jobs/client';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';
|
||||
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
|
||||
import { getFile } from '../../universal/upload/get-file';
|
||||
import { insertFormValuesInPdf } from '../pdf/insert-form-values-in-pdf';
|
||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||
@ -236,6 +237,7 @@ export const sendDocument = async ({
|
||||
status: DocumentStatus.PENDING,
|
||||
},
|
||||
include: {
|
||||
documentMeta: true,
|
||||
Recipient: true,
|
||||
},
|
||||
});
|
||||
@ -243,7 +245,7 @@ export const sendDocument = async ({
|
||||
|
||||
await triggerWebhook({
|
||||
event: WebhookTriggerEvents.DOCUMENT_SENT,
|
||||
data: updatedDocument,
|
||||
data: ZWebhookDocumentSchema.parse(updatedDocument),
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
|
||||
@ -6,8 +6,8 @@ import { ReadStatus } from '@documenso/prisma/client';
|
||||
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||
|
||||
import type { TDocumentAccessAuthTypes } from '../../types/document-auth';
|
||||
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
|
||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||
import { getDocumentAndRecipientByToken } from './get-document-by-token';
|
||||
|
||||
export type ViewedDocumentOptions = {
|
||||
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({
|
||||
event: WebhookTriggerEvents.DOCUMENT_OPENED,
|
||||
data: document,
|
||||
data: ZWebhookDocumentSchema.parse(document),
|
||||
userId: document.userId,
|
||||
teamId: document.teamId ?? undefined,
|
||||
});
|
||||
|
||||
@ -31,6 +31,7 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||
import type { TRecipientActionAuthTypes } from '../../types/document-auth';
|
||||
import { DocumentAccessAuth, ZRecipientAuthOptionsSchema } from '../../types/document-auth';
|
||||
import { ZFieldMetaSchema } from '../../types/field-meta';
|
||||
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
|
||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||
import type { CreateDocumentAuditLogDataResponse } from '../../utils/document-audit-logs';
|
||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
||||
@ -591,7 +592,7 @@ export const createDocumentFromDirectTemplate = async ({
|
||||
requestMetadata,
|
||||
});
|
||||
|
||||
const updatedDocument = await prisma.document.findFirstOrThrow({
|
||||
const createdDocument = await prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
},
|
||||
@ -603,9 +604,9 @@ export const createDocumentFromDirectTemplate = async ({
|
||||
|
||||
await triggerWebhook({
|
||||
event: WebhookTriggerEvents.DOCUMENT_SIGNED,
|
||||
data: updatedDocument,
|
||||
userId: updatedDocument.userId,
|
||||
teamId: updatedDocument.teamId ?? undefined,
|
||||
data: ZWebhookDocumentSchema.parse(createdDocument),
|
||||
userId: template.userId,
|
||||
teamId: template.teamId ?? undefined,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[CREATE_DOCUMENT_FROM_DIRECT_TEMPLATE]:', err);
|
||||
|
||||
@ -18,6 +18,7 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||
import { ZRecipientAuthOptionsSchema } from '../../types/document-auth';
|
||||
import type { TDocumentEmailSettings } from '../../types/document-email';
|
||||
import { ZFieldMetaSchema } from '../../types/field-meta';
|
||||
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
|
||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
||||
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({
|
||||
event: WebhookTriggerEvents.DOCUMENT_CREATED,
|
||||
data: document,
|
||||
data: ZWebhookDocumentSchema.parse(createdDocument),
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
|
||||
@ -60,14 +60,19 @@ msgstr "{0} von {1} Zeile(n) ausgewählt."
|
||||
|
||||
#: 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}."
|
||||
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the 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} hat dich im Namen von {1} eingeladen, das Dokument \"{2}\" {recipientActionVerb}."
|
||||
|
||||
#: packages/email/template-components/template-document-invite.tsx:51
|
||||
#~ msgid "{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>"
|
||||
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}"
|
||||
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}\"."
|
||||
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"
|
||||
|
||||
#: 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}"
|
||||
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {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
|
||||
#~ 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}\""
|
||||
|
||||
#: packages/email/templates/document-invite.tsx:45
|
||||
msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}"
|
||||
msgstr "{inviterName} hat dich im Namen von {teamName} eingeladen, {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}"
|
||||
|
||||
#: packages/email/templates/team-join.tsx:67
|
||||
msgid "{memberEmail} joined the following team"
|
||||
@ -568,7 +581,7 @@ msgstr "Unterschrift löschen"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:394
|
||||
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
|
||||
msgid "Close"
|
||||
@ -711,7 +724,7 @@ msgid "Document created"
|
||||
msgstr "Dokument erstellt"
|
||||
|
||||
#: 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"
|
||||
msgstr "Dokument erstellt aus direkter Vorlage"
|
||||
|
||||
|
||||
@ -51,16 +51,16 @@ msgstr "\"{placeholderEmail}\" im Namen von \"{0}\" hat Sie eingeladen, \"Beispi
|
||||
#~ msgstr "\"{teamUrl}\" hat Sie eingeladen, \"Beispieldokument\" zu unterschreiben."
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83
|
||||
msgid "({0}) has invited you to approve this document"
|
||||
msgstr "({0}) hat dich eingeladen, dieses Dokument zu genehmigen"
|
||||
#~ msgid "({0}) has invited you to approve this document"
|
||||
#~ msgstr "({0}) hat dich eingeladen, dieses Dokument zu genehmigen"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80
|
||||
msgid "({0}) has invited you to sign this document"
|
||||
msgstr "({0}) hat dich eingeladen, dieses Dokument zu unterzeichnen"
|
||||
#~ msgid "({0}) has invited you to sign this document"
|
||||
#~ msgstr "({0}) hat dich eingeladen, dieses Dokument zu unterzeichnen"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77
|
||||
msgid "({0}) has invited you to view this document"
|
||||
msgstr "({0}) hat dich eingeladen, dieses Dokument zu betrachten"
|
||||
#~ msgid "({0}) has invited you to view this document"
|
||||
#~ msgstr "({0}) hat dich eingeladen, dieses Dokument zu betrachten"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313
|
||||
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/sign/[[...url]]/client.tsx:335
|
||||
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/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/components/forms/v2/signup.tsx:534
|
||||
msgid "Complete"
|
||||
msgstr "Vollständig"
|
||||
msgstr "Abschließen"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx:69
|
||||
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."
|
||||
|
||||
#: 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/data-table.tsx:54
|
||||
#: 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}"
|
||||
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
|
||||
msgid "Daily"
|
||||
msgstr "Täglich"
|
||||
@ -1988,6 +1994,18 @@ msgstr "Zum Eigentümer gehen"
|
||||
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."
|
||||
|
||||
#: 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
|
||||
msgid "Here you can edit your personal details."
|
||||
msgstr "Hier können Sie Ihre persönlichen Daten bearbeiten."
|
||||
@ -2204,6 +2222,10 @@ msgstr "Zuletzt aktualisiert am"
|
||||
msgid "Last used"
|
||||
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)/tables/current-user-teams-data-table.tsx:117
|
||||
msgid "Leave"
|
||||
@ -2411,6 +2433,7 @@ msgid "My templates"
|
||||
msgstr "Meine Vorlagen"
|
||||
|
||||
#: 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/data-table-users.tsx:66
|
||||
#: 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"
|
||||
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
|
||||
msgid "On this page, you can create a new webhook."
|
||||
msgstr "Auf dieser Seite können Sie einen neuen Webhook erstellen."
|
||||
@ -3105,6 +3140,7 @@ msgstr "Suchen"
|
||||
msgid "Search by document title"
|
||||
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
|
||||
msgid "Search by name or email"
|
||||
msgstr "Nach Name oder E-Mail suchen"
|
||||
@ -3369,6 +3405,11 @@ msgstr "Unterzeichnungslinks wurden für dieses Dokument erstellt."
|
||||
msgid "Signing up..."
|
||||
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
|
||||
msgid "Since {0}"
|
||||
msgstr "Seit {0}"
|
||||
@ -3377,7 +3418,7 @@ msgstr "Seit {0}"
|
||||
msgid "Site 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
|
||||
msgid "Site Settings"
|
||||
msgstr "Website Einstellungen"
|
||||
|
||||
@ -55,14 +55,19 @@ msgstr "{0} of {1} row(s) selected."
|
||||
|
||||
#: 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}\"."
|
||||
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/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
|
||||
#~ msgid "{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>"
|
||||
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}"
|
||||
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}\"."
|
||||
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}\""
|
||||
|
||||
#: 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}"
|
||||
msgid "{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
|
||||
#~ 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}\""
|
||||
|
||||
#: 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}"
|
||||
#~ 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/templates/team-join.tsx:67
|
||||
msgid "{memberEmail} joined the following team"
|
||||
@ -706,7 +719,7 @@ msgid "Document created"
|
||||
msgstr "Document created"
|
||||
|
||||
#: 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"
|
||||
msgstr "Document created from direct template"
|
||||
|
||||
|
||||
@ -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\"."
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83
|
||||
msgid "({0}) has invited you to approve this document"
|
||||
msgstr "({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"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80
|
||||
msgid "({0}) has invited you to sign this document"
|
||||
msgstr "({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"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77
|
||||
msgid "({0}) has invited you to view this document"
|
||||
msgstr "({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"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313
|
||||
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."
|
||||
|
||||
#: 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/data-table.tsx:54
|
||||
#: 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}"
|
||||
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
|
||||
msgid "Daily"
|
||||
msgstr "Daily"
|
||||
@ -1983,6 +1989,18 @@ msgstr "Go to owner"
|
||||
msgid "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
|
||||
msgid "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"
|
||||
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)/tables/current-user-teams-data-table.tsx:117
|
||||
msgid "Leave"
|
||||
@ -2406,6 +2428,7 @@ msgid "My templates"
|
||||
msgstr "My templates"
|
||||
|
||||
#: 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/data-table-users.tsx:66
|
||||
#: 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"
|
||||
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
|
||||
msgid "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"
|
||||
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
|
||||
msgid "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..."
|
||||
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
|
||||
msgid "Since {0}"
|
||||
msgstr "Since {0}"
|
||||
@ -3372,7 +3413,7 @@ msgstr "Since {0}"
|
||||
msgid "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
|
||||
msgid "Site Settings"
|
||||
msgstr "Site Settings"
|
||||
|
||||
@ -60,14 +60,19 @@ msgstr "{0} de {1} fila(s) seleccionada."
|
||||
|
||||
#: 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}\"."
|
||||
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the 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} en nombre de {1} te ha invitado a {recipientActionVerb} el documento \"{2}\"."
|
||||
|
||||
#: packages/email/template-components/template-document-invite.tsx:51
|
||||
#~ msgid "{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>"
|
||||
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}"
|
||||
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}\"."
|
||||
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}\""
|
||||
|
||||
#: 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}"
|
||||
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {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
|
||||
#~ 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}\""
|
||||
|
||||
#: packages/email/templates/document-invite.tsx:45
|
||||
msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}"
|
||||
msgstr "{inviterName} en nombre de {teamName} te ha invitado a {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}"
|
||||
|
||||
#: packages/email/templates/team-join.tsx:67
|
||||
msgid "{memberEmail} joined the following team"
|
||||
@ -711,7 +724,7 @@ msgid "Document created"
|
||||
msgstr "Documento creado"
|
||||
|
||||
#: 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"
|
||||
msgstr "Documento creado a partir de plantilla directa"
|
||||
|
||||
|
||||
@ -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\"."
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83
|
||||
msgid "({0}) has invited you to approve this document"
|
||||
msgstr "({0}) te ha invitado a aprobar este documento"
|
||||
#~ msgid "({0}) has invited you to approve this document"
|
||||
#~ msgstr "({0}) te ha invitado a aprobar este documento"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80
|
||||
msgid "({0}) has invited you to sign this document"
|
||||
msgstr "({0}) te ha invitado a firmar este documento"
|
||||
#~ msgid "({0}) has invited you to sign this document"
|
||||
#~ msgstr "({0}) te ha invitado a firmar este documento"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77
|
||||
msgid "({0}) has invited you to view this document"
|
||||
msgstr "({0}) te ha invitado a ver este documento"
|
||||
#~ msgid "({0}) has invited you to view this document"
|
||||
#~ msgstr "({0}) te ha invitado a ver este documento"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313
|
||||
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."
|
||||
|
||||
#: 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/data-table.tsx:54
|
||||
#: 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."
|
||||
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
|
||||
msgid "Here you can edit your personal details."
|
||||
msgstr "Aquí puedes editar tus datos personales."
|
||||
@ -2204,6 +2217,10 @@ msgstr "Última actualización el"
|
||||
msgid "Last used"
|
||||
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)/tables/current-user-teams-data-table.tsx:117
|
||||
msgid "Leave"
|
||||
@ -2411,6 +2428,7 @@ msgid "My templates"
|
||||
msgstr "Mis plantillas"
|
||||
|
||||
#: 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/data-table-users.tsx:66
|
||||
#: 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"
|
||||
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
|
||||
msgid "On this page, you can create a new webhook."
|
||||
msgstr "En esta página, puedes crear un nuevo webhook."
|
||||
@ -3105,6 +3135,7 @@ msgstr "Buscar"
|
||||
msgid "Search by document title"
|
||||
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
|
||||
msgid "Search by name or email"
|
||||
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..."
|
||||
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
|
||||
msgid "Since {0}"
|
||||
msgstr "Desde {0}"
|
||||
@ -3377,7 +3413,7 @@ msgstr "Desde {0}"
|
||||
msgid "Site Banner"
|
||||
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
|
||||
msgid "Site Settings"
|
||||
msgstr "Configuraciones del sitio"
|
||||
|
||||
@ -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/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}\"."
|
||||
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the 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
|
||||
#~ msgid "{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>"
|
||||
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}"
|
||||
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}\"."
|
||||
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}\""
|
||||
|
||||
#: 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}"
|
||||
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {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
|
||||
#~ 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}\""
|
||||
|
||||
#: packages/email/templates/document-invite.tsx:45
|
||||
msgid "{inviterName} on behalf of {teamName} has invited you to {action} {documentName}"
|
||||
msgstr "{inviterName} au nom de {teamName} vous a invité à {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}"
|
||||
|
||||
#: packages/email/templates/team-join.tsx:67
|
||||
msgid "{memberEmail} joined the following team"
|
||||
@ -711,7 +724,7 @@ msgid "Document created"
|
||||
msgstr "Document créé"
|
||||
|
||||
#: 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"
|
||||
msgstr "Document créé à partir d'un modèle direct"
|
||||
|
||||
|
||||
@ -51,16 +51,16 @@ msgstr "\"{placeholderEmail}\" au nom de \"{0}\" vous a invité à signer \"exem
|
||||
#~ msgstr "\"{teamUrl}\" vous a invité à signer \"example document\"."
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:83
|
||||
msgid "({0}) has invited you to approve this document"
|
||||
msgstr "({0}) vous a invité à approuver ce document"
|
||||
#~ msgid "({0}) has invited you to approve this document"
|
||||
#~ msgstr "({0}) vous a invité à approuver ce document"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:80
|
||||
msgid "({0}) has invited you to sign this document"
|
||||
msgstr "({0}) vous a invité à signer ce document"
|
||||
#~ msgid "({0}) has invited you to sign this document"
|
||||
#~ msgstr "({0}) vous a invité à signer ce document"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/signing-page-view.tsx:77
|
||||
msgid "({0}) has invited you to view this document"
|
||||
msgstr "({0}) vous a invité à consulter ce document"
|
||||
#~ msgid "({0}) has invited you to view this document"
|
||||
#~ msgstr "({0}) vous a invité à consulter ce document"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/text-field.tsx:313
|
||||
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."
|
||||
|
||||
#: 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/data-table.tsx:54
|
||||
#: 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."
|
||||
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
|
||||
msgid "Here you can edit your personal details."
|
||||
msgstr "Ici, vous pouvez modifier vos coordonnées personnelles."
|
||||
@ -2204,6 +2217,10 @@ msgstr "Dernière mise à jour à"
|
||||
msgid "Last used"
|
||||
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)/tables/current-user-teams-data-table.tsx:117
|
||||
msgid "Leave"
|
||||
@ -2411,6 +2428,7 @@ msgid "My templates"
|
||||
msgstr "Mes modèles"
|
||||
|
||||
#: 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/data-table-users.tsx:66
|
||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144
|
||||
@ -2524,6 +2542,18 @@ msgstr "Rien à faire"
|
||||
msgid "Number"
|
||||
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
|
||||
msgid "On this page, you can create a new webhook."
|
||||
msgstr "Sur cette page, vous pouvez créer un nouveau webhook."
|
||||
@ -3105,6 +3135,7 @@ msgstr "Recherche"
|
||||
msgid "Search by document title"
|
||||
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
|
||||
msgid "Search by name or email"
|
||||
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..."
|
||||
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
|
||||
msgid "Since {0}"
|
||||
msgstr "Depuis {0}"
|
||||
@ -3377,7 +3413,7 @@ msgstr "Depuis {0}"
|
||||
msgid "Site Banner"
|
||||
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
|
||||
msgid "Site Settings"
|
||||
msgstr "Paramètres du site"
|
||||
|
||||
80
packages/lib/types/webhook-payload.ts
Normal file
80
packages/lib/types/webhook-payload.ts
Normal 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>;
|
||||
@ -0,0 +1,2 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "WebhookTriggerEvents" ADD VALUE 'DOCUMENT_REJECTED';
|
||||
@ -162,6 +162,7 @@ enum WebhookTriggerEvents {
|
||||
DOCUMENT_OPENED
|
||||
DOCUMENT_SIGNED
|
||||
DOCUMENT_COMPLETED
|
||||
DOCUMENT_REJECTED
|
||||
}
|
||||
|
||||
model Webhook {
|
||||
|
||||
@ -6,7 +6,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Caveat } from 'next/font/google';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Undo2 } from 'lucide-react';
|
||||
import { Undo2, Upload } from 'lucide-react';
|
||||
import type { StrokeOptions } from 'perfect-freehand';
|
||||
import { getStroke } from 'perfect-freehand';
|
||||
|
||||
@ -33,6 +33,64 @@ const fontCaveat = Caveat({
|
||||
|
||||
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'> & {
|
||||
onChange?: (_signatureDataUrl: string | null) => void;
|
||||
containerClassName?: string;
|
||||
@ -52,12 +110,15 @@ export const SignaturePad = ({
|
||||
}: SignaturePadProps) => {
|
||||
const $el = useRef<HTMLCanvasElement>(null);
|
||||
const $imageData = useRef<ImageData | null>(null);
|
||||
const $fileInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [isPressed, setIsPressed] = useState(false);
|
||||
const [lines, setLines] = useState<Point[][]>([]);
|
||||
const [currentLine, setCurrentLine] = useState<Point[]>([]);
|
||||
const [selectedColor, setSelectedColor] = useState('black');
|
||||
const [typedSignature, setTypedSignature] = useState(defaultValue ?? '');
|
||||
const [typedSignature, setTypedSignature] = useState(
|
||||
defaultValue && !isBase64Image(defaultValue) ? defaultValue : '',
|
||||
);
|
||||
|
||||
const perfectFreehandOptions = useMemo(() => {
|
||||
const size = $el.current ? Math.min($el.current.height, $el.current.width) * 0.03 : 10;
|
||||
@ -80,6 +141,14 @@ export const SignaturePad = ({
|
||||
|
||||
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);
|
||||
|
||||
setCurrentLine([point]);
|
||||
@ -193,6 +262,10 @@ export const SignaturePad = ({
|
||||
$imageData.current = null;
|
||||
}
|
||||
|
||||
if ($fileInput.current) {
|
||||
$fileInput.current.value = '';
|
||||
}
|
||||
|
||||
onChange?.(null);
|
||||
|
||||
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(() => {
|
||||
if (typedSignature.trim() !== '') {
|
||||
if (typedSignature.trim() !== '' && !isBase64Image(typedSignature)) {
|
||||
renderTypedSignature();
|
||||
onChange?.(typedSignature);
|
||||
} else {
|
||||
onClearClick();
|
||||
}
|
||||
}, [typedSignature, selectedColor]);
|
||||
|
||||
@ -370,6 +461,26 @@ export const SignaturePad = ({
|
||||
</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">
|
||||
<Select defaultValue={selectedColor} onValueChange={(value) => setSelectedColor(value)}>
|
||||
<SelectTrigger className="h-auto w-auto border-none p-0.5">
|
||||
|
||||
Reference in New Issue
Block a user