mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
Compare commits
13 Commits
v1.9.0-rc.
...
v1.9.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
| 058d9dd0ba | |||
| 74bb230247 | |||
| 7c1e0f34e8 | |||
| 7e31323faa | |||
| a28cdf437b | |||
| 80dfbeb16f | |||
| 9de3a32ceb | |||
| 0d3864548c | |||
| 9e03747e43 | |||
| 5750f2b477 | |||
| 901be70f97 | |||
| 7d0a9c6439 | |||
| 48b55758e3 |
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@ -10,7 +10,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
permissions:
|
permissions:
|
||||||
actions: read
|
actions: read
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
2
.github/workflows/e2e-tests.yml
vendored
2
.github/workflows/e2e-tests.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
|||||||
e2e_tests:
|
e2e_tests:
|
||||||
name: 'E2E Tests'
|
name: 'E2E Tests'
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
runs-on: ubuntu-22.04
|
runs-on: warp-ubuntu-2204-x64-16x
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,6 @@
|
|||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"typescript": "^5"
|
"typescript": "5.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5,5 +5,6 @@
|
|||||||
"svelte": "Svelte Integration",
|
"svelte": "Svelte Integration",
|
||||||
"solid": "Solid Integration",
|
"solid": "Solid Integration",
|
||||||
"preact": "Preact Integration",
|
"preact": "Preact Integration",
|
||||||
|
"angular": "Angular Integration",
|
||||||
"css-variables": "CSS Variables"
|
"css-variables": "CSS Variables"
|
||||||
}
|
}
|
||||||
|
|||||||
90
apps/documentation/pages/developers/embedding/angular.mdx
Normal file
90
apps/documentation/pages/developers/embedding/angular.mdx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
title: Angular Integration
|
||||||
|
description: Learn how to use our embedding SDK within your Angular application.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Angular Integration
|
||||||
|
|
||||||
|
Our Angular SDK provides a simple way to embed a signing experience within your Angular application. It supports both direct link templates and signing tokens.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install the SDK, run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @documenso/embed-angular
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To embed a signing experience, you'll need to provide the token for the document you want to embed. This can be done in a few different ways, depending on your use case.
|
||||||
|
|
||||||
|
### Direct Link Template
|
||||||
|
|
||||||
|
If you have a direct link template, you can simply provide the token for the template to the `EmbedDirectTemplate` component.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { EmbedDirectTemplate } from '@documenso/embed-angular';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-embedding',
|
||||||
|
template: `
|
||||||
|
<embed-direct-template [token]="token" />
|
||||||
|
`,
|
||||||
|
standalone: true,
|
||||||
|
imports: [EmbedDirectTemplate],
|
||||||
|
})
|
||||||
|
export class EmbeddingComponent {
|
||||||
|
token = 'YOUR_TOKEN_HERE'; // Replace with the actual token
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Props
|
||||||
|
|
||||||
|
| Prop | Type | Description |
|
||||||
|
| ------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
|
||||||
|
| token | string | The token for the document you want to embed |
|
||||||
|
| host | string (optional) | The host to be used for the signing experience, relevant for self-hosters |
|
||||||
|
| name | string (optional) | The name the signer that will be used by default for signing |
|
||||||
|
| lockName | boolean (optional) | Whether or not the name field should be locked disallowing modifications |
|
||||||
|
| email | string (optional) | The email the signer that will be used by default for signing |
|
||||||
|
| lockEmail | boolean (optional) | Whether or not the email field should be locked disallowing modifications |
|
||||||
|
| onDocumentReady | function (optional) | A callback function that will be called when the document is loaded and ready to be signed |
|
||||||
|
| onDocumentCompleted | function (optional) | A callback function that will be called when the document has been completed |
|
||||||
|
| onDocumentError | function (optional) | A callback function that will be called when an error occurs with the document |
|
||||||
|
| onFieldSigned | function (optional) | A callback function that will be called when a field is signed |
|
||||||
|
| onFieldUnsigned | function (optional) | A callback function that will be called when a field is unsigned |
|
||||||
|
|
||||||
|
### Signing Token
|
||||||
|
|
||||||
|
If you have a signing token, you can provide it to the `EmbedSignDocument` component.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { EmbedSignDocument } from '@documenso/embed-angular';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-embedding',
|
||||||
|
template: `
|
||||||
|
<embed-sign-document [token]="token" />
|
||||||
|
`,
|
||||||
|
standalone: true,
|
||||||
|
imports: [EmbedSignDocument],
|
||||||
|
})
|
||||||
|
export class EmbeddingComponent {
|
||||||
|
token = 'YOUR_TOKEN_HERE'; // Replace with the actual token
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Props
|
||||||
|
|
||||||
|
| Prop | Type | Description |
|
||||||
|
| ------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
|
||||||
|
| token | string | The token for the document you want to embed |
|
||||||
|
| host | string (optional) | The host to be used for the signing experience, relevant for self-hosters |
|
||||||
|
| name | string (optional) | The name the signer that will be used by default for signing |
|
||||||
|
| lockName | boolean (optional) | Whether or not the name field should be locked disallowing modifications |
|
||||||
|
| onDocumentReady | function (optional) | A callback function that will be called when the document is loaded and ready to be signed |
|
||||||
|
| onDocumentCompleted | function (optional) | A callback function that will be called when the document has been completed |
|
||||||
|
| onDocumentError | function (optional) | A callback function that will be called when an error occurs with the document |
|
||||||
@ -5,7 +5,7 @@ description: Learn how to use embedding to bring signing to your own website or
|
|||||||
|
|
||||||
# Embedding
|
# Embedding
|
||||||
|
|
||||||
Our embedding feature lets you integrate our document signing experience into your own application or website. Whether you're building with React, Preact, Vue, Svelte, Solid, or using generalized web components, this guide will help you get started with embedding Documenso.
|
Our embedding feature lets you integrate our document signing experience into your own application or website. Whether you're building with React, Preact, Vue, Svelte, Solid, Angular, or using generalized web components, this guide will help you get started with embedding Documenso.
|
||||||
|
|
||||||
## Availability
|
## Availability
|
||||||
|
|
||||||
@ -73,13 +73,14 @@ These customization options are available for both Direct Templates and Signing
|
|||||||
|
|
||||||
We support embedding across a range of popular JavaScript frameworks, including:
|
We support embedding across a range of popular JavaScript frameworks, including:
|
||||||
|
|
||||||
| Framework | Package |
|
| Framework | Package |
|
||||||
| --------- | -------------------------------------------------------------------------------- |
|
| --------- | ---------------------------------------------------------------------------------- |
|
||||||
| React | [@documenso/embed-react](https://www.npmjs.com/package/@documenso/embed-react) |
|
| React | [@documenso/embed-react](https://www.npmjs.com/package/@documenso/embed-react) |
|
||||||
| Preact | [@documenso/embed-preact](https://www.npmjs.com/package/@documenso/embed-preact) |
|
| Preact | [@documenso/embed-preact](https://www.npmjs.com/package/@documenso/embed-preact) |
|
||||||
| Vue | [@documenso/embed-vue](https://www.npmjs.com/package/@documenso/embed-vue) |
|
| Vue | [@documenso/embed-vue](https://www.npmjs.com/package/@documenso/embed-vue) |
|
||||||
| Svelte | [@documenso/embed-svelte](https://www.npmjs.com/package/@documenso/embed-svelte) |
|
| Svelte | [@documenso/embed-svelte](https://www.npmjs.com/package/@documenso/embed-svelte) |
|
||||||
| Solid | [@documenso/embed-solid](https://www.npmjs.com/package/@documenso/embed-solid) |
|
| Solid | [@documenso/embed-solid](https://www.npmjs.com/package/@documenso/embed-solid) |
|
||||||
|
| Angular | [@documenso/embed-angular](https://www.npmjs.com/package/@documenso/embed-angular) |
|
||||||
|
|
||||||
Additionally, we provide **web components** for more generalized use. However, please note that web components are still in their early stages and haven't been extensively tested.
|
Additionally, we provide **web components** for more generalized use. However, please note that web components are still in their early stages and haven't been extensively tested.
|
||||||
|
|
||||||
@ -127,7 +128,7 @@ This will show a dialog which will ask you to configure which recipient should b
|
|||||||
|
|
||||||
## Embedding with Signing Tokens
|
## Embedding with Signing Tokens
|
||||||
|
|
||||||
To embed the signing process for an ordinary document, you’ll need a **document signing token** for the recipient. This token provides the necessary access to load the document and facilitate the signing process securely.
|
To embed the signing process for an ordinary document, you'll need a **document signing token** for the recipient. This token provides the necessary access to load the document and facilitate the signing process securely.
|
||||||
|
|
||||||
#### Instructions
|
#### Instructions
|
||||||
|
|
||||||
@ -164,6 +165,7 @@ Once you've obtained the appropriate tokens, you can integrate the signing exper
|
|||||||
- [Vue](/developers/embedding/vue)
|
- [Vue](/developers/embedding/vue)
|
||||||
- [Svelte](/developers/embedding/svelte)
|
- [Svelte](/developers/embedding/svelte)
|
||||||
- [Solid](/developers/embedding/solid)
|
- [Solid](/developers/embedding/solid)
|
||||||
|
- [Angular](/developers/embedding/angular)
|
||||||
|
|
||||||
If you're using **web components**, the integration process is slightly different. Keep in mind that web components are currently less tested but can still provide flexibility for general use cases.
|
If you're using **web components**, the integration process is slightly different. Keep in mind that web components are currently less tested but can still provide flexibility for general use cases.
|
||||||
|
|
||||||
@ -174,4 +176,5 @@ If you're using **web components**, the integration process is slightly differen
|
|||||||
- [Svelte Integration](/developers/embedding/svelte)
|
- [Svelte Integration](/developers/embedding/svelte)
|
||||||
- [Solid Integration](/developers/embedding/solid)
|
- [Solid Integration](/developers/embedding/solid)
|
||||||
- [Preact Integration](/developers/embedding/preact)
|
- [Preact Integration](/developers/embedding/preact)
|
||||||
|
- [Angular Integration](/developers/embedding/angular)
|
||||||
- [CSS Variables](/developers/embedding/css-variables)
|
- [CSS Variables](/developers/embedding/css-variables)
|
||||||
|
|||||||
@ -16,8 +16,8 @@
|
|||||||
"next": "14.2.6"
|
"next": "14.2.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "20.16.5",
|
"@types/node": "^20",
|
||||||
"@types/react": "18.3.5",
|
"@types/react": "18.3.5",
|
||||||
"typescript": "5.5.4"
|
"typescript": "5.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/web",
|
"name": "@documenso/web",
|
||||||
"version": "1.9.0-rc.8",
|
"version": "1.9.0-rc.10",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -26,7 +26,6 @@
|
|||||||
"@lingui/react": "^4.11.3",
|
"@lingui/react": "^4.11.3",
|
||||||
"@simplewebauthn/browser": "^9.0.1",
|
"@simplewebauthn/browser": "^9.0.1",
|
||||||
"@simplewebauthn/server": "^9.0.3",
|
"@simplewebauthn/server": "^9.0.3",
|
||||||
"@tanstack/react-query": "^4.29.5",
|
|
||||||
"colord": "^2.9.3",
|
"colord": "^2.9.3",
|
||||||
"cookie-es": "^1.0.0",
|
"cookie-es": "^1.0.0",
|
||||||
"formidable": "^2.1.1",
|
"formidable": "^2.1.1",
|
||||||
@ -55,7 +54,7 @@
|
|||||||
"recharts": "^2.7.2",
|
"recharts": "^2.7.2",
|
||||||
"remeda": "^2.17.3",
|
"remeda": "^2.17.3",
|
||||||
"sharp": "0.32.6",
|
"sharp": "0.32.6",
|
||||||
"trpc-openapi": "^1.2.0",
|
"trpc-to-openapi": "2.0.4",
|
||||||
"ts-pattern": "^5.0.5",
|
"ts-pattern": "^5.0.5",
|
||||||
"ua-parser-js": "^1.0.37",
|
"ua-parser-js": "^1.0.37",
|
||||||
"uqr": "^0.1.2",
|
"uqr": "^0.1.2",
|
||||||
@ -68,11 +67,11 @@
|
|||||||
"@simplewebauthn/types": "^9.0.1",
|
"@simplewebauthn/types": "^9.0.1",
|
||||||
"@types/formidable": "^2.0.6",
|
"@types/formidable": "^2.0.6",
|
||||||
"@types/luxon": "^3.3.1",
|
"@types/luxon": "^3.3.1",
|
||||||
"@types/node": "20.1.0",
|
"@types/node": "^20",
|
||||||
"@types/papaparse": "^5.3.14",
|
"@types/papaparse": "^5.3.14",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"@types/ua-parser-js": "^0.7.39",
|
"@types/ua-parser-js": "^0.7.39",
|
||||||
"typescript": "5.2.2"
|
"typescript": "5.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ export const AdminActions = ({ className, document, recipients }: AdminActionsPr
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const { mutate: resealDocument, isLoading: isResealDocumentLoading } =
|
const { mutate: resealDocument, isPending: isResealDocumentLoading } =
|
||||||
trpc.admin.resealDocument.useMutation({
|
trpc.admin.resealDocument.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@ -59,7 +59,7 @@ export default async function AdminDocumentDetailsPage({ params }: AdminDocument
|
|||||||
<Trans>Admin Actions</Trans>
|
<Trans>Admin Actions</Trans>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<AdminActions className="mt-2" document={document} recipients={document.Recipient} />
|
<AdminActions className="mt-2" document={document} recipients={document.recipients} />
|
||||||
|
|
||||||
<hr className="my-4" />
|
<hr className="my-4" />
|
||||||
<h2 className="text-lg font-semibold">
|
<h2 className="text-lg font-semibold">
|
||||||
@ -68,7 +68,7 @@ export default async function AdminDocumentDetailsPage({ params }: AdminDocument
|
|||||||
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Accordion type="multiple" className="space-y-4">
|
<Accordion type="multiple" className="space-y-4">
|
||||||
{document.Recipient.map((recipient) => (
|
{document.recipients.map((recipient) => (
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
key={recipient.id}
|
key={recipient.id}
|
||||||
value={recipient.id.toString()}
|
value={recipient.id.toString()}
|
||||||
|
|||||||
@ -39,9 +39,9 @@ type TAdminUpdateRecipientFormSchema = z.infer<typeof ZAdminUpdateRecipientFormS
|
|||||||
|
|
||||||
export type RecipientItemProps = {
|
export type RecipientItemProps = {
|
||||||
recipient: Recipient & {
|
recipient: Recipient & {
|
||||||
Field: Array<
|
fields: Array<
|
||||||
Field & {
|
Field & {
|
||||||
Signature: Signature | null;
|
signature: Signature | null;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
};
|
};
|
||||||
@ -89,13 +89,13 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
|
|||||||
accessorKey: 'signature',
|
accessorKey: 'signature',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div>
|
<div>
|
||||||
{row.original.Signature?.typedSignature && (
|
{row.original.signature?.typedSignature && (
|
||||||
<span>{row.original.Signature.typedSignature}</span>
|
<span>{row.original.signature.typedSignature}</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{row.original.Signature?.signatureImageAsBase64 && (
|
{row.original.signature?.signatureImageAsBase64 && (
|
||||||
<img
|
<img
|
||||||
src={row.original.Signature.signatureImageAsBase64}
|
src={row.original.signature.signatureImageAsBase64}
|
||||||
alt="Signature"
|
alt="Signature"
|
||||||
className="h-12 w-full dark:invert"
|
className="h-12 w-full dark:invert"
|
||||||
/>
|
/>
|
||||||
@ -103,7 +103,7 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
] satisfies DataTableColumnDef<(typeof recipient)['Field'][number]>[];
|
] satisfies DataTableColumnDef<(typeof recipient)['fields'][number]>[];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onUpdateRecipientFormSubmit = async ({ name, email }: TAdminUpdateRecipientFormSchema) => {
|
const onUpdateRecipientFormSubmit = async ({ name, email }: TAdminUpdateRecipientFormSchema) => {
|
||||||
@ -190,7 +190,7 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
|
|||||||
<Trans>Fields</Trans>
|
<Trans>Fields</Trans>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<DataTable columns={columns} data={recipient.Field} />
|
<DataTable columns={columns} data={recipient.fields} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { Trans, msg } from '@lingui/macro';
|
|||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
|
|
||||||
import type { Document } from '@documenso/prisma/client';
|
import type { Document } from '@documenso/prisma/client';
|
||||||
import { TRPCClientError } from '@documenso/trpc/client';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
@ -36,7 +35,7 @@ export const SuperDeleteDocumentDialog = ({ document }: SuperDeleteDocumentDialo
|
|||||||
|
|
||||||
const [reason, setReason] = useState('');
|
const [reason, setReason] = useState('');
|
||||||
|
|
||||||
const { mutateAsync: deleteDocument, isLoading: isDeletingDocument } =
|
const { mutateAsync: deleteDocument, isPending: isDeletingDocument } =
|
||||||
trpc.admin.deleteDocument.useMutation();
|
trpc.admin.deleteDocument.useMutation();
|
||||||
|
|
||||||
const handleDeleteDocument = async () => {
|
const handleDeleteDocument = async () => {
|
||||||
@ -55,21 +54,12 @@ export const SuperDeleteDocumentDialog = ({ document }: SuperDeleteDocumentDialo
|
|||||||
|
|
||||||
router.push('/admin/documents');
|
router.push('/admin/documents');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') {
|
toast({
|
||||||
toast({
|
title: _(msg`An unknown error occurred`),
|
||||||
title: _(msg`An error occurred`),
|
variant: 'destructive',
|
||||||
description: err.message,
|
description:
|
||||||
variant: 'destructive',
|
'We encountered an unknown error while attempting to delete your document. Please try again later.',
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
title: _(msg`An unknown error occurred`),
|
|
||||||
variant: 'destructive',
|
|
||||||
description:
|
|
||||||
err.message ??
|
|
||||||
'We encountered an unknown error while attempting to delete your document. Please try again later.',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,7 +67,7 @@ export const SuperDeleteDocumentDialog = ({ document }: SuperDeleteDocumentDialo
|
|||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<Alert
|
<Alert
|
||||||
className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row "
|
className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row"
|
||||||
variant="neutral"
|
variant="neutral"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -37,7 +37,7 @@ export const AdminDocumentResults = () => {
|
|||||||
const page = searchParams?.get?.('page') ? Number(searchParams.get('page')) : undefined;
|
const page = searchParams?.get?.('page') ? Number(searchParams.get('page')) : undefined;
|
||||||
const perPage = searchParams?.get?.('perPage') ? Number(searchParams.get('perPage')) : undefined;
|
const perPage = searchParams?.get?.('perPage') ? Number(searchParams.get('perPage')) : undefined;
|
||||||
|
|
||||||
const { data: findDocumentsData, isLoading: isFindDocumentsLoading } =
|
const { data: findDocumentsData, isPending: isFindDocumentsLoading } =
|
||||||
trpc.admin.findDocuments.useQuery(
|
trpc.admin.findDocuments.useQuery(
|
||||||
{
|
{
|
||||||
query: debouncedTerm,
|
query: debouncedTerm,
|
||||||
@ -45,7 +45,7 @@ export const AdminDocumentResults = () => {
|
|||||||
perPage: perPage || 20,
|
perPage: perPage || 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
placeholderData: (previousData) => previousData,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -86,14 +86,14 @@ export const AdminDocumentResults = () => {
|
|||||||
header: _(msg`Owner`),
|
header: _(msg`Owner`),
|
||||||
accessorKey: 'owner',
|
accessorKey: 'owner',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const avatarFallbackText = row.original.User.name
|
const avatarFallbackText = row.original.user.name
|
||||||
? extractInitials(row.original.User.name)
|
? extractInitials(row.original.user.name)
|
||||||
: row.original.User.email.slice(0, 1).toUpperCase();
|
: row.original.user.email.slice(0, 1).toUpperCase();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip delayDuration={200}>
|
<Tooltip delayDuration={200}>
|
||||||
<TooltipTrigger>
|
<TooltipTrigger>
|
||||||
<Link href={`/admin/users/${row.original.User.id}`}>
|
<Link href={`/admin/users/${row.original.user.id}`}>
|
||||||
<Avatar className="dark:border-border h-12 w-12 border-2 border-solid border-white">
|
<Avatar className="dark:border-border h-12 w-12 border-2 border-solid border-white">
|
||||||
<AvatarFallback className="text-xs text-gray-400">
|
<AvatarFallback className="text-xs text-gray-400">
|
||||||
{avatarFallbackText}
|
{avatarFallbackText}
|
||||||
@ -110,8 +110,8 @@ export const AdminDocumentResults = () => {
|
|||||||
</Avatar>
|
</Avatar>
|
||||||
|
|
||||||
<div className="text-muted-foreground flex flex-col text-sm">
|
<div className="text-muted-foreground flex flex-col text-sm">
|
||||||
<span>{row.original.User.name}</span>
|
<span>{row.original.user.name}</span>
|
||||||
<span>{row.original.User.email}</span>
|
<span>{row.original.user.email}</span>
|
||||||
</div>
|
</div>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import {
|
|||||||
SITE_SETTINGS_BANNER_ID,
|
SITE_SETTINGS_BANNER_ID,
|
||||||
ZSiteSettingsBannerSchema,
|
ZSiteSettingsBannerSchema,
|
||||||
} from '@documenso/lib/server-only/site-settings/schemas/banner';
|
} from '@documenso/lib/server-only/site-settings/schemas/banner';
|
||||||
import { TRPCClientError } from '@documenso/trpc/client';
|
|
||||||
import { trpc as trpcReact } from '@documenso/trpc/react';
|
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
import { ColorPicker } from '@documenso/ui/primitives/color-picker';
|
import { ColorPicker } from '@documenso/ui/primitives/color-picker';
|
||||||
@ -59,7 +58,7 @@ export function BannerForm({ banner }: BannerFormProps) {
|
|||||||
|
|
||||||
const enabled = form.watch('enabled');
|
const enabled = form.watch('enabled');
|
||||||
|
|
||||||
const { mutateAsync: updateSiteSetting, isLoading: isUpdateSiteSettingLoading } =
|
const { mutateAsync: updateSiteSetting, isPending: isUpdateSiteSettingLoading } =
|
||||||
trpcReact.admin.updateSiteSetting.useMutation();
|
trpcReact.admin.updateSiteSetting.useMutation();
|
||||||
|
|
||||||
const onBannerUpdate = async ({ id, enabled, data }: TBannerFormSchema) => {
|
const onBannerUpdate = async ({ id, enabled, data }: TBannerFormSchema) => {
|
||||||
@ -78,21 +77,13 @@ export function BannerForm({ banner }: BannerFormProps) {
|
|||||||
|
|
||||||
router.refresh();
|
router.refresh();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') {
|
toast({
|
||||||
toast({
|
title: _(msg`An unknown error occurred`),
|
||||||
title: _(msg`An error occurred`),
|
variant: 'destructive',
|
||||||
description: err.message,
|
description: _(
|
||||||
variant: 'destructive',
|
msg`We encountered an unknown error while attempting to update the banner. Please try again later.`,
|
||||||
});
|
),
|
||||||
} else {
|
});
|
||||||
toast({
|
|
||||||
title: _(msg`An unknown error occurred`),
|
|
||||||
variant: 'destructive',
|
|
||||||
description: _(
|
|
||||||
msg`We encountered an unknown error while attempting to update the banner. Please try again later.`,
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,10 @@ import { useRouter } from 'next/navigation';
|
|||||||
|
|
||||||
import { Trans, msg } from '@lingui/macro';
|
import { Trans, msg } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||||
import type { User } from '@documenso/prisma/client';
|
import type { User } from '@documenso/prisma/client';
|
||||||
import { TRPCClientError } from '@documenso/trpc/client';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
@ -37,7 +38,7 @@ export const DeleteUserDialog = ({ className, user }: DeleteUserDialogProps) =>
|
|||||||
|
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
|
|
||||||
const { mutateAsync: deleteUser, isLoading: isDeletingUser } =
|
const { mutateAsync: deleteUser, isPending: isDeletingUser } =
|
||||||
trpc.admin.deleteUser.useMutation();
|
trpc.admin.deleteUser.useMutation();
|
||||||
|
|
||||||
const onDeleteAccount = async () => {
|
const onDeleteAccount = async () => {
|
||||||
@ -54,23 +55,19 @@ export const DeleteUserDialog = ({ className, user }: DeleteUserDialogProps) =>
|
|||||||
|
|
||||||
router.push('/admin/users');
|
router.push('/admin/users');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') {
|
const error = AppError.parseError(err);
|
||||||
toast({
|
|
||||||
title: _(msg`An error occurred`),
|
const errorMessage = match(error.code)
|
||||||
description: err.message,
|
.with(AppErrorCode.NOT_FOUND, () => msg`User not found.`)
|
||||||
variant: 'destructive',
|
.with(AppErrorCode.UNAUTHORIZED, () => msg`You are not authorized to delete this user.`)
|
||||||
});
|
.otherwise(() => msg`An error occurred while deleting the user.`);
|
||||||
} else {
|
|
||||||
toast({
|
toast({
|
||||||
title: _(msg`An unknown error occurred`),
|
title: _(msg`Error`),
|
||||||
variant: 'destructive',
|
description: _(errorMessage),
|
||||||
description:
|
variant: 'destructive',
|
||||||
err.message ??
|
duration: 7500,
|
||||||
_(
|
});
|
||||||
msg`We encountered an unknown error while attempting to delete your account. Please try again later.`,
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export const DisableUserDialog = ({ className, userToDisable }: DisableUserDialo
|
|||||||
|
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
|
|
||||||
const { mutateAsync: disableUser, isLoading: isDisablingUser } =
|
const { mutateAsync: disableUser, isPending: isDisablingUser } =
|
||||||
trpc.admin.disableUser.useMutation();
|
trpc.admin.disableUser.useMutation();
|
||||||
|
|
||||||
const onDisableAccount = async () => {
|
const onDisableAccount = async () => {
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export const EnableUserDialog = ({ className, userToEnable }: EnableUserDialogPr
|
|||||||
|
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
|
|
||||||
const { mutateAsync: enableUser, isLoading: isEnablingUser } =
|
const { mutateAsync: enableUser, isPending: isEnablingUser } =
|
||||||
trpc.admin.enableUser.useMutation();
|
trpc.admin.enableUser.useMutation();
|
||||||
|
|
||||||
const onEnableAccount = async () => {
|
const onEnableAccount = async () => {
|
||||||
|
|||||||
@ -22,8 +22,8 @@ type UserData = {
|
|||||||
name: string | null;
|
name: string | null;
|
||||||
email: string;
|
email: string;
|
||||||
roles: Role[];
|
roles: Role[];
|
||||||
Subscription?: SubscriptionLite[] | null;
|
subscriptions?: SubscriptionLite[] | null;
|
||||||
Document: DocumentLite[];
|
documents: DocumentLite[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type SubscriptionLite = Pick<
|
type SubscriptionLite = Pick<
|
||||||
@ -81,7 +81,7 @@ export const UsersDataTable = ({
|
|||||||
header: _(msg`Subscription`),
|
header: _(msg`Subscription`),
|
||||||
accessorKey: 'subscription',
|
accessorKey: 'subscription',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const foundIndividualSubscription = (row.original.Subscription ?? []).find((sub) =>
|
const foundIndividualSubscription = (row.original.subscriptions ?? []).find((sub) =>
|
||||||
individualPriceIds.includes(sub.priceId),
|
individualPriceIds.includes(sub.priceId),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ export const UsersDataTable = ({
|
|||||||
header: _(msg`Documents`),
|
header: _(msg`Documents`),
|
||||||
accessorKey: 'documents',
|
accessorKey: 'documents',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return <div>{row.original.Document.length}</div>;
|
return <div>{row.original.documents?.length}</div>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -18,8 +18,8 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
|
|||||||
|
|
||||||
export type DocumentPageViewButtonProps = {
|
export type DocumentPageViewButtonProps = {
|
||||||
document: Document & {
|
document: Document & {
|
||||||
User: Pick<User, 'id' | 'name' | 'email'>;
|
user: Pick<User, 'id' | 'name' | 'email'>;
|
||||||
Recipient: Recipient[];
|
recipients: Recipient[];
|
||||||
team: Pick<Team, 'id' | 'url'> | null;
|
team: Pick<Team, 'id' | 'url'> | null;
|
||||||
};
|
};
|
||||||
team?: Pick<Team, 'id' | 'url'>;
|
team?: Pick<Team, 'id' | 'url'>;
|
||||||
@ -34,7 +34,7 @@ export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const recipient = document.Recipient.find((recipient) => recipient.email === session.user.email);
|
const recipient = document.recipients.find((recipient) => recipient.email === session.user.email);
|
||||||
|
|
||||||
const isRecipient = !!recipient;
|
const isRecipient = !!recipient;
|
||||||
const isPending = document.status === DocumentStatus.PENDING;
|
const isPending = document.status === DocumentStatus.PENDING;
|
||||||
@ -46,9 +46,16 @@ export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps
|
|||||||
|
|
||||||
const onDownloadClick = async () => {
|
const onDownloadClick = async () => {
|
||||||
try {
|
try {
|
||||||
const documentWithData = await trpcClient.document.getDocumentById.query({
|
const documentWithData = await trpcClient.document.getDocumentById.query(
|
||||||
documentId: document.id,
|
{
|
||||||
});
|
documentId: document.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
teamId: document.team?.id?.toString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const documentData = documentWithData?.documentData;
|
const documentData = documentWithData?.documentData;
|
||||||
|
|
||||||
|
|||||||
@ -41,8 +41,8 @@ import { DuplicateDocumentDialog } from '../duplicate-document-dialog';
|
|||||||
|
|
||||||
export type DocumentPageViewDropdownProps = {
|
export type DocumentPageViewDropdownProps = {
|
||||||
document: Document & {
|
document: Document & {
|
||||||
User: Pick<User, 'id' | 'name' | 'email'>;
|
user: Pick<User, 'id' | 'name' | 'email'>;
|
||||||
Recipient: Recipient[];
|
recipients: Recipient[];
|
||||||
team: Pick<Team, 'id' | 'url'> | null;
|
team: Pick<Team, 'id' | 'url'> | null;
|
||||||
};
|
};
|
||||||
team?: Pick<Team, 'id' | 'url'> & { teamEmail: TeamEmail | null };
|
team?: Pick<Team, 'id' | 'url'> & { teamEmail: TeamEmail | null };
|
||||||
@ -60,9 +60,9 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const recipient = document.Recipient.find((recipient) => recipient.email === session.user.email);
|
const recipient = document.recipients.find((recipient) => recipient.email === session.user.email);
|
||||||
|
|
||||||
const isOwner = document.User.id === session.user.id;
|
const isOwner = document.user.id === session.user.id;
|
||||||
const isDraft = document.status === DocumentStatus.DRAFT;
|
const isDraft = document.status === DocumentStatus.DRAFT;
|
||||||
const isPending = document.status === DocumentStatus.PENDING;
|
const isPending = document.status === DocumentStatus.PENDING;
|
||||||
const isDeleted = document.deletedAt !== null;
|
const isDeleted = document.deletedAt !== null;
|
||||||
@ -74,9 +74,16 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro
|
|||||||
|
|
||||||
const onDownloadClick = async () => {
|
const onDownloadClick = async () => {
|
||||||
try {
|
try {
|
||||||
const documentWithData = await trpcClient.document.getDocumentById.query({
|
const documentWithData = await trpcClient.document.getDocumentById.query(
|
||||||
documentId: document.id,
|
{
|
||||||
});
|
documentId: document.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
context: {
|
||||||
|
teamId: team?.id?.toString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const documentData = documentWithData?.documentData;
|
const documentData = documentWithData?.documentData;
|
||||||
|
|
||||||
@ -94,7 +101,7 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const nonSignedRecipients = document.Recipient.filter((item) => item.signingStatus !== 'SIGNED');
|
const nonSignedRecipients = document.recipients.filter((item) => item.signingStatus !== 'SIGNED');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@ -149,7 +156,7 @@ export const DocumentPageViewDropdown = ({ document, team }: DocumentPageViewDro
|
|||||||
|
|
||||||
{canManageDocument && (
|
{canManageDocument && (
|
||||||
<DocumentRecipientLinkCopyDialog
|
<DocumentRecipientLinkCopyDialog
|
||||||
recipients={document.Recipient}
|
recipients={document.recipients}
|
||||||
trigger={
|
trigger={
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
disabled={!isPending || isDeleted}
|
disabled={!isPending || isDeleted}
|
||||||
|
|||||||
@ -12,8 +12,8 @@ import type { Document, Recipient, User } from '@documenso/prisma/client';
|
|||||||
export type DocumentPageViewInformationProps = {
|
export type DocumentPageViewInformationProps = {
|
||||||
userId: number;
|
userId: number;
|
||||||
document: Document & {
|
document: Document & {
|
||||||
User: Pick<User, 'id' | 'name' | 'email'>;
|
user: Pick<User, 'id' | 'name' | 'email'>;
|
||||||
Recipient: Recipient[];
|
recipients: Recipient[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,7 +29,8 @@ export const DocumentPageViewInformation = ({
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
description: msg`Uploaded by`,
|
description: msg`Uploaded by`,
|
||||||
value: userId === document.userId ? _(msg`You`) : document.User.name ?? document.User.email,
|
value:
|
||||||
|
userId === document.userId ? _(msg`You`) : (document.user.name ?? document.user.email),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: msg`Created`,
|
description: msg`Created`,
|
||||||
|
|||||||
@ -28,7 +28,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
|
|||||||
|
|
||||||
export type DocumentPageViewRecipientsProps = {
|
export type DocumentPageViewRecipientsProps = {
|
||||||
document: Document & {
|
document: Document & {
|
||||||
Recipient: Recipient[];
|
recipients: Recipient[];
|
||||||
};
|
};
|
||||||
documentRootPath: string;
|
documentRootPath: string;
|
||||||
};
|
};
|
||||||
@ -40,7 +40,7 @@ export const DocumentPageViewRecipients = ({
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const recipients = document.Recipient;
|
const recipients = document.recipients;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="dark:bg-background border-border bg-widget flex flex-col rounded-xl border">
|
<section className="dark:bg-background border-border bg-widget flex flex-col rounded-xl border">
|
||||||
|
|||||||
@ -71,7 +71,7 @@ export const DocumentPageView = async ({ params, team }: DocumentPageViewProps)
|
|||||||
|
|
||||||
const documentVisibility = document?.visibility;
|
const documentVisibility = document?.visibility;
|
||||||
const currentTeamMemberRole = team?.currentTeamMember?.role;
|
const currentTeamMemberRole = team?.currentTeamMember?.role;
|
||||||
const isRecipient = document?.Recipient.find((recipient) => recipient.email === user.email);
|
const isRecipient = document?.recipients.find((recipient) => recipient.email === user.email);
|
||||||
let canAccessDocument = true;
|
let canAccessDocument = true;
|
||||||
|
|
||||||
if (team && !isRecipient && document?.userId !== user.id) {
|
if (team && !isRecipient && document?.userId !== user.id) {
|
||||||
@ -131,7 +131,7 @@ export const DocumentPageView = async ({ params, team }: DocumentPageViewProps)
|
|||||||
|
|
||||||
const documentWithRecipients = {
|
const documentWithRecipients = {
|
||||||
...document,
|
...document,
|
||||||
Recipient: recipients,
|
recipients,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import {
|
|||||||
DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||||
SKIP_QUERY_BATCH_META,
|
SKIP_QUERY_BATCH_META,
|
||||||
} from '@documenso/lib/constants/trpc';
|
} from '@documenso/lib/constants/trpc';
|
||||||
import type { TGetDocumentWithDetailsByIdResponse } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
|
import type { TDocument } from '@documenso/lib/types/document';
|
||||||
import { DocumentDistributionMethod, DocumentStatus } from '@documenso/prisma/client';
|
import { DocumentDistributionMethod, DocumentStatus } from '@documenso/prisma/client';
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
@ -35,7 +35,7 @@ import { useOptionalCurrentTeam } from '~/providers/team';
|
|||||||
|
|
||||||
export type EditDocumentFormProps = {
|
export type EditDocumentFormProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
initialDocument: TGetDocumentWithDetailsByIdResponse;
|
initialDocument: TDocument;
|
||||||
documentRootPath: string;
|
documentRootPath: string;
|
||||||
isDocumentEnterprise: boolean;
|
isDocumentEnterprise: boolean;
|
||||||
};
|
};
|
||||||
@ -71,7 +71,7 @@ export const EditDocumentForm = ({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { Recipient: recipients, Field: fields } = document;
|
const { recipients, fields } = document;
|
||||||
|
|
||||||
const { mutateAsync: updateDocument } = trpc.document.setSettingsForDocument.useMutation({
|
const { mutateAsync: updateDocument } = trpc.document.setSettingsForDocument.useMutation({
|
||||||
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||||
@ -105,7 +105,7 @@ export const EditDocumentForm = ({
|
|||||||
{
|
{
|
||||||
documentId: initialDocument.id,
|
documentId: initialDocument.id,
|
||||||
},
|
},
|
||||||
(oldData) => ({ ...(oldData || initialDocument), Field: newFields }),
|
(oldData) => ({ ...(oldData || initialDocument), fields: newFields }),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -117,7 +117,7 @@ export const EditDocumentForm = ({
|
|||||||
{
|
{
|
||||||
documentId: initialDocument.id,
|
documentId: initialDocument.id,
|
||||||
},
|
},
|
||||||
(oldData) => ({ ...(oldData || initialDocument), Recipient: newRecipients }),
|
(oldData) => ({ ...(oldData || initialDocument), recipients: newRecipients }),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -52,7 +52,7 @@ export const DocumentEditPageView = async ({ params, team }: DocumentEditPageVie
|
|||||||
|
|
||||||
const documentVisibility = document?.visibility;
|
const documentVisibility = document?.visibility;
|
||||||
const currentTeamMemberRole = team?.currentTeamMember?.role;
|
const currentTeamMemberRole = team?.currentTeamMember?.role;
|
||||||
const isRecipient = document?.Recipient.find((recipient) => recipient.email === user.email);
|
const isRecipient = document?.recipients.find((recipient) => recipient.email === user.email);
|
||||||
let canAccessDocument = true;
|
let canAccessDocument = true;
|
||||||
|
|
||||||
if (!isRecipient && document?.userId !== user.id) {
|
if (!isRecipient && document?.userId !== user.id) {
|
||||||
@ -78,7 +78,7 @@ export const DocumentEditPageView = async ({ params, team }: DocumentEditPageVie
|
|||||||
redirect(`${documentRootPath}/${documentId}`);
|
redirect(`${documentRootPath}/${documentId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { documentMeta, Recipient: recipients } = document;
|
const { documentMeta, recipients } = document;
|
||||||
|
|
||||||
if (documentMeta?.password) {
|
if (documentMeta?.password) {
|
||||||
const key = DOCUMENSO_ENCRYPTION_KEY;
|
const key = DOCUMENSO_ENCRYPTION_KEY;
|
||||||
|
|||||||
@ -37,17 +37,16 @@ export const DocumentLogsDataTable = ({ documentId }: DocumentLogsDataTableProps
|
|||||||
|
|
||||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||||
|
|
||||||
const { data, isLoading, isInitialLoading, isLoadingError } =
|
const { data, isLoading, isLoadingError } = trpc.document.findDocumentAuditLogs.useQuery(
|
||||||
trpc.document.findDocumentAuditLogs.useQuery(
|
{
|
||||||
{
|
documentId,
|
||||||
documentId,
|
page: parsedSearchParams.page,
|
||||||
page: parsedSearchParams.page,
|
perPage: parsedSearchParams.perPage,
|
||||||
perPage: parsedSearchParams.perPage,
|
},
|
||||||
},
|
{
|
||||||
{
|
placeholderData: (previousData) => previousData,
|
||||||
keepPreviousData: true,
|
},
|
||||||
},
|
);
|
||||||
);
|
|
||||||
|
|
||||||
const onPaginationChange = (page: number, perPage: number) => {
|
const onPaginationChange = (page: number, perPage: number) => {
|
||||||
updateSearchParams({
|
updateSearchParams({
|
||||||
@ -132,7 +131,7 @@ export const DocumentLogsDataTable = ({ documentId }: DocumentLogsDataTableProps
|
|||||||
enable: isLoadingError,
|
enable: isLoadingError,
|
||||||
}}
|
}}
|
||||||
skeleton={{
|
skeleton={{
|
||||||
enable: isLoading && isInitialLoading,
|
enable: isLoading,
|
||||||
rows: 3,
|
rows: 3,
|
||||||
component: (
|
component: (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -77,9 +77,9 @@ export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageVie
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: msg`Created by`,
|
description: msg`Created by`,
|
||||||
value: document.User.name
|
value: document.user.name
|
||||||
? `${document.User.name} (${document.User.email})`
|
? `${document.user.name} (${document.user.email})`
|
||||||
: document.User.email,
|
: document.user.email,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: msg`Date created`,
|
description: msg`Date created`,
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export const DownloadAuditLogButton = ({ className, documentId }: DownloadAuditL
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const { mutateAsync: downloadAuditLogs, isLoading } =
|
const { mutateAsync: downloadAuditLogs, isPending } =
|
||||||
trpc.document.downloadAuditLogs.useMutation();
|
trpc.document.downloadAuditLogs.useMutation();
|
||||||
|
|
||||||
const onDownloadAuditLogsClick = async () => {
|
const onDownloadAuditLogsClick = async () => {
|
||||||
@ -70,10 +70,10 @@ export const DownloadAuditLogButton = ({ className, documentId }: DownloadAuditL
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className={cn('w-full sm:w-auto', className)}
|
className={cn('w-full sm:w-auto', className)}
|
||||||
loading={isLoading}
|
loading={isPending}
|
||||||
onClick={() => void onDownloadAuditLogsClick()}
|
onClick={() => void onDownloadAuditLogsClick()}
|
||||||
>
|
>
|
||||||
{!isLoading && <DownloadIcon className="mr-1.5 h-4 w-4" />}
|
{!isPending && <DownloadIcon className="mr-1.5 h-4 w-4" />}
|
||||||
<Trans>Download Audit Logs</Trans>
|
<Trans>Download Audit Logs</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export const DownloadCertificateButton = ({
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const { mutateAsync: downloadCertificate, isLoading } =
|
const { mutateAsync: downloadCertificate, isPending } =
|
||||||
trpc.document.downloadCertificate.useMutation();
|
trpc.document.downloadCertificate.useMutation();
|
||||||
|
|
||||||
const onDownloadCertificatesClick = async () => {
|
const onDownloadCertificatesClick = async () => {
|
||||||
@ -77,12 +77,12 @@ export const DownloadCertificateButton = ({
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className={cn('w-full sm:w-auto', className)}
|
className={cn('w-full sm:w-auto', className)}
|
||||||
loading={isLoading}
|
loading={isPending}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
disabled={documentStatus !== DocumentStatus.COMPLETED}
|
disabled={documentStatus !== DocumentStatus.COMPLETED}
|
||||||
onClick={() => void onDownloadCertificatesClick()}
|
onClick={() => void onDownloadCertificatesClick()}
|
||||||
>
|
>
|
||||||
{!isLoading && <DownloadIcon className="mr-1.5 h-4 w-4" />}
|
{!isPending && <DownloadIcon className="mr-1.5 h-4 w-4" />}
|
||||||
<Trans>Download Certificate</Trans>
|
<Trans>Download Certificate</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -12,15 +12,14 @@ import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
|
|||||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||||
import type { Document, Recipient, Team, User } from '@documenso/prisma/client';
|
import type { Document, Recipient, Team, User } from '@documenso/prisma/client';
|
||||||
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
||||||
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
|
||||||
import { trpc as trpcClient } from '@documenso/trpc/client';
|
import { trpc as trpcClient } from '@documenso/trpc/client';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
export type DataTableActionButtonProps = {
|
export type DataTableActionButtonProps = {
|
||||||
row: Document & {
|
row: Document & {
|
||||||
User: Pick<User, 'id' | 'name' | 'email'>;
|
user: Pick<User, 'id' | 'name' | 'email'>;
|
||||||
Recipient: Recipient[];
|
recipients: Recipient[];
|
||||||
team: Pick<Team, 'id' | 'url'> | null;
|
team: Pick<Team, 'id' | 'url'> | null;
|
||||||
};
|
};
|
||||||
team?: Pick<Team, 'id' | 'url'>;
|
team?: Pick<Team, 'id' | 'url'>;
|
||||||
@ -35,9 +34,9 @@ export const DataTableActionButton = ({ row, team }: DataTableActionButtonProps)
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const recipient = row.Recipient.find((recipient) => recipient.email === session.user.email);
|
const recipient = row.recipients.find((recipient) => recipient.email === session.user.email);
|
||||||
|
|
||||||
const isOwner = row.User.id === session.user.id;
|
const isOwner = row.user.id === session.user.id;
|
||||||
const isRecipient = !!recipient;
|
const isRecipient = !!recipient;
|
||||||
const isDraft = row.status === DocumentStatus.DRAFT;
|
const isDraft = row.status === DocumentStatus.DRAFT;
|
||||||
const isPending = row.status === DocumentStatus.PENDING;
|
const isPending = row.status === DocumentStatus.PENDING;
|
||||||
@ -50,17 +49,20 @@ export const DataTableActionButton = ({ row, team }: DataTableActionButtonProps)
|
|||||||
|
|
||||||
const onDownloadClick = async () => {
|
const onDownloadClick = async () => {
|
||||||
try {
|
try {
|
||||||
let document: DocumentWithData | null = null;
|
const document = !recipient
|
||||||
|
? await trpcClient.document.getDocumentById.query(
|
||||||
if (!recipient) {
|
{
|
||||||
document = await trpcClient.document.getDocumentById.query({
|
documentId: row.id,
|
||||||
documentId: row.id,
|
},
|
||||||
});
|
{
|
||||||
} else {
|
context: {
|
||||||
document = await trpcClient.document.getDocumentByToken.query({
|
teamId: team?.id?.toString(),
|
||||||
token: recipient.token,
|
},
|
||||||
});
|
},
|
||||||
}
|
)
|
||||||
|
: await trpcClient.document.getDocumentByToken.query({
|
||||||
|
token: recipient.token,
|
||||||
|
});
|
||||||
|
|
||||||
const documentData = document?.documentData;
|
const documentData = document?.documentData;
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
|
|||||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||||
import { DocumentStatus, RecipientRole } from '@documenso/prisma/client';
|
import { DocumentStatus, RecipientRole } from '@documenso/prisma/client';
|
||||||
import type { Document, Recipient, Team, User } from '@documenso/prisma/client';
|
import type { Document, Recipient, Team, User } from '@documenso/prisma/client';
|
||||||
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
|
||||||
import { trpc as trpcClient } from '@documenso/trpc/client';
|
import { trpc as trpcClient } from '@documenso/trpc/client';
|
||||||
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
|
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
|
||||||
import {
|
import {
|
||||||
@ -46,8 +45,8 @@ import { MoveDocumentDialog } from './move-document-dialog';
|
|||||||
|
|
||||||
export type DataTableActionDropdownProps = {
|
export type DataTableActionDropdownProps = {
|
||||||
row: Document & {
|
row: Document & {
|
||||||
User: Pick<User, 'id' | 'name' | 'email'>;
|
user: Pick<User, 'id' | 'name' | 'email'>;
|
||||||
Recipient: Recipient[];
|
recipients: Recipient[];
|
||||||
team: Pick<Team, 'id' | 'url'> | null;
|
team: Pick<Team, 'id' | 'url'> | null;
|
||||||
};
|
};
|
||||||
team?: Pick<Team, 'id' | 'url'> & { teamEmail?: string };
|
team?: Pick<Team, 'id' | 'url'> & { teamEmail?: string };
|
||||||
@ -66,9 +65,9 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const recipient = row.Recipient.find((recipient) => recipient.email === session.user.email);
|
const recipient = row.recipients.find((recipient) => recipient.email === session.user.email);
|
||||||
|
|
||||||
const isOwner = row.User.id === session.user.id;
|
const isOwner = row.user.id === session.user.id;
|
||||||
// const isRecipient = !!recipient;
|
// const isRecipient = !!recipient;
|
||||||
const isDraft = row.status === DocumentStatus.DRAFT;
|
const isDraft = row.status === DocumentStatus.DRAFT;
|
||||||
const isPending = row.status === DocumentStatus.PENDING;
|
const isPending = row.status === DocumentStatus.PENDING;
|
||||||
@ -81,17 +80,13 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
|
|
||||||
const onDownloadClick = async () => {
|
const onDownloadClick = async () => {
|
||||||
try {
|
try {
|
||||||
let document: DocumentWithData | null = null;
|
const document = !recipient
|
||||||
|
? await trpcClient.document.getDocumentById.query({
|
||||||
if (!recipient) {
|
documentId: row.id,
|
||||||
document = await trpcClient.document.getDocumentById.query({
|
})
|
||||||
documentId: row.id,
|
: await trpcClient.document.getDocumentByToken.query({
|
||||||
});
|
token: recipient.token,
|
||||||
} else {
|
});
|
||||||
document = await trpcClient.document.getDocumentByToken.query({
|
|
||||||
token: recipient.token,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const documentData = document?.documentData;
|
const documentData = document?.documentData;
|
||||||
|
|
||||||
@ -109,7 +104,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const nonSignedRecipients = row.Recipient.filter((item) => item.signingStatus !== 'SIGNED');
|
const nonSignedRecipients = row.recipients.filter((item) => item.signingStatus !== 'SIGNED');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@ -194,7 +189,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
|
|
||||||
{canManageDocument && (
|
{canManageDocument && (
|
||||||
<DocumentRecipientLinkCopyDialog
|
<DocumentRecipientLinkCopyDialog
|
||||||
recipients={row.Recipient}
|
recipients={row.recipients}
|
||||||
trigger={
|
trigger={
|
||||||
<DropdownMenuItem disabled={!isPending} asChild onSelect={(e) => e.preventDefault()}>
|
<DropdownMenuItem disabled={!isPending} asChild onSelect={(e) => e.preventDefault()}>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export const DataTableSenderFilter = ({ teamId }: DataTableSenderFilterProps) =>
|
|||||||
|
|
||||||
const senderIds = parseToIntegerArray(searchParams?.get('senderIds') ?? '');
|
const senderIds = parseToIntegerArray(searchParams?.get('senderIds') ?? '');
|
||||||
|
|
||||||
const { data, isInitialLoading } = trpc.team.getTeamMembers.useQuery({
|
const { data, isLoading } = trpc.team.getTeamMembers.useQuery({
|
||||||
teamId,
|
teamId,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ export const DataTableSenderFilter = ({ teamId }: DataTableSenderFilterProps) =>
|
|||||||
}
|
}
|
||||||
enableClearAllButton={true}
|
enableClearAllButton={true}
|
||||||
inputPlaceholder={msg`Search`}
|
inputPlaceholder={msg`Search`}
|
||||||
loading={!isMounted || isInitialLoading}
|
loading={!isMounted || isLoading}
|
||||||
options={comboBoxOptions}
|
options={comboBoxOptions}
|
||||||
selectedValues={senderIds}
|
selectedValues={senderIds}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
|||||||
@ -10,9 +10,9 @@ import type { Document, Recipient, Team, User } from '@documenso/prisma/client';
|
|||||||
|
|
||||||
export type DataTableTitleProps = {
|
export type DataTableTitleProps = {
|
||||||
row: Document & {
|
row: Document & {
|
||||||
User: Pick<User, 'id' | 'name' | 'email'>;
|
user: Pick<User, 'id' | 'name' | 'email'>;
|
||||||
team: Pick<Team, 'url'> | null;
|
team: Pick<Team, 'url'> | null;
|
||||||
Recipient: Recipient[];
|
recipients: Recipient[];
|
||||||
};
|
};
|
||||||
teamUrl?: string;
|
teamUrl?: string;
|
||||||
};
|
};
|
||||||
@ -24,9 +24,9 @@ export const DataTableTitle = ({ row, teamUrl }: DataTableTitleProps) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const recipient = row.Recipient.find((recipient) => recipient.email === session.user.email);
|
const recipient = row.recipients.find((recipient) => recipient.email === session.user.email);
|
||||||
|
|
||||||
const isOwner = row.User.id === session.user.id;
|
const isOwner = row.user.id === session.user.id;
|
||||||
const isRecipient = !!recipient;
|
const isRecipient = !!recipient;
|
||||||
const isCurrentTeamDocument = teamUrl && row.team?.url === teamUrl;
|
const isCurrentTeamDocument = teamUrl && row.team?.url === teamUrl;
|
||||||
|
|
||||||
|
|||||||
@ -9,9 +9,9 @@ import { DateTime } from 'luxon';
|
|||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
|
|
||||||
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
|
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
|
||||||
import type { TFindDocumentsResponse } from '@documenso/lib/server-only/document/find-documents';
|
|
||||||
import type { Team } from '@documenso/prisma/client';
|
import type { Team } from '@documenso/prisma/client';
|
||||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||||
|
import type { TFindDocumentsResponse } from '@documenso/trpc/server/document-router/schema';
|
||||||
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
|
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
|
||||||
import { DataTable } from '@documenso/ui/primitives/data-table';
|
import { DataTable } from '@documenso/ui/primitives/data-table';
|
||||||
import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination';
|
import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination';
|
||||||
@ -57,14 +57,14 @@ export const DocumentsDataTable = ({
|
|||||||
{
|
{
|
||||||
id: 'sender',
|
id: 'sender',
|
||||||
header: _(msg`Sender`),
|
header: _(msg`Sender`),
|
||||||
cell: ({ row }) => row.original.User.name ?? row.original.User.email,
|
cell: ({ row }) => row.original.user.name ?? row.original.user.email,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: _(msg`Recipient`),
|
header: _(msg`Recipient`),
|
||||||
accessorKey: 'recipient',
|
accessorKey: 'recipient',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<StackAvatarsWithTooltip
|
<StackAvatarsWithTooltip
|
||||||
recipients={row.original.Recipient}
|
recipients={row.original.recipients}
|
||||||
documentStatus={row.original.status}
|
documentStatus={row.original.status}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export const DeleteDocumentDialog = ({
|
|||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [isDeleteEnabled, setIsDeleteEnabled] = useState(status === DocumentStatus.DRAFT);
|
const [isDeleteEnabled, setIsDeleteEnabled] = useState(status === DocumentStatus.DRAFT);
|
||||||
|
|
||||||
const { mutateAsync: deleteDocument, isLoading } = trpcReact.document.deleteDocument.useMutation({
|
const { mutateAsync: deleteDocument, isPending } = trpcReact.document.deleteDocument.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
router.refresh();
|
router.refresh();
|
||||||
void refreshLimits();
|
void refreshLimits();
|
||||||
@ -92,7 +92,7 @@ export const DeleteDocumentDialog = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}>
|
<Dialog open={open} onOpenChange={(value) => !isPending && onOpenChange(value)}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
@ -193,7 +193,7 @@ export const DeleteDocumentDialog = ({
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
loading={isLoading}
|
loading={isPending}
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
disabled={!isDeleteEnabled && canManageDocument}
|
disabled={!isDeleteEnabled && canManageDocument}
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export const DuplicateDocumentDialog = ({
|
|||||||
|
|
||||||
const documentsPath = formatDocumentsPath(team?.url);
|
const documentsPath = formatDocumentsPath(team?.url);
|
||||||
|
|
||||||
const { mutateAsync: duplicateDocument, isLoading: isDuplicateLoading } =
|
const { mutateAsync: duplicateDocument, isPending: isDuplicateLoading } =
|
||||||
trpcReact.document.duplicateDocument.useMutation({
|
trpcReact.document.duplicateDocument.useMutation({
|
||||||
onSuccess: (newId) => {
|
onSuccess: (newId) => {
|
||||||
router.push(`${documentsPath}/${newId}/edit`);
|
router.push(`${documentsPath}/${newId}/edit`);
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export const MoveDocumentDialog = ({ documentId, open, onOpenChange }: MoveDocum
|
|||||||
|
|
||||||
const { data: teams, isLoading: isLoadingTeams } = trpc.team.getTeams.useQuery();
|
const { data: teams, isLoading: isLoadingTeams } = trpc.team.getTeams.useQuery();
|
||||||
|
|
||||||
const { mutateAsync: moveDocument, isLoading } = trpc.document.moveDocumentToTeam.useMutation({
|
const { mutateAsync: moveDocument, isPending } = trpc.document.moveDocumentToTeam.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
router.refresh();
|
router.refresh();
|
||||||
toast({
|
toast({
|
||||||
@ -119,8 +119,8 @@ export const MoveDocumentDialog = ({ documentId, open, onOpenChange }: MoveDocum
|
|||||||
<Button variant="secondary" onClick={() => onOpenChange(false)}>
|
<Button variant="secondary" onClick={() => onOpenChange(false)}>
|
||||||
<Trans>Cancel</Trans>
|
<Trans>Cancel</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={onMove} loading={isLoading} disabled={!selectedTeamId || isLoading}>
|
<Button onClick={onMove} loading={isPending} disabled={!selectedTeamId || isPending}>
|
||||||
{isLoading ? <Trans>Moving...</Trans> : <Trans>Move</Trans>}
|
{isPending ? <Trans>Moving...</Trans> : <Trans>Move</Trans>}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@ -8,16 +8,16 @@ import { Trans, msg } from '@lingui/macro';
|
|||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { Loader } from 'lucide-react';
|
import { Loader } from 'lucide-react';
|
||||||
import { useSession } from 'next-auth/react';
|
import { useSession } from 'next-auth/react';
|
||||||
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
|
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
|
||||||
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
||||||
import { APP_DOCUMENT_UPLOAD_SIZE_LIMIT } from '@documenso/lib/constants/app';
|
import { APP_DOCUMENT_UPLOAD_SIZE_LIMIT } from '@documenso/lib/constants/app';
|
||||||
import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones';
|
import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones';
|
||||||
import { AppError } from '@documenso/lib/errors/app-error';
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||||
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
|
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
|
||||||
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
|
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
|
||||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||||
import { TRPCClientError } from '@documenso/trpc/client';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
import { DocumentDropzone } from '@documenso/ui/primitives/document-dropzone';
|
import { DocumentDropzone } from '@documenso/ui/primitives/document-dropzone';
|
||||||
@ -99,25 +99,20 @@ export const UploadDocument = ({ className, team }: UploadDocumentProps) => {
|
|||||||
|
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
||||||
if (error.code === 'INVALID_DOCUMENT_FILE') {
|
const errorMessage = match(error.code)
|
||||||
toast({
|
.with('INVALID_DOCUMENT_FILE', () => msg`You cannot upload encrypted PDFs`)
|
||||||
title: _(msg`Invalid file`),
|
.with(
|
||||||
description: _(msg`You cannot upload encrypted PDFs`),
|
AppErrorCode.LIMIT_EXCEEDED,
|
||||||
variant: 'destructive',
|
() => msg`You have reached your document limit for this month. Please upgrade your plan.`,
|
||||||
});
|
)
|
||||||
} else if (err instanceof TRPCClientError) {
|
.otherwise(() => msg`An error occurred while uploading your document.`);
|
||||||
toast({
|
|
||||||
title: _(msg`Error`),
|
toast({
|
||||||
description: err.message,
|
title: _(msg`Error`),
|
||||||
variant: 'destructive',
|
description: _(errorMessage),
|
||||||
});
|
variant: 'destructive',
|
||||||
} else {
|
duration: 7500,
|
||||||
toast({
|
});
|
||||||
title: _(msg`Error`),
|
|
||||||
description: _(msg`An error occurred while uploading your document.`),
|
|
||||||
variant: 'destructive',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export const DeleteAccountDialog = ({ className, user }: DeleteAccountDialogProp
|
|||||||
|
|
||||||
const [enteredEmail, setEnteredEmail] = useState<string>('');
|
const [enteredEmail, setEnteredEmail] = useState<string>('');
|
||||||
|
|
||||||
const { mutateAsync: deleteAccount, isLoading: isDeletingAccount } =
|
const { mutateAsync: deleteAccount, isPending: isDeletingAccount } =
|
||||||
trpc.profile.deleteAccount.useMutation();
|
trpc.profile.deleteAccount.useMutation();
|
||||||
|
|
||||||
const onDeleteAccount = async () => {
|
const onDeleteAccount = async () => {
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { useEffect, useMemo, useState } from 'react';
|
|||||||
import { Trans, msg } from '@lingui/macro';
|
import { Trans, msg } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
|
|
||||||
import type { FindTemplateRow } from '@documenso/lib/server-only/template/find-templates';
|
|
||||||
import type {
|
import type {
|
||||||
Team,
|
Team,
|
||||||
TeamProfile,
|
TeamProfile,
|
||||||
@ -15,6 +14,7 @@ import type {
|
|||||||
} from '@documenso/prisma/client';
|
} from '@documenso/prisma/client';
|
||||||
import { TemplateType } from '@documenso/prisma/client';
|
import { TemplateType } from '@documenso/prisma/client';
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
|
import type { FindTemplateRow } from '@documenso/trpc/server/template-router/schema';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
import { Switch } from '@documenso/ui/primitives/switch';
|
import { Switch } from '@documenso/ui/primitives/switch';
|
||||||
@ -63,10 +63,10 @@ export const PublicProfilePageView = ({ user, team, profile }: PublicProfilePage
|
|||||||
perPage: 100,
|
perPage: 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: updateUserProfile, isLoading: isUpdatingUserProfile } =
|
const { mutateAsync: updateUserProfile, isPending: isUpdatingUserProfile } =
|
||||||
trpc.profile.updatePublicProfile.useMutation();
|
trpc.profile.updatePublicProfile.useMutation();
|
||||||
|
|
||||||
const { mutateAsync: updateTeamProfile, isLoading: isUpdatingTeamProfile } =
|
const { mutateAsync: updateTeamProfile, isPending: isUpdatingTeamProfile } =
|
||||||
trpc.team.updateTeamPublicProfile.useMutation();
|
trpc.team.updateTeamPublicProfile.useMutation();
|
||||||
|
|
||||||
const isUpdating = isUpdatingUserProfile || isUpdatingTeamProfile;
|
const isUpdating = isUpdatingUserProfile || isUpdatingTeamProfile;
|
||||||
|
|||||||
@ -7,11 +7,11 @@ import { useLingui } from '@lingui/react';
|
|||||||
import { EditIcon, FileIcon, LinkIcon, MoreHorizontalIcon, Trash2Icon } from 'lucide-react';
|
import { EditIcon, FileIcon, LinkIcon, MoreHorizontalIcon, Trash2Icon } from 'lucide-react';
|
||||||
|
|
||||||
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||||
import type { FindTemplateRow } from '@documenso/lib/server-only/template/find-templates';
|
|
||||||
import { formatDirectTemplatePath } from '@documenso/lib/utils/templates';
|
import { formatDirectTemplatePath } from '@documenso/lib/utils/templates';
|
||||||
import type { TemplateDirectLink } from '@documenso/prisma/client';
|
import type { TemplateDirectLink } from '@documenso/prisma/client';
|
||||||
import { TemplateType } from '@documenso/prisma/client';
|
import { TemplateType } from '@documenso/prisma/client';
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
|
import type { FindTemplateRow } from '@documenso/trpc/server/template-router/schema';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@ -39,10 +39,10 @@ export const PublicTemplatesDataTable = () => {
|
|||||||
templateId: number;
|
templateId: number;
|
||||||
} | null>(null);
|
} | null>(null);
|
||||||
|
|
||||||
const { data, isInitialLoading, isLoadingError, refetch } = trpc.template.findTemplates.useQuery(
|
const { data, isLoading, isLoadingError, refetch } = trpc.template.findTemplates.useQuery(
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
placeholderData: (previousData) => previousData,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ export const PublicTemplatesDataTable = () => {
|
|||||||
{/* Loading and error handling states. */}
|
{/* Loading and error handling states. */}
|
||||||
{publicDirectTemplates.length === 0 && (
|
{publicDirectTemplates.length === 0 && (
|
||||||
<>
|
<>
|
||||||
{isInitialLoading &&
|
{isLoading &&
|
||||||
Array(3)
|
Array(3)
|
||||||
.fill(0)
|
.fill(0)
|
||||||
.map((_, index) => (
|
.map((_, index) => (
|
||||||
@ -115,7 +115,7 @@ export const PublicTemplatesDataTable = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isInitialLoading && (
|
{!isLoading && (
|
||||||
<div className="text-muted-foreground flex h-32 flex-col items-center justify-center text-sm">
|
<div className="text-muted-foreground flex h-32 flex-col items-center justify-center text-sm">
|
||||||
<Trans>No public profile templates found</Trans>
|
<Trans>No public profile templates found</Trans>
|
||||||
<ManagePublicTemplateDialog
|
<ManagePublicTemplateDialog
|
||||||
|
|||||||
@ -35,16 +35,15 @@ export const UserSecurityActivityDataTable = () => {
|
|||||||
|
|
||||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||||
|
|
||||||
const { data, isLoading, isInitialLoading, isLoadingError } =
|
const { data, isLoading, isLoadingError } = trpc.profile.findUserSecurityAuditLogs.useQuery(
|
||||||
trpc.profile.findUserSecurityAuditLogs.useQuery(
|
{
|
||||||
{
|
page: parsedSearchParams.page,
|
||||||
page: parsedSearchParams.page,
|
perPage: parsedSearchParams.perPage,
|
||||||
perPage: parsedSearchParams.perPage,
|
},
|
||||||
},
|
{
|
||||||
{
|
placeholderData: (previousData) => previousData,
|
||||||
keepPreviousData: true,
|
},
|
||||||
},
|
);
|
||||||
);
|
|
||||||
|
|
||||||
const onPaginationChange = (page: number, perPage: number) => {
|
const onPaginationChange = (page: number, perPage: number) => {
|
||||||
updateSearchParams({
|
updateSearchParams({
|
||||||
@ -134,7 +133,7 @@ export const UserSecurityActivityDataTable = () => {
|
|||||||
enable: isLoadingError,
|
enable: isLoadingError,
|
||||||
}}
|
}}
|
||||||
skeleton={{
|
skeleton={{
|
||||||
enable: isLoading && isInitialLoading,
|
enable: isLoading,
|
||||||
rows: 3,
|
rows: 3,
|
||||||
component: (
|
component: (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -65,7 +65,7 @@ export const CreatePasskeyDialog = ({ trigger, onSuccess, ...props }: CreatePass
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: createPasskeyRegistrationOptions, isLoading } =
|
const { mutateAsync: createPasskeyRegistrationOptions, isPending } =
|
||||||
trpc.auth.createPasskeyRegistrationOptions.useMutation();
|
trpc.auth.createPasskeyRegistrationOptions.useMutation();
|
||||||
|
|
||||||
const { mutateAsync: createPasskey } = trpc.auth.createPasskey.useMutation();
|
const { mutateAsync: createPasskey } = trpc.auth.createPasskey.useMutation();
|
||||||
@ -141,7 +141,7 @@ export const CreatePasskeyDialog = ({ trigger, onSuccess, ...props }: CreatePass
|
|||||||
>
|
>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button variant="secondary" loading={isLoading}>
|
<Button variant="secondary" loading={isPending}>
|
||||||
<KeyRoundIcon className="-ml-1 mr-1 h-5 w-5" />
|
<KeyRoundIcon className="-ml-1 mr-1 h-5 w-5" />
|
||||||
<Trans>Add passkey</Trans>
|
<Trans>Add passkey</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -60,7 +60,7 @@ export const UserPasskeysDataTableActions = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: updatePasskey, isLoading: isUpdatingPasskey } =
|
const { mutateAsync: updatePasskey, isPending: isUpdatingPasskey } =
|
||||||
trpc.auth.updatePasskey.useMutation({
|
trpc.auth.updatePasskey.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
@ -80,7 +80,7 @@ export const UserPasskeysDataTableActions = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: deletePasskey, isLoading: isDeletingPasskey } =
|
const { mutateAsync: deletePasskey, isPending: isDeletingPasskey } =
|
||||||
trpc.auth.deletePasskey.useMutation({
|
trpc.auth.deletePasskey.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@ -29,13 +29,13 @@ export const UserPasskeysDataTable = () => {
|
|||||||
|
|
||||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||||
|
|
||||||
const { data, isLoading, isInitialLoading, isLoadingError } = trpc.auth.findPasskeys.useQuery(
|
const { data, isLoading, isLoadingError } = trpc.auth.findPasskeys.useQuery(
|
||||||
{
|
{
|
||||||
page: parsedSearchParams.page,
|
page: parsedSearchParams.page,
|
||||||
perPage: parsedSearchParams.perPage,
|
perPage: parsedSearchParams.perPage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
placeholderData: (previousData) => previousData,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ export const UserPasskeysDataTable = () => {
|
|||||||
enable: isLoadingError,
|
enable: isLoadingError,
|
||||||
}}
|
}}
|
||||||
skeleton={{
|
skeleton={{
|
||||||
enable: isLoading && isInitialLoading,
|
enable: isLoading,
|
||||||
rows: 3,
|
rows: 3,
|
||||||
component: (
|
component: (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export const AcceptTeamInvitationButton = ({ teamId }: AcceptTeamInvitationButto
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: acceptTeamInvitation,
|
mutateAsync: acceptTeamInvitation,
|
||||||
isLoading,
|
isPending,
|
||||||
isSuccess,
|
isSuccess,
|
||||||
} = trpc.team.acceptTeamInvitation.useMutation({
|
} = trpc.team.acceptTeamInvitation.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@ -40,8 +40,8 @@ export const AcceptTeamInvitationButton = ({ teamId }: AcceptTeamInvitationButto
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => acceptTeamInvitation({ teamId })}
|
onClick={async () => acceptTeamInvitation({ teamId })}
|
||||||
loading={isLoading}
|
loading={isPending}
|
||||||
disabled={isLoading || isSuccess}
|
disabled={isPending || isSuccess}
|
||||||
>
|
>
|
||||||
<Trans>Accept</Trans>
|
<Trans>Accept</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export const DeclineTeamInvitationButton = ({ teamId }: DeclineTeamInvitationBut
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: declineTeamInvitation,
|
mutateAsync: declineTeamInvitation,
|
||||||
isLoading,
|
isPending,
|
||||||
isSuccess,
|
isSuccess,
|
||||||
} = trpc.team.declineTeamInvitation.useMutation({
|
} = trpc.team.declineTeamInvitation.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@ -40,8 +40,8 @@ export const DeclineTeamInvitationButton = ({ teamId }: DeclineTeamInvitationBut
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => declineTeamInvitation({ teamId })}
|
onClick={async () => declineTeamInvitation({ teamId })}
|
||||||
loading={isLoading}
|
loading={isPending}
|
||||||
disabled={isLoading || isSuccess}
|
disabled={isPending || isSuccess}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
<Trans>Decline</Trans>
|
<Trans>Decline</Trans>
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export const TeamEmailUsage = ({ teamEmail }: TeamEmailUsageProps) => {
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const { mutateAsync: deleteTeamEmail, isLoading: isDeletingTeamEmail } =
|
const { mutateAsync: deleteTeamEmail, isPending: isDeletingTeamEmail } =
|
||||||
trpc.team.deleteTeamEmail.useMutation({
|
trpc.team.deleteTeamEmail.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@ -23,11 +23,11 @@ import { AcceptTeamInvitationButton } from './accept-team-invitation-button';
|
|||||||
import { DeclineTeamInvitationButton } from './decline-team-invitation-button';
|
import { DeclineTeamInvitationButton } from './decline-team-invitation-button';
|
||||||
|
|
||||||
export const TeamInvitations = () => {
|
export const TeamInvitations = () => {
|
||||||
const { data, isInitialLoading } = trpc.team.getTeamInvitations.useQuery();
|
const { data, isLoading } = trpc.team.getTeamInvitations.useQuery();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{data && data.length > 0 && !isInitialLoading && (
|
{data && data.length > 0 && !isLoading && (
|
||||||
<AnimateGenericFadeInOut>
|
<AnimateGenericFadeInOut>
|
||||||
<Alert variant="secondary">
|
<Alert variant="secondary">
|
||||||
<div className="flex h-full flex-row items-center p-2">
|
<div className="flex h-full flex-row items-center p-2">
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import {
|
|||||||
DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||||
SKIP_QUERY_BATCH_META,
|
SKIP_QUERY_BATCH_META,
|
||||||
} from '@documenso/lib/constants/trpc';
|
} from '@documenso/lib/constants/trpc';
|
||||||
import type { TemplateWithDetails } from '@documenso/prisma/types/template';
|
import type { TTemplate } from '@documenso/lib/types/template';
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||||
@ -32,7 +32,7 @@ import { useOptionalCurrentTeam } from '~/providers/team';
|
|||||||
|
|
||||||
export type EditTemplateFormProps = {
|
export type EditTemplateFormProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
initialTemplate: TemplateWithDetails;
|
initialTemplate: TTemplate;
|
||||||
isEnterprise: boolean;
|
isEnterprise: boolean;
|
||||||
templateRootPath: string;
|
templateRootPath: string;
|
||||||
};
|
};
|
||||||
@ -69,7 +69,7 @@ export const EditTemplateForm = ({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const { Recipient: recipients, Field: fields, templateDocumentData } = template;
|
const { recipients, fields, templateDocumentData } = template;
|
||||||
|
|
||||||
const documentFlow: Record<EditTemplateStep, DocumentFlowStep> = {
|
const documentFlow: Record<EditTemplateStep, DocumentFlowStep> = {
|
||||||
settings: {
|
settings: {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { Button } from '@documenso/ui/primitives/button';
|
|||||||
import { TemplateDirectLinkDialog } from '../template-direct-link-dialog';
|
import { TemplateDirectLinkDialog } from '../template-direct-link-dialog';
|
||||||
|
|
||||||
export type TemplateDirectLinkDialogWrapperProps = {
|
export type TemplateDirectLinkDialogWrapperProps = {
|
||||||
template: Template & { directLink?: TemplateDirectLink | null; Recipient: Recipient[] };
|
template: Template & { directLink?: TemplateDirectLink | null; recipients: Recipient[] };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TemplateDirectLinkDialogWrapper = ({
|
export const TemplateDirectLinkDialogWrapper = ({
|
||||||
|
|||||||
@ -69,20 +69,19 @@ export const TemplatePageViewDocumentsTable = ({
|
|||||||
Object.fromEntries(searchParams ?? []),
|
Object.fromEntries(searchParams ?? []),
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, isLoading, isInitialLoading, isLoadingError } =
|
const { data, isLoading, isLoadingError } = trpc.document.findDocuments.useQuery(
|
||||||
trpc.document.findDocuments.useQuery(
|
{
|
||||||
{
|
templateId,
|
||||||
templateId,
|
page: parsedSearchParams.page,
|
||||||
page: parsedSearchParams.page,
|
perPage: parsedSearchParams.perPage,
|
||||||
perPage: parsedSearchParams.perPage,
|
query: parsedSearchParams.query,
|
||||||
query: parsedSearchParams.query,
|
source: parsedSearchParams.source,
|
||||||
source: parsedSearchParams.source,
|
status: parsedSearchParams.status,
|
||||||
status: parsedSearchParams.status,
|
},
|
||||||
},
|
{
|
||||||
{
|
placeholderData: (previousData) => previousData,
|
||||||
keepPreviousData: true,
|
},
|
||||||
},
|
);
|
||||||
);
|
|
||||||
|
|
||||||
const onPaginationChange = (page: number, perPage: number) => {
|
const onPaginationChange = (page: number, perPage: number) => {
|
||||||
updateSearchParams({
|
updateSearchParams({
|
||||||
@ -116,7 +115,7 @@ export const TemplatePageViewDocumentsTable = ({
|
|||||||
accessorKey: 'recipient',
|
accessorKey: 'recipient',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<StackAvatarsWithTooltip
|
<StackAvatarsWithTooltip
|
||||||
recipients={row.original.Recipient}
|
recipients={row.original.recipients}
|
||||||
documentStatus={row.original.status}
|
documentStatus={row.original.status}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
@ -241,7 +240,7 @@ export const TemplatePageViewDocumentsTable = ({
|
|||||||
enable: isLoadingError,
|
enable: isLoadingError,
|
||||||
}}
|
}}
|
||||||
skeleton={{
|
skeleton={{
|
||||||
enable: isLoading && isInitialLoading,
|
enable: isLoading,
|
||||||
rows: 3,
|
rows: 3,
|
||||||
component: (
|
component: (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import type { Template, User } from '@documenso/prisma/client';
|
|||||||
export type TemplatePageViewInformationProps = {
|
export type TemplatePageViewInformationProps = {
|
||||||
userId: number;
|
userId: number;
|
||||||
template: Template & {
|
template: Template & {
|
||||||
User: Pick<User, 'id' | 'name' | 'email'>;
|
user: Pick<User, 'id' | 'name' | 'email'>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -28,7 +28,8 @@ export const TemplatePageViewInformation = ({
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
description: msg`Uploaded by`,
|
description: msg`Uploaded by`,
|
||||||
value: userId === template.userId ? _(msg`You`) : template.User.name ?? template.User.email,
|
value:
|
||||||
|
userId === template.userId ? _(msg`You`) : (template.user.name ?? template.user.email),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: msg`Created`,
|
description: msg`Created`,
|
||||||
|
|||||||
@ -123,7 +123,7 @@ export const TemplatePageViewRecentActivity = ({
|
|||||||
{match(document.source)
|
{match(document.source)
|
||||||
.with(DocumentSource.DOCUMENT, DocumentSource.TEMPLATE, () => (
|
.with(DocumentSource.DOCUMENT, DocumentSource.TEMPLATE, () => (
|
||||||
<Trans>
|
<Trans>
|
||||||
Document created by <span className="font-bold">{document.User.name}</span>
|
Document created by <span className="font-bold">{document.user.name}</span>
|
||||||
</Trans>
|
</Trans>
|
||||||
))
|
))
|
||||||
.with(DocumentSource.TEMPLATE_DIRECT_LINK, () => (
|
.with(DocumentSource.TEMPLATE_DIRECT_LINK, () => (
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { AvatarWithText } from '@documenso/ui/primitives/avatar';
|
|||||||
|
|
||||||
export type TemplatePageViewRecipientsProps = {
|
export type TemplatePageViewRecipientsProps = {
|
||||||
template: Template & {
|
template: Template & {
|
||||||
Recipient: Recipient[];
|
recipients: Recipient[];
|
||||||
};
|
};
|
||||||
templateRootPath: string;
|
templateRootPath: string;
|
||||||
};
|
};
|
||||||
@ -21,7 +21,7 @@ export const TemplatePageViewRecipients = ({
|
|||||||
}: TemplatePageViewRecipientsProps) => {
|
}: TemplatePageViewRecipientsProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const recipients = template.Recipient;
|
const recipients = template.recipients;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="dark:bg-background border-border bg-widget flex flex-col rounded-xl border">
|
<section className="dark:bg-background border-border bg-widget flex flex-col rounded-xl border">
|
||||||
|
|||||||
@ -54,10 +54,10 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
|
|||||||
redirect(templateRootPath);
|
redirect(templateRootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { templateDocumentData, Field, Recipient: recipients, templateMeta } = template;
|
const { templateDocumentData, fields, recipients, templateMeta } = template;
|
||||||
|
|
||||||
// Remap to fit the DocumentReadOnlyFields component.
|
// Remap to fit the DocumentReadOnlyFields component.
|
||||||
const readOnlyFields = Field.map((field) => {
|
const readOnlyFields = fields.map((field) => {
|
||||||
const recipient = recipients.find((recipient) => recipient.id === field.recipientId) || {
|
const recipient = recipients.find((recipient) => recipient.id === field.recipientId) || {
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
@ -66,8 +66,8 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...field,
|
...field,
|
||||||
Recipient: recipient,
|
recipient,
|
||||||
Signature: null,
|
signature: null,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
|
|||||||
<UseTemplateDialog
|
<UseTemplateDialog
|
||||||
templateId={template.id}
|
templateId={template.id}
|
||||||
templateSigningOrder={template.templateMeta?.signingOrder}
|
templateSigningOrder={template.templateMeta?.signingOrder}
|
||||||
recipients={template.Recipient}
|
recipients={template.recipients}
|
||||||
documentRootPath={documentRootPath}
|
documentRootPath={documentRootPath}
|
||||||
trigger={
|
trigger={
|
||||||
<Button className="w-full">
|
<Button className="w-full">
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import { TemplateDirectLinkDialog } from './template-direct-link-dialog';
|
|||||||
export type DataTableActionDropdownProps = {
|
export type DataTableActionDropdownProps = {
|
||||||
row: Template & {
|
row: Template & {
|
||||||
directLink?: Pick<TemplateDirectLink, 'token' | 'enabled'> | null;
|
directLink?: Pick<TemplateDirectLink, 'token' | 'enabled'> | null;
|
||||||
Recipient: Recipient[];
|
recipients: Recipient[];
|
||||||
};
|
};
|
||||||
templateRootPath: string;
|
templateRootPath: string;
|
||||||
teamId?: number;
|
teamId?: number;
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { AlertTriangle, Globe2Icon, InfoIcon, Link2Icon, Loader, LockIcon } from
|
|||||||
|
|
||||||
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
|
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
|
||||||
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
|
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
|
||||||
import type { FindTemplateRow } from '@documenso/lib/server-only/template/find-templates';
|
import type { FindTemplateRow } from '@documenso/trpc/server/template-router/schema';
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
||||||
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
|
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
|
||||||
import { DataTable } from '@documenso/ui/primitives/data-table';
|
import { DataTable } from '@documenso/ui/primitives/data-table';
|
||||||
@ -146,7 +146,7 @@ export const TemplatesDataTable = ({
|
|||||||
templateId={row.original.id}
|
templateId={row.original.id}
|
||||||
templateSigningOrder={row.original.templateMeta?.signingOrder}
|
templateSigningOrder={row.original.templateMeta?.signingOrder}
|
||||||
documentDistributionMethod={row.original.templateMeta?.distributionMethod}
|
documentDistributionMethod={row.original.templateMeta?.distributionMethod}
|
||||||
recipients={row.original.Recipient}
|
recipients={row.original.recipients}
|
||||||
documentRootPath={documentRootPath}
|
documentRootPath={documentRootPath}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,7 @@ export const DeleteTemplateDialog = ({ id, open, onOpenChange }: DeleteTemplateD
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const { mutateAsync: deleteTemplate, isLoading } = trpcReact.template.deleteTemplate.useMutation({
|
const { mutateAsync: deleteTemplate, isPending } = trpcReact.template.deleteTemplate.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
router.refresh();
|
router.refresh();
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ export const DeleteTemplateDialog = ({ id, open, onOpenChange }: DeleteTemplateD
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}>
|
<Dialog open={open} onOpenChange={(value) => !isPending && onOpenChange(value)}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
@ -70,7 +70,7 @@ export const DeleteTemplateDialog = ({ id, open, onOpenChange }: DeleteTemplateD
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
disabled={isLoading}
|
disabled={isPending}
|
||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
>
|
>
|
||||||
<Trans>Cancel</Trans>
|
<Trans>Cancel</Trans>
|
||||||
@ -79,7 +79,7 @@ export const DeleteTemplateDialog = ({ id, open, onOpenChange }: DeleteTemplateD
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
loading={isLoading}
|
loading={isPending}
|
||||||
onClick={async () => deleteTemplate({ templateId: id })}
|
onClick={async () => deleteTemplate({ templateId: id })}
|
||||||
>
|
>
|
||||||
<Trans>Delete</Trans>
|
<Trans>Delete</Trans>
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export const DuplicateTemplateDialog = ({
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const { mutateAsync: duplicateTemplate, isLoading } =
|
const { mutateAsync: duplicateTemplate, isPending } =
|
||||||
trpcReact.template.duplicateTemplate.useMutation({
|
trpcReact.template.duplicateTemplate.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
router.refresh();
|
router.refresh();
|
||||||
@ -55,7 +55,7 @@ export const DuplicateTemplateDialog = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}>
|
<Dialog open={open} onOpenChange={(value) => !isPending && onOpenChange(value)}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
@ -70,7 +70,7 @@ export const DuplicateTemplateDialog = ({
|
|||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
disabled={isLoading}
|
disabled={isPending}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => onOpenChange(false)}
|
onClick={() => onOpenChange(false)}
|
||||||
>
|
>
|
||||||
@ -79,7 +79,7 @@ export const DuplicateTemplateDialog = ({
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
loading={isLoading}
|
loading={isPending}
|
||||||
onClick={async () =>
|
onClick={async () =>
|
||||||
duplicateTemplate({
|
duplicateTemplate({
|
||||||
templateId: id,
|
templateId: id,
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export const MoveTemplateDialog = ({ templateId, open, onOpenChange }: MoveTempl
|
|||||||
const [selectedTeamId, setSelectedTeamId] = useState<number | null>(null);
|
const [selectedTeamId, setSelectedTeamId] = useState<number | null>(null);
|
||||||
|
|
||||||
const { data: teams, isLoading: isLoadingTeams } = trpc.team.getTeams.useQuery();
|
const { data: teams, isLoading: isLoadingTeams } = trpc.team.getTeams.useQuery();
|
||||||
const { mutateAsync: moveTemplate, isLoading } = trpc.template.moveTemplateToTeam.useMutation({
|
const { mutateAsync: moveTemplate, isPending } = trpc.template.moveTemplateToTeam.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
router.refresh();
|
router.refresh();
|
||||||
toast({
|
toast({
|
||||||
@ -130,8 +130,8 @@ export const MoveTemplateDialog = ({ templateId, open, onOpenChange }: MoveTempl
|
|||||||
<Button variant="secondary" onClick={() => onOpenChange(false)}>
|
<Button variant="secondary" onClick={() => onOpenChange(false)}>
|
||||||
<Trans>Cancel</Trans>
|
<Trans>Cancel</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={onMove} loading={isLoading} disabled={!selectedTeamId || isLoading}>
|
<Button onClick={onMove} loading={isPending} disabled={!selectedTeamId || isPending}>
|
||||||
{isLoading ? <Trans>Moving...</Trans> : <Trans>Move</Trans>}
|
{isPending ? <Trans>Moving...</Trans> : <Trans>Move</Trans>}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@ -46,12 +46,10 @@ import {
|
|||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
import { useOptionalCurrentTeam } from '~/providers/team';
|
|
||||||
|
|
||||||
type TemplateDirectLinkDialogProps = {
|
type TemplateDirectLinkDialogProps = {
|
||||||
template: Template & {
|
template: Template & {
|
||||||
directLink?: Pick<TemplateDirectLink, 'token' | 'enabled'> | null;
|
directLink?: Pick<TemplateDirectLink, 'token' | 'enabled'> | null;
|
||||||
Recipient: Recipient[];
|
recipients: Recipient[];
|
||||||
};
|
};
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (_open: boolean) => void;
|
onOpenChange: (_open: boolean) => void;
|
||||||
@ -68,8 +66,6 @@ export const TemplateDirectLinkDialog = ({
|
|||||||
const { quota, remaining } = useLimits();
|
const { quota, remaining } = useLimits();
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const team = useOptionalCurrentTeam();
|
|
||||||
|
|
||||||
const [, copy] = useCopyToClipboard();
|
const [, copy] = useCopyToClipboard();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -81,13 +77,13 @@ export const TemplateDirectLinkDialog = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const validDirectTemplateRecipients = useMemo(
|
const validDirectTemplateRecipients = useMemo(
|
||||||
() => template.Recipient.filter((recipient) => recipient.role !== RecipientRole.CC),
|
() => template.recipients.filter((recipient) => recipient.role !== RecipientRole.CC),
|
||||||
[template.Recipient],
|
[template.recipients],
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: createTemplateDirectLink,
|
mutateAsync: createTemplateDirectLink,
|
||||||
isLoading: isCreatingTemplateDirectLink,
|
isPending: isCreatingTemplateDirectLink,
|
||||||
reset: resetCreateTemplateDirectLink,
|
reset: resetCreateTemplateDirectLink,
|
||||||
} = trpcReact.template.createTemplateDirectLink.useMutation({
|
} = trpcReact.template.createTemplateDirectLink.useMutation({
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
@ -108,7 +104,7 @@ export const TemplateDirectLinkDialog = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: toggleTemplateDirectLink, isLoading: isTogglingTemplateAccess } =
|
const { mutateAsync: toggleTemplateDirectLink, isPending: isTogglingTemplateAccess } =
|
||||||
trpcReact.template.toggleTemplateDirectLink.useMutation({
|
trpcReact.template.toggleTemplateDirectLink.useMutation({
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
const enabledDescription = msg`Direct link signing has been enabled`;
|
const enabledDescription = msg`Direct link signing has been enabled`;
|
||||||
@ -131,7 +127,7 @@ export const TemplateDirectLinkDialog = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: deleteTemplateDirectLink, isLoading: isDeletingTemplateDirectLink } =
|
const { mutateAsync: deleteTemplateDirectLink, isPending: isDeletingTemplateDirectLink } =
|
||||||
trpcReact.template.deleteTemplateDirectLink.useMutation({
|
trpcReact.template.deleteTemplateDirectLink.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
onOpenChange(false);
|
onOpenChange(false);
|
||||||
@ -326,7 +322,7 @@ export const TemplateDirectLinkDialog = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Prevent creating placeholder direct template recipient if the email already exists. */}
|
{/* Prevent creating placeholder direct template recipient if the email already exists. */}
|
||||||
{!template.Recipient.some(
|
{!template.recipients.some(
|
||||||
(recipient) => recipient.email === DIRECT_TEMPLATE_RECIPIENT_EMAIL,
|
(recipient) => recipient.email === DIRECT_TEMPLATE_RECIPIENT_EMAIL,
|
||||||
) && (
|
) && (
|
||||||
<DialogFooter className="mx-auto">
|
<DialogFooter className="mx-auto">
|
||||||
|
|||||||
@ -104,7 +104,7 @@ export default async function AuditLog({ searchParams }: AuditLogProps) {
|
|||||||
<span className="font-medium">{_(msg`Owner`)}</span>
|
<span className="font-medium">{_(msg`Owner`)}</span>
|
||||||
|
|
||||||
<span className="mt-1 block break-words">
|
<span className="mt-1 block break-words">
|
||||||
{document.User.name} ({document.User.email})
|
{document.user.name} ({document.user.email})
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ export default async function AuditLog({ searchParams }: AuditLogProps) {
|
|||||||
<p className="font-medium">{_(msg`Recipients`)}</p>
|
<p className="font-medium">{_(msg`Recipients`)}</p>
|
||||||
|
|
||||||
<ul className="mt-1 list-inside list-disc">
|
<ul className="mt-1 list-inside list-disc">
|
||||||
{document.Recipient.map((recipient) => (
|
{document.recipients.map((recipient) => (
|
||||||
<li key={recipient.id}>
|
<li key={recipient.id}>
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
[{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}]
|
[{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}]
|
||||||
|
|||||||
@ -86,7 +86,7 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
});
|
});
|
||||||
|
|
||||||
const isOwner = (email: string) => {
|
const isOwner = (email: string) => {
|
||||||
return email.toLowerCase() === document.User.email.toLowerCase();
|
return email.toLowerCase() === document.user.email.toLowerCase();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDevice = (userAgent?: string | null) => {
|
const getDevice = (userAgent?: string | null) => {
|
||||||
@ -104,7 +104,7 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAuthenticationLevel = (recipientId: number) => {
|
const getAuthenticationLevel = (recipientId: number) => {
|
||||||
const recipient = document.Recipient.find((recipient) => recipient.id === recipientId);
|
const recipient = document.recipients.find((recipient) => recipient.id === recipientId);
|
||||||
|
|
||||||
if (!recipient) {
|
if (!recipient) {
|
||||||
return 'Unknown';
|
return 'Unknown';
|
||||||
@ -157,9 +157,11 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getRecipientSignatureField = (recipientId: number) => {
|
const getRecipientSignatureField = (recipientId: number) => {
|
||||||
return document.Recipient.find((recipient) => recipient.id === recipientId)?.Field.find(
|
return document.recipients
|
||||||
(field) => field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE,
|
.find((recipient) => recipient.id === recipientId)
|
||||||
);
|
?.fields.find(
|
||||||
|
(field) => field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -181,7 +183,7 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
</TableHeader>
|
</TableHeader>
|
||||||
|
|
||||||
<TableBody className="print:text-xs">
|
<TableBody className="print:text-xs">
|
||||||
{document.Recipient.map((recipient, i) => {
|
{document.recipients.map((recipient, i) => {
|
||||||
const logs = getRecipientAuditLogs(recipient.id);
|
const logs = getRecipientAuditLogs(recipient.id);
|
||||||
const signature = getRecipientSignatureField(recipient.id);
|
const signature = getRecipientSignatureField(recipient.id);
|
||||||
|
|
||||||
@ -209,17 +211,17 @@ export default async function SigningCertificate({ searchParams }: SigningCertif
|
|||||||
boxShadow: `0px 0px 0px 4.88px rgba(122, 196, 85, 0.1), 0px 0px 0px 1.22px rgba(122, 196, 85, 0.6), 0px 0px 0px 0.61px rgba(122, 196, 85, 1)`,
|
boxShadow: `0px 0px 0px 4.88px rgba(122, 196, 85, 0.1), 0px 0px 0px 1.22px rgba(122, 196, 85, 0.6), 0px 0px 0px 0.61px rgba(122, 196, 85, 1)`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{signature.Signature?.signatureImageAsBase64 && (
|
{signature.signature?.signatureImageAsBase64 && (
|
||||||
<img
|
<img
|
||||||
src={`${signature.Signature?.signatureImageAsBase64}`}
|
src={`${signature.signature?.signatureImageAsBase64}`}
|
||||||
alt="Signature"
|
alt="Signature"
|
||||||
className="max-h-12 max-w-full"
|
className="max-h-12 max-w-full"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{signature.Signature?.typedSignature && (
|
{signature.signature?.typedSignature && (
|
||||||
<p className="font-signature text-center text-sm">
|
<p className="font-signature text-center text-sm">
|
||||||
{signature.Signature?.typedSignature}
|
{signature.signature?.typedSignature}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -7,8 +7,9 @@ import { useSession } from 'next-auth/react';
|
|||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import type { Field, Recipient } from '@documenso/prisma/client';
|
import type { TTemplate } from '@documenso/lib/types/template';
|
||||||
import type { TemplateWithDetails } from '@documenso/prisma/types/template';
|
import type { Recipient } from '@documenso/prisma/client';
|
||||||
|
import type { Field } from '@documenso/prisma/client';
|
||||||
import {
|
import {
|
||||||
DocumentFlowFormContainerActions,
|
DocumentFlowFormContainerActions,
|
||||||
DocumentFlowFormContainerContent,
|
DocumentFlowFormContainerContent,
|
||||||
@ -40,8 +41,8 @@ export type TConfigureDirectTemplateFormSchema = z.infer<typeof ZConfigureDirect
|
|||||||
export type ConfigureDirectTemplateFormProps = {
|
export type ConfigureDirectTemplateFormProps = {
|
||||||
flowStep: DocumentFlowStep;
|
flowStep: DocumentFlowStep;
|
||||||
isDocumentPdfLoaded: boolean;
|
isDocumentPdfLoaded: boolean;
|
||||||
template: Omit<TemplateWithDetails, 'User'>;
|
template: Omit<TTemplate, 'user'>;
|
||||||
directTemplateRecipient: Recipient & { Field: Field[] };
|
directTemplateRecipient: Recipient & { fields: Field[] };
|
||||||
initialEmail?: string;
|
initialEmail?: string;
|
||||||
onSubmit: (_data: TConfigureDirectTemplateFormSchema) => void;
|
onSubmit: (_data: TConfigureDirectTemplateFormSchema) => void;
|
||||||
};
|
};
|
||||||
@ -57,10 +58,10 @@ export const ConfigureDirectTemplateFormPartial = ({
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
|
|
||||||
const { Recipient } = template;
|
const { recipients } = template;
|
||||||
const { derivedRecipientAccessAuth } = useRequiredDocumentAuthContext();
|
const { derivedRecipientAccessAuth } = useRequiredDocumentAuthContext();
|
||||||
|
|
||||||
const recipientsWithBlankDirectRecipientEmail = Recipient.map((recipient) => {
|
const recipientsWithBlankDirectRecipientEmail = recipients.map((recipient) => {
|
||||||
if (recipient.id === directTemplateRecipient.id) {
|
if (recipient.id === directTemplateRecipient.id) {
|
||||||
return {
|
return {
|
||||||
...recipient,
|
...recipient,
|
||||||
@ -74,7 +75,7 @@ export const ConfigureDirectTemplateFormPartial = ({
|
|||||||
const form = useForm<TConfigureDirectTemplateFormSchema>({
|
const form = useForm<TConfigureDirectTemplateFormSchema>({
|
||||||
resolver: zodResolver(
|
resolver: zodResolver(
|
||||||
ZConfigureDirectTemplateFormSchema.superRefine((items, ctx) => {
|
ZConfigureDirectTemplateFormSchema.superRefine((items, ctx) => {
|
||||||
if (template.Recipient.map((recipient) => recipient.email).includes(items.email)) {
|
if (template.recipients.map((recipient) => recipient.email).includes(items.email)) {
|
||||||
ctx.addIssue({
|
ctx.addIssue({
|
||||||
code: z.ZodIssueCode.custom,
|
code: z.ZodIssueCode.custom,
|
||||||
message: _(msg`Email cannot already exist in the template`),
|
message: _(msg`Email cannot already exist in the template`),
|
||||||
@ -96,7 +97,7 @@ export const ConfigureDirectTemplateFormPartial = ({
|
|||||||
|
|
||||||
<DocumentFlowFormContainerContent>
|
<DocumentFlowFormContainerContent>
|
||||||
{isDocumentPdfLoaded &&
|
{isDocumentPdfLoaded &&
|
||||||
directTemplateRecipient.Field.map((field, index) => (
|
directTemplateRecipient.fields.map((field, index) => (
|
||||||
<ShowFieldItem
|
<ShowFieldItem
|
||||||
key={index}
|
key={index}
|
||||||
field={field}
|
field={field}
|
||||||
|
|||||||
@ -8,9 +8,9 @@ import { msg } from '@lingui/macro';
|
|||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
|
|
||||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||||
|
import type { TTemplate } from '@documenso/lib/types/template';
|
||||||
import type { Field } from '@documenso/prisma/client';
|
import type { Field } from '@documenso/prisma/client';
|
||||||
import { type Recipient } from '@documenso/prisma/client';
|
import { type Recipient } from '@documenso/prisma/client';
|
||||||
import type { TemplateWithDetails } from '@documenso/prisma/types/template';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||||
import { DocumentFlowFormContainer } from '@documenso/ui/primitives/document-flow/document-flow-root';
|
import { DocumentFlowFormContainer } from '@documenso/ui/primitives/document-flow/document-flow-root';
|
||||||
@ -28,9 +28,9 @@ import type { DirectTemplateLocalField } from './sign-direct-template';
|
|||||||
import { SignDirectTemplateForm } from './sign-direct-template';
|
import { SignDirectTemplateForm } from './sign-direct-template';
|
||||||
|
|
||||||
export type TemplatesDirectPageViewProps = {
|
export type TemplatesDirectPageViewProps = {
|
||||||
template: Omit<TemplateWithDetails, 'User'>;
|
template: Omit<TTemplate, 'user'>;
|
||||||
directTemplateToken: string;
|
directTemplateToken: string;
|
||||||
directTemplateRecipient: Recipient & { Field: Field[] };
|
directTemplateRecipient: Recipient & { fields: Field[] };
|
||||||
};
|
};
|
||||||
|
|
||||||
type DirectTemplateStep = 'configure' | 'sign';
|
type DirectTemplateStep = 'configure' | 'sign';
|
||||||
@ -164,7 +164,7 @@ export const DirectTemplatePageView = ({
|
|||||||
<SignDirectTemplateForm
|
<SignDirectTemplateForm
|
||||||
flowStep={directTemplateFlow.sign}
|
flowStep={directTemplateFlow.sign}
|
||||||
directRecipient={recipient}
|
directRecipient={recipient}
|
||||||
directRecipientFields={directTemplateRecipient.Field}
|
directRecipientFields={directTemplateRecipient.fields}
|
||||||
template={template}
|
template={template}
|
||||||
onSubmit={onSignDirectTemplateSubmit}
|
onSubmit={onSignDirectTemplateSubmit}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export default async function TemplatesDirectPage({ params }: TemplatesDirectPag
|
|||||||
notFound();
|
notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const directTemplateRecipient = template.Recipient.find(
|
const directTemplateRecipient = template.recipients.find(
|
||||||
(recipient) => recipient.id === template.directLink?.directTemplateRecipientId,
|
(recipient) => recipient.id === template.directLink?.directTemplateRecipientId,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ export default async function TemplatesDirectPage({ params }: TemplatesDirectPag
|
|||||||
<div className="text-muted-foreground mb-8 mt-2.5 flex items-center gap-x-2">
|
<div className="text-muted-foreground mb-8 mt-2.5 flex items-center gap-x-2">
|
||||||
<UsersIcon className="h-4 w-4" />
|
<UsersIcon className="h-4 w-4" />
|
||||||
<p className="text-muted-foreground/80">
|
<p className="text-muted-foreground/80">
|
||||||
<Plural value={template.Recipient.length} one="# recipient" other="# recipients" />
|
<Plural value={template.recipients.length} one="# recipient" other="# recipients" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -14,10 +14,10 @@ import {
|
|||||||
ZRadioFieldMeta,
|
ZRadioFieldMeta,
|
||||||
ZTextFieldMeta,
|
ZTextFieldMeta,
|
||||||
} from '@documenso/lib/types/field-meta';
|
} from '@documenso/lib/types/field-meta';
|
||||||
|
import type { TTemplate } from '@documenso/lib/types/template';
|
||||||
import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields';
|
import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields';
|
||||||
import type { Field, Recipient, Signature } from '@documenso/prisma/client';
|
import type { Field, Recipient, Signature } from '@documenso/prisma/client';
|
||||||
import { FieldType } from '@documenso/prisma/client';
|
import { FieldType } from '@documenso/prisma/client';
|
||||||
import type { TemplateWithDetails } from '@documenso/prisma/types/template';
|
|
||||||
import type {
|
import type {
|
||||||
TRemovedSignedFieldWithTokenMutationSchema,
|
TRemovedSignedFieldWithTokenMutationSchema,
|
||||||
TSignFieldWithTokenMutationSchema,
|
TSignFieldWithTokenMutationSchema,
|
||||||
@ -55,13 +55,13 @@ export type SignDirectTemplateFormProps = {
|
|||||||
flowStep: DocumentFlowStep;
|
flowStep: DocumentFlowStep;
|
||||||
directRecipient: Recipient;
|
directRecipient: Recipient;
|
||||||
directRecipientFields: Field[];
|
directRecipientFields: Field[];
|
||||||
template: Omit<TemplateWithDetails, 'User'>;
|
template: Omit<TTemplate, 'user'>;
|
||||||
onSubmit: (_data: DirectTemplateLocalField[]) => Promise<void>;
|
onSubmit: (_data: DirectTemplateLocalField[]) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DirectTemplateLocalField = Field & {
|
export type DirectTemplateLocalField = Field & {
|
||||||
signedValue?: TSignFieldWithTokenMutationSchema;
|
signedValue?: TSignFieldWithTokenMutationSchema;
|
||||||
Signature?: Signature;
|
signature?: Signature;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SignDirectTemplateForm = ({
|
export const SignDirectTemplateForm = ({
|
||||||
@ -95,7 +95,7 @@ export const SignDirectTemplateForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (field.type === FieldType.SIGNATURE) {
|
if (field.type === FieldType.SIGNATURE) {
|
||||||
tempField.Signature = {
|
tempField.signature = {
|
||||||
id: 1,
|
id: 1,
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
recipientId: 1,
|
recipientId: 1,
|
||||||
@ -127,7 +127,7 @@ export const SignDirectTemplateForm = ({
|
|||||||
customText: '',
|
customText: '',
|
||||||
inserted: false,
|
inserted: false,
|
||||||
signedValue: undefined,
|
signedValue: undefined,
|
||||||
Signature: undefined,
|
signature: undefined,
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -52,15 +52,15 @@ export async function GET(_request: Request, { params: { slug } }: SharePageOpen
|
|||||||
const isRecipient = 'Signature' in recipientOrSender;
|
const isRecipient = 'Signature' in recipientOrSender;
|
||||||
|
|
||||||
const signatureImage = match(recipientOrSender)
|
const signatureImage = match(recipientOrSender)
|
||||||
.with({ Signature: P.array(P._) }, (recipient) => {
|
.with({ signatures: P.array(P._) }, (recipient) => {
|
||||||
return recipient.Signature?.[0]?.signatureImageAsBase64 || null;
|
return recipient.signatures?.[0]?.signatureImageAsBase64 || null;
|
||||||
})
|
})
|
||||||
.otherwise((sender) => {
|
.otherwise((sender) => {
|
||||||
return sender.signature || null;
|
return sender.signature || null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const signatureName = match(recipientOrSender)
|
const signatureName = match(recipientOrSender)
|
||||||
.with({ Signature: P.array(P._) }, (recipient) => {
|
.with({ signatures: P.array(P._) }, (recipient) => {
|
||||||
return recipient.name || recipient.email;
|
return recipient.name || recipient.email;
|
||||||
})
|
})
|
||||||
.otherwise((sender) => {
|
.otherwise((sender) => {
|
||||||
|
|||||||
@ -81,12 +81,12 @@ export const CheckboxField = ({
|
|||||||
);
|
);
|
||||||
}, [checkedValues, validationSign, checkboxValidationLength]);
|
}, [checkedValues, validationSign, checkboxValidationLength]);
|
||||||
|
|
||||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } =
|
||||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: removeSignedFieldWithToken,
|
mutateAsync: removeSignedFieldWithToken,
|
||||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
isPending: isRemoveSignedFieldWithTokenLoading,
|
||||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||||
|
|||||||
@ -51,12 +51,12 @@ export const DateField = ({
|
|||||||
|
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } =
|
||||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: removeSignedFieldWithToken,
|
mutateAsync: removeSignedFieldWithToken,
|
||||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
isPending: isRemoveSignedFieldWithTokenLoading,
|
||||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||||
|
|||||||
@ -106,7 +106,7 @@ export const DocumentAuthProvider = ({
|
|||||||
perPage: MAXIMUM_PASSKEYS,
|
perPage: MAXIMUM_PASSKEYS,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
placeholderData: (previousData) => previousData,
|
||||||
enabled: derivedRecipientActionAuth === DocumentAuth.PASSKEY,
|
enabled: derivedRecipientActionAuth === DocumentAuth.PASSKEY,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -58,12 +58,12 @@ export const DropdownField = ({
|
|||||||
const defaultValue = parsedFieldMeta?.defaultValue;
|
const defaultValue = parsedFieldMeta?.defaultValue;
|
||||||
const [localChoice, setLocalChoice] = useState(parsedFieldMeta.defaultValue ?? '');
|
const [localChoice, setLocalChoice] = useState(parsedFieldMeta.defaultValue ?? '');
|
||||||
|
|
||||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } =
|
||||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: removeSignedFieldWithToken,
|
mutateAsync: removeSignedFieldWithToken,
|
||||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
isPending: isRemoveSignedFieldWithTokenLoading,
|
||||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||||
|
|||||||
@ -40,12 +40,12 @@ export const EmailField = ({ field, recipient, onSignField, onUnsignField }: Ema
|
|||||||
|
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } =
|
||||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: removeSignedFieldWithToken,
|
mutateAsync: removeSignedFieldWithToken,
|
||||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
isPending: isRemoveSignedFieldWithTokenLoading,
|
||||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||||
|
|||||||
@ -46,12 +46,12 @@ export const InitialsField = ({
|
|||||||
|
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } =
|
||||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: removeSignedFieldWithToken,
|
mutateAsync: removeSignedFieldWithToken,
|
||||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
isPending: isRemoveSignedFieldWithTokenLoading,
|
||||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||||
|
|||||||
@ -48,12 +48,12 @@ export const NameField = ({ field, recipient, onSignField, onUnsignField }: Name
|
|||||||
|
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } =
|
||||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: removeSignedFieldWithToken,
|
mutateAsync: removeSignedFieldWithToken,
|
||||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
isPending: isRemoveSignedFieldWithTokenLoading,
|
||||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||||
|
|||||||
@ -71,12 +71,12 @@ export const NumberField = ({ field, recipient, onSignField, onUnsignField }: Nu
|
|||||||
|
|
||||||
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
|
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
|
||||||
|
|
||||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } =
|
||||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: removeSignedFieldWithToken,
|
mutateAsync: removeSignedFieldWithToken,
|
||||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
isPending: isRemoveSignedFieldWithTokenLoading,
|
||||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||||
|
|||||||
@ -52,12 +52,12 @@ export const RadioField = ({ field, recipient, onSignField, onUnsignField }: Rad
|
|||||||
|
|
||||||
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
|
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
|
||||||
|
|
||||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } =
|
||||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: removeSignedFieldWithToken,
|
mutateAsync: removeSignedFieldWithToken,
|
||||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
isPending: isRemoveSignedFieldWithTokenLoading,
|
||||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||||
|
|||||||
@ -66,15 +66,15 @@ export const SignatureField = ({
|
|||||||
|
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } =
|
||||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: removeSignedFieldWithToken,
|
mutateAsync: removeSignedFieldWithToken,
|
||||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
isPending: isRemoveSignedFieldWithTokenLoading,
|
||||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const { Signature: signature } = field;
|
const { signature } = field;
|
||||||
|
|
||||||
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
|
||||||
|
|
||||||
|
|||||||
@ -56,8 +56,8 @@ export const SigningPageView = ({
|
|||||||
const shouldUseTeamDetails =
|
const shouldUseTeamDetails =
|
||||||
document.teamId && document.team?.teamGlobalSettings?.includeSenderDetails === false;
|
document.teamId && document.team?.teamGlobalSettings?.includeSenderDetails === false;
|
||||||
|
|
||||||
let senderName = document.User.name ?? '';
|
let senderName = document.user.name ?? '';
|
||||||
let senderEmail = `(${document.User.email})`;
|
let senderEmail = `(${document.user.email})`;
|
||||||
|
|
||||||
if (shouldUseTeamDetails) {
|
if (shouldUseTeamDetails) {
|
||||||
senderName = document.team?.name ?? '';
|
senderName = document.team?.name ?? '';
|
||||||
|
|||||||
@ -54,12 +54,12 @@ export const TextField = ({ field, recipient, onSignField, onUnsignField }: Text
|
|||||||
|
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
|
|
||||||
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
|
const { mutateAsync: signFieldWithToken, isPending: isSignFieldWithTokenLoading } =
|
||||||
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
mutateAsync: removeSignedFieldWithToken,
|
mutateAsync: removeSignedFieldWithToken,
|
||||||
isLoading: isRemoveSignedFieldWithTokenLoading,
|
isPending: isRemoveSignedFieldWithTokenLoading,
|
||||||
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
|
||||||
|
|
||||||
const parsedFieldMeta = field.fieldMeta ? ZTextFieldMeta.parse(field.fieldMeta) : null;
|
const parsedFieldMeta = field.fieldMeta ? ZTextFieldMeta.parse(field.fieldMeta) : null;
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export const LayoutBillingBanner = ({
|
|||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
const { mutateAsync: createBillingPortal, isLoading } =
|
const { mutateAsync: createBillingPortal, isPending } =
|
||||||
trpc.team.createBillingPortal.useMutation();
|
trpc.team.createBillingPortal.useMutation();
|
||||||
|
|
||||||
const handleCreatePortal = async () => {
|
const handleCreatePortal = async () => {
|
||||||
@ -92,7 +92,7 @@ export const LayoutBillingBanner = ({
|
|||||||
'text-destructive-foreground hover:bg-destructive-foreground hover:text-white':
|
'text-destructive-foreground hover:bg-destructive-foreground hover:text-white':
|
||||||
subscription.status === SubscriptionStatus.INACTIVE,
|
subscription.status === SubscriptionStatus.INACTIVE,
|
||||||
})}
|
})}
|
||||||
disabled={isLoading}
|
disabled={isPending}
|
||||||
onClick={() => setIsOpen(true)}
|
onClick={() => setIsOpen(true)}
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
@ -101,7 +101,7 @@ export const LayoutBillingBanner = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Dialog open={isOpen} onOpenChange={(value) => !isLoading && setIsOpen(value)}>
|
<Dialog open={isOpen} onOpenChange={(value) => !isPending && setIsOpen(value)}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
<Trans>Payment overdue</Trans>
|
<Trans>Payment overdue</Trans>
|
||||||
@ -128,7 +128,7 @@ export const LayoutBillingBanner = ({
|
|||||||
|
|
||||||
{canExecuteTeamAction('MANAGE_BILLING', userRole) && (
|
{canExecuteTeamAction('MANAGE_BILLING', userRole) && (
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button loading={isLoading} onClick={handleCreatePortal}>
|
<Button loading={isPending} onClick={handleCreatePortal}>
|
||||||
<Trans>Resolve payment</Trans>
|
<Trans>Resolve payment</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export const TeamEmailDropdown = ({ team }: TeamsSettingsPageProps) => {
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const { mutateAsync: resendEmailVerification, isLoading: isResendingEmailVerification } =
|
const { mutateAsync: resendEmailVerification, isPending: isResendingEmailVerification } =
|
||||||
trpc.team.resendTeamEmailVerification.useMutation({
|
trpc.team.resendTeamEmailVerification.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export const TeamTransferStatus = ({
|
|||||||
|
|
||||||
const isExpired = transferVerification && isTokenExpired(transferVerification.expiresAt);
|
const isExpired = transferVerification && isTokenExpired(transferVerification.expiresAt);
|
||||||
|
|
||||||
const { mutateAsync: deleteTeamTransferRequest, isLoading } =
|
const { mutateAsync: deleteTeamTransferRequest, isPending } =
|
||||||
trpc.team.deleteTeamTransferRequest.useMutation({
|
trpc.team.deleteTeamTransferRequest.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
if (!isExpired) {
|
if (!isExpired) {
|
||||||
@ -112,7 +112,7 @@ export const TeamTransferStatus = ({
|
|||||||
{canExecuteTeamAction('DELETE_TEAM_TRANSFER_REQUEST', currentUserTeamRole) && (
|
{canExecuteTeamAction('DELETE_TEAM_TRANSFER_REQUEST', currentUserTeamRole) && (
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => deleteTeamTransferRequest({ teamId })}
|
onClick={async () => deleteTeamTransferRequest({ teamId })}
|
||||||
loading={isLoading}
|
loading={isPending}
|
||||||
variant={isExpired ? 'destructive' : 'ghost'}
|
variant={isExpired ? 'destructive' : 'ghost'}
|
||||||
className={cn('ml-auto', {
|
className={cn('ml-auto', {
|
||||||
'hover:bg-transparent hover:text-blue-800': !isExpired,
|
'hover:bg-transparent hover:text-blue-800': !isExpired,
|
||||||
|
|||||||
@ -100,7 +100,7 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
|
|
||||||
const hasSignatureField = localFields.some((field) => field.type === FieldType.SIGNATURE);
|
const hasSignatureField = localFields.some((field) => field.type === FieldType.SIGNATURE);
|
||||||
|
|
||||||
const { mutateAsync: createDocumentFromDirectTemplate, isLoading: isSubmitting } =
|
const { mutateAsync: createDocumentFromDirectTemplate, isPending: isSubmitting } =
|
||||||
trpc.template.createDocumentFromDirectTemplate.useMutation();
|
trpc.template.createDocumentFromDirectTemplate.useMutation();
|
||||||
|
|
||||||
const onSignField = (payload: TSignFieldWithTokenMutationSchema) => {
|
const onSignField = (payload: TSignFieldWithTokenMutationSchema) => {
|
||||||
@ -118,7 +118,7 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (field.type === FieldType.SIGNATURE) {
|
if (field.type === FieldType.SIGNATURE) {
|
||||||
newField.Signature = {
|
newField.signature = {
|
||||||
id: 1,
|
id: 1,
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
recipientId: 1,
|
recipientId: 1,
|
||||||
@ -163,7 +163,7 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
customText: '',
|
customText: '',
|
||||||
inserted: false,
|
inserted: false,
|
||||||
signedValue: undefined,
|
signedValue: undefined,
|
||||||
Signature: undefined,
|
signature: undefined,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -73,7 +73,7 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem
|
|||||||
|
|
||||||
const { directTemplateRecipientId } = template.directLink;
|
const { directTemplateRecipientId } = template.directLink;
|
||||||
|
|
||||||
const recipient = template.Recipient.find(
|
const recipient = template.recipients.find(
|
||||||
(recipient) => recipient.id === directTemplateRecipientId,
|
(recipient) => recipient.id === directTemplateRecipientId,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem
|
|||||||
return notFound();
|
return notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const fields = template.Field.filter((field) => field.recipientId === directTemplateRecipientId);
|
const fields = template.fields.filter((field) => field.recipientId === directTemplateRecipientId);
|
||||||
|
|
||||||
const team = template.teamId
|
const team = template.teamId
|
||||||
? await getTeamById({ teamId: template.teamId, userId: template.userId }).catch(() => null)
|
? await getTeamById({ teamId: template.teamId, userId: template.userId }).catch(() => null)
|
||||||
|
|||||||
@ -84,7 +84,7 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
fields.filter((field) => field.inserted),
|
fields.filter((field) => field.inserted),
|
||||||
];
|
];
|
||||||
|
|
||||||
const { mutateAsync: completeDocumentWithToken, isLoading: isSubmitting } =
|
const { mutateAsync: completeDocumentWithToken, isPending: isSubmitting } =
|
||||||
trpc.recipient.completeDocumentWithToken.useMutation();
|
trpc.recipient.completeDocumentWithToken.useMutation();
|
||||||
|
|
||||||
const hasSignatureField = fields.some((field) => field.type === FieldType.SIGNATURE);
|
const hasSignatureField = fields.some((field) => field.type === FieldType.SIGNATURE);
|
||||||
|
|||||||
@ -91,7 +91,7 @@ export function CommandMenu({ open, onOpenChange }: CommandMenuProps) {
|
|||||||
query: search,
|
query: search,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
placeholderData: (previousData) => previousData,
|
||||||
// Do not batch this due to relatively long request time compared to
|
// Do not batch this due to relatively long request time compared to
|
||||||
// other queries which are generally batched with this.
|
// other queries which are generally batched with this.
|
||||||
...SKIP_QUERY_BATCH_META,
|
...SKIP_QUERY_BATCH_META,
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
|
|||||||
|
|
||||||
const [isButtonDisabled, setIsButtonDisabled] = useState(false);
|
const [isButtonDisabled, setIsButtonDisabled] = useState(false);
|
||||||
|
|
||||||
const { mutateAsync: sendConfirmationEmail, isLoading } =
|
const { mutateAsync: sendConfirmationEmail, isPending } =
|
||||||
trpc.profile.sendConfirmationEmail.useMutation();
|
trpc.profile.sendConfirmationEmail.useMutation();
|
||||||
|
|
||||||
const onResendConfirmationEmail = async () => {
|
const onResendConfirmationEmail = async () => {
|
||||||
@ -122,10 +122,10 @@ export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
|
|||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
disabled={isButtonDisabled}
|
disabled={isButtonDisabled}
|
||||||
loading={isLoading}
|
loading={isPending}
|
||||||
onClick={onResendConfirmationEmail}
|
onClick={onResendConfirmationEmail}
|
||||||
>
|
>
|
||||||
{isLoading ? <Trans>Sending...</Trans> : <Trans>Resend Confirmation Email</Trans>}
|
{isPending ? <Trans>Sending...</Trans> : <Trans>Resend Confirmation Email</Trans>}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@ -64,7 +64,7 @@ export const AddTeamEmailDialog = ({ teamId, trigger, ...props }: AddTeamEmailDi
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: createTeamEmailVerification, isLoading } =
|
const { mutateAsync: createTeamEmailVerification, isPending } =
|
||||||
trpc.team.createTeamEmailVerification.useMutation();
|
trpc.team.createTeamEmailVerification.useMutation();
|
||||||
|
|
||||||
const onFormSubmit = async ({ name, email }: TCreateTeamEmailFormSchema) => {
|
const onFormSubmit = async ({ name, email }: TCreateTeamEmailFormSchema) => {
|
||||||
@ -120,7 +120,7 @@ export const AddTeamEmailDialog = ({ teamId, trigger, ...props }: AddTeamEmailDi
|
|||||||
>
|
>
|
||||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild={true}>
|
||||||
{trigger ?? (
|
{trigger ?? (
|
||||||
<Button variant="outline" loading={isLoading} className="bg-background">
|
<Button variant="outline" loading={isPending} className="bg-background">
|
||||||
<Plus className="-ml-1 mr-1 h-5 w-5" />
|
<Plus className="-ml-1 mr-1 h-5 w-5" />
|
||||||
<Trans>Add email</Trans>
|
<Trans>Add email</Trans>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export const CreateTeamCheckoutDialog = ({
|
|||||||
|
|
||||||
const { data, isLoading } = trpc.team.getTeamPrices.useQuery();
|
const { data, isLoading } = trpc.team.getTeamPrices.useQuery();
|
||||||
|
|
||||||
const { mutateAsync: createCheckout, isLoading: isCreatingCheckout } =
|
const { mutateAsync: createCheckout, isPending: isCreatingCheckout } =
|
||||||
trpc.team.createTeamPendingCheckout.useMutation({
|
trpc.team.createTeamPendingCheckout.useMutation({
|
||||||
onSuccess: (checkoutUrl) => {
|
onSuccess: (checkoutUrl) => {
|
||||||
window.open(checkoutUrl, '_blank');
|
window.open(checkoutUrl, '_blank');
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export const DeleteTeamMemberDialog = ({
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const { mutateAsync: deleteTeamMembers, isLoading: isDeletingTeamMember } =
|
const { mutateAsync: deleteTeamMembers, isPending: isDeletingTeamMember } =
|
||||||
trpc.team.deleteTeamMembers.useMutation({
|
trpc.team.deleteTeamMembers.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export const LeaveTeamDialog = ({
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const { mutateAsync: leaveTeam, isLoading: isLeavingTeam } = trpc.team.leaveTeam.useMutation({
|
const { mutateAsync: leaveTeam, isPending: isLeavingTeam } = trpc.team.leaveTeam.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
title: _(msg`Success`),
|
title: _(msg`Success`),
|
||||||
|
|||||||
@ -50,7 +50,7 @@ export const RemoveTeamEmailDialog = ({ trigger, teamName, team }: RemoveTeamEma
|
|||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { mutateAsync: deleteTeamEmail, isLoading: isDeletingTeamEmail } =
|
const { mutateAsync: deleteTeamEmail, isPending: isDeletingTeamEmail } =
|
||||||
trpc.team.deleteTeamEmail.useMutation({
|
trpc.team.deleteTeamEmail.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
@ -69,7 +69,7 @@ export const RemoveTeamEmailDialog = ({ trigger, teamName, team }: RemoveTeamEma
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: deleteTeamEmailVerification, isLoading: isDeletingTeamEmailVerification } =
|
const { mutateAsync: deleteTeamEmailVerification, isPending: isDeletingTeamEmailVerification } =
|
||||||
trpc.team.deleteTeamEmailVerification.useMutation({
|
trpc.team.deleteTeamEmailVerification.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@ -32,14 +32,14 @@ export const CurrentUserTeamsDataTable = () => {
|
|||||||
|
|
||||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||||
|
|
||||||
const { data, isLoading, isInitialLoading, isLoadingError } = trpc.team.findTeams.useQuery(
|
const { data, isLoading, isLoadingError } = trpc.team.findTeams.useQuery(
|
||||||
{
|
{
|
||||||
query: parsedSearchParams.query,
|
query: parsedSearchParams.query,
|
||||||
page: parsedSearchParams.page,
|
page: parsedSearchParams.page,
|
||||||
perPage: parsedSearchParams.perPage,
|
perPage: parsedSearchParams.perPage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
placeholderData: (previousData) => previousData,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ export const CurrentUserTeamsDataTable = () => {
|
|||||||
enable: isLoadingError,
|
enable: isLoadingError,
|
||||||
}}
|
}}
|
||||||
skeleton={{
|
skeleton={{
|
||||||
enable: isLoading && isInitialLoading,
|
enable: isLoading,
|
||||||
rows: 3,
|
rows: 3,
|
||||||
component: (
|
component: (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export const PendingUserTeamsDataTableActions = ({
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const { mutateAsync: deleteTeamPending, isLoading: deletingTeam } =
|
const { mutateAsync: deleteTeamPending, isPending: deletingTeam } =
|
||||||
trpc.team.deleteTeamPending.useMutation({
|
trpc.team.deleteTeamPending.useMutation({
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@ -31,14 +31,14 @@ export const PendingUserTeamsDataTable = () => {
|
|||||||
|
|
||||||
const [checkoutPendingTeamId, setCheckoutPendingTeamId] = useState<number | null>(null);
|
const [checkoutPendingTeamId, setCheckoutPendingTeamId] = useState<number | null>(null);
|
||||||
|
|
||||||
const { data, isLoading, isInitialLoading, isLoadingError } = trpc.team.findTeamsPending.useQuery(
|
const { data, isLoading, isLoadingError } = trpc.team.findTeamsPending.useQuery(
|
||||||
{
|
{
|
||||||
query: parsedSearchParams.query,
|
query: parsedSearchParams.query,
|
||||||
page: parsedSearchParams.page,
|
page: parsedSearchParams.page,
|
||||||
perPage: parsedSearchParams.perPage,
|
perPage: parsedSearchParams.perPage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
placeholderData: (previousData) => previousData,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ export const PendingUserTeamsDataTable = () => {
|
|||||||
enable: isLoadingError,
|
enable: isLoadingError,
|
||||||
}}
|
}}
|
||||||
skeleton={{
|
skeleton={{
|
||||||
enable: isLoading && isInitialLoading,
|
enable: isLoading,
|
||||||
rows: 3,
|
rows: 3,
|
||||||
component: (
|
component: (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -24,12 +24,12 @@ export type TeamBillingInvoicesDataTableProps = {
|
|||||||
export const TeamBillingInvoicesDataTable = ({ teamId }: TeamBillingInvoicesDataTableProps) => {
|
export const TeamBillingInvoicesDataTable = ({ teamId }: TeamBillingInvoicesDataTableProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
|
||||||
const { data, isLoading, isInitialLoading, isLoadingError } = trpc.team.findTeamInvoices.useQuery(
|
const { data, isLoading, isLoadingError } = trpc.team.findTeamInvoices.useQuery(
|
||||||
{
|
{
|
||||||
teamId,
|
teamId,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
placeholderData: (previousData) => previousData,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ export const TeamBillingInvoicesDataTable = ({ teamId }: TeamBillingInvoicesData
|
|||||||
enable: isLoadingError,
|
enable: isLoadingError,
|
||||||
}}
|
}}
|
||||||
skeleton={{
|
skeleton={{
|
||||||
enable: isLoading && isInitialLoading,
|
enable: isLoading,
|
||||||
rows: 3,
|
rows: 3,
|
||||||
component: (
|
component: (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -40,18 +40,17 @@ export const TeamMemberInvitesDataTable = ({ teamId }: TeamMemberInvitesDataTabl
|
|||||||
|
|
||||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||||
|
|
||||||
const { data, isLoading, isInitialLoading, isLoadingError } =
|
const { data, isLoading, isLoadingError } = trpc.team.findTeamMemberInvites.useQuery(
|
||||||
trpc.team.findTeamMemberInvites.useQuery(
|
{
|
||||||
{
|
teamId,
|
||||||
teamId,
|
query: parsedSearchParams.query,
|
||||||
query: parsedSearchParams.query,
|
page: parsedSearchParams.page,
|
||||||
page: parsedSearchParams.page,
|
perPage: parsedSearchParams.perPage,
|
||||||
perPage: parsedSearchParams.perPage,
|
},
|
||||||
},
|
{
|
||||||
{
|
placeholderData: (previousData) => previousData,
|
||||||
keepPreviousData: true,
|
},
|
||||||
},
|
);
|
||||||
);
|
|
||||||
|
|
||||||
const { mutateAsync: resendTeamMemberInvitation } =
|
const { mutateAsync: resendTeamMemberInvitation } =
|
||||||
trpc.team.resendTeamMemberInvitation.useMutation({
|
trpc.team.resendTeamMemberInvitation.useMutation({
|
||||||
@ -182,7 +181,7 @@ export const TeamMemberInvitesDataTable = ({ teamId }: TeamMemberInvitesDataTabl
|
|||||||
enable: isLoadingError,
|
enable: isLoadingError,
|
||||||
}}
|
}}
|
||||||
skeleton={{
|
skeleton={{
|
||||||
enable: isLoading && isInitialLoading,
|
enable: isLoading,
|
||||||
rows: 3,
|
rows: 3,
|
||||||
component: (
|
component: (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -52,7 +52,7 @@ export const TeamMembersDataTable = ({
|
|||||||
|
|
||||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||||
|
|
||||||
const { data, isLoading, isInitialLoading, isLoadingError } = trpc.team.findTeamMembers.useQuery(
|
const { data, isLoading, isLoadingError } = trpc.team.findTeamMembers.useQuery(
|
||||||
{
|
{
|
||||||
teamId,
|
teamId,
|
||||||
query: parsedSearchParams.query,
|
query: parsedSearchParams.query,
|
||||||
@ -60,7 +60,7 @@ export const TeamMembersDataTable = ({
|
|||||||
perPage: parsedSearchParams.perPage,
|
perPage: parsedSearchParams.perPage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
keepPreviousData: true,
|
placeholderData: (previousData) => previousData,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ export const TeamMembersDataTable = ({
|
|||||||
enable: isLoadingError,
|
enable: isLoadingError,
|
||||||
}}
|
}}
|
||||||
skeleton={{
|
skeleton={{
|
||||||
enable: isLoading && isInitialLoading,
|
enable: isLoading,
|
||||||
rows: 3,
|
rows: 3,
|
||||||
component: (
|
component: (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user