Compare commits
37 Commits
v1.8.1-rc.
...
feat/delet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5103477e7b | ||
|
|
b19b57dbc9 | ||
|
|
2b3ab9a3b7 | ||
|
|
cac262fcea | ||
|
|
e6d4005cd1 | ||
|
|
337bdb3553 | ||
|
|
ab654a63d8 | ||
|
|
dcb7c2436f | ||
|
|
fa33f83696 | ||
|
|
b15e1d6c47 | ||
|
|
0cd7c25718 | ||
|
|
79eec5f451 | ||
|
|
7ca0975650 | ||
|
|
870b3fb3d7 | ||
|
|
43ea76fae3 | ||
|
|
cd5adce7df | ||
|
|
11e483f1c4 | ||
|
|
2dd122aed3 | ||
|
|
d3872e86f1 | ||
|
|
171398ae2d | ||
|
|
492350612e | ||
|
|
f9935adb57 | ||
|
|
d2b99303f9 | ||
|
|
d33bbe71e7 | ||
|
|
6bf62e0ecb | ||
|
|
16527f01e7 | ||
|
|
7f25508c3c | ||
|
|
754e9e6428 | ||
|
|
2837b178fb | ||
|
|
26ccdc1b23 | ||
|
|
ea63b45a13 | ||
|
|
feef4b1a12 | ||
|
|
1a55f4253b | ||
|
|
8311e0cc29 | ||
|
|
a9adc36732 | ||
|
|
73e375938c | ||
|
|
c6393b7a9e |
@@ -27,9 +27,6 @@
|
|||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"autoprefixer": "^10.0.1",
|
|
||||||
"postcss": "^8",
|
|
||||||
"tailwindcss": "^3.3.0",
|
|
||||||
"typescript": "^5"
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,59 @@ Check out what's new in the latest version and read our thoughts on it. For more
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
# Documenso v1.8.0: Team Preferences, Signature Rejection, and Document Distribution
|
||||||
|
|
||||||
|
We're excited to announce the release of Documenso v1.8.0! This update brings powerful new features to enhance your document signing process. Here's what's new:
|
||||||
|
|
||||||
|
## 🌟 Key New Features
|
||||||
|
|
||||||
|
### 1. Team Preferences
|
||||||
|
|
||||||
|
Introducing **Team Preferences**, allowing administrators to configure settings and preferences that apply to documents across the entire team. This feature ensures consistency and simplifies management by letting you set default options, permissions, and preferences that automatically apply to all team members.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 2. Signature Rejection
|
||||||
|
|
||||||
|
Recipients now have the option to **reject signatures**. This feature enhances communication by allowing recipients to decline signing, providing feedback or requesting changes before the document is finalized.
|
||||||
|
|
||||||
|
<video
|
||||||
|
src="/changelog/v1_8_0/reject-document.mp4"
|
||||||
|
className="aspect-video w-full"
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
controls
|
||||||
|
/>
|
||||||
|
|
||||||
|
### 3. Document Distribution Settings
|
||||||
|
|
||||||
|
With the new **Document Distribution Settings**, you have greater control over how your documents are shared. Distribute communications via our automated emails and templates or take full control using our API and your own notifications infrastructure.
|
||||||
|
|
||||||
|
## 🔧 Other Improvements
|
||||||
|
|
||||||
|
- **Support for Gmail SMTP Service**: Adds support for using Gmail as your SMTP service provider.
|
||||||
|
- **Certificate and Email Translations**: Added support for multiple languages in document certificates and emails, enhancing the experience for international users.
|
||||||
|
- **Field Movement Fixes**: Resolved issues related to moving fields within documents, improving the document preparation experience.
|
||||||
|
- **Docker Environment Update**: Improved Docker setup for smoother deployments and better environment consistency.
|
||||||
|
- **Billing Access Improvements**: Users now have uninterrupted access to billing information, simplifying account management.
|
||||||
|
- **Support Time Windows for 2FA Tokens**: Enhanced two-factor authentication by supporting time windows in 2FA tokens, improving flexibility.
|
||||||
|
|
||||||
|
## 💡 Recent Features
|
||||||
|
|
||||||
|
Don't forget to take advantage of these powerful features from our recent releases:
|
||||||
|
|
||||||
|
- **Signing Order**: Define the sequence in which recipients sign your documents for a structured signing process.
|
||||||
|
- **Document Visibility Controls**: Manage who can view your documents and at what stages, offering greater privacy and control.
|
||||||
|
- **Embedded Signing Experience**: Integrate the signing process directly into your own applications for a seamless user experience.
|
||||||
|
|
||||||
|
**👏 Thank You**
|
||||||
|
|
||||||
|
As always, we're grateful for the community's contributions and feedback. Your support helps us improve Documenso and deliver a top-notch open-source document signing solution.
|
||||||
|
|
||||||
|
We hope you enjoy the new features in Documenso v1.8.0. Happy signing!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Documenso v1.7.1: Signing order and document visibility
|
# Documenso v1.7.1: Signing order and document visibility
|
||||||
|
|
||||||
We're excited to introduce Documenso v1.7.1, bringing you improved control over your document signing process. Here are the key updates:
|
We're excited to introduce Documenso v1.7.1, bringing you improved control over your document signing process. Here are the key updates:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/marketing",
|
"name": "@documenso/marketing",
|
||||||
"version": "1.8.1-rc.0",
|
"version": "1.8.1-rc.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
4
apps/marketing/process-env.d.ts
vendored
4
apps/marketing/process-env.d.ts
vendored
@@ -2,8 +2,8 @@ declare namespace NodeJS {
|
|||||||
export interface ProcessEnv {
|
export interface ProcessEnv {
|
||||||
NEXT_PUBLIC_WEBAPP_URL?: string;
|
NEXT_PUBLIC_WEBAPP_URL?: string;
|
||||||
NEXT_PUBLIC_MARKETING_URL?: string;
|
NEXT_PUBLIC_MARKETING_URL?: string;
|
||||||
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?:string;
|
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?: string;
|
||||||
|
|
||||||
NEXT_PRIVATE_DATABASE_URL: string;
|
NEXT_PRIVATE_DATABASE_URL: string;
|
||||||
|
|
||||||
NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID: string;
|
NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID: string;
|
||||||
|
|||||||
BIN
apps/marketing/public/changelog/v1_8_0/reject-document.mp4
Normal file
BIN
apps/marketing/public/changelog/v1_8_0/reject-document.mp4
Normal file
Binary file not shown.
BIN
apps/marketing/public/changelog/v1_8_0/team-global-settings.jpeg
Normal file
BIN
apps/marketing/public/changelog/v1_8_0/team-global-settings.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 169 KiB |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/web",
|
"name": "@documenso/web",
|
||||||
"version": "1.8.1-rc.0",
|
"version": "1.8.1-rc.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
"@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",
|
"@tanstack/react-query": "^4.29.5",
|
||||||
|
"colord": "^2.9.3",
|
||||||
"cookie-es": "^1.0.0",
|
"cookie-es": "^1.0.0",
|
||||||
"formidable": "^2.1.1",
|
"formidable": "^2.1.1",
|
||||||
"framer-motion": "^10.12.8",
|
"framer-motion": "^10.12.8",
|
||||||
@@ -53,7 +54,7 @@
|
|||||||
"react-icons": "^4.11.0",
|
"react-icons": "^4.11.0",
|
||||||
"react-rnd": "^10.4.1",
|
"react-rnd": "^10.4.1",
|
||||||
"recharts": "^2.7.2",
|
"recharts": "^2.7.2",
|
||||||
"remeda": "^2.12.1",
|
"remeda": "^2.17.3",
|
||||||
"sharp": "0.32.6",
|
"sharp": "0.32.6",
|
||||||
"ts-pattern": "^5.0.5",
|
"ts-pattern": "^5.0.5",
|
||||||
"ua-parser-js": "^1.0.37",
|
"ua-parser-js": "^1.0.37",
|
||||||
|
|||||||
2
apps/web/process-env.d.ts
vendored
2
apps/web/process-env.d.ts
vendored
@@ -2,7 +2,7 @@ declare namespace NodeJS {
|
|||||||
export interface ProcessEnv {
|
export interface ProcessEnv {
|
||||||
NEXT_PUBLIC_WEBAPP_URL?: string;
|
NEXT_PUBLIC_WEBAPP_URL?: string;
|
||||||
NEXT_PUBLIC_MARKETING_URL?: string;
|
NEXT_PUBLIC_MARKETING_URL?: string;
|
||||||
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?:string;
|
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?: string;
|
||||||
|
|
||||||
NEXT_PRIVATE_DATABASE_URL: string;
|
NEXT_PRIVATE_DATABASE_URL: string;
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const DocumentPageViewInformation = ({
|
|||||||
const { _, i18n } = useLingui();
|
const { _, i18n } = useLingui();
|
||||||
|
|
||||||
const documentInformation = useMemo(() => {
|
const documentInformation = useMemo(() => {
|
||||||
return [
|
const info = [
|
||||||
{
|
{
|
||||||
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,
|
||||||
@@ -44,8 +44,20 @@ export const DocumentPageViewInformation = ({
|
|||||||
.toRelative(),
|
.toRelative(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isMounted, document, userId]);
|
if (document.deletedAt) {
|
||||||
|
info.push({
|
||||||
|
description: msg`Deleted`,
|
||||||
|
value:
|
||||||
|
document.deletedAt &&
|
||||||
|
DateTime.fromJSDate(document.deletedAt)
|
||||||
|
.setLocale(i18n.locales?.[0] || i18n.locale)
|
||||||
|
.toFormat('MMMM d, yyyy'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}, [isMounted, document, i18n.locales?.[0] || i18n.locale, userId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="dark:bg-background text-foreground border-border bg-widget flex flex-col rounded-xl border">
|
<section className="dark:bg-background text-foreground border-border bg-widget flex flex-col rounded-xl border">
|
||||||
|
|||||||
@@ -146,7 +146,10 @@ export const DocumentPageView = async ({ params, team }: DocumentPageViewProps)
|
|||||||
|
|
||||||
<div className="flex flex-row justify-between truncate">
|
<div className="flex flex-row justify-between truncate">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={document.title}>
|
<h1
|
||||||
|
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
|
||||||
|
title={document.title}
|
||||||
|
>
|
||||||
{document.title}
|
{document.title}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
@@ -218,7 +221,7 @@ export const DocumentPageView = async ({ params, team }: DocumentPageViewProps)
|
|||||||
<DocumentPageViewDropdown document={documentWithRecipients} team={team} />
|
<DocumentPageViewDropdown document={documentWithRecipients} team={team} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-muted-foreground mt-2 px-4 text-sm ">
|
<p className="text-muted-foreground mt-2 px-4 text-sm">
|
||||||
{match(document.status)
|
{match(document.status)
|
||||||
.with(DocumentStatus.COMPLETED, () => (
|
.with(DocumentStatus.COMPLETED, () => (
|
||||||
<Trans>This document has been signed by all recipients</Trans>
|
<Trans>This document has been signed by all recipients</Trans>
|
||||||
|
|||||||
@@ -109,7 +109,10 @@ export const DocumentEditPageView = async ({ params, team }: DocumentEditPageVie
|
|||||||
<Trans>Documents</Trans>
|
<Trans>Documents</Trans>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={document.title}>
|
<h1
|
||||||
|
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
|
||||||
|
title={document.title}
|
||||||
|
>
|
||||||
{document.title}
|
{document.title}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,10 @@ export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageVie
|
|||||||
|
|
||||||
<div className="flex flex-col justify-between truncate sm:flex-row">
|
<div className="flex flex-col justify-between truncate sm:flex-row">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={document.title}>
|
<h1
|
||||||
|
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
|
||||||
|
title={document.title}
|
||||||
|
>
|
||||||
{document.title}
|
{document.title}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Link from 'next/link';
|
|||||||
import { Trans, msg } from '@lingui/macro';
|
import { Trans, msg } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import {
|
import {
|
||||||
|
ArchiveRestore,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Copy,
|
Copy,
|
||||||
Download,
|
Download,
|
||||||
@@ -23,8 +24,8 @@ import { useSession } from 'next-auth/react';
|
|||||||
|
|
||||||
import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
|
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 type { Document, Recipient, Team, User } from '@documenso/prisma/client';
|
import type { Document, Recipient, Team, User } from '@documenso/prisma/client';
|
||||||
|
import { DocumentStatus, RecipientRole } from '@documenso/prisma/client';
|
||||||
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
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';
|
||||||
@@ -43,6 +44,7 @@ import { ResendDocumentActionItem } from './_action-items/resend-document';
|
|||||||
import { DeleteDocumentDialog } from './delete-document-dialog';
|
import { DeleteDocumentDialog } from './delete-document-dialog';
|
||||||
import { DuplicateDocumentDialog } from './duplicate-document-dialog';
|
import { DuplicateDocumentDialog } from './duplicate-document-dialog';
|
||||||
import { MoveDocumentDialog } from './move-document-dialog';
|
import { MoveDocumentDialog } from './move-document-dialog';
|
||||||
|
import { RestoreDocumentDialog } from './restore-document-dialog';
|
||||||
|
|
||||||
export type DataTableActionDropdownProps = {
|
export type DataTableActionDropdownProps = {
|
||||||
row: Document & {
|
row: Document & {
|
||||||
@@ -61,6 +63,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
|
const [isDuplicateDialogOpen, setDuplicateDialogOpen] = useState(false);
|
||||||
const [isMoveDialogOpen, setMoveDialogOpen] = useState(false);
|
const [isMoveDialogOpen, setMoveDialogOpen] = useState(false);
|
||||||
|
const [isRestoreDialogOpen, setRestoreDialogOpen] = useState(false);
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return null;
|
return null;
|
||||||
@@ -76,6 +79,7 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
// const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
|
// const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
|
||||||
const isCurrentTeamDocument = team && row.team?.url === team.url;
|
const isCurrentTeamDocument = team && row.team?.url === team.url;
|
||||||
const canManageDocument = Boolean(isOwner || isCurrentTeamDocument);
|
const canManageDocument = Boolean(isOwner || isCurrentTeamDocument);
|
||||||
|
const isDeletedDocument = row.deletedAt !== null;
|
||||||
|
|
||||||
const documentsPath = formatDocumentsPath(team?.url);
|
const documentsPath = formatDocumentsPath(team?.url);
|
||||||
|
|
||||||
@@ -181,13 +185,23 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
Void
|
Void
|
||||||
</DropdownMenuItem> */}
|
</DropdownMenuItem> */}
|
||||||
|
|
||||||
<DropdownMenuItem
|
{isDeletedDocument ? (
|
||||||
onClick={() => setDeleteDialogOpen(true)}
|
<DropdownMenuItem
|
||||||
disabled={Boolean(!canManageDocument && team?.teamEmail)}
|
onClick={() => setRestoreDialogOpen(true)}
|
||||||
>
|
disabled={Boolean(!canManageDocument)}
|
||||||
<Trash2 className="mr-2 h-4 w-4" />
|
>
|
||||||
{canManageDocument ? _(msg`Delete`) : _(msg`Hide`)}
|
<ArchiveRestore className="mr-2 h-4 w-4" />
|
||||||
</DropdownMenuItem>
|
Restore
|
||||||
|
</DropdownMenuItem>
|
||||||
|
) : (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => setDeleteDialogOpen(true)}
|
||||||
|
disabled={Boolean(!canManageDocument && team?.teamEmail)}
|
||||||
|
>
|
||||||
|
<Trash2 className="mr-2 h-4 w-4" />
|
||||||
|
{canManageDocument ? 'Delete' : 'Hide'}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
|
||||||
<DropdownMenuLabel>
|
<DropdownMenuLabel>
|
||||||
<Trans>Share</Trans>
|
<Trans>Share</Trans>
|
||||||
@@ -239,6 +253,16 @@ export const DataTableActionDropdown = ({ row, team }: DataTableActionDropdownPr
|
|||||||
onOpenChange={setMoveDialogOpen}
|
onOpenChange={setMoveDialogOpen}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<RestoreDocumentDialog
|
||||||
|
id={row.id}
|
||||||
|
status={row.status}
|
||||||
|
documentTitle={row.title}
|
||||||
|
open={isRestoreDialogOpen}
|
||||||
|
onOpenChange={setRestoreDialogOpen}
|
||||||
|
teamId={team?.id}
|
||||||
|
canManageDocument={canManageDocument}
|
||||||
|
/>
|
||||||
|
|
||||||
{isDuplicateDialogOpen && (
|
{isDuplicateDialogOpen && (
|
||||||
<DuplicateDocumentDialog
|
<DuplicateDocumentDialog
|
||||||
id={row.id}
|
id={row.id}
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ export const DeleteDocumentDialog = ({
|
|||||||
const { refreshLimits } = useLimits();
|
const { refreshLimits } = useLimits();
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
|
||||||
|
const deleteMessage = msg`delete`;
|
||||||
|
|
||||||
const [inputValue, setInputValue] = useState('');
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [isDeleteEnabled, setIsDeleteEnabled] = useState(status === DocumentStatus.DRAFT);
|
const [isDeleteEnabled, setIsDeleteEnabled] = useState(status === DocumentStatus.DRAFT);
|
||||||
|
|
||||||
@@ -87,7 +89,7 @@ export const DeleteDocumentDialog = ({
|
|||||||
|
|
||||||
const onInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const onInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setInputValue(event.target.value);
|
setInputValue(event.target.value);
|
||||||
setIsDeleteEnabled(event.target.value === _(msg`delete`));
|
setIsDeleteEnabled(event.target.value === _(deleteMessage));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -181,7 +183,7 @@ export const DeleteDocumentDialog = ({
|
|||||||
type="text"
|
type="text"
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
placeholder={_(msg`Type 'delete' to confirm`)}
|
placeholder={_(msg`Please type ${`'${_(deleteMessage)}'`} to confirm`)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import { Trans } from '@lingui/macro';
|
|||||||
|
|
||||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||||
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
|
|
||||||
import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents';
|
import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents';
|
||||||
import type { GetStatsInput } from '@documenso/lib/server-only/document/get-stats';
|
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
|
||||||
import { getStats } from '@documenso/lib/server-only/document/get-stats';
|
import type { GetStatsInput } from '@documenso/lib/server-only/document/get-stats-new';
|
||||||
|
import { getStats } from '@documenso/lib/server-only/document/get-stats-new';
|
||||||
import { parseToIntegerArray } from '@documenso/lib/utils/params';
|
import { parseToIntegerArray } from '@documenso/lib/utils/params';
|
||||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||||
import type { Team, TeamEmail, TeamMemberRole } from '@documenso/prisma/client';
|
import type { Team, TeamEmail, TeamMemberRole } from '@documenso/prisma/client';
|
||||||
@@ -35,7 +35,7 @@ export interface DocumentsPageViewProps {
|
|||||||
senderIds?: string;
|
senderIds?: string;
|
||||||
search?: string;
|
search?: string;
|
||||||
};
|
};
|
||||||
team?: Team & { teamEmail?: TeamEmail | null } & { currentTeamMember?: { role: TeamMemberRole } };
|
team?: Team & { teamEmail: TeamEmail | null } & { currentTeamMember?: { role: TeamMemberRole } };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPageViewProps) => {
|
export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPageViewProps) => {
|
||||||
@@ -50,25 +50,14 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
|
|||||||
const currentTeam = team
|
const currentTeam = team
|
||||||
? { id: team.id, url: team.url, teamEmail: team.teamEmail?.email }
|
? { id: team.id, url: team.url, teamEmail: team.teamEmail?.email }
|
||||||
: undefined;
|
: undefined;
|
||||||
const currentTeamMemberRole = team?.currentTeamMember?.role;
|
|
||||||
|
|
||||||
const getStatOptions: GetStatsInput = {
|
const getStatOptions: GetStatsInput = {
|
||||||
user,
|
user,
|
||||||
period,
|
period,
|
||||||
|
team,
|
||||||
search,
|
search,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (team) {
|
|
||||||
getStatOptions.team = {
|
|
||||||
teamId: team.id,
|
|
||||||
teamEmail: team.teamEmail?.email,
|
|
||||||
senderIds,
|
|
||||||
currentTeamMemberRole,
|
|
||||||
currentUserEmail: user.email,
|
|
||||||
userId: user.id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const stats = await getStats(getStatOptions);
|
const stats = await getStats(getStatOptions);
|
||||||
|
|
||||||
const results = await findDocuments({
|
const results = await findDocuments({
|
||||||
@@ -128,6 +117,7 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
|
|||||||
ExtendedDocumentStatus.PENDING,
|
ExtendedDocumentStatus.PENDING,
|
||||||
ExtendedDocumentStatus.COMPLETED,
|
ExtendedDocumentStatus.COMPLETED,
|
||||||
ExtendedDocumentStatus.DRAFT,
|
ExtendedDocumentStatus.DRAFT,
|
||||||
|
ExtendedDocumentStatus.BIN,
|
||||||
ExtendedDocumentStatus.ALL,
|
ExtendedDocumentStatus.ALL,
|
||||||
].map((value) => (
|
].map((value) => (
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { msg } from '@lingui/macro';
|
import { msg } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { Bird, CheckCircle2 } from 'lucide-react';
|
import { Bird, CheckCircle2, Trash } from 'lucide-react';
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||||
@@ -30,6 +30,11 @@ export const EmptyDocumentState = ({ status }: EmptyDocumentProps) => {
|
|||||||
message: msg`You have not yet created or received any documents. To create a document please upload one.`,
|
message: msg`You have not yet created or received any documents. To create a document please upload one.`,
|
||||||
icon: Bird,
|
icon: Bird,
|
||||||
}))
|
}))
|
||||||
|
.with(ExtendedDocumentStatus.BIN, () => ({
|
||||||
|
title: msg`No documents in the bin`,
|
||||||
|
message: msg`There are no documents in the bin.`,
|
||||||
|
icon: Trash,
|
||||||
|
}))
|
||||||
.otherwise(() => ({
|
.otherwise(() => ({
|
||||||
title: msg`Nothing to do`,
|
title: msg`Nothing to do`,
|
||||||
message: msg`All documents have been processed. Any new documents that are sent or received will show here.`,
|
message: msg`All documents have been processed. Any new documents that are sent or received will show here.`,
|
||||||
@@ -42,7 +47,6 @@ export const EmptyDocumentState = ({ status }: EmptyDocumentProps) => {
|
|||||||
data-testid="empty-document-state"
|
data-testid="empty-document-state"
|
||||||
>
|
>
|
||||||
<Icon className="h-12 w-12" strokeWidth={1.5} />
|
<Icon className="h-12 w-12" strokeWidth={1.5} />
|
||||||
|
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h3 className="text-lg font-semibold">{_(title)}</h3>
|
<h3 className="text-lg font-semibold">{_(title)}</h3>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
import type { DocumentStatus } from '@documenso/prisma/client';
|
||||||
|
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
} from '@documenso/ui/primitives/alert-dialog';
|
||||||
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
|
type RestoreDocumentDialogProps = {
|
||||||
|
id: number;
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (_open: boolean) => void;
|
||||||
|
status: DocumentStatus;
|
||||||
|
documentTitle: string;
|
||||||
|
teamId?: number;
|
||||||
|
canManageDocument: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function RestoreDocumentDialog({
|
||||||
|
id,
|
||||||
|
teamId,
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
documentTitle,
|
||||||
|
canManageDocument,
|
||||||
|
}: RestoreDocumentDialogProps) {
|
||||||
|
const router = useRouter();
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const { mutateAsync: restoreDocument, isLoading } =
|
||||||
|
trpcReact.document.restoreDocument.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
router.refresh();
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: 'Document restored',
|
||||||
|
description: `"${documentTitle}" has been successfully restored`,
|
||||||
|
duration: 5000,
|
||||||
|
});
|
||||||
|
|
||||||
|
onOpenChange(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onRestore = async () => {
|
||||||
|
try {
|
||||||
|
await restoreDocument({ id, teamId });
|
||||||
|
} catch {
|
||||||
|
toast({
|
||||||
|
title: 'Something went wrong',
|
||||||
|
description: 'This document could not be restored at this time. Please try again.',
|
||||||
|
variant: 'destructive',
|
||||||
|
duration: 7500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AlertDialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
You are about to restore the document <strong>"{documentTitle}"</strong>
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
loading={isLoading}
|
||||||
|
onClick={onRestore}
|
||||||
|
disabled={!canManageDocument}
|
||||||
|
>
|
||||||
|
Restore
|
||||||
|
</Button>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -141,6 +141,23 @@ export const EditTemplateForm = ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { mutateAsync: updateTypedSignature } =
|
||||||
|
trpc.template.updateTemplateTypedSignatureSettings.useMutation({
|
||||||
|
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||||
|
onSuccess: (newData) => {
|
||||||
|
utils.template.getTemplateWithDetailsById.setData(
|
||||||
|
{
|
||||||
|
id: initialTemplate.id,
|
||||||
|
},
|
||||||
|
(oldData) => ({
|
||||||
|
...(oldData || initialTemplate),
|
||||||
|
...newData,
|
||||||
|
id: Number(newData.id),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const onAddSettingsFormSubmit = async (data: TAddTemplateSettingsFormSchema) => {
|
const onAddSettingsFormSubmit = async (data: TAddTemplateSettingsFormSchema) => {
|
||||||
try {
|
try {
|
||||||
await updateTemplateSettings({
|
await updateTemplateSettings({
|
||||||
@@ -211,6 +228,12 @@ export const EditTemplateForm = ({
|
|||||||
fields: data.fields,
|
fields: data.fields,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await updateTypedSignature({
|
||||||
|
templateId: template.id,
|
||||||
|
teamId: team?.id,
|
||||||
|
typedSignatureEnabled: data.typedSignatureEnabled,
|
||||||
|
});
|
||||||
|
|
||||||
// Clear all field data from localStorage
|
// Clear all field data from localStorage
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
const key = localStorage.key(i);
|
const key = localStorage.key(i);
|
||||||
@@ -225,14 +248,13 @@ export const EditTemplateForm = ({
|
|||||||
duration: 5000,
|
duration: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Router refresh is here to clear the router cache for when navigating to /documents.
|
|
||||||
router.refresh();
|
|
||||||
|
|
||||||
router.push(templateRootPath);
|
router.push(templateRootPath);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: _(msg`Error`),
|
title: _(msg`Error`),
|
||||||
description: _(msg`An error occurred while adding signers.`),
|
description: _(msg`An error occurred while adding fields.`),
|
||||||
variant: 'destructive',
|
variant: 'destructive',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -301,6 +323,7 @@ export const EditTemplateForm = ({
|
|||||||
fields={fields}
|
fields={fields}
|
||||||
onSubmit={onAddFieldsFormSubmit}
|
onSubmit={onAddFieldsFormSubmit}
|
||||||
teamId={team?.id}
|
teamId={team?.id}
|
||||||
|
typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
|
||||||
/>
|
/>
|
||||||
</Stepper>
|
</Stepper>
|
||||||
</DocumentFlowFormContainer>
|
</DocumentFlowFormContainer>
|
||||||
|
|||||||
@@ -63,7 +63,10 @@ export const TemplateEditPageView = async ({ params, team }: TemplateEditPageVie
|
|||||||
<Trans>Template</Trans>
|
<Trans>Template</Trans>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={template.title}>
|
<h1
|
||||||
|
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
|
||||||
|
title={template.title}
|
||||||
|
>
|
||||||
{template.title}
|
{template.title}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
|
|||||||
|
|
||||||
const mockedDocumentMeta = templateMeta
|
const mockedDocumentMeta = templateMeta
|
||||||
? {
|
? {
|
||||||
typedSignatureEnabled: false,
|
|
||||||
...templateMeta,
|
...templateMeta,
|
||||||
signingOrder: templateMeta.signingOrder || DocumentSigningOrder.SEQUENTIAL,
|
signingOrder: templateMeta.signingOrder || DocumentSigningOrder.SEQUENTIAL,
|
||||||
documentId: 0,
|
documentId: 0,
|
||||||
@@ -89,7 +88,10 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
|
|||||||
|
|
||||||
<div className="flex flex-row justify-between truncate">
|
<div className="flex flex-row justify-between truncate">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={template.title}>
|
<h1
|
||||||
|
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
|
||||||
|
title={template.title}
|
||||||
|
>
|
||||||
{template.title}
|
{template.title}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
@@ -155,7 +157,7 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-muted-foreground mt-2 px-4 text-sm ">
|
<p className="text-muted-foreground mt-2 px-4 text-sm">
|
||||||
<Trans>Manage and view template</Trans>
|
<Trans>Manage and view template</Trans>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -209,11 +209,19 @@ 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)`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
{signature.Signature?.signatureImageAsBase64 && (
|
||||||
src={`${signature.Signature?.signatureImageAsBase64}`}
|
<img
|
||||||
alt="Signature"
|
src={`${signature.Signature?.signatureImageAsBase64}`}
|
||||||
className="max-h-12 max-w-full"
|
alt="Signature"
|
||||||
/>
|
className="max-h-12 max-w-full"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{signature.Signature?.typedSignature && (
|
||||||
|
<p className="font-signature text-center text-sm">
|
||||||
|
{signature.Signature?.typedSignature}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
|||||||
|
|
||||||
import { DocumentAuthProvider } from '~/app/(signing)/sign/[token]/document-auth-provider';
|
import { DocumentAuthProvider } from '~/app/(signing)/sign/[token]/document-auth-provider';
|
||||||
import { SigningProvider } from '~/app/(signing)/sign/[token]/provider';
|
import { SigningProvider } from '~/app/(signing)/sign/[token]/provider';
|
||||||
import { truncateTitle } from '~/helpers/truncate-title';
|
|
||||||
|
|
||||||
import { DirectTemplatePageView } from './direct-template';
|
import { DirectTemplatePageView } from './direct-template';
|
||||||
import { DirectTemplateAuthPageView } from './signing-auth-page';
|
import { DirectTemplateAuthPageView } from './signing-auth-page';
|
||||||
@@ -72,8 +71,11 @@ export default async function TemplatesDirectPage({ params }: TemplatesDirectPag
|
|||||||
user={user}
|
user={user}
|
||||||
>
|
>
|
||||||
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
|
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
|
||||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={template.title}>
|
<h1
|
||||||
{truncateTitle(template.title)}
|
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
|
||||||
|
title={template.title}
|
||||||
|
>
|
||||||
|
{template.title}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<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">
|
||||||
|
|||||||
@@ -102,9 +102,9 @@ export const SignDirectTemplateForm = ({
|
|||||||
created: new Date(),
|
created: new Date(),
|
||||||
recipientId: 1,
|
recipientId: 1,
|
||||||
fieldId: 1,
|
fieldId: 1,
|
||||||
signatureImageAsBase64: value.value,
|
signatureImageAsBase64: value.value.startsWith('data:') ? value.value : null,
|
||||||
typedSignature: null,
|
typedSignature: value.value.startsWith('data:') ? null : value.value,
|
||||||
};
|
} satisfies Signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.type === FieldType.DATE) {
|
if (field.type === FieldType.DATE) {
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ import { SigningCard3D } from '@documenso/ui/components/signing-card';
|
|||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
import { Badge } from '@documenso/ui/primitives/badge';
|
import { Badge } from '@documenso/ui/primitives/badge';
|
||||||
|
|
||||||
import { truncateTitle } from '~/helpers/truncate-title';
|
|
||||||
|
|
||||||
import { SigningAuthPageView } from '../signing-auth-page';
|
import { SigningAuthPageView } from '../signing-auth-page';
|
||||||
import { ClaimAccount } from './claim-account';
|
import { ClaimAccount } from './claim-account';
|
||||||
import { DocumentPreviewButton } from './document-preview-button';
|
import { DocumentPreviewButton } from './document-preview-button';
|
||||||
@@ -61,8 +59,6 @@ export default async function CompletedSigningPage({
|
|||||||
return notFound();
|
return notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
const truncatedTitle = truncateTitle(document.title);
|
|
||||||
|
|
||||||
const { documentData } = document;
|
const { documentData } = document;
|
||||||
|
|
||||||
const [fields, recipient] = await Promise.all([
|
const [fields, recipient] = await Promise.all([
|
||||||
@@ -118,7 +114,9 @@ export default async function CompletedSigningPage({
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Badge variant="neutral" size="default" className="mb-6 rounded-xl border bg-transparent">
|
<Badge variant="neutral" size="default" className="mb-6 rounded-xl border bg-transparent">
|
||||||
{truncatedTitle}
|
<span className="block max-w-[10rem] truncate font-medium hover:underline md:max-w-[20rem]">
|
||||||
|
{document.title}
|
||||||
|
</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
{/* Card with recipient */}
|
{/* Card with recipient */}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { createContext, useContext, useState } from 'react';
|
import { createContext, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
export type SigningContextValue = {
|
export type SigningContextValue = {
|
||||||
fullName: string;
|
fullName: string;
|
||||||
@@ -44,6 +44,12 @@ export const SigningProvider = ({
|
|||||||
const [email, setEmail] = useState(initialEmail || '');
|
const [email, setEmail] = useState(initialEmail || '');
|
||||||
const [signature, setSignature] = useState(initialSignature || null);
|
const [signature, setSignature] = useState(initialSignature || null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (initialSignature) {
|
||||||
|
setSignature(initialSignature);
|
||||||
|
}
|
||||||
|
}, [initialSignature]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SigningContext.Provider
|
<SigningContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
} from '@documenso/ui/primitives/dialog';
|
} from '@documenso/ui/primitives/dialog';
|
||||||
|
|
||||||
import { SigningDisclosure } from '~/components/general/signing-disclosure';
|
import { SigningDisclosure } from '~/components/general/signing-disclosure';
|
||||||
import { truncateTitle } from '~/helpers/truncate-title';
|
|
||||||
|
|
||||||
export type SignDialogProps = {
|
export type SignDialogProps = {
|
||||||
isSubmitting: boolean;
|
isSubmitting: boolean;
|
||||||
@@ -36,7 +35,7 @@ export const SignDialog = ({
|
|||||||
disabled = false,
|
disabled = false,
|
||||||
}: SignDialogProps) => {
|
}: SignDialogProps) => {
|
||||||
const [showDialog, setShowDialog] = useState(false);
|
const [showDialog, setShowDialog] = useState(false);
|
||||||
const truncatedTitle = truncateTitle(documentTitle);
|
|
||||||
const isComplete = fields.every((field) => field.inserted);
|
const isComplete = fields.every((field) => field.inserted);
|
||||||
|
|
||||||
const handleOpenChange = (open: boolean) => {
|
const handleOpenChange = (open: boolean) => {
|
||||||
@@ -75,7 +74,13 @@ export const SignDialog = ({
|
|||||||
{role === RecipientRole.VIEWER && (
|
{role === RecipientRole.VIEWER && (
|
||||||
<span>
|
<span>
|
||||||
<Trans>
|
<Trans>
|
||||||
You are about to complete viewing "{truncatedTitle}".
|
<span className="inline-flex flex-wrap">
|
||||||
|
You are about to complete viewing "
|
||||||
|
<span className="inline-block max-w-[11rem] truncate align-baseline">
|
||||||
|
{documentTitle}
|
||||||
|
</span>
|
||||||
|
".
|
||||||
|
</span>
|
||||||
<br /> Are you sure?
|
<br /> Are you sure?
|
||||||
</Trans>
|
</Trans>
|
||||||
</span>
|
</span>
|
||||||
@@ -83,7 +88,13 @@ export const SignDialog = ({
|
|||||||
{role === RecipientRole.SIGNER && (
|
{role === RecipientRole.SIGNER && (
|
||||||
<span>
|
<span>
|
||||||
<Trans>
|
<Trans>
|
||||||
You are about to complete signing "{truncatedTitle}".
|
<span className="inline-flex flex-wrap">
|
||||||
|
You are about to complete signing "
|
||||||
|
<span className="inline-block max-w-[11rem] truncate align-baseline">
|
||||||
|
{documentTitle}
|
||||||
|
</span>
|
||||||
|
".
|
||||||
|
</span>
|
||||||
<br /> Are you sure?
|
<br /> Are you sure?
|
||||||
</Trans>
|
</Trans>
|
||||||
</span>
|
</span>
|
||||||
@@ -91,7 +102,13 @@ export const SignDialog = ({
|
|||||||
{role === RecipientRole.APPROVER && (
|
{role === RecipientRole.APPROVER && (
|
||||||
<span>
|
<span>
|
||||||
<Trans>
|
<Trans>
|
||||||
You are about to complete approving "{truncatedTitle}".
|
<span className="inline-flex flex-wrap">
|
||||||
|
You are about to complete approving{' '}
|
||||||
|
<span className="inline-block max-w-[11rem] truncate align-baseline">
|
||||||
|
"{documentTitle}"
|
||||||
|
</span>
|
||||||
|
.
|
||||||
|
</span>
|
||||||
<br /> Are you sure?
|
<br /> Are you sure?
|
||||||
</Trans>
|
</Trans>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo, useState, useTransition } from 'react';
|
import { useLayoutEffect, useMemo, useRef, useState, useTransition } from 'react';
|
||||||
|
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
@@ -51,6 +51,10 @@ export const SignatureField = ({
|
|||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const signatureRef = useRef<HTMLParagraphElement>(null);
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [fontSize, setFontSize] = useState(2);
|
||||||
|
|
||||||
const { signature: providedSignature, setSignature: setProvidedSignature } =
|
const { signature: providedSignature, setSignature: setProvidedSignature } =
|
||||||
useRequiredSigningContext();
|
useRequiredSigningContext();
|
||||||
|
|
||||||
@@ -108,6 +112,7 @@ export const SignatureField = ({
|
|||||||
actionTarget: field.type,
|
actionTarget: field.type,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSign = async (authOptions?: TRecipientActionAuth, signature?: string) => {
|
const onSign = async (authOptions?: TRecipientActionAuth, signature?: string) => {
|
||||||
try {
|
try {
|
||||||
const value = signature || providedSignature;
|
const value = signature || providedSignature;
|
||||||
@@ -117,11 +122,23 @@ export const SignatureField = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isTypedSignature = !value.startsWith('data:image');
|
||||||
|
|
||||||
|
if (isTypedSignature && !typedSignatureEnabled) {
|
||||||
|
toast({
|
||||||
|
title: _(msg`Error`),
|
||||||
|
description: _(msg`Typed signatures are not allowed. Please draw your signature.`),
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const payload: TSignFieldWithTokenMutationSchema = {
|
const payload: TSignFieldWithTokenMutationSchema = {
|
||||||
token: recipient.token,
|
token: recipient.token,
|
||||||
fieldId: field.id,
|
fieldId: field.id,
|
||||||
value,
|
value,
|
||||||
isBase64: true,
|
isBase64: !isTypedSignature,
|
||||||
authOptions,
|
authOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -176,6 +193,41 @@ export const SignatureField = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (!signatureRef.current || !containerRef.current || !signature?.typedSignature) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const adjustTextSize = () => {
|
||||||
|
const container = containerRef.current;
|
||||||
|
const text = signatureRef.current;
|
||||||
|
|
||||||
|
if (!container || !text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = 2;
|
||||||
|
text.style.fontSize = `${size}rem`;
|
||||||
|
|
||||||
|
while (
|
||||||
|
(text.scrollWidth > container.clientWidth || text.scrollHeight > container.clientHeight) &&
|
||||||
|
size > 0.8
|
||||||
|
) {
|
||||||
|
size -= 0.1;
|
||||||
|
text.style.fontSize = `${size}rem`;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFontSize(size);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver(adjustTextSize);
|
||||||
|
resizeObserver.observe(containerRef.current);
|
||||||
|
|
||||||
|
adjustTextSize();
|
||||||
|
|
||||||
|
return () => resizeObserver.disconnect();
|
||||||
|
}, [signature?.typedSignature]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SigningFieldContainer
|
<SigningFieldContainer
|
||||||
field={field}
|
field={field}
|
||||||
@@ -205,10 +257,15 @@ export const SignatureField = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{state === 'signed-text' && (
|
{state === 'signed-text' && (
|
||||||
<p className="font-signature text-muted-foreground dark:text-background text-lg duration-200 sm:text-xl md:text-2xl lg:text-3xl">
|
<div ref={containerRef} className="flex h-full w-full items-center justify-center p-2">
|
||||||
{/* This optional chaining is intentional, we don't want to move the check into the condition above */}
|
<p
|
||||||
{signature?.typedSignature}
|
ref={signatureRef}
|
||||||
</p>
|
className="font-signature text-muted-foreground dark:text-background w-full overflow-hidden break-all text-center leading-tight duration-200"
|
||||||
|
style={{ fontSize: `${fontSize}rem` }}
|
||||||
|
>
|
||||||
|
{signature?.typedSignature}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Dialog open={showSignatureModal} onOpenChange={setShowSignatureModal}>
|
<Dialog open={showSignatureModal} onOpenChange={setShowSignatureModal}>
|
||||||
|
|||||||
@@ -55,7 +55,10 @@ export const SigningPageView = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-screen-xl">
|
<div className="mx-auto w-full max-w-screen-xl">
|
||||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={document.title}>
|
<h1
|
||||||
|
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
|
||||||
|
title={document.title}
|
||||||
|
>
|
||||||
{document.title}
|
{document.title}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
|
|||||||
@@ -52,13 +52,7 @@ export default async function TeamsSettingsPage({ params }: TeamsSettingsPagePro
|
|||||||
|
|
||||||
<AvatarImageForm className="mb-8" team={team} user={session.user} />
|
<AvatarImageForm className="mb-8" team={team} user={session.user} />
|
||||||
|
|
||||||
<UpdateTeamForm
|
<UpdateTeamForm teamId={team.id} teamName={team.name} teamUrl={team.url} />
|
||||||
teamId={team.id}
|
|
||||||
teamName={team.name}
|
|
||||||
teamUrl={team.url}
|
|
||||||
documentVisibility={team.teamGlobalSettings?.documentVisibility}
|
|
||||||
includeSenderDetails={team.teamGlobalSettings?.includeSenderDetails}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<section className="mt-6 space-y-6">
|
<section className="mt-6 space-y-6">
|
||||||
{(team.teamEmail || team.emailVerification) && (
|
{(team.teamEmail || team.emailVerification) && (
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ const ZTeamDocumentPreferencesFormSchema = z.object({
|
|||||||
documentVisibility: z.nativeEnum(DocumentVisibility),
|
documentVisibility: z.nativeEnum(DocumentVisibility),
|
||||||
documentLanguage: z.enum(SUPPORTED_LANGUAGE_CODES),
|
documentLanguage: z.enum(SUPPORTED_LANGUAGE_CODES),
|
||||||
includeSenderDetails: z.boolean(),
|
includeSenderDetails: z.boolean(),
|
||||||
|
typedSignatureEnabled: z.boolean(),
|
||||||
|
includeSigningCertificate: z.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
type TTeamDocumentPreferencesFormSchema = z.infer<typeof ZTeamDocumentPreferencesFormSchema>;
|
type TTeamDocumentPreferencesFormSchema = z.infer<typeof ZTeamDocumentPreferencesFormSchema>;
|
||||||
@@ -68,6 +70,8 @@ export const TeamDocumentPreferencesForm = ({
|
|||||||
? settings?.documentLanguage
|
? settings?.documentLanguage
|
||||||
: 'en',
|
: 'en',
|
||||||
includeSenderDetails: settings?.includeSenderDetails ?? false,
|
includeSenderDetails: settings?.includeSenderDetails ?? false,
|
||||||
|
typedSignatureEnabled: settings?.typedSignatureEnabled ?? true,
|
||||||
|
includeSigningCertificate: settings?.includeSigningCertificate ?? true,
|
||||||
},
|
},
|
||||||
resolver: zodResolver(ZTeamDocumentPreferencesFormSchema),
|
resolver: zodResolver(ZTeamDocumentPreferencesFormSchema),
|
||||||
});
|
});
|
||||||
@@ -76,7 +80,13 @@ export const TeamDocumentPreferencesForm = ({
|
|||||||
|
|
||||||
const onSubmit = async (data: TTeamDocumentPreferencesFormSchema) => {
|
const onSubmit = async (data: TTeamDocumentPreferencesFormSchema) => {
|
||||||
try {
|
try {
|
||||||
const { documentVisibility, documentLanguage, includeSenderDetails } = data;
|
const {
|
||||||
|
documentVisibility,
|
||||||
|
documentLanguage,
|
||||||
|
includeSenderDetails,
|
||||||
|
includeSigningCertificate,
|
||||||
|
typedSignatureEnabled,
|
||||||
|
} = data;
|
||||||
|
|
||||||
await updateTeamDocumentPreferences({
|
await updateTeamDocumentPreferences({
|
||||||
teamId: team.id,
|
teamId: team.id,
|
||||||
@@ -84,6 +94,8 @@ export const TeamDocumentPreferencesForm = ({
|
|||||||
documentVisibility,
|
documentVisibility,
|
||||||
documentLanguage,
|
documentLanguage,
|
||||||
includeSenderDetails,
|
includeSenderDetails,
|
||||||
|
typedSignatureEnabled,
|
||||||
|
includeSigningCertificate,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -105,7 +117,7 @@ export const TeamDocumentPreferencesForm = ({
|
|||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<fieldset
|
<fieldset
|
||||||
className="flex h-full max-w-xl flex-col gap-y-4"
|
className="flex h-full max-w-xl flex-col gap-y-6"
|
||||||
disabled={form.formState.isSubmitting}
|
disabled={form.formState.isSubmitting}
|
||||||
>
|
>
|
||||||
<FormField
|
<FormField
|
||||||
@@ -227,6 +239,67 @@ export const TeamDocumentPreferencesForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="typedSignatureEnabled"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormLabel>
|
||||||
|
<Trans>Enable Typed Signature</Trans>
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<FormControl className="block">
|
||||||
|
<Switch
|
||||||
|
ref={field.ref}
|
||||||
|
name={field.name}
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormDescription>
|
||||||
|
<Trans>
|
||||||
|
Controls whether the recipients can sign the documents using a typed signature.
|
||||||
|
Enable or disable the typed signature globally.
|
||||||
|
</Trans>
|
||||||
|
</FormDescription>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="includeSigningCertificate"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex-1">
|
||||||
|
<FormLabel>
|
||||||
|
<Trans>Include the Signing Certificate in the Document</Trans>
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<FormControl className="block">
|
||||||
|
<Switch
|
||||||
|
ref={field.ref}
|
||||||
|
name={field.name}
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormDescription>
|
||||||
|
<Trans>
|
||||||
|
Controls whether the signing certificate will be included in the document when
|
||||||
|
it is downloaded. The signing certificate can still be downloaded from the logs
|
||||||
|
page separately.
|
||||||
|
</Trans>
|
||||||
|
</FormDescription>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="flex flex-row justify-end space-x-4">
|
<div className="flex flex-row justify-end space-x-4">
|
||||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||||
<Trans>Save</Trans>
|
<Trans>Save</Trans>
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import { ZCssVarsSchema } from './css-vars';
|
||||||
|
|
||||||
export const ZBaseEmbedDataSchema = z.object({
|
export const ZBaseEmbedDataSchema = z.object({
|
||||||
|
darkModeDisabled: z.boolean().optional().default(false),
|
||||||
css: z
|
css: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.transform((value) => value || undefined),
|
.transform((value) => value || undefined),
|
||||||
|
cssVars: ZCssVarsSchema.optional().default({}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export type EmbedDocumentCompletedPageProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const EmbedDocumentCompleted = ({ name, signature }: EmbedDocumentCompletedPageProps) => {
|
export const EmbedDocumentCompleted = ({ name, signature }: EmbedDocumentCompletedPageProps) => {
|
||||||
|
console.log({ signature });
|
||||||
return (
|
return (
|
||||||
<div className="relative mx-auto flex min-h-[100dvh] max-w-screen-lg flex-col items-center justify-center p-6">
|
<div className="relative mx-auto flex min-h-[100dvh] max-w-screen-lg flex-col items-center justify-center p-6">
|
||||||
<h3 className="text-foreground text-2xl font-semibold">
|
<h3 className="text-foreground text-2xl font-semibold">
|
||||||
|
|||||||
59
apps/web/src/app/embed/css-vars.ts
Normal file
59
apps/web/src/app/embed/css-vars.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { colord } from 'colord';
|
||||||
|
import { toSnakeCase } from 'remeda';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const ZCssVarsSchema = z
|
||||||
|
.object({
|
||||||
|
background: z.string().optional().describe('Base background color'),
|
||||||
|
foreground: z.string().optional().describe('Base text color'),
|
||||||
|
muted: z.string().optional().describe('Muted/subtle background color'),
|
||||||
|
mutedForeground: z.string().optional().describe('Muted/subtle text color'),
|
||||||
|
popover: z.string().optional().describe('Popover/dropdown background color'),
|
||||||
|
popoverForeground: z.string().optional().describe('Popover/dropdown text color'),
|
||||||
|
card: z.string().optional().describe('Card background color'),
|
||||||
|
cardBorder: z.string().optional().describe('Card border color'),
|
||||||
|
cardBorderTint: z.string().optional().describe('Card border tint/highlight color'),
|
||||||
|
cardForeground: z.string().optional().describe('Card text color'),
|
||||||
|
fieldCard: z.string().optional().describe('Field card background color'),
|
||||||
|
fieldCardBorder: z.string().optional().describe('Field card border color'),
|
||||||
|
fieldCardForeground: z.string().optional().describe('Field card text color'),
|
||||||
|
widget: z.string().optional().describe('Widget background color'),
|
||||||
|
widgetForeground: z.string().optional().describe('Widget text color'),
|
||||||
|
border: z.string().optional().describe('Default border color'),
|
||||||
|
input: z.string().optional().describe('Input field border color'),
|
||||||
|
primary: z.string().optional().describe('Primary action/button color'),
|
||||||
|
primaryForeground: z.string().optional().describe('Primary action/button text color'),
|
||||||
|
secondary: z.string().optional().describe('Secondary action/button color'),
|
||||||
|
secondaryForeground: z.string().optional().describe('Secondary action/button text color'),
|
||||||
|
accent: z.string().optional().describe('Accent/highlight color'),
|
||||||
|
accentForeground: z.string().optional().describe('Accent/highlight text color'),
|
||||||
|
destructive: z.string().optional().describe('Destructive/danger action color'),
|
||||||
|
destructiveForeground: z.string().optional().describe('Destructive/danger text color'),
|
||||||
|
ring: z.string().optional().describe('Focus ring color'),
|
||||||
|
radius: z.string().optional().describe('Border radius size in REM units'),
|
||||||
|
warning: z.string().optional().describe('Warning/alert color'),
|
||||||
|
})
|
||||||
|
.describe('Custom CSS variables for theming');
|
||||||
|
|
||||||
|
export type TCssVarsSchema = z.infer<typeof ZCssVarsSchema>;
|
||||||
|
|
||||||
|
export const toNativeCssVars = (vars: TCssVarsSchema) => {
|
||||||
|
const cssVars: Record<string, string> = {};
|
||||||
|
|
||||||
|
const { radius, ...colorVars } = vars;
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(colorVars)) {
|
||||||
|
if (value) {
|
||||||
|
const color = colord(value);
|
||||||
|
const { h, s, l } = color.toHsl();
|
||||||
|
|
||||||
|
cssVars[`--${toSnakeCase(key)}`] = `${h} ${s} ${l}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (radius) {
|
||||||
|
cssVars[`--radius`] = `${radius}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cssVars;
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-form
|
|||||||
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
|
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
|
||||||
import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones';
|
import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones';
|
||||||
import { validateFieldsInserted } from '@documenso/lib/utils/fields';
|
import { validateFieldsInserted } from '@documenso/lib/utils/fields';
|
||||||
import type { DocumentMeta, Recipient, TemplateMeta } from '@documenso/prisma/client';
|
import type { DocumentMeta, Recipient, Signature, TemplateMeta } from '@documenso/prisma/client';
|
||||||
import { type DocumentData, type Field, FieldType } from '@documenso/prisma/client';
|
import { type DocumentData, type Field, FieldType } from '@documenso/prisma/client';
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import type {
|
import type {
|
||||||
@@ -38,6 +38,7 @@ import { Logo } from '~/components/branding/logo';
|
|||||||
import { EmbedClientLoading } from '../../client-loading';
|
import { EmbedClientLoading } from '../../client-loading';
|
||||||
import { EmbedDocumentCompleted } from '../../completed';
|
import { EmbedDocumentCompleted } from '../../completed';
|
||||||
import { EmbedDocumentFields } from '../../document-fields';
|
import { EmbedDocumentFields } from '../../document-fields';
|
||||||
|
import { injectCss } from '../../util';
|
||||||
import { ZDirectTemplateEmbedDataSchema } from './schema';
|
import { ZDirectTemplateEmbedDataSchema } from './schema';
|
||||||
|
|
||||||
export type EmbedDirectTemplateClientPageProps = {
|
export type EmbedDirectTemplateClientPageProps = {
|
||||||
@@ -47,6 +48,8 @@ export type EmbedDirectTemplateClientPageProps = {
|
|||||||
recipient: Recipient;
|
recipient: Recipient;
|
||||||
fields: Field[];
|
fields: Field[];
|
||||||
metadata?: DocumentMeta | TemplateMeta | null;
|
metadata?: DocumentMeta | TemplateMeta | null;
|
||||||
|
hidePoweredBy?: boolean;
|
||||||
|
isPlatformOrEnterprise?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EmbedDirectTemplateClientPage = ({
|
export const EmbedDirectTemplateClientPage = ({
|
||||||
@@ -56,6 +59,8 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
recipient,
|
recipient,
|
||||||
fields,
|
fields,
|
||||||
metadata,
|
metadata,
|
||||||
|
hidePoweredBy = false,
|
||||||
|
isPlatformOrEnterprise = false,
|
||||||
}: EmbedDirectTemplateClientPageProps) => {
|
}: EmbedDirectTemplateClientPageProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@@ -108,9 +113,9 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
created: new Date(),
|
created: new Date(),
|
||||||
recipientId: 1,
|
recipientId: 1,
|
||||||
fieldId: 1,
|
fieldId: 1,
|
||||||
signatureImageAsBase64: payload.value,
|
signatureImageAsBase64: payload.value.startsWith('data:') ? payload.value : null,
|
||||||
typedSignature: null,
|
typedSignature: payload.value.startsWith('data:') ? null : payload.value,
|
||||||
};
|
} satisfies Signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.type === FieldType.DATE) {
|
if (field.type === FieldType.DATE) {
|
||||||
@@ -249,7 +254,7 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const hash = window.location.hash.slice(1);
|
const hash = window.location.hash.slice(1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -264,6 +269,17 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
setFullName(data.name);
|
setFullName(data.name);
|
||||||
setIsNameLocked(!!data.lockName);
|
setIsNameLocked(!!data.lockName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.darkModeDisabled) {
|
||||||
|
document.documentElement.classList.add('dark-mode-disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPlatformOrEnterprise) {
|
||||||
|
injectCss({
|
||||||
|
css: data.css,
|
||||||
|
cssVars: data.cssVars,
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
@@ -296,8 +312,8 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
fieldId: 1,
|
fieldId: 1,
|
||||||
recipientId: 1,
|
recipientId: 1,
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
typedSignature: null,
|
signatureImageAsBase64: signature?.startsWith('data:') ? signature : null,
|
||||||
signatureImageAsBase64: signature,
|
typedSignature: signature?.startsWith('data:') ? null : signature,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -452,10 +468,12 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-primary text-primary-foreground fixed bottom-0 left-0 z-40 rounded-tr px-2 py-1 text-xs font-medium opacity-60 hover:opacity-100">
|
{!hidePoweredBy && (
|
||||||
<span>Powered by</span>
|
<div className="bg-primary text-primary-foreground fixed bottom-0 left-0 z-40 rounded-tr px-2 py-1 text-xs font-medium opacity-60 hover:opacity-100">
|
||||||
<Logo className="ml-2 inline-block h-[14px]" />
|
<span>Powered by</span>
|
||||||
</div>
|
<Logo className="ml-2 inline-block h-[14px]" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import { notFound } from 'next/navigation';
|
|||||||
|
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
|
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
||||||
|
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
|
||||||
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||||
import { getServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
import { getServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||||
|
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
|
||||||
import { getTemplateByDirectLinkToken } from '@documenso/lib/server-only/template/get-template-by-direct-link-token';
|
import { getTemplateByDirectLinkToken } from '@documenso/lib/server-only/template/get-template-by-direct-link-token';
|
||||||
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
|
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
|
||||||
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
||||||
@@ -51,6 +54,14 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem
|
|||||||
documentAuth: template.authOptions,
|
documentAuth: template.authOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [isPlatformDocument, isEnterpriseDocument] = await Promise.all([
|
||||||
|
isDocumentPlatform(template),
|
||||||
|
isUserEnterprise({
|
||||||
|
userId: template.userId,
|
||||||
|
teamId: template.teamId ?? undefined,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
const isAccessAuthValid = match(derivedRecipientAccessAuth)
|
const isAccessAuthValid = match(derivedRecipientAccessAuth)
|
||||||
.with(DocumentAccessAuth.ACCOUNT, () => user !== null)
|
.with(DocumentAccessAuth.ACCOUNT, () => user !== null)
|
||||||
.with(null, () => true)
|
.with(null, () => true)
|
||||||
@@ -72,6 +83,12 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem
|
|||||||
|
|
||||||
const fields = template.Field.filter((field) => field.recipientId === directTemplateRecipientId);
|
const fields = template.Field.filter((field) => field.recipientId === directTemplateRecipientId);
|
||||||
|
|
||||||
|
const team = template.teamId
|
||||||
|
? await getTeamById({ teamId: template.teamId, userId: template.userId }).catch(() => null)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const hidePoweredBy = team?.teamGlobalSettings?.brandingHidePoweredBy ?? false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SigningProvider email={user?.email} fullName={user?.name} signature={user?.signature}>
|
<SigningProvider email={user?.email} fullName={user?.name} signature={user?.signature}>
|
||||||
<DocumentAuthProvider
|
<DocumentAuthProvider
|
||||||
@@ -86,6 +103,8 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem
|
|||||||
recipient={recipient}
|
recipient={recipient}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
metadata={template.templateMeta}
|
metadata={template.templateMeta}
|
||||||
|
hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy}
|
||||||
|
isPlatformOrEnterprise={isPlatformDocument || isEnterpriseDocument}
|
||||||
/>
|
/>
|
||||||
</DocumentAuthProvider>
|
</DocumentAuthProvider>
|
||||||
</SigningProvider>
|
</SigningProvider>
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export const EmbedDocumentFields = ({
|
|||||||
recipient={recipient}
|
recipient={recipient}
|
||||||
onSignField={onSignField}
|
onSignField={onSignField}
|
||||||
onUnsignField={onUnsignField}
|
onUnsignField={onUnsignField}
|
||||||
|
typedSignatureEnabled={metadata?.typedSignatureEnabled}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
.with(FieldType.INITIALS, () => (
|
.with(FieldType.INITIALS, () => (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useLayoutEffect, 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';
|
||||||
@@ -28,6 +28,7 @@ import { Logo } from '~/components/branding/logo';
|
|||||||
import { EmbedClientLoading } from '../../client-loading';
|
import { EmbedClientLoading } from '../../client-loading';
|
||||||
import { EmbedDocumentCompleted } from '../../completed';
|
import { EmbedDocumentCompleted } from '../../completed';
|
||||||
import { EmbedDocumentFields } from '../../document-fields';
|
import { EmbedDocumentFields } from '../../document-fields';
|
||||||
|
import { injectCss } from '../../util';
|
||||||
import { ZSignDocumentEmbedDataSchema } from './schema';
|
import { ZSignDocumentEmbedDataSchema } from './schema';
|
||||||
|
|
||||||
export type EmbedSignDocumentClientPageProps = {
|
export type EmbedSignDocumentClientPageProps = {
|
||||||
@@ -38,6 +39,8 @@ export type EmbedSignDocumentClientPageProps = {
|
|||||||
fields: Field[];
|
fields: Field[];
|
||||||
metadata?: DocumentMeta | TemplateMeta | null;
|
metadata?: DocumentMeta | TemplateMeta | null;
|
||||||
isCompleted?: boolean;
|
isCompleted?: boolean;
|
||||||
|
hidePoweredBy?: boolean;
|
||||||
|
isPlatformOrEnterprise?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EmbedSignDocumentClientPage = ({
|
export const EmbedSignDocumentClientPage = ({
|
||||||
@@ -48,6 +51,8 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
fields,
|
fields,
|
||||||
metadata,
|
metadata,
|
||||||
isCompleted,
|
isCompleted,
|
||||||
|
hidePoweredBy = false,
|
||||||
|
isPlatformOrEnterprise = false,
|
||||||
}: EmbedSignDocumentClientPageProps) => {
|
}: EmbedSignDocumentClientPageProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@@ -131,7 +136,7 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const hash = window.location.hash.slice(1);
|
const hash = window.location.hash.slice(1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -144,6 +149,17 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
// Since a recipient can be provided a name we can lock it without requiring
|
// Since a recipient can be provided a name we can lock it without requiring
|
||||||
// a to be provided by the parent application, unlike direct templates.
|
// a to be provided by the parent application, unlike direct templates.
|
||||||
setIsNameLocked(!!data.lockName);
|
setIsNameLocked(!!data.lockName);
|
||||||
|
|
||||||
|
if (data.darkModeDisabled) {
|
||||||
|
document.documentElement.classList.add('dark-mode-disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPlatformOrEnterprise) {
|
||||||
|
injectCss({
|
||||||
|
css: data.css,
|
||||||
|
cssVars: data.cssVars,
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
@@ -176,8 +192,8 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
fieldId: 1,
|
fieldId: 1,
|
||||||
recipientId: 1,
|
recipientId: 1,
|
||||||
created: new Date(),
|
created: new Date(),
|
||||||
typedSignature: null,
|
signatureImageAsBase64: signature?.startsWith('data:') ? signature : null,
|
||||||
signatureImageAsBase64: signature,
|
typedSignature: signature?.startsWith('data:') ? null : signature,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -202,7 +218,7 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
className="group/document-widget fixed bottom-8 left-0 z-50 h-fit w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0"
|
className="group/document-widget fixed bottom-8 left-0 z-50 h-fit w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0"
|
||||||
data-expanded={isExpanded || undefined}
|
data-expanded={isExpanded || undefined}
|
||||||
>
|
>
|
||||||
<div className="border-border bg-widget flex w-full flex-col rounded-xl border px-4 py-4 md:py-6">
|
<div className="border-border bg-widget flex w-full flex-col rounded-xl border px-4 py-4 md:py-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between gap-x-2">
|
<div className="flex items-center justify-between gap-x-2">
|
||||||
@@ -325,10 +341,12 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
<EmbedDocumentFields recipient={recipient} fields={fields} metadata={metadata} />
|
<EmbedDocumentFields recipient={recipient} fields={fields} metadata={metadata} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-primary text-primary-foreground fixed bottom-0 left-0 z-40 rounded-tr px-2 py-1 text-xs font-medium opacity-60 hover:opacity-100">
|
{!hidePoweredBy && (
|
||||||
<span>Powered by</span>
|
<div className="bg-primary text-primary-foreground fixed bottom-0 left-0 z-40 rounded-tr px-2 py-1 text-xs font-medium opacity-60 hover:opacity-100">
|
||||||
<Logo className="ml-2 inline-block h-[14px]" />
|
<span>Powered by</span>
|
||||||
</div>
|
<Logo className="ml-2 inline-block h-[14px]" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import { notFound } from 'next/navigation';
|
|||||||
|
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
|
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
||||||
|
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
|
||||||
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||||
import { getServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
import { getServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||||
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
|
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
|
||||||
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
|
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
|
||||||
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
|
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
|
||||||
|
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
|
||||||
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
|
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
|
||||||
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
|
||||||
import { DocumentStatus } from '@documenso/prisma/client';
|
import { DocumentStatus } from '@documenso/prisma/client';
|
||||||
@@ -56,6 +59,14 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen
|
|||||||
return <EmbedPaywall />;
|
return <EmbedPaywall />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [isPlatformDocument, isEnterpriseDocument] = await Promise.all([
|
||||||
|
isDocumentPlatform(document),
|
||||||
|
isUserEnterprise({
|
||||||
|
userId: document.userId,
|
||||||
|
teamId: document.teamId ?? undefined,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({
|
const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({
|
||||||
documentAuth: document.authOptions,
|
documentAuth: document.authOptions,
|
||||||
});
|
});
|
||||||
@@ -74,6 +85,12 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const team = document.teamId
|
||||||
|
? await getTeamById({ teamId: document.teamId, userId: document.userId }).catch(() => null)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const hidePoweredBy = team?.teamGlobalSettings?.brandingHidePoweredBy ?? false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SigningProvider
|
<SigningProvider
|
||||||
email={recipient.email}
|
email={recipient.email}
|
||||||
@@ -93,6 +110,8 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen
|
|||||||
fields={fields}
|
fields={fields}
|
||||||
metadata={document.documentMeta}
|
metadata={document.documentMeta}
|
||||||
isCompleted={document.status === DocumentStatus.COMPLETED}
|
isCompleted={document.status === DocumentStatus.COMPLETED}
|
||||||
|
hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy}
|
||||||
|
isPlatformOrEnterprise={isPlatformDocument || isEnterpriseDocument}
|
||||||
/>
|
/>
|
||||||
</DocumentAuthProvider>
|
</DocumentAuthProvider>
|
||||||
</SigningProvider>
|
</SigningProvider>
|
||||||
|
|||||||
20
apps/web/src/app/embed/util.ts
Normal file
20
apps/web/src/app/embed/util.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { type TCssVarsSchema, toNativeCssVars } from './css-vars';
|
||||||
|
|
||||||
|
export const injectCss = (options: { css?: string; cssVars?: TCssVarsSchema }) => {
|
||||||
|
const { css, cssVars } = options;
|
||||||
|
|
||||||
|
if (css) {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.innerHTML = css;
|
||||||
|
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cssVars) {
|
||||||
|
const nativeVars = toNativeCssVars(cssVars);
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(nativeVars)) {
|
||||||
|
document.documentElement.style.setProperty(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -6,22 +6,14 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|||||||
import { Trans, msg } from '@lingui/macro';
|
import { Trans, msg } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { useSession } from 'next-auth/react';
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { match } from 'ts-pattern';
|
|
||||||
import type { z } from 'zod';
|
import type { z } from 'zod';
|
||||||
|
|
||||||
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||||
import { DocumentVisibility } from '@documenso/prisma/client';
|
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
import { ZUpdateTeamMutationSchema } from '@documenso/trpc/server/team-router/schema';
|
import { ZUpdateTeamMutationSchema } from '@documenso/trpc/server/team-router/schema';
|
||||||
import {
|
|
||||||
DocumentVisibilitySelect,
|
|
||||||
DocumentVisibilityTooltip,
|
|
||||||
} from '@documenso/ui/components/document/document-visibility-select';
|
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
import { Checkbox } from '@documenso/ui/primitives/checkbox';
|
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -37,29 +29,17 @@ export type UpdateTeamDialogProps = {
|
|||||||
teamId: number;
|
teamId: number;
|
||||||
teamName: string;
|
teamName: string;
|
||||||
teamUrl: string;
|
teamUrl: string;
|
||||||
documentVisibility?: DocumentVisibility;
|
|
||||||
includeSenderDetails?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const ZUpdateTeamFormSchema = ZUpdateTeamMutationSchema.shape.data.pick({
|
const ZUpdateTeamFormSchema = ZUpdateTeamMutationSchema.shape.data.pick({
|
||||||
name: true,
|
name: true,
|
||||||
url: true,
|
url: true,
|
||||||
documentVisibility: true,
|
|
||||||
includeSenderDetails: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type TUpdateTeamFormSchema = z.infer<typeof ZUpdateTeamFormSchema>;
|
type TUpdateTeamFormSchema = z.infer<typeof ZUpdateTeamFormSchema>;
|
||||||
|
|
||||||
export const UpdateTeamForm = ({
|
export const UpdateTeamForm = ({ teamId, teamName, teamUrl }: UpdateTeamDialogProps) => {
|
||||||
teamId,
|
|
||||||
teamName,
|
|
||||||
teamUrl,
|
|
||||||
documentVisibility,
|
|
||||||
includeSenderDetails,
|
|
||||||
}: UpdateTeamDialogProps) => {
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { data: session } = useSession();
|
|
||||||
const email = session?.user?.email;
|
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
@@ -68,36 +48,17 @@ export const UpdateTeamForm = ({
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: teamName,
|
name: teamName,
|
||||||
url: teamUrl,
|
url: teamUrl,
|
||||||
documentVisibility,
|
|
||||||
includeSenderDetails,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutateAsync: updateTeam } = trpc.team.updateTeam.useMutation();
|
const { mutateAsync: updateTeam } = trpc.team.updateTeam.useMutation();
|
||||||
const includeSenderDetailsCheck = form.watch('includeSenderDetails');
|
|
||||||
|
|
||||||
const mapVisibilityToRole = (visibility: DocumentVisibility): DocumentVisibility =>
|
const onFormSubmit = async ({ name, url }: TUpdateTeamFormSchema) => {
|
||||||
match(visibility)
|
|
||||||
.with(DocumentVisibility.ADMIN, () => DocumentVisibility.ADMIN)
|
|
||||||
.with(DocumentVisibility.MANAGER_AND_ABOVE, () => DocumentVisibility.MANAGER_AND_ABOVE)
|
|
||||||
.otherwise(() => DocumentVisibility.EVERYONE);
|
|
||||||
|
|
||||||
const currentVisibilityRole = mapVisibilityToRole(
|
|
||||||
documentVisibility ?? DocumentVisibility.EVERYONE,
|
|
||||||
);
|
|
||||||
const onFormSubmit = async ({
|
|
||||||
name,
|
|
||||||
url,
|
|
||||||
documentVisibility,
|
|
||||||
includeSenderDetails,
|
|
||||||
}: TUpdateTeamFormSchema) => {
|
|
||||||
try {
|
try {
|
||||||
await updateTeam({
|
await updateTeam({
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
url,
|
url,
|
||||||
documentVisibility,
|
|
||||||
includeSenderDetails,
|
|
||||||
},
|
},
|
||||||
teamId,
|
teamId,
|
||||||
});
|
});
|
||||||
@@ -111,8 +72,6 @@ export const UpdateTeamForm = ({
|
|||||||
form.reset({
|
form.reset({
|
||||||
name,
|
name,
|
||||||
url,
|
url,
|
||||||
documentVisibility,
|
|
||||||
includeSenderDetails,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (url !== teamUrl) {
|
if (url !== teamUrl) {
|
||||||
@@ -186,68 +145,6 @@ export const UpdateTeamForm = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="documentVisibility"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<FormLabel className="mt-4 flex flex-row items-center">
|
|
||||||
<Trans>Default Document Visibility</Trans>
|
|
||||||
<DocumentVisibilityTooltip />
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<DocumentVisibilitySelect
|
|
||||||
currentMemberRole={currentVisibilityRole}
|
|
||||||
isTeamSettings={true}
|
|
||||||
{...field}
|
|
||||||
onValueChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="includeSenderDetails"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem>
|
|
||||||
<div className="mt-6 flex flex-row items-center gap-4">
|
|
||||||
<FormLabel>
|
|
||||||
<Trans>Send on Behalf of Team</Trans>
|
|
||||||
</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Checkbox
|
|
||||||
className="h-5 w-5"
|
|
||||||
checkClassName="text-white"
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{includeSenderDetailsCheck ? (
|
|
||||||
<blockquote className="text-foreground/50 text-xs italic">
|
|
||||||
<Trans>
|
|
||||||
"{email}" on behalf of "{teamName}" has invited you to sign "example
|
|
||||||
document".
|
|
||||||
</Trans>
|
|
||||||
</blockquote>
|
|
||||||
) : (
|
|
||||||
<blockquote className="text-foreground/50 text-xs italic">
|
|
||||||
<Trans>"{teamUrl}" has invited you to sign "example document".</Trans>
|
|
||||||
</blockquote>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-row justify-end space-x-4">
|
<div className="flex flex-row justify-end space-x-4">
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{form.formState.isDirty && (
|
{form.formState.isDirty && (
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ export const DocumentHistorySheet = ({
|
|||||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED },
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED },
|
||||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED },
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED },
|
||||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED },
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED },
|
||||||
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RESTORED },
|
||||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED },
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED },
|
||||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED },
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED },
|
||||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED },
|
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED },
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { HTMLAttributes } from 'react';
|
|||||||
import type { MessageDescriptor } from '@lingui/core';
|
import type { MessageDescriptor } from '@lingui/core';
|
||||||
import { msg } from '@lingui/macro';
|
import { msg } from '@lingui/macro';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import { CheckCircle2, Clock, File } from 'lucide-react';
|
import { CheckCircle2, Clock, File, TrashIcon } from 'lucide-react';
|
||||||
import type { LucideIcon } from 'lucide-react/dist/lucide-react';
|
import type { LucideIcon } from 'lucide-react/dist/lucide-react';
|
||||||
|
|
||||||
import type { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
import type { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||||
@@ -47,6 +47,12 @@ export const FRIENDLY_STATUS_MAP: Record<ExtendedDocumentStatus, FriendlyStatus>
|
|||||||
labelExtended: msg`Document All`,
|
labelExtended: msg`Document All`,
|
||||||
color: 'text-muted-foreground',
|
color: 'text-muted-foreground',
|
||||||
},
|
},
|
||||||
|
BIN: {
|
||||||
|
label: msg`Bin`,
|
||||||
|
labelExtended: msg`Document Bin`,
|
||||||
|
icon: TrashIcon,
|
||||||
|
color: 'text-red-500 dark:text-red-200',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DocumentStatusProps = HTMLAttributes<HTMLSpanElement> & {
|
export type DocumentStatusProps = HTMLAttributes<HTMLSpanElement> & {
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ export const ProfileForm = ({ className, user }: ProfileFormProps) => {
|
|||||||
containerClassName={cn('rounded-lg border bg-background')}
|
containerClassName={cn('rounded-lg border bg-background')}
|
||||||
defaultValue={user.signature ?? undefined}
|
defaultValue={user.signature ?? undefined}
|
||||||
onChange={(v) => onChange(v ?? '')}
|
onChange={(v) => onChange(v ?? '')}
|
||||||
|
allowTypedSignature={true}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|||||||
773
package-lock.json
generated
773
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.8.1-rc.0",
|
"version": "1.8.1-rc.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"build:web": "turbo run build --filter=@documenso/web",
|
"build:web": "turbo run build --filter=@documenso/web",
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"husky": "^9.0.11",
|
"husky": "^9.0.11",
|
||||||
"lint-staged": "^15.2.2",
|
"lint-staged": "^15.2.2",
|
||||||
"playwright": "1.43.0",
|
"playwright": "1.43.0",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^3.3.3",
|
||||||
"rimraf": "^5.0.1",
|
"rimraf": "^5.0.1",
|
||||||
"turbo": "^1.9.3"
|
"turbo": "^1.9.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
|||||||
redirectUrl: body.meta.redirectUrl,
|
redirectUrl: body.meta.redirectUrl,
|
||||||
signingOrder: body.meta.signingOrder,
|
signingOrder: body.meta.signingOrder,
|
||||||
language: body.meta.language,
|
language: body.meta.language,
|
||||||
|
typedSignatureEnabled: body.meta.typedSignatureEnabled,
|
||||||
requestMetadata: extractNextApiRequestMetadata(args.req),
|
requestMetadata: extractNextApiRequestMetadata(args.req),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { z } from 'zod';
|
|||||||
|
|
||||||
import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
|
import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
|
||||||
import { SUPPORTED_LANGUAGE_CODES } from '@documenso/lib/constants/i18n';
|
import { SUPPORTED_LANGUAGE_CODES } from '@documenso/lib/constants/i18n';
|
||||||
import '@documenso/lib/constants/time-zones';
|
|
||||||
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 { ZUrlSchema } from '@documenso/lib/schemas/common';
|
import { ZUrlSchema } from '@documenso/lib/schemas/common';
|
||||||
import {
|
import {
|
||||||
@@ -14,6 +13,7 @@ import {
|
|||||||
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
|
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
|
||||||
import {
|
import {
|
||||||
DocumentDataType,
|
DocumentDataType,
|
||||||
|
DocumentDistributionMethod,
|
||||||
DocumentSigningOrder,
|
DocumentSigningOrder,
|
||||||
FieldType,
|
FieldType,
|
||||||
ReadStatus,
|
ReadStatus,
|
||||||
@@ -132,6 +132,7 @@ export const ZCreateDocumentMutationSchema = z.object({
|
|||||||
redirectUrl: z.string(),
|
redirectUrl: z.string(),
|
||||||
signingOrder: z.nativeEnum(DocumentSigningOrder).optional(),
|
signingOrder: z.nativeEnum(DocumentSigningOrder).optional(),
|
||||||
language: z.enum(SUPPORTED_LANGUAGE_CODES).optional(),
|
language: z.enum(SUPPORTED_LANGUAGE_CODES).optional(),
|
||||||
|
typedSignatureEnabled: z.boolean().optional().default(true),
|
||||||
})
|
})
|
||||||
.partial(),
|
.partial(),
|
||||||
authOptions: z
|
authOptions: z
|
||||||
@@ -226,14 +227,14 @@ export type TCreateDocumentFromTemplateMutationResponseSchema = z.infer<
|
|||||||
|
|
||||||
export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
|
export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
externalId: z.string().nullish(),
|
externalId: z.string().optional(),
|
||||||
recipients: z
|
recipients: z
|
||||||
.array(
|
.array(
|
||||||
z.object({
|
z.object({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
|
email: z.string().email(),
|
||||||
name: z.string().optional(),
|
name: z.string().optional(),
|
||||||
email: z.string().email().min(1),
|
signingOrder: z.number().optional(),
|
||||||
signingOrder: z.number().nullish(),
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.refine(
|
.refine(
|
||||||
@@ -252,8 +253,10 @@ export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
|
|||||||
timezone: z.string(),
|
timezone: z.string(),
|
||||||
dateFormat: z.string(),
|
dateFormat: z.string(),
|
||||||
redirectUrl: ZUrlSchema,
|
redirectUrl: ZUrlSchema,
|
||||||
signingOrder: z.nativeEnum(DocumentSigningOrder).optional(),
|
signingOrder: z.nativeEnum(DocumentSigningOrder),
|
||||||
language: z.enum(SUPPORTED_LANGUAGE_CODES).optional(),
|
language: z.enum(SUPPORTED_LANGUAGE_CODES),
|
||||||
|
distributionMethod: z.nativeEnum(DocumentDistributionMethod),
|
||||||
|
typedSignatureEnabled: z.boolean(),
|
||||||
})
|
})
|
||||||
.partial()
|
.partial()
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
import { PDFDocument } from 'pdf-lib';
|
||||||
|
|
||||||
|
import { getDocumentByToken } from '@documenso/lib/server-only/document/get-document-by-token';
|
||||||
|
import { getFile } from '@documenso/lib/universal/upload/get-file';
|
||||||
|
import { prisma } from '@documenso/prisma';
|
||||||
|
import { DocumentStatus, FieldType } from '@documenso/prisma/client';
|
||||||
|
import { seedPendingDocumentWithFullFields } from '@documenso/prisma/seed/documents';
|
||||||
|
import { seedTeam } from '@documenso/prisma/seed/teams';
|
||||||
|
import { seedUser } from '@documenso/prisma/seed/users';
|
||||||
|
|
||||||
|
import { apiSignin } from '../fixtures/authentication';
|
||||||
|
|
||||||
|
test.describe('Signing Certificate Tests', () => {
|
||||||
|
test('individual document should always include signing certificate', async ({ page }) => {
|
||||||
|
const user = await seedUser();
|
||||||
|
|
||||||
|
const { document, recipients } = await seedPendingDocumentWithFullFields({
|
||||||
|
owner: user,
|
||||||
|
recipients: ['signer@example.com'],
|
||||||
|
fields: [FieldType.SIGNATURE],
|
||||||
|
});
|
||||||
|
|
||||||
|
const documentData = await prisma.documentData
|
||||||
|
.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: document.documentDataId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(async (data) => getFile(data));
|
||||||
|
|
||||||
|
const originalPdf = await PDFDocument.load(documentData);
|
||||||
|
|
||||||
|
const recipient = recipients[0];
|
||||||
|
|
||||||
|
// Sign the document
|
||||||
|
await page.goto(`/sign/${recipient.token}`);
|
||||||
|
|
||||||
|
const canvas = page.locator('canvas');
|
||||||
|
const box = await canvas.boundingBox();
|
||||||
|
if (box) {
|
||||||
|
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.move(box.x + box.width / 4, box.y + box.height / 4);
|
||||||
|
await page.mouse.up();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const field of recipient.Field) {
|
||||||
|
await page.locator(`#field-${field.id}`).getByRole('button').click();
|
||||||
|
|
||||||
|
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Complete' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Sign' }).click();
|
||||||
|
await page.waitForURL(`/sign/${recipient.token}/complete`);
|
||||||
|
|
||||||
|
await expect(async () => {
|
||||||
|
const { status } = await getDocumentByToken({
|
||||||
|
token: recipient.token,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(DocumentStatus.COMPLETED);
|
||||||
|
}).toPass();
|
||||||
|
|
||||||
|
// Get the completed document
|
||||||
|
const completedDocument = await prisma.document.findFirstOrThrow({
|
||||||
|
where: { id: document.id },
|
||||||
|
include: { documentData: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const completedDocumentData = await getFile(completedDocument.documentData);
|
||||||
|
|
||||||
|
// Load the PDF and check number of pages
|
||||||
|
const pdfDoc = await PDFDocument.load(completedDocumentData);
|
||||||
|
|
||||||
|
expect(pdfDoc.getPageCount()).toBe(originalPdf.getPageCount() + 1); // Original + Certificate
|
||||||
|
});
|
||||||
|
|
||||||
|
test('team document with signing certificate enabled should include certificate', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const team = await seedTeam();
|
||||||
|
|
||||||
|
const { document, recipients } = await seedPendingDocumentWithFullFields({
|
||||||
|
owner: team.owner,
|
||||||
|
recipients: ['signer@example.com'],
|
||||||
|
fields: [FieldType.SIGNATURE],
|
||||||
|
updateDocumentOptions: {
|
||||||
|
teamId: team.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.teamGlobalSettings.create({
|
||||||
|
data: {
|
||||||
|
teamId: team.id,
|
||||||
|
includeSigningCertificate: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const documentData = await prisma.documentData
|
||||||
|
.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: document.documentDataId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(async (data) => getFile(data));
|
||||||
|
|
||||||
|
const originalPdf = await PDFDocument.load(documentData);
|
||||||
|
|
||||||
|
const recipient = recipients[0];
|
||||||
|
|
||||||
|
// Sign the document
|
||||||
|
await page.goto(`/sign/${recipient.token}`);
|
||||||
|
|
||||||
|
const canvas = page.locator('canvas');
|
||||||
|
const box = await canvas.boundingBox();
|
||||||
|
if (box) {
|
||||||
|
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.move(box.x + box.width / 4, box.y + box.height / 4);
|
||||||
|
await page.mouse.up();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const field of recipient.Field) {
|
||||||
|
await page.locator(`#field-${field.id}`).getByRole('button').click();
|
||||||
|
|
||||||
|
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Complete' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Sign' }).click();
|
||||||
|
await page.waitForURL(`/sign/${recipient.token}/complete`);
|
||||||
|
|
||||||
|
await expect(async () => {
|
||||||
|
const { status } = await getDocumentByToken({
|
||||||
|
token: recipient.token,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(DocumentStatus.COMPLETED);
|
||||||
|
}).toPass();
|
||||||
|
|
||||||
|
// Get the completed document
|
||||||
|
const completedDocument = await prisma.document.findFirstOrThrow({
|
||||||
|
where: { id: document.id },
|
||||||
|
include: { documentData: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const completedDocumentData = await getFile(completedDocument.documentData);
|
||||||
|
|
||||||
|
// Load the PDF and check number of pages
|
||||||
|
const completedPdf = await PDFDocument.load(completedDocumentData);
|
||||||
|
|
||||||
|
expect(completedPdf.getPageCount()).toBe(originalPdf.getPageCount() + 1); // Original + Certificate
|
||||||
|
});
|
||||||
|
|
||||||
|
test('team document with signing certificate disabled should not include certificate', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const team = await seedTeam();
|
||||||
|
|
||||||
|
const { document, recipients } = await seedPendingDocumentWithFullFields({
|
||||||
|
owner: team.owner,
|
||||||
|
recipients: ['signer@example.com'],
|
||||||
|
fields: [FieldType.SIGNATURE],
|
||||||
|
updateDocumentOptions: {
|
||||||
|
teamId: team.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.teamGlobalSettings.create({
|
||||||
|
data: {
|
||||||
|
teamId: team.id,
|
||||||
|
includeSigningCertificate: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const documentData = await prisma.documentData
|
||||||
|
.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: document.documentDataId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(async (data) => getFile(data));
|
||||||
|
|
||||||
|
const originalPdf = await PDFDocument.load(documentData);
|
||||||
|
|
||||||
|
const recipient = recipients[0];
|
||||||
|
|
||||||
|
// Sign the document
|
||||||
|
await page.goto(`/sign/${recipient.token}`);
|
||||||
|
|
||||||
|
const canvas = page.locator('canvas');
|
||||||
|
const box = await canvas.boundingBox();
|
||||||
|
if (box) {
|
||||||
|
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||||||
|
await page.mouse.down();
|
||||||
|
await page.mouse.move(box.x + box.width / 4, box.y + box.height / 4);
|
||||||
|
await page.mouse.up();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const field of recipient.Field) {
|
||||||
|
await page.locator(`#field-${field.id}`).getByRole('button').click();
|
||||||
|
|
||||||
|
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Complete' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Sign' }).click();
|
||||||
|
await page.waitForURL(`/sign/${recipient.token}/complete`);
|
||||||
|
|
||||||
|
await expect(async () => {
|
||||||
|
const { status } = await getDocumentByToken({
|
||||||
|
token: recipient.token,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(status).toBe(DocumentStatus.COMPLETED);
|
||||||
|
}).toPass();
|
||||||
|
|
||||||
|
// Get the completed document
|
||||||
|
const completedDocument = await prisma.document.findFirstOrThrow({
|
||||||
|
where: { id: document.id },
|
||||||
|
include: { documentData: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const completedDocumentData = await getFile(completedDocument.documentData);
|
||||||
|
|
||||||
|
// Load the PDF and check number of pages
|
||||||
|
const completedPdf = await PDFDocument.load(completedDocumentData);
|
||||||
|
|
||||||
|
expect(completedPdf.getPageCount()).toBe(originalPdf.getPageCount());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('team can toggle signing certificate setting', async ({ page }) => {
|
||||||
|
const team = await seedTeam();
|
||||||
|
|
||||||
|
await apiSignin({
|
||||||
|
page,
|
||||||
|
email: team.owner.email,
|
||||||
|
redirectPath: `/t/${team.url}/settings/preferences`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle signing certificate setting
|
||||||
|
await page.getByLabel('Include the Signing Certificate in the Document').click();
|
||||||
|
await page.getByRole('button', { name: /Save/ }).first().click();
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
// Verify the setting was saved
|
||||||
|
const updatedTeam = await prisma.team.findFirstOrThrow({
|
||||||
|
where: { id: team.id },
|
||||||
|
include: { teamGlobalSettings: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(updatedTeam.teamGlobalSettings?.includeSigningCertificate).toBe(false);
|
||||||
|
|
||||||
|
// Toggle the setting back to true
|
||||||
|
await page.getByLabel('Include the Signing Certificate in the Document').click();
|
||||||
|
await page.getByRole('button', { name: /Save/ }).first().click();
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
// Verify the setting was saved
|
||||||
|
const updatedTeam2 = await prisma.team.findFirstOrThrow({
|
||||||
|
where: { id: team.id },
|
||||||
|
include: { teamGlobalSettings: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(updatedTeam2.teamGlobalSettings?.includeSigningCertificate).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -17,19 +17,17 @@ test('[TEAMS]: update the default document visibility in the team global setting
|
|||||||
page,
|
page,
|
||||||
email: team.owner.email,
|
email: team.owner.email,
|
||||||
password: 'password',
|
password: 'password',
|
||||||
redirectPath: `/t/${team.url}/settings`,
|
redirectPath: `/t/${team.url}/settings/preferences`,
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.getByRole('combobox').click();
|
// !: Brittle selector
|
||||||
|
await page.getByRole('combobox').first().click();
|
||||||
await page.getByRole('option', { name: 'Admin' }).click();
|
await page.getByRole('option', { name: 'Admin' }).click();
|
||||||
await page.getByRole('button', { name: 'Update team' }).click();
|
await page.getByRole('button', { name: 'Save' }).first().click();
|
||||||
|
|
||||||
const toast = page.locator('li[role="status"][data-state="open"]').first();
|
const toast = page.locator('li[role="status"][data-state="open"]').first();
|
||||||
await expect(toast).toBeVisible();
|
await expect(toast).toBeVisible();
|
||||||
await expect(toast.getByText('Success', { exact: true })).toBeVisible();
|
await expect(toast.getByText('Document preferences updated', { exact: true })).toBeVisible();
|
||||||
await expect(
|
|
||||||
toast.getByText('Your team has been successfully updated.', { exact: true }),
|
|
||||||
).toBeVisible();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[TEAMS]: update the sender details in the team global settings', async ({ page }) => {
|
test('[TEAMS]: update the sender details in the team global settings', async ({ page }) => {
|
||||||
@@ -41,7 +39,7 @@ test('[TEAMS]: update the sender details in the team global settings', async ({
|
|||||||
page,
|
page,
|
||||||
email: team.owner.email,
|
email: team.owner.email,
|
||||||
password: 'password',
|
password: 'password',
|
||||||
redirectPath: `/t/${team.url}/settings`,
|
redirectPath: `/t/${team.url}/settings/preferences`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const checkbox = page.getByLabel('Send on Behalf of Team');
|
const checkbox = page.getByLabel('Send on Behalf of Team');
|
||||||
@@ -49,14 +47,11 @@ test('[TEAMS]: update the sender details in the team global settings', async ({
|
|||||||
|
|
||||||
await expect(checkbox).toBeChecked();
|
await expect(checkbox).toBeChecked();
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Update team' }).click();
|
await page.getByRole('button', { name: 'Save' }).first().click();
|
||||||
|
|
||||||
const toast = page.locator('li[role="status"][data-state="open"]').first();
|
const toast = page.locator('li[role="status"][data-state="open"]').first();
|
||||||
await expect(toast).toBeVisible();
|
await expect(toast).toBeVisible();
|
||||||
await expect(toast.getByText('Success', { exact: true })).toBeVisible();
|
await expect(toast.getByText('Document preferences updated', { exact: true })).toBeVisible();
|
||||||
await expect(
|
|
||||||
toast.getByText('Your team has been successfully updated.', { exact: true }),
|
|
||||||
).toBeVisible();
|
|
||||||
|
|
||||||
await expect(checkbox).toBeChecked();
|
await expect(checkbox).toBeChecked();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,15 +7,17 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test:dev": "NODE_OPTIONS=--experimental-require-module playwright test",
|
"test:dev": "NODE_OPTIONS=--experimental-require-module playwright test",
|
||||||
"test-ui:dev": "NODE_OPTIONS=--experimental-require-module playwright test --ui",
|
"test-ui:dev": "NODE_OPTIONS=--experimental-require-module playwright test --ui",
|
||||||
"test:e2e": "NODE_OPTIONS=--experimental-require-module start-server-and-test \"npm run start -w @documenso/web\" http://localhost:3000 \"playwright test\""
|
"test:e2e": "NODE_OPTIONS=--experimental-require-module start-server-and-test \"npm run start -w @documenso/web\" http://localhost:3000 \"playwright test $E2E_TEST_PATH\""
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.18.1",
|
"@playwright/test": "^1.18.1",
|
||||||
"@types/node": "^20.8.2",
|
"@types/node": "^20.8.2",
|
||||||
|
"@documenso/lib": "*",
|
||||||
"@documenso/prisma": "*",
|
"@documenso/prisma": "*",
|
||||||
"@documenso/web": "*"
|
"@documenso/web": "*",
|
||||||
|
"pdf-lib": "^1.17.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"start-server-and-test": "^2.0.1"
|
"start-server-and-test": "^2.0.1"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export const getDocumentRelatedPrices = async () => {
|
|||||||
return await getPricesByPlan([
|
return await getPricesByPlan([
|
||||||
STRIPE_PLAN_TYPE.REGULAR,
|
STRIPE_PLAN_TYPE.REGULAR,
|
||||||
STRIPE_PLAN_TYPE.COMMUNITY,
|
STRIPE_PLAN_TYPE.COMMUNITY,
|
||||||
|
STRIPE_PLAN_TYPE.PLATFORM,
|
||||||
STRIPE_PLAN_TYPE.ENTERPRISE,
|
STRIPE_PLAN_TYPE.ENTERPRISE,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
13
packages/ee/server-only/stripe/get-platform-plan-prices.ts
Normal file
13
packages/ee/server-only/stripe/get-platform-plan-prices.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { STRIPE_PLAN_TYPE } from '@documenso/lib/constants/billing';
|
||||||
|
|
||||||
|
import { getPricesByPlan } from './get-prices-by-plan';
|
||||||
|
|
||||||
|
export const getPlatformPlanPrices = async () => {
|
||||||
|
return await getPricesByPlan(STRIPE_PLAN_TYPE.PLATFORM);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPlatformPlanPriceIds = async () => {
|
||||||
|
const prices = await getPlatformPlanPrices();
|
||||||
|
|
||||||
|
return prices.map((price) => price.id);
|
||||||
|
};
|
||||||
@@ -9,6 +9,7 @@ export const getPrimaryAccountPlanPrices = async () => {
|
|||||||
return await getPricesByPlan([
|
return await getPricesByPlan([
|
||||||
STRIPE_PLAN_TYPE.REGULAR,
|
STRIPE_PLAN_TYPE.REGULAR,
|
||||||
STRIPE_PLAN_TYPE.COMMUNITY,
|
STRIPE_PLAN_TYPE.COMMUNITY,
|
||||||
|
STRIPE_PLAN_TYPE.PLATFORM,
|
||||||
STRIPE_PLAN_TYPE.ENTERPRISE,
|
STRIPE_PLAN_TYPE.ENTERPRISE,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ import { getPricesByPlan } from './get-prices-by-plan';
|
|||||||
* Returns the Stripe prices of items that affect the amount of teams a user can create.
|
* Returns the Stripe prices of items that affect the amount of teams a user can create.
|
||||||
*/
|
*/
|
||||||
export const getTeamRelatedPrices = async () => {
|
export const getTeamRelatedPrices = async () => {
|
||||||
return await getPricesByPlan([STRIPE_PLAN_TYPE.COMMUNITY, STRIPE_PLAN_TYPE.ENTERPRISE]);
|
return await getPricesByPlan([
|
||||||
|
STRIPE_PLAN_TYPE.COMMUNITY,
|
||||||
|
STRIPE_PLAN_TYPE.PLATFORM,
|
||||||
|
STRIPE_PLAN_TYPE.ENTERPRISE,
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
61
packages/ee/server-only/util/is-document-platform.ts
Normal file
61
packages/ee/server-only/util/is-document-platform.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||||
|
import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing';
|
||||||
|
import { prisma } from '@documenso/prisma';
|
||||||
|
import type { Document, Subscription } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { getPlatformPlanPriceIds } from '../stripe/get-platform-plan-prices';
|
||||||
|
|
||||||
|
export type IsDocumentPlatformOptions = Pick<Document, 'id' | 'userId' | 'teamId'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user is platform, or has permission to use platform features on
|
||||||
|
* behalf of their team.
|
||||||
|
*
|
||||||
|
* It is assumed that the provided user is part of the provided team.
|
||||||
|
*/
|
||||||
|
export const isDocumentPlatform = async ({
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
}: IsDocumentPlatformOptions): Promise<boolean> => {
|
||||||
|
let subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
|
if (!IS_BILLING_ENABLED()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (teamId) {
|
||||||
|
subscriptions = await prisma.team
|
||||||
|
.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: teamId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
owner: {
|
||||||
|
include: {
|
||||||
|
Subscription: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((team) => team.owner.Subscription);
|
||||||
|
} else {
|
||||||
|
subscriptions = await prisma.user
|
||||||
|
.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
Subscription: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((user) => user.Subscription);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subscriptions.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const platformPlanPriceIds = await getPlatformPlanPriceIds();
|
||||||
|
|
||||||
|
return subscriptionsContainsActivePlan(subscriptions, platformPlanPriceIds);
|
||||||
|
};
|
||||||
@@ -7,5 +7,6 @@ export enum STRIPE_PLAN_TYPE {
|
|||||||
REGULAR = 'regular',
|
REGULAR = 'regular',
|
||||||
TEAM = 'team',
|
TEAM = 'team',
|
||||||
COMMUNITY = 'community',
|
COMMUNITY = 'community',
|
||||||
|
PLATFORM = 'platform',
|
||||||
ENTERPRISE = 'enterprise',
|
ENTERPRISE = 'enterprise',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ const SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
|
|||||||
documentVisibility: z.nativeEnum(DocumentVisibility),
|
documentVisibility: z.nativeEnum(DocumentVisibility),
|
||||||
documentLanguage: z.string(),
|
documentLanguage: z.string(),
|
||||||
includeSenderDetails: z.boolean(),
|
includeSenderDetails: z.boolean(),
|
||||||
|
includeSigningCertificate: z.boolean(),
|
||||||
brandingEnabled: z.boolean(),
|
brandingEnabled: z.boolean(),
|
||||||
brandingLogo: z.string(),
|
brandingLogo: z.string(),
|
||||||
brandingUrl: z.string(),
|
brandingUrl: z.string(),
|
||||||
brandingCompanyDetails: z.string(),
|
brandingCompanyDetails: z.string(),
|
||||||
brandingHidePoweredBy: z.boolean(),
|
brandingHidePoweredBy: z.boolean(),
|
||||||
teamId: z.number(),
|
teamId: z.number(),
|
||||||
|
typedSignatureEnabled: z.boolean(),
|
||||||
})
|
})
|
||||||
.nullish(),
|
.nullish(),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -57,7 +57,17 @@ export const SEAL_DOCUMENT_JOB_DEFINITION = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
|
documentMeta: true,
|
||||||
Recipient: true,
|
Recipient: true,
|
||||||
|
team: {
|
||||||
|
select: {
|
||||||
|
teamGlobalSettings: {
|
||||||
|
select: {
|
||||||
|
includeSigningCertificate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -117,7 +127,13 @@ export const SEAL_DOCUMENT_JOB_DEFINITION = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pdfData = await getFile(documentData);
|
const pdfData = await getFile(documentData);
|
||||||
const certificateData = await getCertificatePdf({ documentId }).catch(() => null);
|
const certificateData =
|
||||||
|
(document.team?.teamGlobalSettings?.includeSigningCertificate ?? true)
|
||||||
|
? await getCertificatePdf({
|
||||||
|
documentId,
|
||||||
|
language: document.documentMeta?.language,
|
||||||
|
}).catch(() => null)
|
||||||
|
: null;
|
||||||
|
|
||||||
const newDataId = await io.runTask('decorate-and-sign-pdf', async () => {
|
const newDataId = await io.runTask('decorate-and-sign-pdf', async () => {
|
||||||
const pdfDoc = await PDFDocument.load(pdfData);
|
const pdfDoc = await PDFDocument.load(pdfData);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
"pg": "^8.11.3",
|
"pg": "^8.11.3",
|
||||||
"playwright": "1.43.0",
|
"playwright": "1.43.0",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"remeda": "^2.12.1",
|
"remeda": "^2.17.3",
|
||||||
"sharp": "0.32.6",
|
"sharp": "0.32.6",
|
||||||
"stripe": "^12.7.0",
|
"stripe": "^12.7.0",
|
||||||
"ts-pattern": "^5.0.5",
|
"ts-pattern": "^5.0.5",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export const getDocumentStats = async () => {
|
|||||||
[ExtendedDocumentStatus.DRAFT]: 0,
|
[ExtendedDocumentStatus.DRAFT]: 0,
|
||||||
[ExtendedDocumentStatus.PENDING]: 0,
|
[ExtendedDocumentStatus.PENDING]: 0,
|
||||||
[ExtendedDocumentStatus.COMPLETED]: 0,
|
[ExtendedDocumentStatus.COMPLETED]: 0,
|
||||||
|
[ExtendedDocumentStatus.BIN]: 0,
|
||||||
[ExtendedDocumentStatus.ALL]: 0,
|
[ExtendedDocumentStatus.ALL]: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ export const createDocument = async ({
|
|||||||
documentMeta: {
|
documentMeta: {
|
||||||
create: {
|
create: {
|
||||||
language: team?.teamGlobalSettings?.documentLanguage,
|
language: team?.teamGlobalSettings?.documentLanguage,
|
||||||
|
typedSignatureEnabled: team?.teamGlobalSettings?.typedSignatureEnabled,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -158,6 +158,16 @@ const handleDocumentOwnerDelete = async ({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Soft delete for document recipients since the owner is deleting it
|
||||||
|
await tx.recipient.updateMany({
|
||||||
|
where: {
|
||||||
|
documentId: document.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
documentDeletedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return await tx.document.update({
|
return await tx.document.update({
|
||||||
where: {
|
where: {
|
||||||
id: document.id,
|
id: document.id,
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export const findDocumentAuditLogs = async ({
|
|||||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED,
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED,
|
||||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED,
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED,
|
||||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
|
||||||
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RESTORED,
|
||||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
|
||||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED,
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED,
|
||||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED,
|
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED,
|
||||||
|
|||||||
@@ -2,15 +2,8 @@ import { DateTime } from 'luxon';
|
|||||||
import { P, match } from 'ts-pattern';
|
import { P, match } from 'ts-pattern';
|
||||||
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { RecipientRole, SigningStatus, TeamMemberRole } from '@documenso/prisma/client';
|
import type { Document, DocumentSource, Team, TeamEmail, User } from '@documenso/prisma/client';
|
||||||
import type {
|
import { Prisma, RecipientRole, SigningStatus, TeamMemberRole } from '@documenso/prisma/client';
|
||||||
Document,
|
|
||||||
DocumentSource,
|
|
||||||
Prisma,
|
|
||||||
Team,
|
|
||||||
TeamEmail,
|
|
||||||
User,
|
|
||||||
} from '@documenso/prisma/client';
|
|
||||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||||
|
|
||||||
import { DocumentVisibility } from '../../types/document-visibility';
|
import { DocumentVisibility } from '../../types/document-visibility';
|
||||||
@@ -88,14 +81,12 @@ export const findDocuments = async ({
|
|||||||
const teamMemberRole = team?.members[0].role ?? null;
|
const teamMemberRole = team?.members[0].role ?? null;
|
||||||
|
|
||||||
const termFilters = match(term)
|
const termFilters = match(term)
|
||||||
.with(P.string.minLength(1), () => {
|
.with(P.string.minLength(1), () => ({
|
||||||
return {
|
title: {
|
||||||
title: {
|
contains: term,
|
||||||
contains: term,
|
mode: Prisma.QueryMode.insensitive,
|
||||||
mode: 'insensitive',
|
},
|
||||||
},
|
}))
|
||||||
} as const;
|
|
||||||
})
|
|
||||||
.otherwise(() => undefined);
|
.otherwise(() => undefined);
|
||||||
|
|
||||||
const searchFilter: Prisma.DocumentWhereInput = {
|
const searchFilter: Prisma.DocumentWhereInput = {
|
||||||
@@ -141,6 +132,8 @@ export const findDocuments = async ({
|
|||||||
|
|
||||||
let filters: Prisma.DocumentWhereInput | null = findDocumentsFilter(status, user);
|
let filters: Prisma.DocumentWhereInput | null = findDocumentsFilter(status, user);
|
||||||
|
|
||||||
|
console.log('find documets team', team);
|
||||||
|
|
||||||
if (team) {
|
if (team) {
|
||||||
filters = findTeamDocumentsFilter(status, team, visibilityFilters);
|
filters = findTeamDocumentsFilter(status, team, visibilityFilters);
|
||||||
}
|
}
|
||||||
@@ -293,19 +286,21 @@ export const findDocuments = async ({
|
|||||||
} satisfies FindResultSet<typeof data>;
|
} satisfies FindResultSet<typeof data>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
|
export const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
|
||||||
return match<ExtendedDocumentStatus, Prisma.DocumentWhereInput>(status)
|
return match<ExtendedDocumentStatus, Prisma.DocumentWhereInput>(status)
|
||||||
.with(ExtendedDocumentStatus.ALL, () => ({
|
.with(ExtendedDocumentStatus.ALL, () => ({
|
||||||
OR: [
|
OR: [
|
||||||
{
|
{
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
teamId: null,
|
teamId: null,
|
||||||
|
deletedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: ExtendedDocumentStatus.COMPLETED,
|
status: ExtendedDocumentStatus.COMPLETED,
|
||||||
Recipient: {
|
Recipient: {
|
||||||
some: {
|
some: {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
|
documentDeletedAt: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -314,6 +309,7 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
|
|||||||
Recipient: {
|
Recipient: {
|
||||||
some: {
|
some: {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
|
documentDeletedAt: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -330,6 +326,7 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
|
|||||||
role: {
|
role: {
|
||||||
not: RecipientRole.CC,
|
not: RecipientRole.CC,
|
||||||
},
|
},
|
||||||
|
documentDeletedAt: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
@@ -344,6 +341,7 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
teamId: null,
|
teamId: null,
|
||||||
status: ExtendedDocumentStatus.PENDING,
|
status: ExtendedDocumentStatus.PENDING,
|
||||||
|
deletedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: ExtendedDocumentStatus.PENDING,
|
status: ExtendedDocumentStatus.PENDING,
|
||||||
@@ -354,6 +352,7 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
|
|||||||
role: {
|
role: {
|
||||||
not: RecipientRole.CC,
|
not: RecipientRole.CC,
|
||||||
},
|
},
|
||||||
|
documentDeletedAt: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -365,12 +364,49 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
teamId: null,
|
teamId: null,
|
||||||
status: ExtendedDocumentStatus.COMPLETED,
|
status: ExtendedDocumentStatus.COMPLETED,
|
||||||
|
deletedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: ExtendedDocumentStatus.COMPLETED,
|
status: ExtendedDocumentStatus.COMPLETED,
|
||||||
Recipient: {
|
Recipient: {
|
||||||
some: {
|
some: {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
|
documentDeletedAt: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}))
|
||||||
|
.with(ExtendedDocumentStatus.BIN, () => ({
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
teamId: null,
|
||||||
|
deletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: ExtendedDocumentStatus.PENDING,
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: user.email,
|
||||||
|
signingStatus: SigningStatus.SIGNED,
|
||||||
|
documentDeletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: ExtendedDocumentStatus.COMPLETED,
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: user.email,
|
||||||
|
signingStatus: SigningStatus.SIGNED,
|
||||||
|
documentDeletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -408,7 +444,7 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
|
|||||||
* @param team The team to find the documents for.
|
* @param team The team to find the documents for.
|
||||||
* @returns A filter which can be applied to the Prisma Document schema.
|
* @returns A filter which can be applied to the Prisma Document schema.
|
||||||
*/
|
*/
|
||||||
const findTeamDocumentsFilter = (
|
export const findTeamDocumentsFilter = (
|
||||||
status: ExtendedDocumentStatus,
|
status: ExtendedDocumentStatus,
|
||||||
team: Team & { teamEmail: TeamEmail | null },
|
team: Team & { teamEmail: TeamEmail | null },
|
||||||
visibilityFilters: Prisma.DocumentWhereInput[],
|
visibilityFilters: Prisma.DocumentWhereInput[],
|
||||||
@@ -418,17 +454,16 @@ const findTeamDocumentsFilter = (
|
|||||||
return match<ExtendedDocumentStatus, Prisma.DocumentWhereInput | null>(status)
|
return match<ExtendedDocumentStatus, Prisma.DocumentWhereInput | null>(status)
|
||||||
.with(ExtendedDocumentStatus.ALL, () => {
|
.with(ExtendedDocumentStatus.ALL, () => {
|
||||||
const filter: Prisma.DocumentWhereInput = {
|
const filter: Prisma.DocumentWhereInput = {
|
||||||
// Filter to display all documents that belong to the team.
|
|
||||||
OR: [
|
OR: [
|
||||||
{
|
{
|
||||||
teamId: team.id,
|
teamId: team.id,
|
||||||
|
deletedAt: null,
|
||||||
OR: visibilityFilters,
|
OR: visibilityFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (teamEmail && filter.OR) {
|
if (teamEmail && filter.OR) {
|
||||||
// Filter to display all documents received by the team email that are not draft.
|
|
||||||
filter.OR.push({
|
filter.OR.push({
|
||||||
status: {
|
status: {
|
||||||
not: ExtendedDocumentStatus.DRAFT,
|
not: ExtendedDocumentStatus.DRAFT,
|
||||||
@@ -438,14 +473,15 @@ const findTeamDocumentsFilter = (
|
|||||||
email: teamEmail,
|
email: teamEmail,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
deletedAt: null,
|
||||||
OR: visibilityFilters,
|
OR: visibilityFilters,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Filter to display all documents that have been sent by the team email.
|
|
||||||
filter.OR.push({
|
filter.OR.push({
|
||||||
User: {
|
User: {
|
||||||
email: teamEmail,
|
email: teamEmail,
|
||||||
},
|
},
|
||||||
|
deletedAt: null,
|
||||||
OR: visibilityFilters,
|
OR: visibilityFilters,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -453,7 +489,6 @@ const findTeamDocumentsFilter = (
|
|||||||
return filter;
|
return filter;
|
||||||
})
|
})
|
||||||
.with(ExtendedDocumentStatus.INBOX, () => {
|
.with(ExtendedDocumentStatus.INBOX, () => {
|
||||||
// Return a filter that will return nothing.
|
|
||||||
if (!teamEmail) {
|
if (!teamEmail) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -471,6 +506,7 @@ const findTeamDocumentsFilter = (
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
deletedAt: null,
|
||||||
OR: visibilityFilters,
|
OR: visibilityFilters,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
@@ -480,6 +516,7 @@ const findTeamDocumentsFilter = (
|
|||||||
{
|
{
|
||||||
teamId: team.id,
|
teamId: team.id,
|
||||||
status: ExtendedDocumentStatus.DRAFT,
|
status: ExtendedDocumentStatus.DRAFT,
|
||||||
|
deletedAt: null,
|
||||||
OR: visibilityFilters,
|
OR: visibilityFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -491,6 +528,7 @@ const findTeamDocumentsFilter = (
|
|||||||
User: {
|
User: {
|
||||||
email: teamEmail,
|
email: teamEmail,
|
||||||
},
|
},
|
||||||
|
deletedAt: null,
|
||||||
OR: visibilityFilters,
|
OR: visibilityFilters,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -503,6 +541,7 @@ const findTeamDocumentsFilter = (
|
|||||||
{
|
{
|
||||||
teamId: team.id,
|
teamId: team.id,
|
||||||
status: ExtendedDocumentStatus.PENDING,
|
status: ExtendedDocumentStatus.PENDING,
|
||||||
|
deletedAt: null,
|
||||||
OR: visibilityFilters,
|
OR: visibilityFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -531,6 +570,7 @@ const findTeamDocumentsFilter = (
|
|||||||
OR: visibilityFilters,
|
OR: visibilityFilters,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
deletedAt: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,6 +579,7 @@ const findTeamDocumentsFilter = (
|
|||||||
.with(ExtendedDocumentStatus.COMPLETED, () => {
|
.with(ExtendedDocumentStatus.COMPLETED, () => {
|
||||||
const filter: Prisma.DocumentWhereInput = {
|
const filter: Prisma.DocumentWhereInput = {
|
||||||
status: ExtendedDocumentStatus.COMPLETED,
|
status: ExtendedDocumentStatus.COMPLETED,
|
||||||
|
deletedAt: null,
|
||||||
OR: [
|
OR: [
|
||||||
{
|
{
|
||||||
teamId: team.id,
|
teamId: team.id,
|
||||||
@@ -568,5 +609,42 @@ const findTeamDocumentsFilter = (
|
|||||||
|
|
||||||
return filter;
|
return filter;
|
||||||
})
|
})
|
||||||
|
.with(ExtendedDocumentStatus.BIN, () => {
|
||||||
|
const filters: Prisma.DocumentWhereInput[] = [
|
||||||
|
{
|
||||||
|
teamId: team.id,
|
||||||
|
deletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (teamEmail) {
|
||||||
|
filters.push(
|
||||||
|
{
|
||||||
|
User: {
|
||||||
|
email: teamEmail,
|
||||||
|
},
|
||||||
|
deletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: teamEmail,
|
||||||
|
documentDeletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
OR: filters,
|
||||||
|
};
|
||||||
|
})
|
||||||
.exhaustive();
|
.exhaustive();
|
||||||
};
|
};
|
||||||
|
|||||||
118
packages/lib/server-only/document/get-stats-new.tsx
Normal file
118
packages/lib/server-only/document/get-stats-new.tsx
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
|
import {
|
||||||
|
type PeriodSelectorValue,
|
||||||
|
findDocumentsFilter,
|
||||||
|
findTeamDocumentsFilter,
|
||||||
|
} from '@documenso/lib/server-only/document/find-documents';
|
||||||
|
import { prisma } from '@documenso/prisma';
|
||||||
|
import type { Prisma, Team, TeamEmail, User } from '@documenso/prisma/client';
|
||||||
|
import { DocumentVisibility, TeamMemberRole } from '@documenso/prisma/client';
|
||||||
|
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||||
|
|
||||||
|
export type GetStatsInput = {
|
||||||
|
user: User;
|
||||||
|
team?: Team & { teamEmail: TeamEmail | null } & { currentTeamMember?: { role: TeamMemberRole } };
|
||||||
|
period?: PeriodSelectorValue;
|
||||||
|
search?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStats = async ({ user, period, search, ...options }: GetStatsInput) => {
|
||||||
|
let createdAt: Prisma.DocumentWhereInput['createdAt'];
|
||||||
|
|
||||||
|
if (period) {
|
||||||
|
const daysAgo = parseInt(period.replace(/d$/, ''), 10);
|
||||||
|
|
||||||
|
const startOfPeriod = DateTime.now().minus({ days: daysAgo }).startOf('day');
|
||||||
|
|
||||||
|
createdAt = {
|
||||||
|
gte: startOfPeriod.toJSDate(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const stats: Record<ExtendedDocumentStatus, number> = {
|
||||||
|
[ExtendedDocumentStatus.DRAFT]: 0,
|
||||||
|
[ExtendedDocumentStatus.PENDING]: 0,
|
||||||
|
[ExtendedDocumentStatus.COMPLETED]: 0,
|
||||||
|
[ExtendedDocumentStatus.INBOX]: 0,
|
||||||
|
[ExtendedDocumentStatus.ALL]: 0,
|
||||||
|
[ExtendedDocumentStatus.BIN]: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchFilter: Prisma.DocumentWhereInput = search
|
||||||
|
? {
|
||||||
|
OR: [
|
||||||
|
{ title: { contains: search, mode: 'insensitive' } },
|
||||||
|
{ Recipient: { some: { name: { contains: search, mode: 'insensitive' } } } },
|
||||||
|
{ Recipient: { some: { email: { contains: search, mode: 'insensitive' } } } },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
|
||||||
|
const visibilityFilters = [
|
||||||
|
match(options.team?.currentTeamMember?.role)
|
||||||
|
.with(TeamMemberRole.ADMIN, () => ({
|
||||||
|
visibility: {
|
||||||
|
in: [
|
||||||
|
DocumentVisibility.EVERYONE,
|
||||||
|
DocumentVisibility.MANAGER_AND_ABOVE,
|
||||||
|
DocumentVisibility.ADMIN,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.with(TeamMemberRole.MANAGER, () => ({
|
||||||
|
visibility: {
|
||||||
|
in: [DocumentVisibility.EVERYONE, DocumentVisibility.MANAGER_AND_ABOVE],
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.otherwise(() => ({ visibility: DocumentVisibility.EVERYONE })),
|
||||||
|
];
|
||||||
|
|
||||||
|
const statusCounts = await Promise.all(
|
||||||
|
Object.values(ExtendedDocumentStatus).map(async (status) => {
|
||||||
|
if (status === ExtendedDocumentStatus.ALL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filter = options.team
|
||||||
|
? findTeamDocumentsFilter(status, options.team, visibilityFilters)
|
||||||
|
: findDocumentsFilter(status, user);
|
||||||
|
|
||||||
|
if (filter === null) {
|
||||||
|
return { status, count: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const whereClause = {
|
||||||
|
...filter,
|
||||||
|
...(createdAt && { createdAt }),
|
||||||
|
...searchFilter,
|
||||||
|
};
|
||||||
|
|
||||||
|
const count = await prisma.document.count({
|
||||||
|
where: whereClause,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { status, count };
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
statusCounts.forEach((result) => {
|
||||||
|
if (result) {
|
||||||
|
stats[result.status] = result.count;
|
||||||
|
if (
|
||||||
|
result.status !== ExtendedDocumentStatus.BIN &&
|
||||||
|
[
|
||||||
|
ExtendedDocumentStatus.DRAFT,
|
||||||
|
ExtendedDocumentStatus.PENDING,
|
||||||
|
ExtendedDocumentStatus.COMPLETED,
|
||||||
|
ExtendedDocumentStatus.INBOX,
|
||||||
|
].includes(result.status)
|
||||||
|
) {
|
||||||
|
stats[ExtendedDocumentStatus.ALL] += result.count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
};
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents';
|
import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { TeamMemberRole } from '@documenso/prisma/client';
|
|
||||||
import type { Prisma, User } from '@documenso/prisma/client';
|
import type { Prisma, User } from '@documenso/prisma/client';
|
||||||
import { SigningStatus } from '@documenso/prisma/client';
|
import {
|
||||||
import { DocumentVisibility } from '@documenso/prisma/client';
|
DocumentVisibility,
|
||||||
|
RecipientRole,
|
||||||
|
SigningStatus,
|
||||||
|
TeamMemberRole,
|
||||||
|
} from '@documenso/prisma/client';
|
||||||
import { isExtendedDocumentStatus } from '@documenso/prisma/guards/is-extended-document-status';
|
import { isExtendedDocumentStatus } from '@documenso/prisma/guards/is-extended-document-status';
|
||||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||||
|
|
||||||
@@ -30,7 +34,7 @@ export const getStats = async ({ user, period, search, ...options }: GetStatsInp
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const [ownerCounts, notSignedCounts, hasSignedCounts] = await (options.team
|
const [ownerCounts, notSignedCounts, hasSignedCounts, deletedCounts] = await (options.team
|
||||||
? getTeamCounts({
|
? getTeamCounts({
|
||||||
...options.team,
|
...options.team,
|
||||||
createdAt,
|
createdAt,
|
||||||
@@ -46,6 +50,7 @@ export const getStats = async ({ user, period, search, ...options }: GetStatsInp
|
|||||||
[ExtendedDocumentStatus.COMPLETED]: 0,
|
[ExtendedDocumentStatus.COMPLETED]: 0,
|
||||||
[ExtendedDocumentStatus.INBOX]: 0,
|
[ExtendedDocumentStatus.INBOX]: 0,
|
||||||
[ExtendedDocumentStatus.ALL]: 0,
|
[ExtendedDocumentStatus.ALL]: 0,
|
||||||
|
[ExtendedDocumentStatus.BIN]: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
ownerCounts.forEach((stat) => {
|
ownerCounts.forEach((stat) => {
|
||||||
@@ -66,6 +71,10 @@ export const getStats = async ({ user, period, search, ...options }: GetStatsInp
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
deletedCounts.forEach((stat) => {
|
||||||
|
stats[ExtendedDocumentStatus.BIN] += stat._count._all;
|
||||||
|
});
|
||||||
|
|
||||||
Object.keys(stats).forEach((key) => {
|
Object.keys(stats).forEach((key) => {
|
||||||
if (key !== ExtendedDocumentStatus.ALL && isExtendedDocumentStatus(key)) {
|
if (key !== ExtendedDocumentStatus.ALL && isExtendedDocumentStatus(key)) {
|
||||||
stats[ExtendedDocumentStatus.ALL] += stats[key];
|
stats[ExtendedDocumentStatus.ALL] += stats[key];
|
||||||
@@ -98,25 +107,45 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
|
|||||||
_all: true,
|
_all: true,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
userId: user.id,
|
OR: [
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
teamId: null,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: {
|
||||||
|
not: ExtendedDocumentStatus.DRAFT,
|
||||||
|
},
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: user.email,
|
||||||
|
documentDeletedAt: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
createdAt,
|
createdAt,
|
||||||
teamId: null,
|
|
||||||
deletedAt: null,
|
|
||||||
AND: [searchFilter],
|
AND: [searchFilter],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
// Not signed counts.
|
// Not signed counts (Inbox).
|
||||||
prisma.document.groupBy({
|
prisma.document.groupBy({
|
||||||
by: ['status'],
|
by: ['status'],
|
||||||
_count: {
|
_count: {
|
||||||
_all: true,
|
_all: true,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
status: ExtendedDocumentStatus.PENDING,
|
status: {
|
||||||
|
not: ExtendedDocumentStatus.DRAFT,
|
||||||
|
},
|
||||||
Recipient: {
|
Recipient: {
|
||||||
some: {
|
some: {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
signingStatus: SigningStatus.NOT_SIGNED,
|
signingStatus: SigningStatus.NOT_SIGNED,
|
||||||
|
role: {
|
||||||
|
not: RecipientRole.CC,
|
||||||
|
},
|
||||||
documentDeletedAt: null,
|
documentDeletedAt: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -131,30 +160,81 @@ const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
|
|||||||
_all: true,
|
_all: true,
|
||||||
},
|
},
|
||||||
where: {
|
where: {
|
||||||
createdAt,
|
|
||||||
User: {
|
|
||||||
email: {
|
|
||||||
not: user.email,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
OR: [
|
OR: [
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
teamId: null,
|
||||||
|
status: ExtendedDocumentStatus.PENDING,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
status: ExtendedDocumentStatus.PENDING,
|
status: ExtendedDocumentStatus.PENDING,
|
||||||
Recipient: {
|
Recipient: {
|
||||||
some: {
|
some: {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
signingStatus: SigningStatus.SIGNED,
|
signingStatus: SigningStatus.SIGNED,
|
||||||
|
role: {
|
||||||
|
not: RecipientRole.CC,
|
||||||
|
},
|
||||||
documentDeletedAt: null,
|
documentDeletedAt: null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
teamId: null,
|
||||||
|
status: ExtendedDocumentStatus.COMPLETED,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: ExtendedDocumentStatus.COMPLETED,
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: user.email,
|
||||||
|
documentDeletedAt: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
createdAt,
|
||||||
|
AND: [searchFilter],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
// Deleted counts.
|
||||||
|
prisma.document.groupBy({
|
||||||
|
by: ['status'],
|
||||||
|
_count: {
|
||||||
|
_all: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
userId: user.id,
|
||||||
|
teamId: null,
|
||||||
|
deletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: ExtendedDocumentStatus.PENDING,
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: user.email,
|
||||||
|
signingStatus: SigningStatus.SIGNED,
|
||||||
|
documentDeletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
status: ExtendedDocumentStatus.COMPLETED,
|
status: ExtendedDocumentStatus.COMPLETED,
|
||||||
Recipient: {
|
Recipient: {
|
||||||
some: {
|
some: {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
signingStatus: SigningStatus.SIGNED,
|
documentDeletedAt: {
|
||||||
documentDeletedAt: null,
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -177,9 +257,7 @@ type GetTeamCountsOption = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getTeamCounts = async (options: GetTeamCountsOption) => {
|
const getTeamCounts = async (options: GetTeamCountsOption) => {
|
||||||
const { createdAt, teamId, teamEmail } = options;
|
const { createdAt, teamId, teamEmail, senderIds = [], currentTeamMemberRole, search } = options;
|
||||||
|
|
||||||
const senderIds = options.senderIds ?? [];
|
|
||||||
|
|
||||||
const userIdWhereClause: Prisma.DocumentWhereInput['userId'] =
|
const userIdWhereClause: Prisma.DocumentWhereInput['userId'] =
|
||||||
senderIds.length > 0
|
senderIds.length > 0
|
||||||
@@ -188,148 +266,226 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
|
|||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const searchFilter: Prisma.DocumentWhereInput = {
|
const searchFilter: Prisma.DocumentWhereInput = search
|
||||||
OR: [
|
? {
|
||||||
{ title: { contains: options.search, mode: 'insensitive' } },
|
|
||||||
{ Recipient: { some: { name: { contains: options.search, mode: 'insensitive' } } } },
|
|
||||||
{ Recipient: { some: { email: { contains: options.search, mode: 'insensitive' } } } },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let ownerCountsWhereInput: Prisma.DocumentWhereInput = {
|
|
||||||
userId: userIdWhereClause,
|
|
||||||
createdAt,
|
|
||||||
teamId,
|
|
||||||
deletedAt: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
let notSignedCountsGroupByArgs = null;
|
|
||||||
let hasSignedCountsGroupByArgs = null;
|
|
||||||
|
|
||||||
const visibilityFiltersWhereInput: Prisma.DocumentWhereInput = {
|
|
||||||
AND: [
|
|
||||||
{ deletedAt: null },
|
|
||||||
{
|
|
||||||
OR: [
|
OR: [
|
||||||
match(options.currentTeamMemberRole)
|
{ title: { contains: search, mode: 'insensitive' } },
|
||||||
.with(TeamMemberRole.ADMIN, () => ({
|
{ Recipient: { some: { name: { contains: search, mode: 'insensitive' } } } },
|
||||||
visibility: {
|
{ Recipient: { some: { email: { contains: search, mode: 'insensitive' } } } },
|
||||||
in: [
|
|
||||||
DocumentVisibility.EVERYONE,
|
|
||||||
DocumentVisibility.MANAGER_AND_ABOVE,
|
|
||||||
DocumentVisibility.ADMIN,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
.with(TeamMemberRole.MANAGER, () => ({
|
|
||||||
visibility: {
|
|
||||||
in: [DocumentVisibility.EVERYONE, DocumentVisibility.MANAGER_AND_ABOVE],
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
.otherwise(() => ({
|
|
||||||
visibility: {
|
|
||||||
equals: DocumentVisibility.EVERYONE,
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
{
|
|
||||||
OR: [
|
|
||||||
{ userId: options.userId },
|
|
||||||
{ Recipient: { some: { email: options.currentUserEmail } } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
}
|
||||||
],
|
: {};
|
||||||
};
|
|
||||||
|
|
||||||
ownerCountsWhereInput = {
|
const visibilityFilters = [
|
||||||
...ownerCountsWhereInput,
|
match(currentTeamMemberRole)
|
||||||
...visibilityFiltersWhereInput,
|
.with(TeamMemberRole.ADMIN, () => ({
|
||||||
...searchFilter,
|
visibility: {
|
||||||
};
|
in: [
|
||||||
|
DocumentVisibility.EVERYONE,
|
||||||
if (teamEmail) {
|
DocumentVisibility.MANAGER_AND_ABOVE,
|
||||||
ownerCountsWhereInput = {
|
DocumentVisibility.ADMIN,
|
||||||
userId: userIdWhereClause,
|
],
|
||||||
createdAt,
|
|
||||||
OR: [
|
|
||||||
{
|
|
||||||
teamId,
|
|
||||||
},
|
},
|
||||||
{
|
}))
|
||||||
User: {
|
.with(TeamMemberRole.MANAGER, () => ({
|
||||||
email: teamEmail,
|
visibility: {
|
||||||
},
|
in: [DocumentVisibility.EVERYONE, DocumentVisibility.MANAGER_AND_ABOVE],
|
||||||
},
|
},
|
||||||
],
|
}))
|
||||||
deletedAt: null,
|
.otherwise(() => ({ visibility: DocumentVisibility.EVERYONE })),
|
||||||
};
|
];
|
||||||
|
|
||||||
notSignedCountsGroupByArgs = {
|
|
||||||
by: ['status'],
|
|
||||||
_count: {
|
|
||||||
_all: true,
|
|
||||||
},
|
|
||||||
where: {
|
|
||||||
userId: userIdWhereClause,
|
|
||||||
createdAt,
|
|
||||||
status: ExtendedDocumentStatus.PENDING,
|
|
||||||
Recipient: {
|
|
||||||
some: {
|
|
||||||
email: teamEmail,
|
|
||||||
signingStatus: SigningStatus.NOT_SIGNED,
|
|
||||||
documentDeletedAt: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
deletedAt: null,
|
|
||||||
},
|
|
||||||
} satisfies Prisma.DocumentGroupByArgs;
|
|
||||||
|
|
||||||
hasSignedCountsGroupByArgs = {
|
|
||||||
by: ['status'],
|
|
||||||
_count: {
|
|
||||||
_all: true,
|
|
||||||
},
|
|
||||||
where: {
|
|
||||||
userId: userIdWhereClause,
|
|
||||||
createdAt,
|
|
||||||
OR: [
|
|
||||||
{
|
|
||||||
status: ExtendedDocumentStatus.PENDING,
|
|
||||||
Recipient: {
|
|
||||||
some: {
|
|
||||||
email: teamEmail,
|
|
||||||
signingStatus: SigningStatus.SIGNED,
|
|
||||||
documentDeletedAt: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
deletedAt: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: ExtendedDocumentStatus.COMPLETED,
|
|
||||||
Recipient: {
|
|
||||||
some: {
|
|
||||||
email: teamEmail,
|
|
||||||
signingStatus: SigningStatus.SIGNED,
|
|
||||||
documentDeletedAt: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
deletedAt: null,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
} satisfies Prisma.DocumentGroupByArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
|
// Owner counts (ALL)
|
||||||
prisma.document.groupBy({
|
prisma.document.groupBy({
|
||||||
by: ['status'],
|
by: ['status'],
|
||||||
_count: {
|
_count: { _all: true },
|
||||||
_all: true,
|
where: {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
deletedAt: null,
|
||||||
|
OR: visibilityFilters,
|
||||||
|
},
|
||||||
|
...(teamEmail
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
status: {
|
||||||
|
not: ExtendedDocumentStatus.DRAFT,
|
||||||
|
},
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: teamEmail,
|
||||||
|
documentDeletedAt: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
deletedAt: null,
|
||||||
|
OR: visibilityFilters,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
User: {
|
||||||
|
email: teamEmail,
|
||||||
|
},
|
||||||
|
deletedAt: null,
|
||||||
|
OR: visibilityFilters,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
userId: userIdWhereClause,
|
||||||
|
createdAt,
|
||||||
|
...searchFilter,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Not signed counts (INBOX)
|
||||||
|
prisma.document.groupBy({
|
||||||
|
by: ['status'],
|
||||||
|
_count: { _all: true },
|
||||||
|
where: teamEmail
|
||||||
|
? {
|
||||||
|
userId: userIdWhereClause,
|
||||||
|
createdAt,
|
||||||
|
status: {
|
||||||
|
not: ExtendedDocumentStatus.DRAFT,
|
||||||
|
},
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: teamEmail,
|
||||||
|
signingStatus: SigningStatus.NOT_SIGNED,
|
||||||
|
role: {
|
||||||
|
not: RecipientRole.CC,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
deletedAt: null,
|
||||||
|
OR: visibilityFilters,
|
||||||
|
...searchFilter,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
userId: userIdWhereClause,
|
||||||
|
createdAt,
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
OR: [{ id: -1 }], // Empty set if no team email
|
||||||
|
},
|
||||||
|
searchFilter,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Has signed counts (PENDING + COMPLETED)
|
||||||
|
prisma.document.groupBy({
|
||||||
|
by: ['status'],
|
||||||
|
_count: { _all: true },
|
||||||
|
where: {
|
||||||
|
userId: userIdWhereClause,
|
||||||
|
createdAt,
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
status: ExtendedDocumentStatus.PENDING,
|
||||||
|
deletedAt: null,
|
||||||
|
OR: visibilityFilters,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
status: ExtendedDocumentStatus.COMPLETED,
|
||||||
|
deletedAt: null,
|
||||||
|
OR: visibilityFilters,
|
||||||
|
},
|
||||||
|
...(teamEmail
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
status: ExtendedDocumentStatus.PENDING,
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: teamEmail,
|
||||||
|
signingStatus: SigningStatus.SIGNED,
|
||||||
|
role: {
|
||||||
|
not: RecipientRole.CC,
|
||||||
|
},
|
||||||
|
documentDeletedAt: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OR: visibilityFilters,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
User: {
|
||||||
|
email: teamEmail,
|
||||||
|
},
|
||||||
|
OR: visibilityFilters,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: ExtendedDocumentStatus.COMPLETED,
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: teamEmail,
|
||||||
|
documentDeletedAt: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OR: visibilityFilters,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
User: {
|
||||||
|
email: teamEmail,
|
||||||
|
},
|
||||||
|
OR: visibilityFilters,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
...searchFilter,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Deleted counts (BIN)
|
||||||
|
prisma.document.groupBy({
|
||||||
|
by: ['status'],
|
||||||
|
_count: { _all: true },
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
deletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...(teamEmail
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
User: {
|
||||||
|
email: teamEmail,
|
||||||
|
},
|
||||||
|
deletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Recipient: {
|
||||||
|
some: {
|
||||||
|
email: teamEmail,
|
||||||
|
documentDeletedAt: {
|
||||||
|
gte: DateTime.now().minus({ days: 30 }).startOf('day').toJSDate(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
...searchFilter,
|
||||||
},
|
},
|
||||||
where: ownerCountsWhereInput,
|
|
||||||
}),
|
}),
|
||||||
notSignedCountsGroupByArgs ? prisma.document.groupBy(notSignedCountsGroupByArgs) : [],
|
|
||||||
hasSignedCountsGroupByArgs ? prisma.document.groupBy(hasSignedCountsGroupByArgs) : [],
|
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
149
packages/lib/server-only/document/restore-document.ts
Normal file
149
packages/lib/server-only/document/restore-document.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
'use server';
|
||||||
|
|
||||||
|
import { prisma } from '@documenso/prisma';
|
||||||
|
import type { Document, DocumentMeta, Recipient, User } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
|
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
||||||
|
|
||||||
|
export type RestoreDocumentOptions = {
|
||||||
|
id: number;
|
||||||
|
userId: number;
|
||||||
|
teamId?: number;
|
||||||
|
requestMetadata?: RequestMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const restoreDocument = async ({
|
||||||
|
id,
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
requestMetadata,
|
||||||
|
}: RestoreDocumentOptions) => {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('User not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const document = await prisma.document.findUnique({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Recipient: true,
|
||||||
|
documentMeta: true,
|
||||||
|
team: {
|
||||||
|
select: {
|
||||||
|
members: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!document || (teamId !== undefined && teamId !== document.teamId)) {
|
||||||
|
throw new Error('Document not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const isUserOwner = document.userId === userId;
|
||||||
|
const isUserTeamMember = document.team?.members.some((member) => member.userId === userId);
|
||||||
|
const userRecipient = document.Recipient.find((recipient) => recipient.email === user.email);
|
||||||
|
|
||||||
|
if (!isUserOwner && !isUserTeamMember && !userRecipient) {
|
||||||
|
throw new Error('Not allowed');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle restoring the actual document if user has permission.
|
||||||
|
if (isUserOwner || isUserTeamMember) {
|
||||||
|
await handleDocumentOwnerRestore({
|
||||||
|
document,
|
||||||
|
user,
|
||||||
|
requestMetadata,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue to show the document to the user if they are a recipient.
|
||||||
|
if (userRecipient?.documentDeletedAt !== null) {
|
||||||
|
await prisma.recipient
|
||||||
|
.update({
|
||||||
|
where: {
|
||||||
|
id: userRecipient?.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
documentDeletedAt: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// Do nothing.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return partial document for API v1 response.
|
||||||
|
return {
|
||||||
|
id: document.id,
|
||||||
|
userId: document.userId,
|
||||||
|
teamId: document.teamId,
|
||||||
|
title: document.title,
|
||||||
|
status: document.status,
|
||||||
|
documentDataId: document.documentDataId,
|
||||||
|
createdAt: document.createdAt,
|
||||||
|
updatedAt: document.updatedAt,
|
||||||
|
completedAt: document.completedAt,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type HandleDocumentOwnerRestoreOptions = {
|
||||||
|
document: Document & {
|
||||||
|
Recipient: Recipient[];
|
||||||
|
documentMeta: DocumentMeta | null;
|
||||||
|
};
|
||||||
|
user: User;
|
||||||
|
requestMetadata?: RequestMetadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDocumentOwnerRestore = async ({
|
||||||
|
document,
|
||||||
|
user,
|
||||||
|
requestMetadata,
|
||||||
|
}: HandleDocumentOwnerRestoreOptions) => {
|
||||||
|
if (!document.deletedAt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore soft-deleted documents.
|
||||||
|
return await prisma.$transaction(async (tx) => {
|
||||||
|
await tx.documentAuditLog.create({
|
||||||
|
data: createDocumentAuditLogData({
|
||||||
|
documentId: document.id,
|
||||||
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RESTORED,
|
||||||
|
user,
|
||||||
|
requestMetadata,
|
||||||
|
data: {
|
||||||
|
type: 'RESTORE',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.recipient.updateMany({
|
||||||
|
where: {
|
||||||
|
documentId: document.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
documentDeletedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return await tx.document.update({
|
||||||
|
where: {
|
||||||
|
id: document.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -10,7 +10,6 @@ import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/
|
|||||||
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||||
import { signPdf } from '@documenso/signing';
|
import { signPdf } from '@documenso/signing';
|
||||||
|
|
||||||
import { ZSupportedLanguageCodeSchema } from '../../constants/i18n';
|
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { getFile } from '../../universal/upload/get-file';
|
import { getFile } from '../../universal/upload/get-file';
|
||||||
import { putPdfFile } from '../../universal/upload/put-file';
|
import { putPdfFile } from '../../universal/upload/put-file';
|
||||||
@@ -48,6 +47,15 @@ export const sealDocument = async ({
|
|||||||
documentData: true,
|
documentData: true,
|
||||||
documentMeta: true,
|
documentMeta: true,
|
||||||
Recipient: true,
|
Recipient: true,
|
||||||
|
team: {
|
||||||
|
select: {
|
||||||
|
teamGlobalSettings: {
|
||||||
|
select: {
|
||||||
|
includeSigningCertificate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -92,11 +100,13 @@ export const sealDocument = async ({
|
|||||||
// !: Need to write the fields onto the document as a hard copy
|
// !: Need to write the fields onto the document as a hard copy
|
||||||
const pdfData = await getFile(documentData);
|
const pdfData = await getFile(documentData);
|
||||||
|
|
||||||
const documentLanguage = ZSupportedLanguageCodeSchema.parse(document.documentMeta?.language);
|
const certificateData =
|
||||||
|
(document.team?.teamGlobalSettings?.includeSigningCertificate ?? true)
|
||||||
const certificate = await getCertificatePdf({ documentId, language: documentLanguage })
|
? await getCertificatePdf({
|
||||||
.then(async (doc) => PDFDocument.load(doc))
|
documentId,
|
||||||
.catch(() => null);
|
language: document.documentMeta?.language,
|
||||||
|
}).catch(() => null)
|
||||||
|
: null;
|
||||||
|
|
||||||
const doc = await PDFDocument.load(pdfData);
|
const doc = await PDFDocument.load(pdfData);
|
||||||
|
|
||||||
@@ -105,7 +115,9 @@ export const sealDocument = async ({
|
|||||||
flattenForm(doc);
|
flattenForm(doc);
|
||||||
flattenAnnotations(doc);
|
flattenAnnotations(doc);
|
||||||
|
|
||||||
if (certificate) {
|
if (certificateData) {
|
||||||
|
const certificate = await PDFDocument.load(certificateData);
|
||||||
|
|
||||||
const certificatePages = await doc.copyPages(certificate, certificate.getPageIndices());
|
const certificatePages = await doc.copyPages(certificate, certificate.getPageIndices());
|
||||||
|
|
||||||
certificatePages.forEach((page) => {
|
certificatePages.forEach((page) => {
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ import { getToken } from 'next-auth/jwt';
|
|||||||
import { LOCAL_FEATURE_FLAGS } from '@documenso/lib/constants/feature-flags';
|
import { LOCAL_FEATURE_FLAGS } from '@documenso/lib/constants/feature-flags';
|
||||||
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client';
|
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client';
|
||||||
|
|
||||||
import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL, NEXT_PRIVATE_INTERNAL_WEBAPP_URL } from '../../constants/app';
|
import {
|
||||||
|
NEXT_PRIVATE_INTERNAL_WEBAPP_URL,
|
||||||
|
NEXT_PUBLIC_MARKETING_URL,
|
||||||
|
NEXT_PUBLIC_WEBAPP_URL,
|
||||||
|
} from '../../constants/app';
|
||||||
import { extractDistinctUserId, mapJwtToFlagProperties } from './get';
|
import { extractDistinctUserId, mapJwtToFlagProperties } from './get';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import { getToken } from 'next-auth/jwt';
|
|||||||
import { LOCAL_FEATURE_FLAGS, extractPostHogConfig } from '@documenso/lib/constants/feature-flags';
|
import { LOCAL_FEATURE_FLAGS, extractPostHogConfig } from '@documenso/lib/constants/feature-flags';
|
||||||
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client';
|
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client';
|
||||||
|
|
||||||
import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL, NEXT_PRIVATE_INTERNAL_WEBAPP_URL } from '../../constants/app';
|
import {
|
||||||
|
NEXT_PRIVATE_INTERNAL_WEBAPP_URL,
|
||||||
|
NEXT_PUBLIC_MARKETING_URL,
|
||||||
|
NEXT_PUBLIC_WEBAPP_URL,
|
||||||
|
} from '../../constants/app';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate a single feature flag based on the current user if possible.
|
* Evaluate a single feature flag based on the current user if possible.
|
||||||
@@ -67,7 +71,7 @@ export default async function handleFeatureFlagGet(req: Request) {
|
|||||||
if (origin.startsWith(NEXT_PUBLIC_MARKETING_URL() ?? 'http://localhost:3001')) {
|
if (origin.startsWith(NEXT_PUBLIC_MARKETING_URL() ?? 'http://localhost:3001')) {
|
||||||
res.headers.set('Access-Control-Allow-Origin', origin);
|
res.headers.set('Access-Control-Allow-Origin', origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (origin.startsWith(NEXT_PRIVATE_INTERNAL_WEBAPP_URL ?? 'http://localhost:3000')) {
|
if (origin.startsWith(NEXT_PRIVATE_INTERNAL_WEBAPP_URL ?? 'http://localhost:3000')) {
|
||||||
res.headers.set('Access-Control-Allow-Origin', origin);
|
res.headers.set('Access-Control-Allow-Origin', origin);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,6 +177,10 @@ export const signFieldWithToken = async ({
|
|||||||
throw new Error('Signature field must have a signature');
|
throw new Error('Signature field must have a signature');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isSignatureField && !documentMeta?.typedSignatureEnabled && typedSignature) {
|
||||||
|
throw new Error('Typed signatures are not allowed. Please draw your signature');
|
||||||
|
}
|
||||||
|
|
||||||
return await prisma.$transaction(async (tx) => {
|
return await prisma.$transaction(async (tx) => {
|
||||||
const updatedField = await tx.field.update({
|
const updatedField = await tx.field.update({
|
||||||
where: {
|
where: {
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import { DateTime } from 'luxon';
|
|||||||
import type { Browser } from 'playwright';
|
import type { Browser } from 'playwright';
|
||||||
|
|
||||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
||||||
import type { SupportedLanguageCodes } from '../../constants/i18n';
|
import { type SupportedLanguageCodes, isValidLanguageCode } from '../../constants/i18n';
|
||||||
import { encryptSecondaryData } from '../crypto/encrypt';
|
import { encryptSecondaryData } from '../crypto/encrypt';
|
||||||
|
|
||||||
export type GetCertificatePdfOptions = {
|
export type GetCertificatePdfOptions = {
|
||||||
documentId: number;
|
documentId: number;
|
||||||
language?: SupportedLanguageCodes;
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
language?: SupportedLanguageCodes | (string & {});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getCertificatePdf = async ({ documentId, language }: GetCertificatePdfOptions) => {
|
export const getCertificatePdf = async ({ documentId, language }: GetCertificatePdfOptions) => {
|
||||||
@@ -38,15 +39,15 @@ export const getCertificatePdf = async ({ documentId, language }: GetCertificate
|
|||||||
|
|
||||||
const page = await browserContext.newPage();
|
const page = await browserContext.newPage();
|
||||||
|
|
||||||
if (language) {
|
const lang = isValidLanguageCode(language) ? language : 'en';
|
||||||
await page.context().addCookies([
|
|
||||||
{
|
await page.context().addCookies([
|
||||||
name: 'language',
|
{
|
||||||
value: language,
|
name: 'language',
|
||||||
url: NEXT_PUBLIC_WEBAPP_URL(),
|
value: lang,
|
||||||
},
|
url: NEXT_PUBLIC_WEBAPP_URL(),
|
||||||
]);
|
},
|
||||||
}
|
]);
|
||||||
|
|
||||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/__htmltopdf/certificate?d=${encryptedId}`, {
|
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/__htmltopdf/certificate?d=${encryptedId}`, {
|
||||||
waitUntil: 'networkidle',
|
waitUntil: 'networkidle',
|
||||||
|
|||||||
@@ -82,7 +82,10 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
|||||||
const fieldX = pageWidth * (Number(field.positionX) / 100);
|
const fieldX = pageWidth * (Number(field.positionX) / 100);
|
||||||
const fieldY = pageHeight * (Number(field.positionY) / 100);
|
const fieldY = pageHeight * (Number(field.positionY) / 100);
|
||||||
|
|
||||||
const font = await pdf.embedFont(isSignatureField ? fontCaveat : fontNoto);
|
const font = await pdf.embedFont(
|
||||||
|
isSignatureField ? fontCaveat : fontNoto,
|
||||||
|
isSignatureField ? { features: { calt: false } } : undefined,
|
||||||
|
);
|
||||||
|
|
||||||
if (field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE) {
|
if (field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE) {
|
||||||
await pdf.embedFont(fontCaveat);
|
await pdf.embedFont(fontCaveat);
|
||||||
@@ -92,45 +95,89 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
|||||||
.with(
|
.with(
|
||||||
{
|
{
|
||||||
type: P.union(FieldType.SIGNATURE, FieldType.FREE_SIGNATURE),
|
type: P.union(FieldType.SIGNATURE, FieldType.FREE_SIGNATURE),
|
||||||
Signature: { signatureImageAsBase64: P.string },
|
|
||||||
},
|
},
|
||||||
async (field) => {
|
async (field) => {
|
||||||
const image = await pdf.embedPng(field.Signature?.signatureImageAsBase64 ?? '');
|
if (field.Signature?.signatureImageAsBase64) {
|
||||||
|
const image = await pdf.embedPng(field.Signature?.signatureImageAsBase64 ?? '');
|
||||||
|
|
||||||
let imageWidth = image.width;
|
let imageWidth = image.width;
|
||||||
let imageHeight = image.height;
|
let imageHeight = image.height;
|
||||||
|
|
||||||
const scalingFactor = Math.min(fieldWidth / imageWidth, fieldHeight / imageHeight, 1);
|
const scalingFactor = Math.min(fieldWidth / imageWidth, fieldHeight / imageHeight, 1);
|
||||||
|
|
||||||
imageWidth = imageWidth * scalingFactor;
|
imageWidth = imageWidth * scalingFactor;
|
||||||
imageHeight = imageHeight * scalingFactor;
|
imageHeight = imageHeight * scalingFactor;
|
||||||
|
|
||||||
let imageX = fieldX + (fieldWidth - imageWidth) / 2;
|
let imageX = fieldX + (fieldWidth - imageWidth) / 2;
|
||||||
let imageY = fieldY + (fieldHeight - imageHeight) / 2;
|
let imageY = fieldY + (fieldHeight - imageHeight) / 2;
|
||||||
|
|
||||||
// Invert the Y axis since PDFs use a bottom-left coordinate system
|
// Invert the Y axis since PDFs use a bottom-left coordinate system
|
||||||
imageY = pageHeight - imageY - imageHeight;
|
imageY = pageHeight - imageY - imageHeight;
|
||||||
|
|
||||||
if (pageRotationInDegrees !== 0) {
|
if (pageRotationInDegrees !== 0) {
|
||||||
const adjustedPosition = adjustPositionForRotation(
|
const adjustedPosition = adjustPositionForRotation(
|
||||||
pageWidth,
|
pageWidth,
|
||||||
pageHeight,
|
pageHeight,
|
||||||
imageX,
|
imageX,
|
||||||
imageY,
|
imageY,
|
||||||
pageRotationInDegrees,
|
pageRotationInDegrees,
|
||||||
);
|
);
|
||||||
|
|
||||||
imageX = adjustedPosition.xPos;
|
imageX = adjustedPosition.xPos;
|
||||||
imageY = adjustedPosition.yPos;
|
imageY = adjustedPosition.yPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
page.drawImage(image, {
|
||||||
|
x: imageX,
|
||||||
|
y: imageY,
|
||||||
|
width: imageWidth,
|
||||||
|
height: imageHeight,
|
||||||
|
rotate: degrees(pageRotationInDegrees),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const signatureText = field.Signature?.typedSignature ?? '';
|
||||||
|
|
||||||
|
const longestLineInTextForWidth = signatureText
|
||||||
|
.split('\n')
|
||||||
|
.sort((a, b) => b.length - a.length)[0];
|
||||||
|
|
||||||
|
let fontSize = maxFontSize;
|
||||||
|
let textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
|
||||||
|
let textHeight = font.heightAtSize(fontSize);
|
||||||
|
|
||||||
|
const scalingFactor = Math.min(fieldWidth / textWidth, fieldHeight / textHeight, 1);
|
||||||
|
fontSize = Math.max(Math.min(fontSize * scalingFactor, maxFontSize), minFontSize);
|
||||||
|
|
||||||
|
textWidth = font.widthOfTextAtSize(longestLineInTextForWidth, fontSize);
|
||||||
|
textHeight = font.heightAtSize(fontSize);
|
||||||
|
|
||||||
|
let textX = fieldX + (fieldWidth - textWidth) / 2;
|
||||||
|
let textY = fieldY + (fieldHeight - textHeight) / 2;
|
||||||
|
|
||||||
|
// Invert the Y axis since PDFs use a bottom-left coordinate system
|
||||||
|
textY = pageHeight - textY - textHeight;
|
||||||
|
|
||||||
|
if (pageRotationInDegrees !== 0) {
|
||||||
|
const adjustedPosition = adjustPositionForRotation(
|
||||||
|
pageWidth,
|
||||||
|
pageHeight,
|
||||||
|
textX,
|
||||||
|
textY,
|
||||||
|
pageRotationInDegrees,
|
||||||
|
);
|
||||||
|
|
||||||
|
textX = adjustedPosition.xPos;
|
||||||
|
textY = adjustedPosition.yPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
page.drawText(signatureText, {
|
||||||
|
x: textX,
|
||||||
|
y: textY,
|
||||||
|
size: fontSize,
|
||||||
|
font,
|
||||||
|
rotate: degrees(pageRotationInDegrees),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
page.drawImage(image, {
|
|
||||||
x: imageX,
|
|
||||||
y: imageY,
|
|
||||||
width: imageWidth,
|
|
||||||
height: imageHeight,
|
|
||||||
rotate: degrees(pageRotationInDegrees),
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.with({ type: FieldType.CHECKBOX }, (field) => {
|
.with({ type: FieldType.CHECKBOX }, (field) => {
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export type UpdateTeamDocumentSettingsOptions = {
|
|||||||
documentVisibility: DocumentVisibility;
|
documentVisibility: DocumentVisibility;
|
||||||
documentLanguage: SupportedLanguageCodes;
|
documentLanguage: SupportedLanguageCodes;
|
||||||
includeSenderDetails: boolean;
|
includeSenderDetails: boolean;
|
||||||
|
typedSignatureEnabled: boolean;
|
||||||
|
includeSigningCertificate: boolean;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,7 +22,13 @@ export const updateTeamDocumentSettings = async ({
|
|||||||
teamId,
|
teamId,
|
||||||
settings,
|
settings,
|
||||||
}: UpdateTeamDocumentSettingsOptions) => {
|
}: UpdateTeamDocumentSettingsOptions) => {
|
||||||
const { documentVisibility, documentLanguage, includeSenderDetails } = settings;
|
const {
|
||||||
|
documentVisibility,
|
||||||
|
documentLanguage,
|
||||||
|
includeSenderDetails,
|
||||||
|
includeSigningCertificate,
|
||||||
|
typedSignatureEnabled,
|
||||||
|
} = settings;
|
||||||
|
|
||||||
const member = await prisma.teamMember.findFirst({
|
const member = await prisma.teamMember.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@@ -42,11 +50,15 @@ export const updateTeamDocumentSettings = async ({
|
|||||||
documentVisibility,
|
documentVisibility,
|
||||||
documentLanguage,
|
documentLanguage,
|
||||||
includeSenderDetails,
|
includeSenderDetails,
|
||||||
|
typedSignatureEnabled,
|
||||||
|
includeSigningCertificate,
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
documentVisibility,
|
documentVisibility,
|
||||||
documentLanguage,
|
documentLanguage,
|
||||||
includeSenderDetails,
|
includeSenderDetails,
|
||||||
|
typedSignatureEnabled,
|
||||||
|
includeSigningCertificate,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams
|
|||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { Prisma } from '@documenso/prisma/client';
|
import { Prisma } from '@documenso/prisma/client';
|
||||||
import type { DocumentVisibility } from '@documenso/prisma/client';
|
|
||||||
|
|
||||||
export type UpdateTeamOptions = {
|
export type UpdateTeamOptions = {
|
||||||
userId: number;
|
userId: number;
|
||||||
@@ -12,8 +11,6 @@ export type UpdateTeamOptions = {
|
|||||||
data: {
|
data: {
|
||||||
name?: string;
|
name?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
documentVisibility?: DocumentVisibility;
|
|
||||||
includeSenderDetails?: boolean;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,18 +42,6 @@ export const updateTeam = async ({ userId, teamId, data }: UpdateTeamOptions) =>
|
|||||||
data: {
|
data: {
|
||||||
url: data.url,
|
url: data.url,
|
||||||
name: data.name,
|
name: data.name,
|
||||||
teamGlobalSettings: {
|
|
||||||
upsert: {
|
|
||||||
create: {
|
|
||||||
documentVisibility: data.documentVisibility,
|
|
||||||
includeSenderDetails: data.includeSenderDetails,
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
documentVisibility: data.documentVisibility,
|
|
||||||
includeSenderDetails: data.includeSenderDetails,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export type CreateDocumentFromTemplateOptions = {
|
|||||||
signingOrder?: DocumentSigningOrder;
|
signingOrder?: DocumentSigningOrder;
|
||||||
language?: SupportedLanguageCodes;
|
language?: SupportedLanguageCodes;
|
||||||
distributionMethod?: DocumentDistributionMethod;
|
distributionMethod?: DocumentDistributionMethod;
|
||||||
|
typedSignatureEnabled?: boolean;
|
||||||
};
|
};
|
||||||
requestMetadata?: RequestMetadata;
|
requestMetadata?: RequestMetadata;
|
||||||
};
|
};
|
||||||
@@ -146,7 +147,7 @@ export const createDocumentFromTemplate = async ({
|
|||||||
return {
|
return {
|
||||||
templateRecipientId: templateRecipient.id,
|
templateRecipientId: templateRecipient.id,
|
||||||
fields: templateRecipient.Field,
|
fields: templateRecipient.Field,
|
||||||
name: foundRecipient ? foundRecipient.name ?? '' : templateRecipient.name,
|
name: foundRecipient ? (foundRecipient.name ?? '') : templateRecipient.name,
|
||||||
email: foundRecipient ? foundRecipient.email : templateRecipient.email,
|
email: foundRecipient ? foundRecipient.email : templateRecipient.email,
|
||||||
role: templateRecipient.role,
|
role: templateRecipient.role,
|
||||||
signingOrder: foundRecipient?.signingOrder ?? templateRecipient.signingOrder,
|
signingOrder: foundRecipient?.signingOrder ?? templateRecipient.signingOrder,
|
||||||
@@ -196,6 +197,8 @@ export const createDocumentFromTemplate = async ({
|
|||||||
override?.language ||
|
override?.language ||
|
||||||
template.templateMeta?.language ||
|
template.templateMeta?.language ||
|
||||||
template.team?.teamGlobalSettings?.documentLanguage,
|
template.team?.teamGlobalSettings?.documentLanguage,
|
||||||
|
typedSignatureEnabled:
|
||||||
|
override?.typedSignatureEnabled ?? template.templateMeta?.typedSignatureEnabled,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Recipient: {
|
Recipient: {
|
||||||
|
|||||||
@@ -135,11 +135,11 @@ msgstr "{prefix} hat das Dokument erstellt"
|
|||||||
msgid "{prefix} deleted the document"
|
msgid "{prefix} deleted the document"
|
||||||
msgstr "{prefix} hat das Dokument gelöscht"
|
msgstr "{prefix} hat das Dokument gelöscht"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:335
|
#: packages/lib/utils/document-audit-logs.ts:339
|
||||||
msgid "{prefix} moved the document to team"
|
msgid "{prefix} moved the document to team"
|
||||||
msgstr "{prefix} hat das Dokument ins Team verschoben"
|
msgstr "{prefix} hat das Dokument ins Team verschoben"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:319
|
#: packages/lib/utils/document-audit-logs.ts:323
|
||||||
msgid "{prefix} opened the document"
|
msgid "{prefix} opened the document"
|
||||||
msgstr "{prefix} hat das Dokument geöffnet"
|
msgstr "{prefix} hat das Dokument geöffnet"
|
||||||
|
|
||||||
@@ -151,23 +151,27 @@ msgstr "{prefix} hat ein Feld entfernt"
|
|||||||
msgid "{prefix} removed a recipient"
|
msgid "{prefix} removed a recipient"
|
||||||
msgstr "{prefix} hat einen Empfänger entfernt"
|
msgstr "{prefix} hat einen Empfänger entfernt"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:365
|
#: packages/lib/utils/document-audit-logs.ts:369
|
||||||
msgid "{prefix} resent an email to {0}"
|
msgid "{prefix} resent an email to {0}"
|
||||||
msgstr "{prefix} hat eine E-Mail an {0} erneut gesendet"
|
msgstr "{prefix} hat eine E-Mail an {0} erneut gesendet"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:366
|
#: packages/lib/utils/document-audit-logs.ts:295
|
||||||
|
msgid "{prefix} restored the document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:370
|
||||||
msgid "{prefix} sent an email to {0}"
|
msgid "{prefix} sent an email to {0}"
|
||||||
msgstr "{prefix} hat eine E-Mail an {0} gesendet"
|
msgstr "{prefix} hat eine E-Mail an {0} gesendet"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:331
|
#: packages/lib/utils/document-audit-logs.ts:335
|
||||||
msgid "{prefix} sent the document"
|
msgid "{prefix} sent the document"
|
||||||
msgstr "{prefix} hat das Dokument gesendet"
|
msgstr "{prefix} hat das Dokument gesendet"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:295
|
#: packages/lib/utils/document-audit-logs.ts:299
|
||||||
msgid "{prefix} signed a field"
|
msgid "{prefix} signed a field"
|
||||||
msgstr "{prefix} hat ein Feld unterschrieben"
|
msgstr "{prefix} hat ein Feld unterschrieben"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:299
|
#: packages/lib/utils/document-audit-logs.ts:303
|
||||||
msgid "{prefix} unsigned a field"
|
msgid "{prefix} unsigned a field"
|
||||||
msgstr "{prefix} hat ein Feld ungültig gemacht"
|
msgstr "{prefix} hat ein Feld ungültig gemacht"
|
||||||
|
|
||||||
@@ -179,27 +183,27 @@ msgstr "{prefix} hat ein Feld aktualisiert"
|
|||||||
msgid "{prefix} updated a recipient"
|
msgid "{prefix} updated a recipient"
|
||||||
msgstr "{prefix} hat einen Empfänger aktualisiert"
|
msgstr "{prefix} hat einen Empfänger aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:315
|
#: packages/lib/utils/document-audit-logs.ts:319
|
||||||
msgid "{prefix} updated the document"
|
msgid "{prefix} updated the document"
|
||||||
msgstr "{prefix} hat das Dokument aktualisiert"
|
msgstr "{prefix} hat das Dokument aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:307
|
#: packages/lib/utils/document-audit-logs.ts:311
|
||||||
msgid "{prefix} updated the document access auth requirements"
|
msgid "{prefix} updated the document access auth requirements"
|
||||||
msgstr "{prefix} hat die Anforderungen an die Dokumentenzugriffsautorisierung aktualisiert"
|
msgstr "{prefix} hat die Anforderungen an die Dokumentenzugriffsautorisierung aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:327
|
#: packages/lib/utils/document-audit-logs.ts:331
|
||||||
msgid "{prefix} updated the document external ID"
|
msgid "{prefix} updated the document external ID"
|
||||||
msgstr "{prefix} hat die externe ID des Dokuments aktualisiert"
|
msgstr "{prefix} hat die externe ID des Dokuments aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:311
|
#: packages/lib/utils/document-audit-logs.ts:315
|
||||||
msgid "{prefix} updated the document signing auth requirements"
|
msgid "{prefix} updated the document signing auth requirements"
|
||||||
msgstr "{prefix} hat die Authentifizierungsanforderungen für die Dokumentenunterzeichnung aktualisiert"
|
msgstr "{prefix} hat die Authentifizierungsanforderungen für die Dokumentenunterzeichnung aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:323
|
#: packages/lib/utils/document-audit-logs.ts:327
|
||||||
msgid "{prefix} updated the document title"
|
msgid "{prefix} updated the document title"
|
||||||
msgstr "{prefix} hat den Titel des Dokuments aktualisiert"
|
msgstr "{prefix} hat den Titel des Dokuments aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:303
|
#: packages/lib/utils/document-audit-logs.ts:307
|
||||||
msgid "{prefix} updated the document visibility"
|
msgid "{prefix} updated the document visibility"
|
||||||
msgstr "{prefix} hat die Sichtbarkeit des Dokuments aktualisiert"
|
msgstr "{prefix} hat die Sichtbarkeit des Dokuments aktualisiert"
|
||||||
|
|
||||||
@@ -227,27 +231,27 @@ msgstr "{teamName} hat Sie eingeladen, {action} {documentName}"
|
|||||||
msgid "{teamName} ownership transfer request"
|
msgid "{teamName} ownership transfer request"
|
||||||
msgstr "Anfrage zur Übertragung des Eigentums von {teamName}"
|
msgstr "Anfrage zur Übertragung des Eigentums von {teamName}"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:343
|
#: packages/lib/utils/document-audit-logs.ts:347
|
||||||
msgid "{userName} approved the document"
|
msgid "{userName} approved the document"
|
||||||
msgstr "{userName} hat das Dokument genehmigt"
|
msgstr "{userName} hat das Dokument genehmigt"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:344
|
#: packages/lib/utils/document-audit-logs.ts:348
|
||||||
msgid "{userName} CC'd the document"
|
msgid "{userName} CC'd the document"
|
||||||
msgstr "{userName} hat das Dokument in CC gesetzt"
|
msgstr "{userName} hat das Dokument in CC gesetzt"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:345
|
#: packages/lib/utils/document-audit-logs.ts:349
|
||||||
msgid "{userName} completed their task"
|
msgid "{userName} completed their task"
|
||||||
msgstr "{userName} hat ihre Aufgabe abgeschlossen"
|
msgstr "{userName} hat ihre Aufgabe abgeschlossen"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:355
|
#: packages/lib/utils/document-audit-logs.ts:359
|
||||||
msgid "{userName} rejected the document"
|
msgid "{userName} rejected the document"
|
||||||
msgstr "{userName} hat das Dokument abgelehnt"
|
msgstr "{userName} hat das Dokument abgelehnt"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:341
|
#: packages/lib/utils/document-audit-logs.ts:345
|
||||||
msgid "{userName} signed the document"
|
msgid "{userName} signed the document"
|
||||||
msgstr "{userName} hat das Dokument unterschrieben"
|
msgstr "{userName} hat das Dokument unterschrieben"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:342
|
#: packages/lib/utils/document-audit-logs.ts:346
|
||||||
msgid "{userName} viewed the document"
|
msgid "{userName} viewed the document"
|
||||||
msgstr "{userName} hat das Dokument angesehen"
|
msgstr "{userName} hat das Dokument angesehen"
|
||||||
|
|
||||||
@@ -444,7 +448,7 @@ msgid "Advanced Options"
|
|||||||
msgstr "Erweiterte Optionen"
|
msgstr "Erweiterte Optionen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:576
|
#: packages/ui/primitives/document-flow/add-fields.tsx:576
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:409
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:414
|
||||||
msgid "Advanced settings"
|
msgid "Advanced settings"
|
||||||
msgstr "Erweiterte Einstellungen"
|
msgstr "Erweiterte Einstellungen"
|
||||||
|
|
||||||
@@ -500,11 +504,11 @@ msgstr "Genehmigung"
|
|||||||
msgid "Before you get started, please confirm your email address by clicking the button below:"
|
msgid "Before you get started, please confirm your email address by clicking the button below:"
|
||||||
msgstr "Bitte bestätige vor dem Start deine E-Mail-Adresse, indem du auf den Button unten klickst:"
|
msgstr "Bitte bestätige vor dem Start deine E-Mail-Adresse, indem du auf den Button unten klickst:"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:377
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:383
|
||||||
msgid "Black"
|
msgid "Black"
|
||||||
msgstr "Schwarz"
|
msgstr "Schwarz"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:391
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:397
|
||||||
msgid "Blue"
|
msgid "Blue"
|
||||||
msgstr "Blau"
|
msgstr "Blau"
|
||||||
|
|
||||||
@@ -562,7 +566,7 @@ msgstr "Checkbox-Werte"
|
|||||||
msgid "Clear filters"
|
msgid "Clear filters"
|
||||||
msgstr "Filter löschen"
|
msgstr "Filter löschen"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:411
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:417
|
||||||
msgid "Clear Signature"
|
msgid "Clear Signature"
|
||||||
msgstr "Unterschrift löschen"
|
msgstr "Unterschrift löschen"
|
||||||
|
|
||||||
@@ -590,7 +594,7 @@ msgid "Configure Direct Recipient"
|
|||||||
msgstr "Direkten Empfänger konfigurieren"
|
msgstr "Direkten Empfänger konfigurieren"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:410
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:415
|
||||||
msgid "Configure the {0} field"
|
msgid "Configure the {0} field"
|
||||||
msgstr "Konfigurieren Sie das Feld {0}"
|
msgstr "Konfigurieren Sie das Feld {0}"
|
||||||
|
|
||||||
@@ -653,7 +657,7 @@ msgstr "Benutzerdefinierter Text"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:934
|
#: packages/ui/primitives/document-flow/add-fields.tsx:934
|
||||||
#: packages/ui/primitives/document-flow/types.ts:53
|
#: packages/ui/primitives/document-flow/types.ts:53
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:697
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:729
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Datum"
|
msgstr "Datum"
|
||||||
|
|
||||||
@@ -688,17 +692,17 @@ msgstr "Dokument \"{0}\" - Ablehnung Bestätigt"
|
|||||||
msgid "Document access"
|
msgid "Document access"
|
||||||
msgstr "Dokumentenzugriff"
|
msgstr "Dokumentenzugriff"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:306
|
#: packages/lib/utils/document-audit-logs.ts:310
|
||||||
msgid "Document access auth updated"
|
msgid "Document access auth updated"
|
||||||
msgstr "Die Authentifizierung für den Dokumentenzugriff wurde aktualisiert"
|
msgstr "Die Authentifizierung für den Dokumentenzugriff wurde aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/server-only/document/delete-document.ts:246
|
#: packages/lib/server-only/document/delete-document.ts:256
|
||||||
#: packages/lib/server-only/document/super-delete-document.ts:98
|
#: packages/lib/server-only/document/super-delete-document.ts:98
|
||||||
msgid "Document Cancelled"
|
msgid "Document Cancelled"
|
||||||
msgstr "Dokument storniert"
|
msgstr "Dokument storniert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:369
|
#: packages/lib/utils/document-audit-logs.ts:373
|
||||||
#: packages/lib/utils/document-audit-logs.ts:370
|
#: packages/lib/utils/document-audit-logs.ts:374
|
||||||
msgid "Document completed"
|
msgid "Document completed"
|
||||||
msgstr "Dokument abgeschlossen"
|
msgstr "Dokument abgeschlossen"
|
||||||
|
|
||||||
@@ -736,15 +740,15 @@ msgstr "Dokument gelöscht!"
|
|||||||
msgid "Document Distribution Method"
|
msgid "Document Distribution Method"
|
||||||
msgstr "Verteilungsmethode für Dokumente"
|
msgstr "Verteilungsmethode für Dokumente"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:326
|
#: packages/lib/utils/document-audit-logs.ts:330
|
||||||
msgid "Document external ID updated"
|
msgid "Document external ID updated"
|
||||||
msgstr "Externe ID des Dokuments aktualisiert"
|
msgstr "Externe ID des Dokuments aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:334
|
#: packages/lib/utils/document-audit-logs.ts:338
|
||||||
msgid "Document moved to team"
|
msgid "Document moved to team"
|
||||||
msgstr "Dokument ins Team verschoben"
|
msgstr "Dokument ins Team verschoben"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:318
|
#: packages/lib/utils/document-audit-logs.ts:322
|
||||||
msgid "Document opened"
|
msgid "Document opened"
|
||||||
msgstr "Dokument geöffnet"
|
msgstr "Dokument geöffnet"
|
||||||
|
|
||||||
@@ -759,23 +763,27 @@ msgstr "Dokument Abgelehnt"
|
|||||||
#~ msgid "Document Rejection Confirmed"
|
#~ msgid "Document Rejection Confirmed"
|
||||||
#~ msgstr "Document Rejection Confirmed"
|
#~ msgstr "Document Rejection Confirmed"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:330
|
#: packages/lib/utils/document-audit-logs.ts:294
|
||||||
|
msgid "Document restored"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:334
|
||||||
msgid "Document sent"
|
msgid "Document sent"
|
||||||
msgstr "Dokument gesendet"
|
msgstr "Dokument gesendet"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:310
|
#: packages/lib/utils/document-audit-logs.ts:314
|
||||||
msgid "Document signing auth updated"
|
msgid "Document signing auth updated"
|
||||||
msgstr "Dokument unterzeichnen Authentifizierung aktualisiert"
|
msgstr "Dokument unterzeichnen Authentifizierung aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:322
|
#: packages/lib/utils/document-audit-logs.ts:326
|
||||||
msgid "Document title updated"
|
msgid "Document title updated"
|
||||||
msgstr "Dokumenttitel aktualisiert"
|
msgstr "Dokumenttitel aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:314
|
#: packages/lib/utils/document-audit-logs.ts:318
|
||||||
msgid "Document updated"
|
msgid "Document updated"
|
||||||
msgstr "Dokument aktualisiert"
|
msgstr "Dokument aktualisiert"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:302
|
#: packages/lib/utils/document-audit-logs.ts:306
|
||||||
msgid "Document visibility updated"
|
msgid "Document visibility updated"
|
||||||
msgstr "Sichtbarkeit des Dokuments aktualisiert"
|
msgstr "Sichtbarkeit des Dokuments aktualisiert"
|
||||||
|
|
||||||
@@ -793,7 +801,7 @@ msgid "Drag & drop your PDF here."
|
|||||||
msgstr "Ziehen Sie Ihr PDF hierher."
|
msgstr "Ziehen Sie Ihr PDF hierher."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1065
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1065
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:827
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:860
|
||||||
msgid "Dropdown"
|
msgid "Dropdown"
|
||||||
msgstr "Dropdown"
|
msgstr "Dropdown"
|
||||||
|
|
||||||
@@ -807,7 +815,7 @@ msgstr "Dropdown-Optionen"
|
|||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:512
|
#: packages/ui/primitives/document-flow/add-signers.tsx:512
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:519
|
#: packages/ui/primitives/document-flow/add-signers.tsx:519
|
||||||
#: packages/ui/primitives/document-flow/types.ts:54
|
#: packages/ui/primitives/document-flow/types.ts:54
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:645
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:677
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:471
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:471
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:478
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:478
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
@@ -821,11 +829,11 @@ msgstr "E-Mail ist erforderlich"
|
|||||||
msgid "Email Options"
|
msgid "Email Options"
|
||||||
msgstr "E-Mail-Optionen"
|
msgstr "E-Mail-Optionen"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:363
|
#: packages/lib/utils/document-audit-logs.ts:367
|
||||||
msgid "Email resent"
|
msgid "Email resent"
|
||||||
msgstr "E-Mail erneut gesendet"
|
msgstr "E-Mail erneut gesendet"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:363
|
#: packages/lib/utils/document-audit-logs.ts:367
|
||||||
msgid "Email sent"
|
msgid "Email sent"
|
||||||
msgstr "E-Mail gesendet"
|
msgstr "E-Mail gesendet"
|
||||||
|
|
||||||
@@ -843,6 +851,7 @@ msgid "Enable signing order"
|
|||||||
msgstr "Aktiviere die Signaturreihenfolge"
|
msgstr "Aktiviere die Signaturreihenfolge"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:802
|
#: packages/ui/primitives/document-flow/add-fields.tsx:802
|
||||||
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:597
|
||||||
msgid "Enable Typed Signatures"
|
msgid "Enable Typed Signatures"
|
||||||
msgstr "Aktivieren Sie getippte Unterschriften"
|
msgstr "Aktivieren Sie getippte Unterschriften"
|
||||||
|
|
||||||
@@ -889,11 +898,11 @@ msgstr "Feldbeschriftung"
|
|||||||
msgid "Field placeholder"
|
msgid "Field placeholder"
|
||||||
msgstr "Feldplatzhalter"
|
msgstr "Feldplatzhalter"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:294
|
#: packages/lib/utils/document-audit-logs.ts:298
|
||||||
msgid "Field signed"
|
msgid "Field signed"
|
||||||
msgstr "Feld unterschrieben"
|
msgstr "Feld unterschrieben"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:298
|
#: packages/lib/utils/document-audit-logs.ts:302
|
||||||
msgid "Field unsigned"
|
msgid "Field unsigned"
|
||||||
msgstr "Feld nicht unterschrieben"
|
msgstr "Feld nicht unterschrieben"
|
||||||
|
|
||||||
@@ -930,7 +939,7 @@ msgstr "Globale Empfängerauthentifizierung"
|
|||||||
msgid "Go Back"
|
msgid "Go Back"
|
||||||
msgstr "Zurück"
|
msgstr "Zurück"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:398
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:404
|
||||||
msgid "Green"
|
msgid "Green"
|
||||||
msgstr "Grün"
|
msgstr "Grün"
|
||||||
|
|
||||||
@@ -1025,7 +1034,7 @@ msgstr "Min"
|
|||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:550
|
#: packages/ui/primitives/document-flow/add-signers.tsx:550
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:556
|
#: packages/ui/primitives/document-flow/add-signers.tsx:556
|
||||||
#: packages/ui/primitives/document-flow/types.ts:55
|
#: packages/ui/primitives/document-flow/types.ts:55
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:671
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:703
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:506
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:506
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:512
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:512
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
@@ -1044,7 +1053,7 @@ msgid "Needs to view"
|
|||||||
msgstr "Muss sehen"
|
msgstr "Muss sehen"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:693
|
#: packages/ui/primitives/document-flow/add-fields.tsx:693
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:511
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:516
|
||||||
msgid "No recipient matching this description was found."
|
msgid "No recipient matching this description was found."
|
||||||
msgstr "Kein passender Empfänger mit dieser Beschreibung gefunden."
|
msgstr "Kein passender Empfänger mit dieser Beschreibung gefunden."
|
||||||
|
|
||||||
@@ -1053,7 +1062,7 @@ msgid "No recipients"
|
|||||||
msgstr "Keine Empfänger"
|
msgstr "Keine Empfänger"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:708
|
#: packages/ui/primitives/document-flow/add-fields.tsx:708
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:526
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:531
|
||||||
msgid "No recipients with this role"
|
msgid "No recipients with this role"
|
||||||
msgstr "Keine Empfänger mit dieser Rolle"
|
msgstr "Keine Empfänger mit dieser Rolle"
|
||||||
|
|
||||||
@@ -1083,7 +1092,7 @@ msgstr "Keine"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:986
|
#: packages/ui/primitives/document-flow/add-fields.tsx:986
|
||||||
#: packages/ui/primitives/document-flow/types.ts:56
|
#: packages/ui/primitives/document-flow/types.ts:56
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:749
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:781
|
||||||
msgid "Number"
|
msgid "Number"
|
||||||
msgstr "Nummer"
|
msgstr "Nummer"
|
||||||
|
|
||||||
@@ -1175,7 +1184,6 @@ msgid "Please try again or contact our support."
|
|||||||
msgstr "Bitte versuchen Sie es erneut oder kontaktieren Sie unseren Support."
|
msgstr "Bitte versuchen Sie es erneut oder kontaktieren Sie unseren Support."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/types.ts:57
|
#: packages/ui/primitives/document-flow/types.ts:57
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:775
|
|
||||||
msgid "Radio"
|
msgid "Radio"
|
||||||
msgstr "Radio"
|
msgstr "Radio"
|
||||||
|
|
||||||
@@ -1199,8 +1207,8 @@ msgstr "Grund für die Ablehnung: {rejectionReason}"
|
|||||||
msgid "Receives copy"
|
msgid "Receives copy"
|
||||||
msgstr "Erhält Kopie"
|
msgstr "Erhält Kopie"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:338
|
#: packages/lib/utils/document-audit-logs.ts:342
|
||||||
#: packages/lib/utils/document-audit-logs.ts:353
|
#: packages/lib/utils/document-audit-logs.ts:357
|
||||||
msgid "Recipient"
|
msgid "Recipient"
|
||||||
msgstr "Empfänger"
|
msgstr "Empfänger"
|
||||||
|
|
||||||
@@ -1218,7 +1226,7 @@ msgstr "E-Mail des entfernten Empfängers"
|
|||||||
msgid "Recipient signing request email"
|
msgid "Recipient signing request email"
|
||||||
msgstr "E-Mail zur Unterzeichnungsanfrage des Empfängers"
|
msgstr "E-Mail zur Unterzeichnungsanfrage des Empfängers"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:384
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:390
|
||||||
msgid "Red"
|
msgid "Red"
|
||||||
msgstr "Rot"
|
msgstr "Rot"
|
||||||
|
|
||||||
@@ -1287,7 +1295,7 @@ msgstr "Zeilen pro Seite"
|
|||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Speichern"
|
msgstr "Speichern"
|
||||||
|
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:861
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:893
|
||||||
msgid "Save Template"
|
msgid "Save Template"
|
||||||
msgstr "Vorlage speichern"
|
msgstr "Vorlage speichern"
|
||||||
|
|
||||||
@@ -1380,7 +1388,7 @@ msgstr "Anmelden"
|
|||||||
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
||||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||||
#: packages/ui/primitives/document-flow/types.ts:49
|
#: packages/ui/primitives/document-flow/types.ts:49
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:593
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:625
|
||||||
msgid "Signature"
|
msgid "Signature"
|
||||||
msgstr "Unterschrift"
|
msgstr "Unterschrift"
|
||||||
|
|
||||||
@@ -1465,7 +1473,7 @@ msgstr "Vorlagentitel"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:960
|
#: packages/ui/primitives/document-flow/add-fields.tsx:960
|
||||||
#: packages/ui/primitives/document-flow/types.ts:52
|
#: packages/ui/primitives/document-flow/types.ts:52
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:723
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:755
|
||||||
msgid "Text"
|
msgid "Text"
|
||||||
msgstr "Text"
|
msgstr "Text"
|
||||||
|
|
||||||
@@ -1629,7 +1637,7 @@ msgid "Title"
|
|||||||
msgstr "Titel"
|
msgstr "Titel"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1080
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1080
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:841
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:873
|
||||||
msgid "To proceed further, please set at least one value for the {0} field."
|
msgid "To proceed further, please set at least one value for the {0} field."
|
||||||
msgstr "Um fortzufahren, legen Sie bitte mindestens einen Wert für das Feld {0} fest."
|
msgstr "Um fortzufahren, legen Sie bitte mindestens einen Wert für das Feld {0} fest."
|
||||||
|
|
||||||
@@ -1814,4 +1822,3 @@ msgstr "Dein Passwort wurde aktualisiert."
|
|||||||
#: packages/email/templates/team-delete.tsx:32
|
#: packages/email/templates/team-delete.tsx:32
|
||||||
msgid "Your team has been deleted"
|
msgid "Your team has been deleted"
|
||||||
msgstr "Dein Team wurde gelöscht"
|
msgstr "Dein Team wurde gelöscht"
|
||||||
|
|
||||||
|
|||||||
@@ -602,4 +602,3 @@ msgstr "Sie können Documenso kostenlos selbst hosten oder unsere sofort einsatz
|
|||||||
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
||||||
msgid "Your browser does not support the video tag."
|
msgid "Your browser does not support the video tag."
|
||||||
msgstr "Ihr Browser unterstützt das Video-Tag nicht."
|
msgstr "Ihr Browser unterstützt das Video-Tag nicht."
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -130,11 +130,11 @@ msgstr "{prefix} created the document"
|
|||||||
msgid "{prefix} deleted the document"
|
msgid "{prefix} deleted the document"
|
||||||
msgstr "{prefix} deleted the document"
|
msgstr "{prefix} deleted the document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:335
|
#: packages/lib/utils/document-audit-logs.ts:339
|
||||||
msgid "{prefix} moved the document to team"
|
msgid "{prefix} moved the document to team"
|
||||||
msgstr "{prefix} moved the document to team"
|
msgstr "{prefix} moved the document to team"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:319
|
#: packages/lib/utils/document-audit-logs.ts:323
|
||||||
msgid "{prefix} opened the document"
|
msgid "{prefix} opened the document"
|
||||||
msgstr "{prefix} opened the document"
|
msgstr "{prefix} opened the document"
|
||||||
|
|
||||||
@@ -146,23 +146,27 @@ msgstr "{prefix} removed a field"
|
|||||||
msgid "{prefix} removed a recipient"
|
msgid "{prefix} removed a recipient"
|
||||||
msgstr "{prefix} removed a recipient"
|
msgstr "{prefix} removed a recipient"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:365
|
#: packages/lib/utils/document-audit-logs.ts:369
|
||||||
msgid "{prefix} resent an email to {0}"
|
msgid "{prefix} resent an email to {0}"
|
||||||
msgstr "{prefix} resent an email to {0}"
|
msgstr "{prefix} resent an email to {0}"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:366
|
#: packages/lib/utils/document-audit-logs.ts:295
|
||||||
|
msgid "{prefix} restored the document"
|
||||||
|
msgstr "{prefix} restored the document"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:370
|
||||||
msgid "{prefix} sent an email to {0}"
|
msgid "{prefix} sent an email to {0}"
|
||||||
msgstr "{prefix} sent an email to {0}"
|
msgstr "{prefix} sent an email to {0}"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:331
|
#: packages/lib/utils/document-audit-logs.ts:335
|
||||||
msgid "{prefix} sent the document"
|
msgid "{prefix} sent the document"
|
||||||
msgstr "{prefix} sent the document"
|
msgstr "{prefix} sent the document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:295
|
#: packages/lib/utils/document-audit-logs.ts:299
|
||||||
msgid "{prefix} signed a field"
|
msgid "{prefix} signed a field"
|
||||||
msgstr "{prefix} signed a field"
|
msgstr "{prefix} signed a field"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:299
|
#: packages/lib/utils/document-audit-logs.ts:303
|
||||||
msgid "{prefix} unsigned a field"
|
msgid "{prefix} unsigned a field"
|
||||||
msgstr "{prefix} unsigned a field"
|
msgstr "{prefix} unsigned a field"
|
||||||
|
|
||||||
@@ -174,27 +178,27 @@ msgstr "{prefix} updated a field"
|
|||||||
msgid "{prefix} updated a recipient"
|
msgid "{prefix} updated a recipient"
|
||||||
msgstr "{prefix} updated a recipient"
|
msgstr "{prefix} updated a recipient"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:315
|
#: packages/lib/utils/document-audit-logs.ts:319
|
||||||
msgid "{prefix} updated the document"
|
msgid "{prefix} updated the document"
|
||||||
msgstr "{prefix} updated the document"
|
msgstr "{prefix} updated the document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:307
|
#: packages/lib/utils/document-audit-logs.ts:311
|
||||||
msgid "{prefix} updated the document access auth requirements"
|
msgid "{prefix} updated the document access auth requirements"
|
||||||
msgstr "{prefix} updated the document access auth requirements"
|
msgstr "{prefix} updated the document access auth requirements"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:327
|
#: packages/lib/utils/document-audit-logs.ts:331
|
||||||
msgid "{prefix} updated the document external ID"
|
msgid "{prefix} updated the document external ID"
|
||||||
msgstr "{prefix} updated the document external ID"
|
msgstr "{prefix} updated the document external ID"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:311
|
#: packages/lib/utils/document-audit-logs.ts:315
|
||||||
msgid "{prefix} updated the document signing auth requirements"
|
msgid "{prefix} updated the document signing auth requirements"
|
||||||
msgstr "{prefix} updated the document signing auth requirements"
|
msgstr "{prefix} updated the document signing auth requirements"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:323
|
#: packages/lib/utils/document-audit-logs.ts:327
|
||||||
msgid "{prefix} updated the document title"
|
msgid "{prefix} updated the document title"
|
||||||
msgstr "{prefix} updated the document title"
|
msgstr "{prefix} updated the document title"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:303
|
#: packages/lib/utils/document-audit-logs.ts:307
|
||||||
msgid "{prefix} updated the document visibility"
|
msgid "{prefix} updated the document visibility"
|
||||||
msgstr "{prefix} updated the document visibility"
|
msgstr "{prefix} updated the document visibility"
|
||||||
|
|
||||||
@@ -222,27 +226,27 @@ msgstr "{teamName} has invited you to {action} {documentName}"
|
|||||||
msgid "{teamName} ownership transfer request"
|
msgid "{teamName} ownership transfer request"
|
||||||
msgstr "{teamName} ownership transfer request"
|
msgstr "{teamName} ownership transfer request"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:343
|
#: packages/lib/utils/document-audit-logs.ts:347
|
||||||
msgid "{userName} approved the document"
|
msgid "{userName} approved the document"
|
||||||
msgstr "{userName} approved the document"
|
msgstr "{userName} approved the document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:344
|
#: packages/lib/utils/document-audit-logs.ts:348
|
||||||
msgid "{userName} CC'd the document"
|
msgid "{userName} CC'd the document"
|
||||||
msgstr "{userName} CC'd the document"
|
msgstr "{userName} CC'd the document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:345
|
#: packages/lib/utils/document-audit-logs.ts:349
|
||||||
msgid "{userName} completed their task"
|
msgid "{userName} completed their task"
|
||||||
msgstr "{userName} completed their task"
|
msgstr "{userName} completed their task"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:355
|
#: packages/lib/utils/document-audit-logs.ts:359
|
||||||
msgid "{userName} rejected the document"
|
msgid "{userName} rejected the document"
|
||||||
msgstr "{userName} rejected the document"
|
msgstr "{userName} rejected the document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:341
|
#: packages/lib/utils/document-audit-logs.ts:345
|
||||||
msgid "{userName} signed the document"
|
msgid "{userName} signed the document"
|
||||||
msgstr "{userName} signed the document"
|
msgstr "{userName} signed the document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:342
|
#: packages/lib/utils/document-audit-logs.ts:346
|
||||||
msgid "{userName} viewed the document"
|
msgid "{userName} viewed the document"
|
||||||
msgstr "{userName} viewed the document"
|
msgstr "{userName} viewed the document"
|
||||||
|
|
||||||
@@ -439,7 +443,7 @@ msgid "Advanced Options"
|
|||||||
msgstr "Advanced Options"
|
msgstr "Advanced Options"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:576
|
#: packages/ui/primitives/document-flow/add-fields.tsx:576
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:409
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:414
|
||||||
msgid "Advanced settings"
|
msgid "Advanced settings"
|
||||||
msgstr "Advanced settings"
|
msgstr "Advanced settings"
|
||||||
|
|
||||||
@@ -495,11 +499,11 @@ msgstr "Approving"
|
|||||||
msgid "Before you get started, please confirm your email address by clicking the button below:"
|
msgid "Before you get started, please confirm your email address by clicking the button below:"
|
||||||
msgstr "Before you get started, please confirm your email address by clicking the button below:"
|
msgstr "Before you get started, please confirm your email address by clicking the button below:"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:377
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:383
|
||||||
msgid "Black"
|
msgid "Black"
|
||||||
msgstr "Black"
|
msgstr "Black"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:391
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:397
|
||||||
msgid "Blue"
|
msgid "Blue"
|
||||||
msgstr "Blue"
|
msgstr "Blue"
|
||||||
|
|
||||||
@@ -557,7 +561,7 @@ msgstr "Checkbox values"
|
|||||||
msgid "Clear filters"
|
msgid "Clear filters"
|
||||||
msgstr "Clear filters"
|
msgstr "Clear filters"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:411
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:417
|
||||||
msgid "Clear Signature"
|
msgid "Clear Signature"
|
||||||
msgstr "Clear Signature"
|
msgstr "Clear Signature"
|
||||||
|
|
||||||
@@ -585,7 +589,7 @@ msgid "Configure Direct Recipient"
|
|||||||
msgstr "Configure Direct Recipient"
|
msgstr "Configure Direct Recipient"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:410
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:415
|
||||||
msgid "Configure the {0} field"
|
msgid "Configure the {0} field"
|
||||||
msgstr "Configure the {0} field"
|
msgstr "Configure the {0} field"
|
||||||
|
|
||||||
@@ -648,7 +652,7 @@ msgstr "Custom Text"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:934
|
#: packages/ui/primitives/document-flow/add-fields.tsx:934
|
||||||
#: packages/ui/primitives/document-flow/types.ts:53
|
#: packages/ui/primitives/document-flow/types.ts:53
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:697
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:729
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Date"
|
msgstr "Date"
|
||||||
|
|
||||||
@@ -683,17 +687,17 @@ msgstr "Document \"{0}\" - Rejection Confirmed"
|
|||||||
msgid "Document access"
|
msgid "Document access"
|
||||||
msgstr "Document access"
|
msgstr "Document access"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:306
|
#: packages/lib/utils/document-audit-logs.ts:310
|
||||||
msgid "Document access auth updated"
|
msgid "Document access auth updated"
|
||||||
msgstr "Document access auth updated"
|
msgstr "Document access auth updated"
|
||||||
|
|
||||||
#: packages/lib/server-only/document/delete-document.ts:246
|
#: packages/lib/server-only/document/delete-document.ts:256
|
||||||
#: packages/lib/server-only/document/super-delete-document.ts:98
|
#: packages/lib/server-only/document/super-delete-document.ts:98
|
||||||
msgid "Document Cancelled"
|
msgid "Document Cancelled"
|
||||||
msgstr "Document Cancelled"
|
msgstr "Document Cancelled"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:369
|
#: packages/lib/utils/document-audit-logs.ts:373
|
||||||
#: packages/lib/utils/document-audit-logs.ts:370
|
#: packages/lib/utils/document-audit-logs.ts:374
|
||||||
msgid "Document completed"
|
msgid "Document completed"
|
||||||
msgstr "Document completed"
|
msgstr "Document completed"
|
||||||
|
|
||||||
@@ -731,15 +735,15 @@ msgstr "Document Deleted!"
|
|||||||
msgid "Document Distribution Method"
|
msgid "Document Distribution Method"
|
||||||
msgstr "Document Distribution Method"
|
msgstr "Document Distribution Method"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:326
|
#: packages/lib/utils/document-audit-logs.ts:330
|
||||||
msgid "Document external ID updated"
|
msgid "Document external ID updated"
|
||||||
msgstr "Document external ID updated"
|
msgstr "Document external ID updated"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:334
|
#: packages/lib/utils/document-audit-logs.ts:338
|
||||||
msgid "Document moved to team"
|
msgid "Document moved to team"
|
||||||
msgstr "Document moved to team"
|
msgstr "Document moved to team"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:318
|
#: packages/lib/utils/document-audit-logs.ts:322
|
||||||
msgid "Document opened"
|
msgid "Document opened"
|
||||||
msgstr "Document opened"
|
msgstr "Document opened"
|
||||||
|
|
||||||
@@ -754,23 +758,27 @@ msgstr "Document Rejected"
|
|||||||
#~ msgid "Document Rejection Confirmed"
|
#~ msgid "Document Rejection Confirmed"
|
||||||
#~ msgstr "Document Rejection Confirmed"
|
#~ msgstr "Document Rejection Confirmed"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:330
|
#: packages/lib/utils/document-audit-logs.ts:294
|
||||||
|
msgid "Document restored"
|
||||||
|
msgstr "Document restored"
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:334
|
||||||
msgid "Document sent"
|
msgid "Document sent"
|
||||||
msgstr "Document sent"
|
msgstr "Document sent"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:310
|
#: packages/lib/utils/document-audit-logs.ts:314
|
||||||
msgid "Document signing auth updated"
|
msgid "Document signing auth updated"
|
||||||
msgstr "Document signing auth updated"
|
msgstr "Document signing auth updated"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:322
|
#: packages/lib/utils/document-audit-logs.ts:326
|
||||||
msgid "Document title updated"
|
msgid "Document title updated"
|
||||||
msgstr "Document title updated"
|
msgstr "Document title updated"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:314
|
#: packages/lib/utils/document-audit-logs.ts:318
|
||||||
msgid "Document updated"
|
msgid "Document updated"
|
||||||
msgstr "Document updated"
|
msgstr "Document updated"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:302
|
#: packages/lib/utils/document-audit-logs.ts:306
|
||||||
msgid "Document visibility updated"
|
msgid "Document visibility updated"
|
||||||
msgstr "Document visibility updated"
|
msgstr "Document visibility updated"
|
||||||
|
|
||||||
@@ -788,7 +796,7 @@ msgid "Drag & drop your PDF here."
|
|||||||
msgstr "Drag & drop your PDF here."
|
msgstr "Drag & drop your PDF here."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1065
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1065
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:827
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:860
|
||||||
msgid "Dropdown"
|
msgid "Dropdown"
|
||||||
msgstr "Dropdown"
|
msgstr "Dropdown"
|
||||||
|
|
||||||
@@ -802,7 +810,7 @@ msgstr "Dropdown options"
|
|||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:512
|
#: packages/ui/primitives/document-flow/add-signers.tsx:512
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:519
|
#: packages/ui/primitives/document-flow/add-signers.tsx:519
|
||||||
#: packages/ui/primitives/document-flow/types.ts:54
|
#: packages/ui/primitives/document-flow/types.ts:54
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:645
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:677
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:471
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:471
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:478
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:478
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
@@ -816,11 +824,11 @@ msgstr "Email is required"
|
|||||||
msgid "Email Options"
|
msgid "Email Options"
|
||||||
msgstr "Email Options"
|
msgstr "Email Options"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:363
|
#: packages/lib/utils/document-audit-logs.ts:367
|
||||||
msgid "Email resent"
|
msgid "Email resent"
|
||||||
msgstr "Email resent"
|
msgstr "Email resent"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:363
|
#: packages/lib/utils/document-audit-logs.ts:367
|
||||||
msgid "Email sent"
|
msgid "Email sent"
|
||||||
msgstr "Email sent"
|
msgstr "Email sent"
|
||||||
|
|
||||||
@@ -838,6 +846,7 @@ msgid "Enable signing order"
|
|||||||
msgstr "Enable signing order"
|
msgstr "Enable signing order"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:802
|
#: packages/ui/primitives/document-flow/add-fields.tsx:802
|
||||||
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:597
|
||||||
msgid "Enable Typed Signatures"
|
msgid "Enable Typed Signatures"
|
||||||
msgstr "Enable Typed Signatures"
|
msgstr "Enable Typed Signatures"
|
||||||
|
|
||||||
@@ -884,11 +893,11 @@ msgstr "Field label"
|
|||||||
msgid "Field placeholder"
|
msgid "Field placeholder"
|
||||||
msgstr "Field placeholder"
|
msgstr "Field placeholder"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:294
|
#: packages/lib/utils/document-audit-logs.ts:298
|
||||||
msgid "Field signed"
|
msgid "Field signed"
|
||||||
msgstr "Field signed"
|
msgstr "Field signed"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:298
|
#: packages/lib/utils/document-audit-logs.ts:302
|
||||||
msgid "Field unsigned"
|
msgid "Field unsigned"
|
||||||
msgstr "Field unsigned"
|
msgstr "Field unsigned"
|
||||||
|
|
||||||
@@ -925,7 +934,7 @@ msgstr "Global recipient action authentication"
|
|||||||
msgid "Go Back"
|
msgid "Go Back"
|
||||||
msgstr "Go Back"
|
msgstr "Go Back"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:398
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:404
|
||||||
msgid "Green"
|
msgid "Green"
|
||||||
msgstr "Green"
|
msgstr "Green"
|
||||||
|
|
||||||
@@ -1020,7 +1029,7 @@ msgstr "Min"
|
|||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:550
|
#: packages/ui/primitives/document-flow/add-signers.tsx:550
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:556
|
#: packages/ui/primitives/document-flow/add-signers.tsx:556
|
||||||
#: packages/ui/primitives/document-flow/types.ts:55
|
#: packages/ui/primitives/document-flow/types.ts:55
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:671
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:703
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:506
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:506
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:512
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:512
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
@@ -1039,7 +1048,7 @@ msgid "Needs to view"
|
|||||||
msgstr "Needs to view"
|
msgstr "Needs to view"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:693
|
#: packages/ui/primitives/document-flow/add-fields.tsx:693
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:511
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:516
|
||||||
msgid "No recipient matching this description was found."
|
msgid "No recipient matching this description was found."
|
||||||
msgstr "No recipient matching this description was found."
|
msgstr "No recipient matching this description was found."
|
||||||
|
|
||||||
@@ -1048,7 +1057,7 @@ msgid "No recipients"
|
|||||||
msgstr "No recipients"
|
msgstr "No recipients"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:708
|
#: packages/ui/primitives/document-flow/add-fields.tsx:708
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:526
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:531
|
||||||
msgid "No recipients with this role"
|
msgid "No recipients with this role"
|
||||||
msgstr "No recipients with this role"
|
msgstr "No recipients with this role"
|
||||||
|
|
||||||
@@ -1078,7 +1087,7 @@ msgstr "None"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:986
|
#: packages/ui/primitives/document-flow/add-fields.tsx:986
|
||||||
#: packages/ui/primitives/document-flow/types.ts:56
|
#: packages/ui/primitives/document-flow/types.ts:56
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:749
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:781
|
||||||
msgid "Number"
|
msgid "Number"
|
||||||
msgstr "Number"
|
msgstr "Number"
|
||||||
|
|
||||||
@@ -1170,7 +1179,6 @@ msgid "Please try again or contact our support."
|
|||||||
msgstr "Please try again or contact our support."
|
msgstr "Please try again or contact our support."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/types.ts:57
|
#: packages/ui/primitives/document-flow/types.ts:57
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:775
|
|
||||||
msgid "Radio"
|
msgid "Radio"
|
||||||
msgstr "Radio"
|
msgstr "Radio"
|
||||||
|
|
||||||
@@ -1194,8 +1202,8 @@ msgstr "Reason for rejection: {rejectionReason}"
|
|||||||
msgid "Receives copy"
|
msgid "Receives copy"
|
||||||
msgstr "Receives copy"
|
msgstr "Receives copy"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:338
|
#: packages/lib/utils/document-audit-logs.ts:342
|
||||||
#: packages/lib/utils/document-audit-logs.ts:353
|
#: packages/lib/utils/document-audit-logs.ts:357
|
||||||
msgid "Recipient"
|
msgid "Recipient"
|
||||||
msgstr "Recipient"
|
msgstr "Recipient"
|
||||||
|
|
||||||
@@ -1213,7 +1221,7 @@ msgstr "Recipient removed email"
|
|||||||
msgid "Recipient signing request email"
|
msgid "Recipient signing request email"
|
||||||
msgstr "Recipient signing request email"
|
msgstr "Recipient signing request email"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:384
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:390
|
||||||
msgid "Red"
|
msgid "Red"
|
||||||
msgstr "Red"
|
msgstr "Red"
|
||||||
|
|
||||||
@@ -1282,7 +1290,7 @@ msgstr "Rows per page"
|
|||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Save"
|
msgstr "Save"
|
||||||
|
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:861
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:893
|
||||||
msgid "Save Template"
|
msgid "Save Template"
|
||||||
msgstr "Save Template"
|
msgstr "Save Template"
|
||||||
|
|
||||||
@@ -1375,7 +1383,7 @@ msgstr "Sign In"
|
|||||||
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
||||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||||
#: packages/ui/primitives/document-flow/types.ts:49
|
#: packages/ui/primitives/document-flow/types.ts:49
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:593
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:625
|
||||||
msgid "Signature"
|
msgid "Signature"
|
||||||
msgstr "Signature"
|
msgstr "Signature"
|
||||||
|
|
||||||
@@ -1460,7 +1468,7 @@ msgstr "Template title"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:960
|
#: packages/ui/primitives/document-flow/add-fields.tsx:960
|
||||||
#: packages/ui/primitives/document-flow/types.ts:52
|
#: packages/ui/primitives/document-flow/types.ts:52
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:723
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:755
|
||||||
msgid "Text"
|
msgid "Text"
|
||||||
msgstr "Text"
|
msgstr "Text"
|
||||||
|
|
||||||
@@ -1624,7 +1632,7 @@ msgid "Title"
|
|||||||
msgstr "Title"
|
msgstr "Title"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1080
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1080
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:841
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:873
|
||||||
msgid "To proceed further, please set at least one value for the {0} field."
|
msgid "To proceed further, please set at least one value for the {0} field."
|
||||||
msgstr "To proceed further, please set at least one value for the {0} field."
|
msgstr "To proceed further, please set at least one value for the {0} field."
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -135,11 +135,11 @@ msgstr "{prefix} creó el documento"
|
|||||||
msgid "{prefix} deleted the document"
|
msgid "{prefix} deleted the document"
|
||||||
msgstr "{prefix} eliminó el documento"
|
msgstr "{prefix} eliminó el documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:335
|
#: packages/lib/utils/document-audit-logs.ts:339
|
||||||
msgid "{prefix} moved the document to team"
|
msgid "{prefix} moved the document to team"
|
||||||
msgstr "{prefix} movió el documento al equipo"
|
msgstr "{prefix} movió el documento al equipo"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:319
|
#: packages/lib/utils/document-audit-logs.ts:323
|
||||||
msgid "{prefix} opened the document"
|
msgid "{prefix} opened the document"
|
||||||
msgstr "{prefix} abrió el documento"
|
msgstr "{prefix} abrió el documento"
|
||||||
|
|
||||||
@@ -151,23 +151,27 @@ msgstr "{prefix} eliminó un campo"
|
|||||||
msgid "{prefix} removed a recipient"
|
msgid "{prefix} removed a recipient"
|
||||||
msgstr "{prefix} eliminó un destinatario"
|
msgstr "{prefix} eliminó un destinatario"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:365
|
#: packages/lib/utils/document-audit-logs.ts:369
|
||||||
msgid "{prefix} resent an email to {0}"
|
msgid "{prefix} resent an email to {0}"
|
||||||
msgstr "{prefix} reenviaron un correo electrónico a {0}"
|
msgstr "{prefix} reenviaron un correo electrónico a {0}"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:366
|
#: packages/lib/utils/document-audit-logs.ts:295
|
||||||
|
msgid "{prefix} restored the document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:370
|
||||||
msgid "{prefix} sent an email to {0}"
|
msgid "{prefix} sent an email to {0}"
|
||||||
msgstr "{prefix} envió un correo electrónico a {0}"
|
msgstr "{prefix} envió un correo electrónico a {0}"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:331
|
#: packages/lib/utils/document-audit-logs.ts:335
|
||||||
msgid "{prefix} sent the document"
|
msgid "{prefix} sent the document"
|
||||||
msgstr "{prefix} envió el documento"
|
msgstr "{prefix} envió el documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:295
|
#: packages/lib/utils/document-audit-logs.ts:299
|
||||||
msgid "{prefix} signed a field"
|
msgid "{prefix} signed a field"
|
||||||
msgstr "{prefix} firmó un campo"
|
msgstr "{prefix} firmó un campo"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:299
|
#: packages/lib/utils/document-audit-logs.ts:303
|
||||||
msgid "{prefix} unsigned a field"
|
msgid "{prefix} unsigned a field"
|
||||||
msgstr "{prefix} no firmó un campo"
|
msgstr "{prefix} no firmó un campo"
|
||||||
|
|
||||||
@@ -179,27 +183,27 @@ msgstr "{prefix} actualizó un campo"
|
|||||||
msgid "{prefix} updated a recipient"
|
msgid "{prefix} updated a recipient"
|
||||||
msgstr "{prefix} actualizó un destinatario"
|
msgstr "{prefix} actualizó un destinatario"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:315
|
#: packages/lib/utils/document-audit-logs.ts:319
|
||||||
msgid "{prefix} updated the document"
|
msgid "{prefix} updated the document"
|
||||||
msgstr "{prefix} actualizó el documento"
|
msgstr "{prefix} actualizó el documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:307
|
#: packages/lib/utils/document-audit-logs.ts:311
|
||||||
msgid "{prefix} updated the document access auth requirements"
|
msgid "{prefix} updated the document access auth requirements"
|
||||||
msgstr "{prefix} actualizó los requisitos de autorización de acceso al documento"
|
msgstr "{prefix} actualizó los requisitos de autorización de acceso al documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:327
|
#: packages/lib/utils/document-audit-logs.ts:331
|
||||||
msgid "{prefix} updated the document external ID"
|
msgid "{prefix} updated the document external ID"
|
||||||
msgstr "{prefix} actualizó el ID externo del documento"
|
msgstr "{prefix} actualizó el ID externo del documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:311
|
#: packages/lib/utils/document-audit-logs.ts:315
|
||||||
msgid "{prefix} updated the document signing auth requirements"
|
msgid "{prefix} updated the document signing auth requirements"
|
||||||
msgstr "{prefix} actualizó los requisitos de autenticación para la firma del documento"
|
msgstr "{prefix} actualizó los requisitos de autenticación para la firma del documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:323
|
#: packages/lib/utils/document-audit-logs.ts:327
|
||||||
msgid "{prefix} updated the document title"
|
msgid "{prefix} updated the document title"
|
||||||
msgstr "{prefix} actualizó el título del documento"
|
msgstr "{prefix} actualizó el título del documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:303
|
#: packages/lib/utils/document-audit-logs.ts:307
|
||||||
msgid "{prefix} updated the document visibility"
|
msgid "{prefix} updated the document visibility"
|
||||||
msgstr "{prefix} actualizó la visibilidad del documento"
|
msgstr "{prefix} actualizó la visibilidad del documento"
|
||||||
|
|
||||||
@@ -227,27 +231,27 @@ msgstr "{teamName} te ha invitado a {action} {documentName}"
|
|||||||
msgid "{teamName} ownership transfer request"
|
msgid "{teamName} ownership transfer request"
|
||||||
msgstr "solicitud de transferencia de propiedad de {teamName}"
|
msgstr "solicitud de transferencia de propiedad de {teamName}"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:343
|
#: packages/lib/utils/document-audit-logs.ts:347
|
||||||
msgid "{userName} approved the document"
|
msgid "{userName} approved the document"
|
||||||
msgstr "{userName} aprobó el documento"
|
msgstr "{userName} aprobó el documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:344
|
#: packages/lib/utils/document-audit-logs.ts:348
|
||||||
msgid "{userName} CC'd the document"
|
msgid "{userName} CC'd the document"
|
||||||
msgstr "{userName} envió una copia del documento"
|
msgstr "{userName} envió una copia del documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:345
|
#: packages/lib/utils/document-audit-logs.ts:349
|
||||||
msgid "{userName} completed their task"
|
msgid "{userName} completed their task"
|
||||||
msgstr "{userName} completó su tarea"
|
msgstr "{userName} completó su tarea"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:355
|
#: packages/lib/utils/document-audit-logs.ts:359
|
||||||
msgid "{userName} rejected the document"
|
msgid "{userName} rejected the document"
|
||||||
msgstr "{userName} rechazó el documento"
|
msgstr "{userName} rechazó el documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:341
|
#: packages/lib/utils/document-audit-logs.ts:345
|
||||||
msgid "{userName} signed the document"
|
msgid "{userName} signed the document"
|
||||||
msgstr "{userName} firmó el documento"
|
msgstr "{userName} firmó el documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:342
|
#: packages/lib/utils/document-audit-logs.ts:346
|
||||||
msgid "{userName} viewed the document"
|
msgid "{userName} viewed the document"
|
||||||
msgstr "{userName} vio el documento"
|
msgstr "{userName} vio el documento"
|
||||||
|
|
||||||
@@ -444,7 +448,7 @@ msgid "Advanced Options"
|
|||||||
msgstr "Opciones avanzadas"
|
msgstr "Opciones avanzadas"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:576
|
#: packages/ui/primitives/document-flow/add-fields.tsx:576
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:409
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:414
|
||||||
msgid "Advanced settings"
|
msgid "Advanced settings"
|
||||||
msgstr "Configuraciones avanzadas"
|
msgstr "Configuraciones avanzadas"
|
||||||
|
|
||||||
@@ -500,11 +504,11 @@ msgstr "Aprobando"
|
|||||||
msgid "Before you get started, please confirm your email address by clicking the button below:"
|
msgid "Before you get started, please confirm your email address by clicking the button below:"
|
||||||
msgstr "Antes de comenzar, por favor confirma tu dirección de correo electrónico haciendo clic en el botón de abajo:"
|
msgstr "Antes de comenzar, por favor confirma tu dirección de correo electrónico haciendo clic en el botón de abajo:"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:377
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:383
|
||||||
msgid "Black"
|
msgid "Black"
|
||||||
msgstr "Negro"
|
msgstr "Negro"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:391
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:397
|
||||||
msgid "Blue"
|
msgid "Blue"
|
||||||
msgstr "Azul"
|
msgstr "Azul"
|
||||||
|
|
||||||
@@ -562,7 +566,7 @@ msgstr "Valores de Checkbox"
|
|||||||
msgid "Clear filters"
|
msgid "Clear filters"
|
||||||
msgstr "Limpiar filtros"
|
msgstr "Limpiar filtros"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:411
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:417
|
||||||
msgid "Clear Signature"
|
msgid "Clear Signature"
|
||||||
msgstr "Limpiar firma"
|
msgstr "Limpiar firma"
|
||||||
|
|
||||||
@@ -590,7 +594,7 @@ msgid "Configure Direct Recipient"
|
|||||||
msgstr "Configurar destinatario directo"
|
msgstr "Configurar destinatario directo"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:410
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:415
|
||||||
msgid "Configure the {0} field"
|
msgid "Configure the {0} field"
|
||||||
msgstr "Configurar el campo {0}"
|
msgstr "Configurar el campo {0}"
|
||||||
|
|
||||||
@@ -653,7 +657,7 @@ msgstr "Texto personalizado"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:934
|
#: packages/ui/primitives/document-flow/add-fields.tsx:934
|
||||||
#: packages/ui/primitives/document-flow/types.ts:53
|
#: packages/ui/primitives/document-flow/types.ts:53
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:697
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:729
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Fecha"
|
msgstr "Fecha"
|
||||||
|
|
||||||
@@ -688,17 +692,17 @@ msgstr "Documento \"{0}\" - Rechazo confirmado"
|
|||||||
msgid "Document access"
|
msgid "Document access"
|
||||||
msgstr "Acceso al documento"
|
msgstr "Acceso al documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:306
|
#: packages/lib/utils/document-audit-logs.ts:310
|
||||||
msgid "Document access auth updated"
|
msgid "Document access auth updated"
|
||||||
msgstr "Se actualizó la autenticación de acceso al documento"
|
msgstr "Se actualizó la autenticación de acceso al documento"
|
||||||
|
|
||||||
#: packages/lib/server-only/document/delete-document.ts:246
|
#: packages/lib/server-only/document/delete-document.ts:256
|
||||||
#: packages/lib/server-only/document/super-delete-document.ts:98
|
#: packages/lib/server-only/document/super-delete-document.ts:98
|
||||||
msgid "Document Cancelled"
|
msgid "Document Cancelled"
|
||||||
msgstr "Documento cancelado"
|
msgstr "Documento cancelado"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:369
|
#: packages/lib/utils/document-audit-logs.ts:373
|
||||||
#: packages/lib/utils/document-audit-logs.ts:370
|
#: packages/lib/utils/document-audit-logs.ts:374
|
||||||
msgid "Document completed"
|
msgid "Document completed"
|
||||||
msgstr "Documento completado"
|
msgstr "Documento completado"
|
||||||
|
|
||||||
@@ -736,15 +740,15 @@ msgstr "¡Documento eliminado!"
|
|||||||
msgid "Document Distribution Method"
|
msgid "Document Distribution Method"
|
||||||
msgstr "Método de distribución de documentos"
|
msgstr "Método de distribución de documentos"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:326
|
#: packages/lib/utils/document-audit-logs.ts:330
|
||||||
msgid "Document external ID updated"
|
msgid "Document external ID updated"
|
||||||
msgstr "ID externo del documento actualizado"
|
msgstr "ID externo del documento actualizado"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:334
|
#: packages/lib/utils/document-audit-logs.ts:338
|
||||||
msgid "Document moved to team"
|
msgid "Document moved to team"
|
||||||
msgstr "Documento movido al equipo"
|
msgstr "Documento movido al equipo"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:318
|
#: packages/lib/utils/document-audit-logs.ts:322
|
||||||
msgid "Document opened"
|
msgid "Document opened"
|
||||||
msgstr "Documento abierto"
|
msgstr "Documento abierto"
|
||||||
|
|
||||||
@@ -759,23 +763,27 @@ msgstr "Documento Rechazado"
|
|||||||
#~ msgid "Document Rejection Confirmed"
|
#~ msgid "Document Rejection Confirmed"
|
||||||
#~ msgstr "Document Rejection Confirmed"
|
#~ msgstr "Document Rejection Confirmed"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:330
|
#: packages/lib/utils/document-audit-logs.ts:294
|
||||||
|
msgid "Document restored"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:334
|
||||||
msgid "Document sent"
|
msgid "Document sent"
|
||||||
msgstr "Documento enviado"
|
msgstr "Documento enviado"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:310
|
#: packages/lib/utils/document-audit-logs.ts:314
|
||||||
msgid "Document signing auth updated"
|
msgid "Document signing auth updated"
|
||||||
msgstr "Se actualizó la autenticación de firma del documento"
|
msgstr "Se actualizó la autenticación de firma del documento"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:322
|
#: packages/lib/utils/document-audit-logs.ts:326
|
||||||
msgid "Document title updated"
|
msgid "Document title updated"
|
||||||
msgstr "Título del documento actualizado"
|
msgstr "Título del documento actualizado"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:314
|
#: packages/lib/utils/document-audit-logs.ts:318
|
||||||
msgid "Document updated"
|
msgid "Document updated"
|
||||||
msgstr "Documento actualizado"
|
msgstr "Documento actualizado"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:302
|
#: packages/lib/utils/document-audit-logs.ts:306
|
||||||
msgid "Document visibility updated"
|
msgid "Document visibility updated"
|
||||||
msgstr "Visibilidad del documento actualizada"
|
msgstr "Visibilidad del documento actualizada"
|
||||||
|
|
||||||
@@ -793,7 +801,7 @@ msgid "Drag & drop your PDF here."
|
|||||||
msgstr "Arrastre y suelte su PDF aquí."
|
msgstr "Arrastre y suelte su PDF aquí."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1065
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1065
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:827
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:860
|
||||||
msgid "Dropdown"
|
msgid "Dropdown"
|
||||||
msgstr "Menú desplegable"
|
msgstr "Menú desplegable"
|
||||||
|
|
||||||
@@ -807,7 +815,7 @@ msgstr "Opciones de menú desplegable"
|
|||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:512
|
#: packages/ui/primitives/document-flow/add-signers.tsx:512
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:519
|
#: packages/ui/primitives/document-flow/add-signers.tsx:519
|
||||||
#: packages/ui/primitives/document-flow/types.ts:54
|
#: packages/ui/primitives/document-flow/types.ts:54
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:645
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:677
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:471
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:471
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:478
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:478
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
@@ -821,11 +829,11 @@ msgstr "Se requiere email"
|
|||||||
msgid "Email Options"
|
msgid "Email Options"
|
||||||
msgstr "Opciones de correo electrónico"
|
msgstr "Opciones de correo electrónico"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:363
|
#: packages/lib/utils/document-audit-logs.ts:367
|
||||||
msgid "Email resent"
|
msgid "Email resent"
|
||||||
msgstr "Correo electrónico reeenviado"
|
msgstr "Correo electrónico reeenviado"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:363
|
#: packages/lib/utils/document-audit-logs.ts:367
|
||||||
msgid "Email sent"
|
msgid "Email sent"
|
||||||
msgstr "Correo electrónico enviado"
|
msgstr "Correo electrónico enviado"
|
||||||
|
|
||||||
@@ -843,6 +851,7 @@ msgid "Enable signing order"
|
|||||||
msgstr "Habilitar orden de firma"
|
msgstr "Habilitar orden de firma"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:802
|
#: packages/ui/primitives/document-flow/add-fields.tsx:802
|
||||||
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:597
|
||||||
msgid "Enable Typed Signatures"
|
msgid "Enable Typed Signatures"
|
||||||
msgstr "Habilitar firmas escritas"
|
msgstr "Habilitar firmas escritas"
|
||||||
|
|
||||||
@@ -889,11 +898,11 @@ msgstr "Etiqueta de campo"
|
|||||||
msgid "Field placeholder"
|
msgid "Field placeholder"
|
||||||
msgstr "Marcador de posición de campo"
|
msgstr "Marcador de posición de campo"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:294
|
#: packages/lib/utils/document-audit-logs.ts:298
|
||||||
msgid "Field signed"
|
msgid "Field signed"
|
||||||
msgstr "Campo firmado"
|
msgstr "Campo firmado"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:298
|
#: packages/lib/utils/document-audit-logs.ts:302
|
||||||
msgid "Field unsigned"
|
msgid "Field unsigned"
|
||||||
msgstr "Campo no firmado"
|
msgstr "Campo no firmado"
|
||||||
|
|
||||||
@@ -930,7 +939,7 @@ msgstr "Autenticación de acción de destinatario global"
|
|||||||
msgid "Go Back"
|
msgid "Go Back"
|
||||||
msgstr "Regresar"
|
msgstr "Regresar"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:398
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:404
|
||||||
msgid "Green"
|
msgid "Green"
|
||||||
msgstr "Verde"
|
msgstr "Verde"
|
||||||
|
|
||||||
@@ -1025,7 +1034,7 @@ msgstr "Mín"
|
|||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:550
|
#: packages/ui/primitives/document-flow/add-signers.tsx:550
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:556
|
#: packages/ui/primitives/document-flow/add-signers.tsx:556
|
||||||
#: packages/ui/primitives/document-flow/types.ts:55
|
#: packages/ui/primitives/document-flow/types.ts:55
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:671
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:703
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:506
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:506
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:512
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:512
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
@@ -1044,7 +1053,7 @@ msgid "Needs to view"
|
|||||||
msgstr "Necesita ver"
|
msgstr "Necesita ver"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:693
|
#: packages/ui/primitives/document-flow/add-fields.tsx:693
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:511
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:516
|
||||||
msgid "No recipient matching this description was found."
|
msgid "No recipient matching this description was found."
|
||||||
msgstr "No se encontró ningún destinatario que coincidiera con esta descripción."
|
msgstr "No se encontró ningún destinatario que coincidiera con esta descripción."
|
||||||
|
|
||||||
@@ -1053,7 +1062,7 @@ msgid "No recipients"
|
|||||||
msgstr "Sin destinatarios"
|
msgstr "Sin destinatarios"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:708
|
#: packages/ui/primitives/document-flow/add-fields.tsx:708
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:526
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:531
|
||||||
msgid "No recipients with this role"
|
msgid "No recipients with this role"
|
||||||
msgstr "No hay destinatarios con este rol"
|
msgstr "No hay destinatarios con este rol"
|
||||||
|
|
||||||
@@ -1083,7 +1092,7 @@ msgstr "Ninguno"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:986
|
#: packages/ui/primitives/document-flow/add-fields.tsx:986
|
||||||
#: packages/ui/primitives/document-flow/types.ts:56
|
#: packages/ui/primitives/document-flow/types.ts:56
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:749
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:781
|
||||||
msgid "Number"
|
msgid "Number"
|
||||||
msgstr "Número"
|
msgstr "Número"
|
||||||
|
|
||||||
@@ -1175,7 +1184,6 @@ msgid "Please try again or contact our support."
|
|||||||
msgstr "Por favor, inténtalo de nuevo o contacta a nuestro soporte."
|
msgstr "Por favor, inténtalo de nuevo o contacta a nuestro soporte."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/types.ts:57
|
#: packages/ui/primitives/document-flow/types.ts:57
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:775
|
|
||||||
msgid "Radio"
|
msgid "Radio"
|
||||||
msgstr "Radio"
|
msgstr "Radio"
|
||||||
|
|
||||||
@@ -1199,8 +1207,8 @@ msgstr "Razón del rechazo: {rejectionReason}"
|
|||||||
msgid "Receives copy"
|
msgid "Receives copy"
|
||||||
msgstr "Recibe copia"
|
msgstr "Recibe copia"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:338
|
#: packages/lib/utils/document-audit-logs.ts:342
|
||||||
#: packages/lib/utils/document-audit-logs.ts:353
|
#: packages/lib/utils/document-audit-logs.ts:357
|
||||||
msgid "Recipient"
|
msgid "Recipient"
|
||||||
msgstr "Destinatario"
|
msgstr "Destinatario"
|
||||||
|
|
||||||
@@ -1218,7 +1226,7 @@ msgstr "Correo electrónico de destinatario eliminado"
|
|||||||
msgid "Recipient signing request email"
|
msgid "Recipient signing request email"
|
||||||
msgstr "Correo electrónico de solicitud de firma de destinatario"
|
msgstr "Correo electrónico de solicitud de firma de destinatario"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:384
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:390
|
||||||
msgid "Red"
|
msgid "Red"
|
||||||
msgstr "Rojo"
|
msgstr "Rojo"
|
||||||
|
|
||||||
@@ -1287,7 +1295,7 @@ msgstr "Filas por página"
|
|||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Guardar"
|
msgstr "Guardar"
|
||||||
|
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:861
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:893
|
||||||
msgid "Save Template"
|
msgid "Save Template"
|
||||||
msgstr "Guardar plantilla"
|
msgstr "Guardar plantilla"
|
||||||
|
|
||||||
@@ -1380,7 +1388,7 @@ msgstr "Iniciar sesión"
|
|||||||
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
||||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||||
#: packages/ui/primitives/document-flow/types.ts:49
|
#: packages/ui/primitives/document-flow/types.ts:49
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:593
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:625
|
||||||
msgid "Signature"
|
msgid "Signature"
|
||||||
msgstr "Firma"
|
msgstr "Firma"
|
||||||
|
|
||||||
@@ -1465,7 +1473,7 @@ msgstr "Título de plantilla"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:960
|
#: packages/ui/primitives/document-flow/add-fields.tsx:960
|
||||||
#: packages/ui/primitives/document-flow/types.ts:52
|
#: packages/ui/primitives/document-flow/types.ts:52
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:723
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:755
|
||||||
msgid "Text"
|
msgid "Text"
|
||||||
msgstr "Texto"
|
msgstr "Texto"
|
||||||
|
|
||||||
@@ -1629,7 +1637,7 @@ msgid "Title"
|
|||||||
msgstr "Título"
|
msgstr "Título"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1080
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1080
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:841
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:873
|
||||||
msgid "To proceed further, please set at least one value for the {0} field."
|
msgid "To proceed further, please set at least one value for the {0} field."
|
||||||
msgstr "Para continuar, por favor establezca al menos un valor para el campo {0}."
|
msgstr "Para continuar, por favor establezca al menos un valor para el campo {0}."
|
||||||
|
|
||||||
@@ -1814,4 +1822,3 @@ msgstr "Tu contraseña ha sido actualizada."
|
|||||||
#: packages/email/templates/team-delete.tsx:32
|
#: packages/email/templates/team-delete.tsx:32
|
||||||
msgid "Your team has been deleted"
|
msgid "Your team has been deleted"
|
||||||
msgstr "Tu equipo ha sido eliminado"
|
msgstr "Tu equipo ha sido eliminado"
|
||||||
|
|
||||||
|
|||||||
@@ -602,4 +602,3 @@ msgstr "Puedes autoalojar Documenso de forma gratuita o usar nuestra versión al
|
|||||||
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
||||||
msgid "Your browser does not support the video tag."
|
msgid "Your browser does not support the video tag."
|
||||||
msgstr "Tu navegador no soporta la etiqueta de video."
|
msgstr "Tu navegador no soporta la etiqueta de video."
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -135,11 +135,11 @@ msgstr "{prefix} a créé le document"
|
|||||||
msgid "{prefix} deleted the document"
|
msgid "{prefix} deleted the document"
|
||||||
msgstr "{prefix} a supprimé le document"
|
msgstr "{prefix} a supprimé le document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:335
|
#: packages/lib/utils/document-audit-logs.ts:339
|
||||||
msgid "{prefix} moved the document to team"
|
msgid "{prefix} moved the document to team"
|
||||||
msgstr "{prefix} a déplacé le document vers l'équipe"
|
msgstr "{prefix} a déplacé le document vers l'équipe"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:319
|
#: packages/lib/utils/document-audit-logs.ts:323
|
||||||
msgid "{prefix} opened the document"
|
msgid "{prefix} opened the document"
|
||||||
msgstr "{prefix} a ouvert le document"
|
msgstr "{prefix} a ouvert le document"
|
||||||
|
|
||||||
@@ -151,23 +151,27 @@ msgstr "{prefix} a supprimé un champ"
|
|||||||
msgid "{prefix} removed a recipient"
|
msgid "{prefix} removed a recipient"
|
||||||
msgstr "{prefix} a supprimé un destinataire"
|
msgstr "{prefix} a supprimé un destinataire"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:365
|
#: packages/lib/utils/document-audit-logs.ts:369
|
||||||
msgid "{prefix} resent an email to {0}"
|
msgid "{prefix} resent an email to {0}"
|
||||||
msgstr "{prefix} a renvoyé un e-mail à {0}"
|
msgstr "{prefix} a renvoyé un e-mail à {0}"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:366
|
#: packages/lib/utils/document-audit-logs.ts:295
|
||||||
|
msgid "{prefix} restored the document"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:370
|
||||||
msgid "{prefix} sent an email to {0}"
|
msgid "{prefix} sent an email to {0}"
|
||||||
msgstr "{prefix} a envoyé un email à {0}"
|
msgstr "{prefix} a envoyé un email à {0}"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:331
|
#: packages/lib/utils/document-audit-logs.ts:335
|
||||||
msgid "{prefix} sent the document"
|
msgid "{prefix} sent the document"
|
||||||
msgstr "{prefix} a envoyé le document"
|
msgstr "{prefix} a envoyé le document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:295
|
#: packages/lib/utils/document-audit-logs.ts:299
|
||||||
msgid "{prefix} signed a field"
|
msgid "{prefix} signed a field"
|
||||||
msgstr "{prefix} a signé un champ"
|
msgstr "{prefix} a signé un champ"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:299
|
#: packages/lib/utils/document-audit-logs.ts:303
|
||||||
msgid "{prefix} unsigned a field"
|
msgid "{prefix} unsigned a field"
|
||||||
msgstr "{prefix} n'a pas signé un champ"
|
msgstr "{prefix} n'a pas signé un champ"
|
||||||
|
|
||||||
@@ -179,27 +183,27 @@ msgstr "{prefix} a mis à jour un champ"
|
|||||||
msgid "{prefix} updated a recipient"
|
msgid "{prefix} updated a recipient"
|
||||||
msgstr "{prefix} a mis à jour un destinataire"
|
msgstr "{prefix} a mis à jour un destinataire"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:315
|
#: packages/lib/utils/document-audit-logs.ts:319
|
||||||
msgid "{prefix} updated the document"
|
msgid "{prefix} updated the document"
|
||||||
msgstr "{prefix} a mis à jour le document"
|
msgstr "{prefix} a mis à jour le document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:307
|
#: packages/lib/utils/document-audit-logs.ts:311
|
||||||
msgid "{prefix} updated the document access auth requirements"
|
msgid "{prefix} updated the document access auth requirements"
|
||||||
msgstr "{prefix} a mis à jour les exigences d'authentification d'accès au document"
|
msgstr "{prefix} a mis à jour les exigences d'authentification d'accès au document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:327
|
#: packages/lib/utils/document-audit-logs.ts:331
|
||||||
msgid "{prefix} updated the document external ID"
|
msgid "{prefix} updated the document external ID"
|
||||||
msgstr "{prefix} a mis à jour l'ID externe du document"
|
msgstr "{prefix} a mis à jour l'ID externe du document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:311
|
#: packages/lib/utils/document-audit-logs.ts:315
|
||||||
msgid "{prefix} updated the document signing auth requirements"
|
msgid "{prefix} updated the document signing auth requirements"
|
||||||
msgstr "{prefix} a mis à jour les exigences d'authentification pour la signature du document"
|
msgstr "{prefix} a mis à jour les exigences d'authentification pour la signature du document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:323
|
#: packages/lib/utils/document-audit-logs.ts:327
|
||||||
msgid "{prefix} updated the document title"
|
msgid "{prefix} updated the document title"
|
||||||
msgstr "{prefix} a mis à jour le titre du document"
|
msgstr "{prefix} a mis à jour le titre du document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:303
|
#: packages/lib/utils/document-audit-logs.ts:307
|
||||||
msgid "{prefix} updated the document visibility"
|
msgid "{prefix} updated the document visibility"
|
||||||
msgstr "{prefix} a mis à jour la visibilité du document"
|
msgstr "{prefix} a mis à jour la visibilité du document"
|
||||||
|
|
||||||
@@ -227,27 +231,27 @@ msgstr "{teamName} vous a invité à {action} {documentName}"
|
|||||||
msgid "{teamName} ownership transfer request"
|
msgid "{teamName} ownership transfer request"
|
||||||
msgstr "Demande de transfert de propriété de {teamName}"
|
msgstr "Demande de transfert de propriété de {teamName}"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:343
|
#: packages/lib/utils/document-audit-logs.ts:347
|
||||||
msgid "{userName} approved the document"
|
msgid "{userName} approved the document"
|
||||||
msgstr "{userName} a approuvé le document"
|
msgstr "{userName} a approuvé le document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:344
|
#: packages/lib/utils/document-audit-logs.ts:348
|
||||||
msgid "{userName} CC'd the document"
|
msgid "{userName} CC'd the document"
|
||||||
msgstr "{userName} a mis en copie le document"
|
msgstr "{userName} a mis en copie le document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:345
|
#: packages/lib/utils/document-audit-logs.ts:349
|
||||||
msgid "{userName} completed their task"
|
msgid "{userName} completed their task"
|
||||||
msgstr "{userName} a complété sa tâche"
|
msgstr "{userName} a complété sa tâche"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:355
|
#: packages/lib/utils/document-audit-logs.ts:359
|
||||||
msgid "{userName} rejected the document"
|
msgid "{userName} rejected the document"
|
||||||
msgstr "{userName} a rejeté le document"
|
msgstr "{userName} a rejeté le document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:341
|
#: packages/lib/utils/document-audit-logs.ts:345
|
||||||
msgid "{userName} signed the document"
|
msgid "{userName} signed the document"
|
||||||
msgstr "{userName} a signé le document"
|
msgstr "{userName} a signé le document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:342
|
#: packages/lib/utils/document-audit-logs.ts:346
|
||||||
msgid "{userName} viewed the document"
|
msgid "{userName} viewed the document"
|
||||||
msgstr "{userName} a consulté le document"
|
msgstr "{userName} a consulté le document"
|
||||||
|
|
||||||
@@ -444,7 +448,7 @@ msgid "Advanced Options"
|
|||||||
msgstr "Options avancées"
|
msgstr "Options avancées"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:576
|
#: packages/ui/primitives/document-flow/add-fields.tsx:576
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:409
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:414
|
||||||
msgid "Advanced settings"
|
msgid "Advanced settings"
|
||||||
msgstr "Paramètres avancés"
|
msgstr "Paramètres avancés"
|
||||||
|
|
||||||
@@ -500,11 +504,11 @@ msgstr "En attente d'approbation"
|
|||||||
msgid "Before you get started, please confirm your email address by clicking the button below:"
|
msgid "Before you get started, please confirm your email address by clicking the button below:"
|
||||||
msgstr "Avant de commencer, veuillez confirmer votre adresse email en cliquant sur le bouton ci-dessous :"
|
msgstr "Avant de commencer, veuillez confirmer votre adresse email en cliquant sur le bouton ci-dessous :"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:377
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:383
|
||||||
msgid "Black"
|
msgid "Black"
|
||||||
msgstr "Noir"
|
msgstr "Noir"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:391
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:397
|
||||||
msgid "Blue"
|
msgid "Blue"
|
||||||
msgstr "Bleu"
|
msgstr "Bleu"
|
||||||
|
|
||||||
@@ -562,7 +566,7 @@ msgstr "Valeurs de case à cocher"
|
|||||||
msgid "Clear filters"
|
msgid "Clear filters"
|
||||||
msgstr "Effacer les filtres"
|
msgstr "Effacer les filtres"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:411
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:417
|
||||||
msgid "Clear Signature"
|
msgid "Clear Signature"
|
||||||
msgstr "Effacer la signature"
|
msgstr "Effacer la signature"
|
||||||
|
|
||||||
@@ -590,7 +594,7 @@ msgid "Configure Direct Recipient"
|
|||||||
msgstr "Configurer le destinataire direct"
|
msgstr "Configurer le destinataire direct"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:410
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:415
|
||||||
msgid "Configure the {0} field"
|
msgid "Configure the {0} field"
|
||||||
msgstr "Configurer le champ {0}"
|
msgstr "Configurer le champ {0}"
|
||||||
|
|
||||||
@@ -653,7 +657,7 @@ msgstr "Texte personnalisé"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:934
|
#: packages/ui/primitives/document-flow/add-fields.tsx:934
|
||||||
#: packages/ui/primitives/document-flow/types.ts:53
|
#: packages/ui/primitives/document-flow/types.ts:53
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:697
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:729
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Date"
|
msgstr "Date"
|
||||||
|
|
||||||
@@ -688,17 +692,17 @@ msgstr "Document \"{0}\" - Rejet Confirmé"
|
|||||||
msgid "Document access"
|
msgid "Document access"
|
||||||
msgstr "Accès au document"
|
msgstr "Accès au document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:306
|
#: packages/lib/utils/document-audit-logs.ts:310
|
||||||
msgid "Document access auth updated"
|
msgid "Document access auth updated"
|
||||||
msgstr "L'authentification d'accès au document a été mise à jour"
|
msgstr "L'authentification d'accès au document a été mise à jour"
|
||||||
|
|
||||||
#: packages/lib/server-only/document/delete-document.ts:246
|
#: packages/lib/server-only/document/delete-document.ts:256
|
||||||
#: packages/lib/server-only/document/super-delete-document.ts:98
|
#: packages/lib/server-only/document/super-delete-document.ts:98
|
||||||
msgid "Document Cancelled"
|
msgid "Document Cancelled"
|
||||||
msgstr "Document Annulé"
|
msgstr "Document Annulé"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:369
|
#: packages/lib/utils/document-audit-logs.ts:373
|
||||||
#: packages/lib/utils/document-audit-logs.ts:370
|
#: packages/lib/utils/document-audit-logs.ts:374
|
||||||
msgid "Document completed"
|
msgid "Document completed"
|
||||||
msgstr "Document terminé"
|
msgstr "Document terminé"
|
||||||
|
|
||||||
@@ -736,15 +740,15 @@ msgstr "Document Supprimé !"
|
|||||||
msgid "Document Distribution Method"
|
msgid "Document Distribution Method"
|
||||||
msgstr "Méthode de distribution du document"
|
msgstr "Méthode de distribution du document"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:326
|
#: packages/lib/utils/document-audit-logs.ts:330
|
||||||
msgid "Document external ID updated"
|
msgid "Document external ID updated"
|
||||||
msgstr "ID externe du document mis à jour"
|
msgstr "ID externe du document mis à jour"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:334
|
#: packages/lib/utils/document-audit-logs.ts:338
|
||||||
msgid "Document moved to team"
|
msgid "Document moved to team"
|
||||||
msgstr "Document déplacé vers l'équipe"
|
msgstr "Document déplacé vers l'équipe"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:318
|
#: packages/lib/utils/document-audit-logs.ts:322
|
||||||
msgid "Document opened"
|
msgid "Document opened"
|
||||||
msgstr "Document ouvert"
|
msgstr "Document ouvert"
|
||||||
|
|
||||||
@@ -759,23 +763,27 @@ msgstr "Document Rejeté"
|
|||||||
#~ msgid "Document Rejection Confirmed"
|
#~ msgid "Document Rejection Confirmed"
|
||||||
#~ msgstr "Document Rejection Confirmed"
|
#~ msgstr "Document Rejection Confirmed"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:330
|
#: packages/lib/utils/document-audit-logs.ts:294
|
||||||
|
msgid "Document restored"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: packages/lib/utils/document-audit-logs.ts:334
|
||||||
msgid "Document sent"
|
msgid "Document sent"
|
||||||
msgstr "Document envoyé"
|
msgstr "Document envoyé"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:310
|
#: packages/lib/utils/document-audit-logs.ts:314
|
||||||
msgid "Document signing auth updated"
|
msgid "Document signing auth updated"
|
||||||
msgstr "Authentification de signature de document mise à jour"
|
msgstr "Authentification de signature de document mise à jour"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:322
|
#: packages/lib/utils/document-audit-logs.ts:326
|
||||||
msgid "Document title updated"
|
msgid "Document title updated"
|
||||||
msgstr "Titre du document mis à jour"
|
msgstr "Titre du document mis à jour"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:314
|
#: packages/lib/utils/document-audit-logs.ts:318
|
||||||
msgid "Document updated"
|
msgid "Document updated"
|
||||||
msgstr "Document mis à jour"
|
msgstr "Document mis à jour"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:302
|
#: packages/lib/utils/document-audit-logs.ts:306
|
||||||
msgid "Document visibility updated"
|
msgid "Document visibility updated"
|
||||||
msgstr "Visibilité du document mise à jour"
|
msgstr "Visibilité du document mise à jour"
|
||||||
|
|
||||||
@@ -793,7 +801,7 @@ msgid "Drag & drop your PDF here."
|
|||||||
msgstr "Faites glisser et déposez votre PDF ici."
|
msgstr "Faites glisser et déposez votre PDF ici."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1065
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1065
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:827
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:860
|
||||||
msgid "Dropdown"
|
msgid "Dropdown"
|
||||||
msgstr "Liste déroulante"
|
msgstr "Liste déroulante"
|
||||||
|
|
||||||
@@ -807,7 +815,7 @@ msgstr "Options de liste déroulante"
|
|||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:512
|
#: packages/ui/primitives/document-flow/add-signers.tsx:512
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:519
|
#: packages/ui/primitives/document-flow/add-signers.tsx:519
|
||||||
#: packages/ui/primitives/document-flow/types.ts:54
|
#: packages/ui/primitives/document-flow/types.ts:54
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:645
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:677
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:471
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:471
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:478
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:478
|
||||||
msgid "Email"
|
msgid "Email"
|
||||||
@@ -821,11 +829,11 @@ msgstr "L'email est requis"
|
|||||||
msgid "Email Options"
|
msgid "Email Options"
|
||||||
msgstr "Options d'email"
|
msgstr "Options d'email"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:363
|
#: packages/lib/utils/document-audit-logs.ts:367
|
||||||
msgid "Email resent"
|
msgid "Email resent"
|
||||||
msgstr "Email renvoyé"
|
msgstr "Email renvoyé"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:363
|
#: packages/lib/utils/document-audit-logs.ts:367
|
||||||
msgid "Email sent"
|
msgid "Email sent"
|
||||||
msgstr "Email envoyé"
|
msgstr "Email envoyé"
|
||||||
|
|
||||||
@@ -843,6 +851,7 @@ msgid "Enable signing order"
|
|||||||
msgstr "Activer l'ordre de signature"
|
msgstr "Activer l'ordre de signature"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:802
|
#: packages/ui/primitives/document-flow/add-fields.tsx:802
|
||||||
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:597
|
||||||
msgid "Enable Typed Signatures"
|
msgid "Enable Typed Signatures"
|
||||||
msgstr "Activer les signatures tapées"
|
msgstr "Activer les signatures tapées"
|
||||||
|
|
||||||
@@ -889,11 +898,11 @@ msgstr "Étiquette du champ"
|
|||||||
msgid "Field placeholder"
|
msgid "Field placeholder"
|
||||||
msgstr "Espace réservé du champ"
|
msgstr "Espace réservé du champ"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:294
|
#: packages/lib/utils/document-audit-logs.ts:298
|
||||||
msgid "Field signed"
|
msgid "Field signed"
|
||||||
msgstr "Champ signé"
|
msgstr "Champ signé"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:298
|
#: packages/lib/utils/document-audit-logs.ts:302
|
||||||
msgid "Field unsigned"
|
msgid "Field unsigned"
|
||||||
msgstr "Champ non signé"
|
msgstr "Champ non signé"
|
||||||
|
|
||||||
@@ -930,7 +939,7 @@ msgstr "Authentification d'action de destinataire globale"
|
|||||||
msgid "Go Back"
|
msgid "Go Back"
|
||||||
msgstr "Retourner"
|
msgstr "Retourner"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:398
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:404
|
||||||
msgid "Green"
|
msgid "Green"
|
||||||
msgstr "Vert"
|
msgstr "Vert"
|
||||||
|
|
||||||
@@ -1025,7 +1034,7 @@ msgstr "Min"
|
|||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:550
|
#: packages/ui/primitives/document-flow/add-signers.tsx:550
|
||||||
#: packages/ui/primitives/document-flow/add-signers.tsx:556
|
#: packages/ui/primitives/document-flow/add-signers.tsx:556
|
||||||
#: packages/ui/primitives/document-flow/types.ts:55
|
#: packages/ui/primitives/document-flow/types.ts:55
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:671
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:703
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:506
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:506
|
||||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:512
|
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:512
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
@@ -1044,7 +1053,7 @@ msgid "Needs to view"
|
|||||||
msgstr "Nécessite une visualisation"
|
msgstr "Nécessite une visualisation"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:693
|
#: packages/ui/primitives/document-flow/add-fields.tsx:693
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:511
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:516
|
||||||
msgid "No recipient matching this description was found."
|
msgid "No recipient matching this description was found."
|
||||||
msgstr "Aucun destinataire correspondant à cette description n'a été trouvé."
|
msgstr "Aucun destinataire correspondant à cette description n'a été trouvé."
|
||||||
|
|
||||||
@@ -1053,7 +1062,7 @@ msgid "No recipients"
|
|||||||
msgstr "Aucun destinataire"
|
msgstr "Aucun destinataire"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:708
|
#: packages/ui/primitives/document-flow/add-fields.tsx:708
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:526
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:531
|
||||||
msgid "No recipients with this role"
|
msgid "No recipients with this role"
|
||||||
msgstr "Aucun destinataire avec ce rôle"
|
msgstr "Aucun destinataire avec ce rôle"
|
||||||
|
|
||||||
@@ -1083,7 +1092,7 @@ msgstr "Aucun"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:986
|
#: packages/ui/primitives/document-flow/add-fields.tsx:986
|
||||||
#: packages/ui/primitives/document-flow/types.ts:56
|
#: packages/ui/primitives/document-flow/types.ts:56
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:749
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:781
|
||||||
msgid "Number"
|
msgid "Number"
|
||||||
msgstr "Numéro"
|
msgstr "Numéro"
|
||||||
|
|
||||||
@@ -1175,7 +1184,6 @@ msgid "Please try again or contact our support."
|
|||||||
msgstr "Veuillez réessayer ou contacter notre support."
|
msgstr "Veuillez réessayer ou contacter notre support."
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/types.ts:57
|
#: packages/ui/primitives/document-flow/types.ts:57
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:775
|
|
||||||
msgid "Radio"
|
msgid "Radio"
|
||||||
msgstr "Radio"
|
msgstr "Radio"
|
||||||
|
|
||||||
@@ -1199,8 +1207,8 @@ msgstr "Raison du rejet : {rejectionReason}"
|
|||||||
msgid "Receives copy"
|
msgid "Receives copy"
|
||||||
msgstr "Recevoir une copie"
|
msgstr "Recevoir une copie"
|
||||||
|
|
||||||
#: packages/lib/utils/document-audit-logs.ts:338
|
#: packages/lib/utils/document-audit-logs.ts:342
|
||||||
#: packages/lib/utils/document-audit-logs.ts:353
|
#: packages/lib/utils/document-audit-logs.ts:357
|
||||||
msgid "Recipient"
|
msgid "Recipient"
|
||||||
msgstr "Destinataire"
|
msgstr "Destinataire"
|
||||||
|
|
||||||
@@ -1218,7 +1226,7 @@ msgstr "E-mail de destinataire supprimé"
|
|||||||
msgid "Recipient signing request email"
|
msgid "Recipient signing request email"
|
||||||
msgstr "E-mail de demande de signature de destinataire"
|
msgstr "E-mail de demande de signature de destinataire"
|
||||||
|
|
||||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:384
|
#: packages/ui/primitives/signature-pad/signature-pad.tsx:390
|
||||||
msgid "Red"
|
msgid "Red"
|
||||||
msgstr "Rouge"
|
msgstr "Rouge"
|
||||||
|
|
||||||
@@ -1287,7 +1295,7 @@ msgstr "Lignes par page"
|
|||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Sauvegarder"
|
msgstr "Sauvegarder"
|
||||||
|
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:861
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:893
|
||||||
msgid "Save Template"
|
msgid "Save Template"
|
||||||
msgstr "Sauvegarder le modèle"
|
msgstr "Sauvegarder le modèle"
|
||||||
|
|
||||||
@@ -1380,7 +1388,7 @@ msgstr "Se connecter"
|
|||||||
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
||||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||||
#: packages/ui/primitives/document-flow/types.ts:49
|
#: packages/ui/primitives/document-flow/types.ts:49
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:593
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:625
|
||||||
msgid "Signature"
|
msgid "Signature"
|
||||||
msgstr "Signature"
|
msgstr "Signature"
|
||||||
|
|
||||||
@@ -1465,7 +1473,7 @@ msgstr "Titre du modèle"
|
|||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:960
|
#: packages/ui/primitives/document-flow/add-fields.tsx:960
|
||||||
#: packages/ui/primitives/document-flow/types.ts:52
|
#: packages/ui/primitives/document-flow/types.ts:52
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:723
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:755
|
||||||
msgid "Text"
|
msgid "Text"
|
||||||
msgstr "Texte"
|
msgstr "Texte"
|
||||||
|
|
||||||
@@ -1629,7 +1637,7 @@ msgid "Title"
|
|||||||
msgstr "Titre"
|
msgstr "Titre"
|
||||||
|
|
||||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1080
|
#: packages/ui/primitives/document-flow/add-fields.tsx:1080
|
||||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:841
|
#: packages/ui/primitives/template-flow/add-template-fields.tsx:873
|
||||||
msgid "To proceed further, please set at least one value for the {0} field."
|
msgid "To proceed further, please set at least one value for the {0} field."
|
||||||
msgstr "Pour continuer, veuillez définir au moins une valeur pour le champ {0}."
|
msgstr "Pour continuer, veuillez définir au moins une valeur pour le champ {0}."
|
||||||
|
|
||||||
@@ -1814,4 +1822,3 @@ msgstr "Votre mot de passe a été mis à jour."
|
|||||||
#: packages/email/templates/team-delete.tsx:32
|
#: packages/email/templates/team-delete.tsx:32
|
||||||
msgid "Your team has been deleted"
|
msgid "Your team has been deleted"
|
||||||
msgstr "Votre équipe a été supprimée"
|
msgstr "Votre équipe a été supprimée"
|
||||||
|
|
||||||
|
|||||||
@@ -602,4 +602,3 @@ msgstr "Vous pouvez auto-héberger Documenso gratuitement ou utiliser notre vers
|
|||||||
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
#: apps/marketing/src/components/(marketing)/carousel.tsx:272
|
||||||
msgid "Your browser does not support the video tag."
|
msgid "Your browser does not support the video tag."
|
||||||
msgstr "Votre navigateur ne prend pas en charge la balise vidéo."
|
msgstr "Votre navigateur ne prend pas en charge la balise vidéo."
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,7 @@ export const ZDocumentAuditLogTypeSchema = z.enum([
|
|||||||
'DOCUMENT_COMPLETED', // When the document is sealed and fully completed.
|
'DOCUMENT_COMPLETED', // When the document is sealed and fully completed.
|
||||||
'DOCUMENT_CREATED', // When the document is created.
|
'DOCUMENT_CREATED', // When the document is created.
|
||||||
'DOCUMENT_DELETED', // When the document is soft deleted.
|
'DOCUMENT_DELETED', // When the document is soft deleted.
|
||||||
|
'DOCUMENT_RESTORED', // When the document is restored.
|
||||||
'DOCUMENT_FIELD_INSERTED', // When a field is inserted (signed/approved/etc) by a recipient.
|
'DOCUMENT_FIELD_INSERTED', // When a field is inserted (signed/approved/etc) by a recipient.
|
||||||
'DOCUMENT_FIELD_UNINSERTED', // When a field is uninserted by a recipient.
|
'DOCUMENT_FIELD_UNINSERTED', // When a field is uninserted by a recipient.
|
||||||
'DOCUMENT_VISIBILITY_UPDATED', // When the document visibility scope is updated
|
'DOCUMENT_VISIBILITY_UPDATED', // When the document visibility scope is updated
|
||||||
@@ -225,6 +226,16 @@ export const ZDocumentAuditLogEventDocumentDeletedSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event: Document restored.
|
||||||
|
*/
|
||||||
|
export const ZDocumentAuditLogEventDocumentRestoredSchema = z.object({
|
||||||
|
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RESTORED),
|
||||||
|
data: z.object({
|
||||||
|
type: z.enum(['RESTORE']),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event: Document field inserted.
|
* Event: Document field inserted.
|
||||||
*/
|
*/
|
||||||
@@ -490,6 +501,7 @@ export const ZDocumentAuditLogSchema = ZDocumentAuditLogBaseSchema.and(
|
|||||||
ZDocumentAuditLogEventDocumentCreatedSchema,
|
ZDocumentAuditLogEventDocumentCreatedSchema,
|
||||||
ZDocumentAuditLogEventDocumentDeletedSchema,
|
ZDocumentAuditLogEventDocumentDeletedSchema,
|
||||||
ZDocumentAuditLogEventDocumentMovedToTeamSchema,
|
ZDocumentAuditLogEventDocumentMovedToTeamSchema,
|
||||||
|
ZDocumentAuditLogEventDocumentRestoredSchema,
|
||||||
ZDocumentAuditLogEventDocumentFieldInsertedSchema,
|
ZDocumentAuditLogEventDocumentFieldInsertedSchema,
|
||||||
ZDocumentAuditLogEventDocumentFieldUninsertedSchema,
|
ZDocumentAuditLogEventDocumentFieldUninsertedSchema,
|
||||||
ZDocumentAuditLogEventDocumentVisibilitySchema,
|
ZDocumentAuditLogEventDocumentVisibilitySchema,
|
||||||
|
|||||||
@@ -290,6 +290,10 @@ export const formatDocumentAuditLogAction = (
|
|||||||
anonymous: msg`Document deleted`,
|
anonymous: msg`Document deleted`,
|
||||||
identified: msg`${prefix} deleted the document`,
|
identified: msg`${prefix} deleted the document`,
|
||||||
}))
|
}))
|
||||||
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RESTORED }, () => ({
|
||||||
|
anonymous: msg`Document restored`,
|
||||||
|
identified: msg`${prefix} restored the document`,
|
||||||
|
}))
|
||||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED }, () => ({
|
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED }, () => ({
|
||||||
anonymous: msg`Field signed`,
|
anonymous: msg`Field signed`,
|
||||||
identified: msg`${prefix} signed a field`,
|
identified: msg`${prefix} signed a field`,
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ module.exports = {
|
|||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
'@trivago/prettier-plugin-sort-imports',
|
'@trivago/prettier-plugin-sort-imports',
|
||||||
'prettier-plugin-sql',
|
// !: Disabled until Prettier 3.x is supported.
|
||||||
|
// 'prettier-plugin-sql',
|
||||||
'prettier-plugin-tailwindcss',
|
'prettier-plugin-tailwindcss',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,9 @@
|
|||||||
"clean": "rimraf node_modules"
|
"clean": "rimraf node_modules"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
|
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-sql": "^0.14.0",
|
"prettier-plugin-tailwindcss": "^0.6.9"
|
||||||
"prettier-plugin-tailwindcss": "^0.2.8"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "DocumentMeta" ALTER COLUMN "typedSignatureEnabled" SET DEFAULT true;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "TeamGlobalSettings" ADD COLUMN "typedSignatureEnabled" BOOLEAN NOT NULL DEFAULT true;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "TeamGlobalSettings" ADD COLUMN "includeSigningCertificate" BOOLEAN NOT NULL DEFAULT true;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
-- Existing templates should not have this enabled by default.
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "TemplateMeta" ADD COLUMN "typedSignatureEnabled" BOOLEAN NOT NULL DEFAULT false;
|
||||||
|
|
||||||
|
-- New templates should have this enabled by default.
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "TemplateMeta" ALTER COLUMN "typedSignatureEnabled" SET DEFAULT true;
|
||||||
@@ -374,7 +374,7 @@ model DocumentMeta {
|
|||||||
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
||||||
redirectUrl String?
|
redirectUrl String?
|
||||||
signingOrder DocumentSigningOrder @default(PARALLEL)
|
signingOrder DocumentSigningOrder @default(PARALLEL)
|
||||||
typedSignatureEnabled Boolean @default(false)
|
typedSignatureEnabled Boolean @default(true)
|
||||||
language String @default("en")
|
language String @default("en")
|
||||||
distributionMethod DocumentDistributionMethod @default(EMAIL)
|
distributionMethod DocumentDistributionMethod @default(EMAIL)
|
||||||
emailSettings Json?
|
emailSettings Json?
|
||||||
@@ -511,10 +511,12 @@ enum TeamMemberInviteStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model TeamGlobalSettings {
|
model TeamGlobalSettings {
|
||||||
teamId Int @unique
|
teamId Int @unique
|
||||||
documentVisibility DocumentVisibility @default(EVERYONE)
|
documentVisibility DocumentVisibility @default(EVERYONE)
|
||||||
documentLanguage String @default("en")
|
documentLanguage String @default("en")
|
||||||
includeSenderDetails Boolean @default(true)
|
includeSenderDetails Boolean @default(true)
|
||||||
|
typedSignatureEnabled Boolean @default(true)
|
||||||
|
includeSigningCertificate Boolean @default(true)
|
||||||
|
|
||||||
brandingEnabled Boolean @default(false)
|
brandingEnabled Boolean @default(false)
|
||||||
brandingLogo String @default("")
|
brandingLogo String @default("")
|
||||||
@@ -628,19 +630,21 @@ enum TemplateType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model TemplateMeta {
|
model TemplateMeta {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
subject String?
|
subject String?
|
||||||
message String?
|
message String?
|
||||||
timezone String? @default("Etc/UTC") @db.Text
|
timezone String? @default("Etc/UTC") @db.Text
|
||||||
password String?
|
password String?
|
||||||
dateFormat String? @default("yyyy-MM-dd hh:mm a") @db.Text
|
dateFormat String? @default("yyyy-MM-dd hh:mm a") @db.Text
|
||||||
signingOrder DocumentSigningOrder? @default(PARALLEL)
|
signingOrder DocumentSigningOrder? @default(PARALLEL)
|
||||||
templateId Int @unique
|
typedSignatureEnabled Boolean @default(true)
|
||||||
template Template @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
distributionMethod DocumentDistributionMethod @default(EMAIL)
|
||||||
redirectUrl String?
|
|
||||||
language String @default("en")
|
templateId Int @unique
|
||||||
distributionMethod DocumentDistributionMethod @default(EMAIL)
|
template Template @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
||||||
emailSettings Json?
|
redirectUrl String?
|
||||||
|
language String @default("en")
|
||||||
|
emailSettings Json?
|
||||||
}
|
}
|
||||||
|
|
||||||
model Template {
|
model Template {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export const ExtendedDocumentStatus = {
|
|||||||
...DocumentStatus,
|
...DocumentStatus,
|
||||||
INBOX: 'INBOX',
|
INBOX: 'INBOX',
|
||||||
ALL: 'ALL',
|
ALL: 'ALL',
|
||||||
|
BIN: 'BIN',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type ExtendedDocumentStatus =
|
export type ExtendedDocumentStatus =
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const { fontFamily } = require('tailwindcss/defaultTheme');
|
|||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
darkMode: ['class'],
|
darkMode: ['variant', '&:is(.dark:not(.dark-mode-disabled) *)'],
|
||||||
content: ['src/**/*.{ts,tsx}'],
|
content: ['src/**/*.{ts,tsx}'],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
@@ -108,6 +108,9 @@ module.exports = {
|
|||||||
'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
||||||
},
|
},
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
|
DEFAULT: 'calc(var(--radius) - 3px)',
|
||||||
|
'2xl': 'calc(var(--radius) + 4px)',
|
||||||
|
xl: 'calc(var(--radius) + 2px)',
|
||||||
lg: 'var(--radius)',
|
lg: 'var(--radius)',
|
||||||
md: 'calc(var(--radius) - 2px)',
|
md: 'calc(var(--radius) - 2px)',
|
||||||
sm: 'calc(var(--radius) - 4px)',
|
sm: 'calc(var(--radius) - 4px)',
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"postcss": "^8.4.32",
|
"postcss": "^8.4.32",
|
||||||
"tailwindcss": "3.3.2",
|
"tailwindcss": "3.4.15",
|
||||||
"tailwindcss-animate": "^1.0.5"
|
"tailwindcss-animate": "^1.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
"devDependencies": {},
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document
|
|||||||
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
|
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
|
||||||
import { moveDocumentToTeam } from '@documenso/lib/server-only/document/move-document-to-team';
|
import { moveDocumentToTeam } from '@documenso/lib/server-only/document/move-document-to-team';
|
||||||
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
|
import { resendDocument } from '@documenso/lib/server-only/document/resend-document';
|
||||||
|
import { restoreDocument } from '@documenso/lib/server-only/document/restore-document';
|
||||||
import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/search-documents-with-keyword';
|
import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/search-documents-with-keyword';
|
||||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||||
import { updateDocumentSettings } from '@documenso/lib/server-only/document/update-document-settings';
|
import { updateDocumentSettings } from '@documenso/lib/server-only/document/update-document-settings';
|
||||||
@@ -38,6 +39,7 @@ import {
|
|||||||
ZGetDocumentWithDetailsByIdQuerySchema,
|
ZGetDocumentWithDetailsByIdQuerySchema,
|
||||||
ZMoveDocumentsToTeamSchema,
|
ZMoveDocumentsToTeamSchema,
|
||||||
ZResendDocumentMutationSchema,
|
ZResendDocumentMutationSchema,
|
||||||
|
ZRestoreDocumentMutationSchema,
|
||||||
ZSearchDocumentsMutationSchema,
|
ZSearchDocumentsMutationSchema,
|
||||||
ZSendDocumentMutationSchema,
|
ZSendDocumentMutationSchema,
|
||||||
ZSetPasswordForDocumentMutationSchema,
|
ZSetPasswordForDocumentMutationSchema,
|
||||||
@@ -223,6 +225,32 @@ export const documentRouter = router({
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
restoreDocument: authenticatedProcedure
|
||||||
|
.input(ZRestoreDocumentMutationSchema)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
try {
|
||||||
|
const { id, teamId } = input;
|
||||||
|
|
||||||
|
const userId = ctx.user.id;
|
||||||
|
|
||||||
|
const restoredDocument = await restoreDocument({
|
||||||
|
id,
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
requestMetadata: extractNextApiRequestMetadata(ctx.req),
|
||||||
|
});
|
||||||
|
|
||||||
|
return restoredDocument;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'BAD_REQUEST',
|
||||||
|
message: 'We were unable to restore this document. Please try again later.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
findDocumentAuditLogs: authenticatedProcedure
|
findDocumentAuditLogs: authenticatedProcedure
|
||||||
.input(ZFindDocumentAuditLogsQuerySchema)
|
.input(ZFindDocumentAuditLogsQuerySchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user