mirror of
https://github.com/documenso/documenso.git
synced 2025-11-11 04:52:41 +10:00
Compare commits
30 Commits
v1.9.0-rc.
...
fix/refact
| Author | SHA1 | Date | |
|---|---|---|---|
| 22665543c0 | |||
| df33fbf91b | |||
| ee6efc4cca | |||
| 6da15ab12b | |||
| 7ef2a8769b | |||
| 487f52e194 | |||
| 39b1c5bbec | |||
| 32857bbfeb | |||
| 41218e2585 | |||
| a1a2d0801b | |||
| c588c09b26 | |||
| 74382e21e7 | |||
| 8a7ec7e982 | |||
| 2948a33bf9 | |||
| 98b2da5018 | |||
| fc1f76b543 | |||
| 22c9fb777b | |||
| 2da051a7f9 | |||
| 390a317bd3 | |||
| c161553d1d | |||
| c960a48b4f | |||
| 9502f4361d | |||
| 82deab41f4 | |||
| 2245812f0b | |||
| 861e9c976b | |||
| f55808199b | |||
| b4a7f1887d | |||
| f73441ee85 | |||
| d7de3b08c1 | |||
| 7d201f05d9 |
2
.github/workflows/e2e-tests.yml
vendored
2
.github/workflows/e2e-tests.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
e2e_tests:
|
||||
name: 'E2E Tests'
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import '../styles.css';
|
||||
export default function App({ Component, pageProps }) {
|
||||
return (
|
||||
<PlausibleProvider>
|
||||
<Component {...pageProps} />;
|
||||
<Component {...pageProps} />
|
||||
</PlausibleProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@ -17,23 +17,25 @@ The default document visibility option allows you to control who can view and ac
|
||||
|
||||
The default document visibility is set to "_EVERYONE_" by default. You can change this setting by going to the [team's general preferences page](/users/teams/preferences) and selecting a different visibility option.
|
||||
|
||||
<Callout type="warning">
|
||||
If the team member uploading the document has a role lower than the default document visibility,
|
||||
the document visibility will be set to a lower visibility level matching the team member's role.
|
||||
</Callout>
|
||||
|
||||
Here's how it works:
|
||||
|
||||
- If a user with the "_Member_" role creates a document and the default document visibility is set to "_Admin_" or "_Managers and above_", the document's visibility is set to "_Everyone_".
|
||||
- If a user with the "_Manager_" role creates a document and the default document visibility is set to "_Admin_", the document's visibility is set to "_Managers and above_".
|
||||
- Otherwise, the document's visibility is set to the default document visibility.
|
||||
- If a user with the "_Member_" role creates a document and the default document visibility is set to "_Everyone_", the document's visibility is set to "_EVERYONE_".
|
||||
- The user can't change the visibility of the document in the document editor.
|
||||
- If a user with the "_Member_" role creates a document and the default document visibility is set to "_Admin_" or "_Managers and above_", the document's visibility is set to the default document visibility ("_Admin_" or "_Managers and above_" in this case).
|
||||
- The user can't change the visibility of the document in the document editor.
|
||||
- If a user with the "_Manager_" role creates a document and the default document visibility is set to "_Everyone_" or "_Managers and above_", the document's visibility is set to the default document visibility ("_Everyone_" or "_Managers and above_" in this case).
|
||||
- The user can change the visibility of the document to any of these options, except "_Admin_", in the document editor.
|
||||
- If a user with the "_Manager_" role creates a document and the default document visibility is set to "_Admin_", the document's visibility is set to "_Admin_".
|
||||
- The user can't change the visibility of the document in the document editor.
|
||||
- If a user with the "_Admin_" role creates a document, and the default document visibility is set to "_Everyone_", "_Managers and above_", or "_Admin_", the document's visibility is set to the default document visibility.
|
||||
- The user can change the visibility of the document to any of these options in the document editor.
|
||||
|
||||
You can change the visibility of a document at any time by editing the document and selecting a different visibility option.
|
||||
|
||||

|
||||
|
||||
<Callout type="warning">
|
||||
Updating the default document visibility in the team's general settings will not affect the
|
||||
Updating the default document visibility in the team's general preferences will not affect the
|
||||
visibility of existing documents. You will need to update the visibility of each document
|
||||
individually.
|
||||
</Callout>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@documenso/marketing",
|
||||
"version": "1.9.0-rc.2",
|
||||
"version": "1.9.0-rc.5",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
@ -47,7 +47,7 @@
|
||||
"recharts": "^2.7.2",
|
||||
"sharp": "0.32.6",
|
||||
"typescript": "5.2.2",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lingui/loader": "^4.11.3",
|
||||
|
||||
@ -10,16 +10,13 @@ import { msg } from '@lingui/macro';
|
||||
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { base64 } from '@documenso/lib/universal/base64';
|
||||
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
|
||||
import type { Field, Recipient } from '@documenso/prisma/client';
|
||||
import { DocumentDataType, Prisma } from '@documenso/prisma/client';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
import { DocumentDropzone } from '@documenso/ui/primitives/document-dropzone';
|
||||
import { AddFieldsFormPartial } from '@documenso/ui/primitives/document-flow/add-fields';
|
||||
import type { TAddFieldsFormSchema } from '@documenso/ui/primitives/document-flow/add-fields.types';
|
||||
import { AddSignatureFormPartial } from '@documenso/ui/primitives/document-flow/add-signature';
|
||||
import type { TAddSignatureFormSchema } from '@documenso/ui/primitives/document-flow/add-signature.types';
|
||||
import { DocumentFlowFormContainer } from '@documenso/ui/primitives/document-flow/document-flow-root';
|
||||
import type { DocumentFlowStep } from '@documenso/ui/primitives/document-flow/types';
|
||||
import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
|
||||
@ -43,9 +40,6 @@ export const SinglePlayerClient = () => {
|
||||
const [step, setStep] = useState<SinglePlayerModeStep>('fields');
|
||||
const [fields, setFields] = useState<Field[]>([]);
|
||||
|
||||
const { mutateAsync: createSinglePlayerDocument } =
|
||||
trpc.singleplayer.createSinglePlayerDocument.useMutation();
|
||||
|
||||
const documentFlow: Record<SinglePlayerModeStep, DocumentFlowStep> = {
|
||||
fields: {
|
||||
title: msg`Add document`,
|
||||
@ -112,38 +106,35 @@ export const SinglePlayerClient = () => {
|
||||
/**
|
||||
* Upload, create, sign and send the document.
|
||||
*/
|
||||
const onSignSubmit = async (data: TAddSignatureFormSchema) => {
|
||||
const onSignSubmit = (data: unknown) => {
|
||||
if (!uploadedFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const putFileData = await putPdfFile(uploadedFile.file);
|
||||
|
||||
const documentToken = await createSinglePlayerDocument({
|
||||
documentData: {
|
||||
type: putFileData.type,
|
||||
data: putFileData.data,
|
||||
},
|
||||
documentName: uploadedFile.file.name,
|
||||
signer: data,
|
||||
fields: fields.map((field) => ({
|
||||
page: field.page,
|
||||
type: field.type,
|
||||
positionX: field.positionX.toNumber(),
|
||||
positionY: field.positionY.toNumber(),
|
||||
width: field.width.toNumber(),
|
||||
height: field.height.toNumber(),
|
||||
fieldMeta: field.fieldMeta,
|
||||
})),
|
||||
fieldMeta: { type: undefined },
|
||||
});
|
||||
|
||||
analytics.capture('Marketing: SPM - Document signed', {
|
||||
signer: data.email,
|
||||
});
|
||||
|
||||
router.push(`/singleplayer/${documentToken}/success`);
|
||||
// const putFileData = await putPdfFile(uploadedFile.file);
|
||||
// const documentToken = await createSinglePlayerDocument({
|
||||
// documentData: {
|
||||
// type: putFileData.type,
|
||||
// data: putFileData.data,
|
||||
// },
|
||||
// documentName: uploadedFile.file.name,
|
||||
// signer: data,
|
||||
// fields: fields.map((field) => ({
|
||||
// page: field.page,
|
||||
// type: field.type,
|
||||
// positionX: field.positionX.toNumber(),
|
||||
// positionY: field.positionY.toNumber(),
|
||||
// width: field.width.toNumber(),
|
||||
// height: field.height.toNumber(),
|
||||
// fieldMeta: field.fieldMeta,
|
||||
// })),
|
||||
// fieldMeta: { type: undefined },
|
||||
// });
|
||||
// analytics.capture('Marketing: SPM - Document signed', {
|
||||
// signer: data.email,
|
||||
// });
|
||||
// router.push(`/singleplayer/${documentToken}/success`);
|
||||
} catch {
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
|
||||
@ -2,7 +2,7 @@ import cors from '@/lib/cors';
|
||||
import { getUserMonthlyGrowth } from '@/lib/growth/get-user-monthly-growth';
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const totalUsers = await getUserMonthlyGrowth("cumulative");
|
||||
const totalUsers = await getUserMonthlyGrowth('cumulative');
|
||||
|
||||
return cors(
|
||||
request,
|
||||
|
||||
@ -30,10 +30,10 @@ function isOriginAllowed(origin: string, allowed: StaticOrigin): boolean {
|
||||
return Array.isArray(allowed)
|
||||
? allowed.some((o) => isOriginAllowed(origin, o))
|
||||
: typeof allowed === 'string'
|
||||
? origin === allowed
|
||||
: allowed instanceof RegExp
|
||||
? allowed.test(origin)
|
||||
: !!allowed;
|
||||
? origin === allowed
|
||||
: allowed instanceof RegExp
|
||||
? allowed.test(origin)
|
||||
: !!allowed;
|
||||
}
|
||||
|
||||
function getOriginHeaders(reqOrigin: string | undefined, origin: StaticOrigin) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@documenso/web",
|
||||
"version": "1.9.0-rc.2",
|
||||
"version": "1.9.0-rc.5",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
@ -56,10 +56,11 @@
|
||||
"recharts": "^2.7.2",
|
||||
"remeda": "^2.17.3",
|
||||
"sharp": "0.32.6",
|
||||
"trpc-openapi": "^1.2.0",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"uqr": "^0.1.2",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@documenso/tailwind-config": "*",
|
||||
|
||||
@ -134,7 +134,7 @@ export const LeaderboardTable = ({
|
||||
startTransition(() => {
|
||||
updateSearchParams({
|
||||
sortBy: column,
|
||||
sortOrder: sortOrder === 'asc' ? 'desc' : 'asc',
|
||||
sortOrder: sortBy === column && sortOrder === 'asc' ? 'desc' : 'asc',
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -30,8 +30,8 @@ export type DeleteUserDialogProps = {
|
||||
};
|
||||
|
||||
export const DeleteUserDialog = ({ className, user }: DeleteUserDialogProps) => {
|
||||
const { toast } = useToast();
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@ -44,7 +44,6 @@ export const DeleteUserDialog = ({ className, user }: DeleteUserDialogProps) =>
|
||||
try {
|
||||
await deleteUser({
|
||||
id: user.id,
|
||||
email,
|
||||
});
|
||||
|
||||
toast({
|
||||
@ -78,7 +77,7 @@ export const DeleteUserDialog = ({ className, user }: DeleteUserDialogProps) =>
|
||||
return (
|
||||
<div className={className}>
|
||||
<Alert
|
||||
className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row "
|
||||
className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row"
|
||||
variant="neutral"
|
||||
>
|
||||
<div>
|
||||
|
||||
@ -0,0 +1,141 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import type { User } from '@documenso/prisma/client';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@documenso/ui/primitives/dialog';
|
||||
import { Input } from '@documenso/ui/primitives/input';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
export type DisableUserDialogProps = {
|
||||
className?: string;
|
||||
userToDisable: User;
|
||||
};
|
||||
|
||||
export const DisableUserDialog = ({ className, userToDisable }: DisableUserDialogProps) => {
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const [email, setEmail] = useState('');
|
||||
|
||||
const { mutateAsync: disableUser, isLoading: isDisablingUser } =
|
||||
trpc.admin.disableUser.useMutation();
|
||||
|
||||
const onDisableAccount = async () => {
|
||||
try {
|
||||
await disableUser({
|
||||
id: userToDisable.id,
|
||||
});
|
||||
|
||||
toast({
|
||||
title: _(msg`Account disabled`),
|
||||
description: _(msg`The account has been disabled successfully.`),
|
||||
duration: 5000,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = AppError.parseError(err);
|
||||
|
||||
const errorMessage = match(error.code)
|
||||
.with(AppErrorCode.NOT_FOUND, () => msg`User not found.`)
|
||||
.with(AppErrorCode.UNAUTHORIZED, () => msg`You are not authorized to disable this user.`)
|
||||
.otherwise(() => msg`An error occurred while disabling the user.`);
|
||||
|
||||
toast({
|
||||
title: _(msg`Error`),
|
||||
description: _(errorMessage),
|
||||
variant: 'destructive',
|
||||
duration: 7500,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Alert
|
||||
className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row"
|
||||
variant="neutral"
|
||||
>
|
||||
<div>
|
||||
<AlertTitle>Disable Account</AlertTitle>
|
||||
<AlertDescription className="mr-2">
|
||||
<Trans>
|
||||
Disabling the user results in the user not being able to use the account. It also
|
||||
disables all the related contents such as subscription, webhooks, teams, and API keys.
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</div>
|
||||
|
||||
<div className="flex-shrink-0">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="destructive">
|
||||
<Trans>Disable Account</Trans>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader className="space-y-4">
|
||||
<DialogTitle>
|
||||
<Trans>Disable Account</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription className="selection:bg-red-100">
|
||||
<Trans>
|
||||
This action is reversible, but please be careful as the account may be
|
||||
affected permanently (e.g. their settings and contents not being restored
|
||||
properly).
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</DialogHeader>
|
||||
|
||||
<div>
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
To confirm, please enter the accounts email address <br />({userToDisable.email}
|
||||
).
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
|
||||
<Input
|
||||
className="mt-2"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
onClick={onDisableAccount}
|
||||
loading={isDisablingUser}
|
||||
variant="destructive"
|
||||
disabled={email !== userToDisable.email}
|
||||
>
|
||||
<Trans>Disable account</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</Alert>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,130 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import type { User } from '@documenso/prisma/client';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@documenso/ui/primitives/dialog';
|
||||
import { Input } from '@documenso/ui/primitives/input';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
export type EnableUserDialogProps = {
|
||||
className?: string;
|
||||
userToEnable: User;
|
||||
};
|
||||
|
||||
export const EnableUserDialog = ({ className, userToEnable }: EnableUserDialogProps) => {
|
||||
const { toast } = useToast();
|
||||
const { _ } = useLingui();
|
||||
|
||||
const [email, setEmail] = useState('');
|
||||
|
||||
const { mutateAsync: enableUser, isLoading: isEnablingUser } =
|
||||
trpc.admin.enableUser.useMutation();
|
||||
|
||||
const onEnableAccount = async () => {
|
||||
try {
|
||||
await enableUser({
|
||||
id: userToEnable.id,
|
||||
});
|
||||
|
||||
toast({
|
||||
title: _(msg`Account enabled`),
|
||||
description: _(msg`The account has been enabled successfully.`),
|
||||
duration: 5000,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = AppError.parseError(err);
|
||||
|
||||
const errorMessage = match(error.code)
|
||||
.with(AppErrorCode.NOT_FOUND, () => msg`User not found.`)
|
||||
.with(AppErrorCode.UNAUTHORIZED, () => msg`You are not authorized to enable this user.`)
|
||||
.otherwise(() => msg`An error occurred while enabling the user.`);
|
||||
|
||||
toast({
|
||||
title: _(msg`Error`),
|
||||
description: _(errorMessage),
|
||||
variant: 'destructive',
|
||||
duration: 7500,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<Alert
|
||||
className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row"
|
||||
variant="neutral"
|
||||
>
|
||||
<div>
|
||||
<AlertTitle>Enable Account</AlertTitle>
|
||||
<AlertDescription className="mr-2">
|
||||
<Trans>
|
||||
Enabling the account results in the user being able to use the account again, and all
|
||||
the related features such as webhooks, teams, and API keys for example.
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</div>
|
||||
|
||||
<div className="flex-shrink-0">
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button>
|
||||
<Trans>Enable Account</Trans>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader className="space-y-4">
|
||||
<DialogTitle>
|
||||
<Trans>Enable Account</Trans>
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div>
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
To confirm, please enter the accounts email address <br />({userToEnable.email}
|
||||
).
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
|
||||
<Input
|
||||
className="mt-2"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
onClick={onEnableAccount}
|
||||
loading={isEnablingUser}
|
||||
disabled={email !== userToEnable.email}
|
||||
>
|
||||
<Trans>Enable account</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</Alert>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -23,6 +23,8 @@ import { Input } from '@documenso/ui/primitives/input';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { DeleteUserDialog } from './delete-user-dialog';
|
||||
import { DisableUserDialog } from './disable-user-dialog';
|
||||
import { EnableUserDialog } from './enable-user-dialog';
|
||||
import { MultiSelectRoleCombobox } from './multiselect-role-combobox';
|
||||
|
||||
const ZUserFormSchema = ZAdminUpdateProfileMutationSchema.omit({ id: true });
|
||||
@ -35,7 +37,7 @@ export default function UserPage({ params }: { params: { id: number } }) {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const { data: user } = trpc.profile.getUser.useQuery(
|
||||
const { data: user } = trpc.admin.getUser.useQuery(
|
||||
{
|
||||
id: Number(params.id),
|
||||
},
|
||||
@ -153,7 +155,11 @@ export default function UserPage({ params }: { params: { id: number } }) {
|
||||
|
||||
<hr className="my-4" />
|
||||
|
||||
{user && <DeleteUserDialog user={user} />}
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
{user && <DeleteUserDialog user={user} />}
|
||||
{user && user.disabled && <EnableUserDialog userToEnable={user} />}
|
||||
{user && !user.disabled && <DisableUserDialog userToDisable={user} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -37,10 +37,8 @@ export const DocumentPageViewRecentActivity = ({
|
||||
{
|
||||
documentId,
|
||||
filterForRecentActivity: true,
|
||||
orderBy: {
|
||||
column: 'createdAt',
|
||||
direction: 'asc',
|
||||
},
|
||||
orderByColumn: 'createdAt',
|
||||
orderByDirection: 'asc',
|
||||
perPage: 10,
|
||||
},
|
||||
{
|
||||
|
||||
@ -12,8 +12,8 @@ import {
|
||||
DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||
SKIP_QUERY_BATCH_META,
|
||||
} from '@documenso/lib/constants/trpc';
|
||||
import type { TGetDocumentWithDetailsByIdResponse } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
|
||||
import { DocumentDistributionMethod, DocumentStatus } from '@documenso/prisma/client';
|
||||
import type { DocumentWithDetails } from '@documenso/prisma/types/document';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
@ -35,7 +35,7 @@ import { useOptionalCurrentTeam } from '~/providers/team';
|
||||
|
||||
export type EditDocumentFormProps = {
|
||||
className?: string;
|
||||
initialDocument: DocumentWithDetails;
|
||||
initialDocument: TGetDocumentWithDetailsByIdResponse;
|
||||
documentRootPath: string;
|
||||
isDocumentEnterprise: boolean;
|
||||
};
|
||||
@ -103,7 +103,7 @@ export const EditDocumentForm = ({
|
||||
|
||||
const { mutateAsync: addFields } = trpc.field.addFields.useMutation({
|
||||
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||
onSuccess: (newFields) => {
|
||||
onSuccess: ({ fields: newFields }) => {
|
||||
utils.document.getDocumentWithDetailsById.setData(
|
||||
{
|
||||
documentId: initialDocument.id,
|
||||
@ -132,9 +132,9 @@ export const EditDocumentForm = ({
|
||||
},
|
||||
});
|
||||
|
||||
const { mutateAsync: addSigners } = trpc.recipient.addSigners.useMutation({
|
||||
const { mutateAsync: addSigners } = trpc.recipient.setDocumentRecipients.useMutation({
|
||||
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||
onSuccess: (newRecipients) => {
|
||||
onSuccess: ({ recipients: newRecipients }) => {
|
||||
utils.document.getDocumentWithDetailsById.setData(
|
||||
{
|
||||
documentId: initialDocument.id,
|
||||
|
||||
@ -9,8 +9,8 @@ import { DateTime } from 'luxon';
|
||||
import { useSession } from 'next-auth/react';
|
||||
|
||||
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
|
||||
import type { FindResultResponse } from '@documenso/lib/types/search-params';
|
||||
import type { Document, Recipient, Team, User } from '@documenso/prisma/client';
|
||||
import type { TFindDocumentsResponse } from '@documenso/lib/server-only/document/find-documents';
|
||||
import type { Team } from '@documenso/prisma/client';
|
||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
|
||||
import { DataTable } from '@documenso/ui/primitives/data-table';
|
||||
@ -24,13 +24,7 @@ import { DataTableActionDropdown } from './data-table-action-dropdown';
|
||||
import { DataTableTitle } from './data-table-title';
|
||||
|
||||
export type DocumentsDataTableProps = {
|
||||
results: FindResultResponse<
|
||||
Document & {
|
||||
Recipient: Recipient[];
|
||||
User: Pick<User, 'id' | 'name' | 'email'>;
|
||||
team: Pick<Team, 'id' | 'url'> | null;
|
||||
}
|
||||
>;
|
||||
results: TFindDocumentsResponse;
|
||||
showSenderColumn?: boolean;
|
||||
team?: Pick<Team, 'id' | 'url'> & { teamEmail?: string };
|
||||
};
|
||||
|
||||
@ -26,10 +26,8 @@ export const TemplatePageViewRecentActivity = ({
|
||||
const { data, isLoading, isLoadingError, refetch } = trpc.document.findDocuments.useQuery({
|
||||
templateId,
|
||||
teamId,
|
||||
orderBy: {
|
||||
column: 'createdAt',
|
||||
direction: 'asc',
|
||||
},
|
||||
orderByColumn: 'createdAt',
|
||||
orderByDirection: 'asc',
|
||||
perPage: 5,
|
||||
});
|
||||
|
||||
|
||||
@ -4,8 +4,10 @@ import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
@ -51,10 +53,20 @@ export const MoveTemplateDialog = ({ templateId, open, onOpenChange }: MoveTempl
|
||||
});
|
||||
onOpenChange(false);
|
||||
},
|
||||
onError: (error) => {
|
||||
onError: (err) => {
|
||||
const error = AppError.parseError(err);
|
||||
|
||||
const errorMessage = match(error.code)
|
||||
.with(
|
||||
AppErrorCode.NOT_FOUND,
|
||||
() => msg`Template not found or already associated with a team.`,
|
||||
)
|
||||
.with(AppErrorCode.UNAUTHORIZED, () => msg`You are not a member of this team.`)
|
||||
.otherwise(() => msg`An error occurred while moving the template.`);
|
||||
|
||||
toast({
|
||||
title: _(msg`Error`),
|
||||
description: error.message || _(msg`An error occurred while moving the template.`),
|
||||
description: _(errorMessage),
|
||||
variant: 'destructive',
|
||||
duration: 7500,
|
||||
});
|
||||
|
||||
@ -12,6 +12,7 @@ import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/tr
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
|
||||
import { ZCheckboxFieldMeta } from '@documenso/lib/types/field-meta';
|
||||
import { fromCheckboxValue, toCheckboxValue } from '@documenso/lib/universal/field-checkbox';
|
||||
import type { Recipient } from '@documenso/prisma/client';
|
||||
import type { FieldWithSignatureAndFieldMeta } from '@documenso/prisma/types/field-with-signature-and-fieldmeta';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
@ -54,6 +55,7 @@ export const CheckboxField = ({
|
||||
...item,
|
||||
value: item.value.length > 0 ? item.value : `empty-value-${item.id}`,
|
||||
}));
|
||||
|
||||
const [checkedValues, setCheckedValues] = useState(
|
||||
values
|
||||
?.map((item) =>
|
||||
@ -97,7 +99,7 @@ export const CheckboxField = ({
|
||||
const payload: TSignFieldWithTokenMutationSchema = {
|
||||
token: recipient.token,
|
||||
fieldId: field.id,
|
||||
value: checkedValues.join(','),
|
||||
value: toCheckboxValue(checkedValues),
|
||||
isBase64: true,
|
||||
authOptions,
|
||||
};
|
||||
@ -191,7 +193,7 @@ export const CheckboxField = ({
|
||||
await signFieldWithToken({
|
||||
token: recipient.token,
|
||||
fieldId: field.id,
|
||||
value: updatedValues.join(','),
|
||||
value: toCheckboxValue(checkedValues),
|
||||
isBase64: true,
|
||||
});
|
||||
}
|
||||
@ -228,6 +230,11 @@ export const CheckboxField = ({
|
||||
}
|
||||
}, [checkedValues, isLengthConditionMet, field.inserted]);
|
||||
|
||||
const parsedCheckedValues = useMemo(
|
||||
() => fromCheckboxValue(field.customText),
|
||||
[field.customText],
|
||||
);
|
||||
|
||||
return (
|
||||
<SigningFieldContainer field={field} onSign={onSign} onRemove={onRemove} type="Checkbox">
|
||||
{isLoading && (
|
||||
@ -277,9 +284,7 @@ export const CheckboxField = ({
|
||||
className="h-3 w-3"
|
||||
checkClassName="text-white"
|
||||
id={`checkbox-${index}`}
|
||||
checked={field.customText
|
||||
.split(',')
|
||||
.some((customValue) => customValue === itemValue)}
|
||||
checked={parsedCheckedValues.includes(itemValue)}
|
||||
disabled={isLoading}
|
||||
onCheckedChange={() => void handleCheckboxOptionClick(item)}
|
||||
/>
|
||||
|
||||
@ -178,7 +178,7 @@ export const DropdownField = ({
|
||||
)}
|
||||
|
||||
{!field.inserted && (
|
||||
<p className="group-hover:text-primary text-muted-foreground flex flex-col items-center justify-center duration-200 ">
|
||||
<p className="group-hover:text-primary text-muted-foreground flex flex-col items-center justify-center duration-200">
|
||||
<Select value={localChoice} onValueChange={handleSelectItem}>
|
||||
<SelectTrigger
|
||||
className={cn(
|
||||
|
||||
@ -11,6 +11,7 @@ import { useForm } from 'react-hook-form';
|
||||
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
|
||||
import type { DocumentAndSender } from '@documenso/lib/server-only/document/get-document-by-token';
|
||||
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
|
||||
import { isFieldUnsignedAndRequired } from '@documenso/lib/utils/advanced-fields-helpers';
|
||||
import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields';
|
||||
import { type Field, FieldType, type Recipient, RecipientRole } from '@documenso/prisma/client';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
@ -57,26 +58,31 @@ export const SigningForm = ({
|
||||
// Keep the loading state going if successful since the redirect may take some time.
|
||||
const isSubmitting = formState.isSubmitting || formState.isSubmitSuccessful;
|
||||
|
||||
const fieldsRequiringValidation = useMemo(
|
||||
() => fields.filter(isFieldUnsignedAndRequired),
|
||||
[fields],
|
||||
);
|
||||
|
||||
const hasSignatureField = fields.some((field) => field.type === FieldType.SIGNATURE);
|
||||
|
||||
const uninsertedFields = useMemo(() => {
|
||||
return sortFieldsByPosition(fields.filter((field) => !field.inserted));
|
||||
return sortFieldsByPosition(fieldsRequiringValidation.filter((field) => !field.inserted));
|
||||
}, [fields]);
|
||||
|
||||
const fieldsValidated = () => {
|
||||
setValidateUninsertedFields(true);
|
||||
validateFieldsInserted(fields);
|
||||
validateFieldsInserted(fieldsRequiringValidation);
|
||||
};
|
||||
|
||||
const onFormSubmit = async () => {
|
||||
setValidateUninsertedFields(true);
|
||||
|
||||
const isFieldsValid = validateFieldsInserted(fieldsRequiringValidation);
|
||||
|
||||
if (hasSignatureField && !signatureValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isFieldsValid = validateFieldsInserted(fields);
|
||||
|
||||
if (!isFieldsValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -205,7 +205,7 @@ export const NameField = ({ field, recipient, onSignField, onUnsignField }: Name
|
||||
<div className="flex w-full flex-1 flex-nowrap gap-4">
|
||||
<Button
|
||||
type="button"
|
||||
className="dark:bg-muted dark:hover:bg-muted/80 flex-1 bg-black/5 hover:bg-black/10"
|
||||
className="dark:bg-muted dark:hover:bg-muted/80 flex-1 bg-black/5 hover:bg-black/10"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
setShowFullNameModal(false);
|
||||
|
||||
@ -318,7 +318,7 @@ export const NumberField = ({ field, recipient, onSignField, onUnsignField }: Nu
|
||||
<div className="flex w-full flex-1 flex-nowrap gap-4">
|
||||
<Button
|
||||
type="button"
|
||||
className="dark:bg-muted dark:hover:bg-muted/80 flex-1 bg-black/5 hover:bg-black/10"
|
||||
className="dark:bg-muted dark:hover:bg-muted/80 flex-1 bg-black/5 hover:bg-black/10"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
setShowRadioModal(false);
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import { fieldsContainUnsignedRequiredField } from '@documenso/lib/utils/advanced-fields-helpers';
|
||||
import type { Field } from '@documenso/prisma/client';
|
||||
import { RecipientRole } from '@documenso/prisma/client';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
@ -36,7 +37,7 @@ export const SignDialog = ({
|
||||
}: SignDialogProps) => {
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
|
||||
const isComplete = fields.every((field) => field.inserted);
|
||||
const isComplete = useMemo(() => !fieldsContainUnsignedRequiredField(fields), [fields]);
|
||||
|
||||
const handleOpenChange = (open: boolean) => {
|
||||
if (isSubmitting || !isComplete) {
|
||||
|
||||
@ -214,15 +214,15 @@ export const TextField = ({ field, recipient, onSignField, onUnsignField }: Text
|
||||
parsedField?.label && parsedField.label.length < 20
|
||||
? parsedField.label
|
||||
: parsedField?.label
|
||||
? parsedField?.label.substring(0, 20) + '...'
|
||||
: undefined;
|
||||
? parsedField?.label.substring(0, 20) + '...'
|
||||
: undefined;
|
||||
|
||||
const textDisplay =
|
||||
parsedField?.text && parsedField.text.length < 20
|
||||
? parsedField.text
|
||||
: parsedField?.text
|
||||
? parsedField?.text.substring(0, 20) + '...'
|
||||
: undefined;
|
||||
? parsedField?.text.substring(0, 20) + '...'
|
||||
: undefined;
|
||||
|
||||
const fieldDisplayName = labelDisplay ? labelDisplay : textDisplay;
|
||||
const charactersRemaining = (parsedFieldMeta?.characterLimit ?? 0) - (localText.length ?? 0);
|
||||
@ -325,7 +325,7 @@ export const TextField = ({ field, recipient, onSignField, onUnsignField }: Text
|
||||
<div className="mt-4 flex w-full flex-1 flex-nowrap gap-4">
|
||||
<Button
|
||||
type="button"
|
||||
className="dark:bg-muted dark:hover:bg-muted/80 flex-1 bg-black/5 hover:bg-black/10"
|
||||
className="dark:bg-muted dark:hover:bg-muted/80 flex-1 bg-black/5 hover:bg-black/10"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
setShowCustomTextModal(false);
|
||||
|
||||
@ -2,6 +2,7 @@ import { Plural, Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { DateTime } from 'luxon';
|
||||
import type Stripe from 'stripe';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
@ -44,15 +45,24 @@ export default async function TeamsSettingBillingPage({ params }: TeamsSettingsB
|
||||
|
||||
const numberOfSeats = subscription.items.data[0].quantity ?? 0;
|
||||
|
||||
const formattedTeamMemberQuanity = (
|
||||
<Plural value={numberOfSeats} one="# member" other="# members" />
|
||||
);
|
||||
|
||||
const formattedDate = DateTime.fromSeconds(subscription.current_period_end).toFormat(
|
||||
'LLL dd, yyyy',
|
||||
);
|
||||
|
||||
return _(msg`${formattedTeamMemberQuanity} • Monthly • Renews: ${formattedDate}`);
|
||||
const subscriptionInterval = match(subscription?.items.data[0].plan.interval)
|
||||
.with('year', () => _(msg`Yearly`))
|
||||
.with('month', () => _(msg`Monthly`))
|
||||
.otherwise(() => _(msg`Unknown`));
|
||||
|
||||
return (
|
||||
<span>
|
||||
<Plural value={numberOfSeats} one="# member" other="# members" />
|
||||
{' • '}
|
||||
<span>{subscriptionInterval}</span>
|
||||
{' • '}
|
||||
<Trans>Renews: {formattedDate}</Trans>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -66,10 +76,6 @@ export default async function TeamsSettingBillingPage({ params }: TeamsSettingsB
|
||||
<CardContent className="flex flex-row items-center justify-between p-4">
|
||||
<div className="flex flex-col text-sm">
|
||||
<p className="text-foreground font-semibold">
|
||||
<Trans>Current plan: {teamSubscription ? 'Team' : 'Early Adopter Team'}</Trans>
|
||||
</p>
|
||||
|
||||
<p className="text-muted-foreground mt-0.5">
|
||||
{formatTeamSubscriptionDetails(teamSubscription)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -47,10 +47,7 @@ export const StackAvatar = ({ first, zIndex, fallbackText = '', type }: StackAva
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
className={`
|
||||
${zIndexClass}
|
||||
${firstClass}
|
||||
dark:border-border h-10 w-10 border-2 border-solid border-white`}
|
||||
className={` ${zIndexClass} ${firstClass} dark:border-border h-10 w-10 border-2 border-solid border-white`}
|
||||
>
|
||||
<AvatarFallback className={classes}>{fallbackText}</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
@ -14,7 +14,7 @@ import type { z } from 'zod';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { ZCreateTeamEmailVerificationMutationSchema } from '@documenso/trpc/server/team-router/schema';
|
||||
import { ZCreateTeamEmailVerificationRequestSchema } from '@documenso/trpc/server/team-router/create-team-email-verification-route';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
@ -41,7 +41,7 @@ export type AddTeamEmailDialogProps = {
|
||||
trigger?: React.ReactNode;
|
||||
} & Omit<DialogPrimitive.DialogProps, 'children'>;
|
||||
|
||||
const ZCreateTeamEmailFormSchema = ZCreateTeamEmailVerificationMutationSchema.pick({
|
||||
const ZCreateTeamEmailFormSchema = ZCreateTeamEmailVerificationRequestSchema.pick({
|
||||
name: true,
|
||||
email: true,
|
||||
});
|
||||
|
||||
@ -15,7 +15,7 @@ import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-upda
|
||||
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { ZCreateTeamMutationSchema } from '@documenso/trpc/server/team-router/schema';
|
||||
import { ZCreateTeamRequestSchema } from '@documenso/trpc/server/team-router/create-team-route';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
@ -41,7 +41,7 @@ export type CreateTeamDialogProps = {
|
||||
trigger?: React.ReactNode;
|
||||
} & Omit<DialogPrimitive.DialogProps, 'children'>;
|
||||
|
||||
const ZCreateTeamFormSchema = ZCreateTeamMutationSchema.pick({
|
||||
const ZCreateTeamFormSchema = ZCreateTeamRequestSchema.pick({
|
||||
teamName: true,
|
||||
teamUrl: true,
|
||||
});
|
||||
|
||||
@ -15,7 +15,7 @@ import { downloadFile } from '@documenso/lib/client-only/download-file';
|
||||
import { TEAM_MEMBER_ROLE_HIERARCHY, TEAM_MEMBER_ROLE_MAP } from '@documenso/lib/constants/teams';
|
||||
import { TeamMemberRole } from '@documenso/prisma/client';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { ZCreateTeamMemberInvitesMutationSchema } from '@documenso/trpc/server/team-router/schema';
|
||||
import { ZCreateTeamMemberInvitesRequestSchema } from '@documenso/trpc/server/team-router/create-team-member-invites-route';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
@ -55,7 +55,7 @@ export type InviteTeamMembersDialogProps = {
|
||||
|
||||
const ZInviteTeamMembersFormSchema = z
|
||||
.object({
|
||||
invitations: ZCreateTeamMemberInvitesMutationSchema.shape.invitations,
|
||||
invitations: ZCreateTeamMemberInvitesRequestSchema.shape.invitations,
|
||||
})
|
||||
// Display exactly which rows are duplicates.
|
||||
.superRefine((items, ctx) => {
|
||||
|
||||
@ -12,7 +12,7 @@ import type { z } from 'zod';
|
||||
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { ZUpdateTeamMutationSchema } from '@documenso/trpc/server/team-router/schema';
|
||||
import { ZUpdateTeamRequestSchema } from '@documenso/trpc/server/team-router/update-team-route';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Form,
|
||||
@ -31,7 +31,7 @@ export type UpdateTeamDialogProps = {
|
||||
teamUrl: string;
|
||||
};
|
||||
|
||||
const ZUpdateTeamFormSchema = ZUpdateTeamMutationSchema.shape.data.pick({
|
||||
const ZUpdateTeamFormSchema = ZUpdateTeamRequestSchema.shape.data.pick({
|
||||
name: true,
|
||||
url: true,
|
||||
});
|
||||
|
||||
@ -17,8 +17,8 @@ import { formatUserProfilePath } from '@documenso/lib/utils/public-profiles';
|
||||
import type { TeamProfile, UserProfile } from '@documenso/prisma/client';
|
||||
import {
|
||||
MAX_PROFILE_BIO_LENGTH,
|
||||
ZUpdatePublicProfileMutationSchema,
|
||||
} from '@documenso/trpc/server/profile-router/schema';
|
||||
ZUpdatePublicProfileRequestSchema,
|
||||
} from '@documenso/trpc/server/profile-router/update-public-profile-route';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
@ -33,7 +33,7 @@ import { Input } from '@documenso/ui/primitives/input';
|
||||
import { Textarea } from '@documenso/ui/primitives/textarea';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
export const ZPublicProfileFormSchema = ZUpdatePublicProfileMutationSchema.pick({
|
||||
export const ZPublicProfileFormSchema = ZUpdatePublicProfileRequestSchema.pick({
|
||||
bio: true,
|
||||
enabled: true,
|
||||
url: true,
|
||||
|
||||
@ -53,6 +53,7 @@ const ERROR_MESSAGES: Partial<Record<keyof typeof ErrorCode, string>> = {
|
||||
[ErrorCode.INCORRECT_TWO_FACTOR_BACKUP_CODE]: 'The backup code provided is incorrect',
|
||||
[ErrorCode.UNVERIFIED_EMAIL]:
|
||||
'This account has not been verified. Please verify your account before signing in.',
|
||||
[ErrorCode.ACCOUNT_DISABLED]: 'This account has been disabled. Please contact support.',
|
||||
};
|
||||
|
||||
const TwoFactorEnabledErrorCode = ErrorCode.TWO_FACTOR_MISSING_CREDENTIALS;
|
||||
|
||||
@ -70,6 +70,7 @@ export const ZSignUpFormV2Schema = z
|
||||
},
|
||||
{
|
||||
message: msg`Password should not be common or based on personal information`.id,
|
||||
path: ['password'],
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
47
apps/web/src/pages/api/beta/[...trpc].ts
Normal file
47
apps/web/src/pages/api/beta/[...trpc].ts
Normal file
@ -0,0 +1,47 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { createOpenApiNextHandler } from 'trpc-openapi';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { buildLogger } from '@documenso/lib/utils/logger';
|
||||
import type { TRPCError } from '@documenso/trpc/server';
|
||||
import { createTrpcContext } from '@documenso/trpc/server/context';
|
||||
import { appRouter } from '@documenso/trpc/server/router';
|
||||
|
||||
const logger = buildLogger();
|
||||
|
||||
export default createOpenApiNextHandler<typeof appRouter>({
|
||||
router: appRouter,
|
||||
createContext: async ({ req, res }: { req: NextApiRequest; res: NextApiResponse }) =>
|
||||
createTrpcContext({ req, res }),
|
||||
onError: ({ error, path }: { error: TRPCError; path?: string }) => {
|
||||
// Always log the error for now.
|
||||
console.error(error.message);
|
||||
|
||||
const appError = AppError.parseError(error.cause || error);
|
||||
|
||||
const isAppError = error.cause instanceof AppError;
|
||||
|
||||
// Only log AppErrors that are explicitly set to 500 or the error code
|
||||
// is in the errorCodesToAlertOn list.
|
||||
const isLoggableAppError =
|
||||
isAppError && (appError.statusCode === 500 || errorCodesToAlertOn.includes(appError.code));
|
||||
|
||||
// Only log TRPC errors that are in the `errorCodesToAlertOn` list and is
|
||||
// not an AppError.
|
||||
const isLoggableTrpcError = !isAppError && errorCodesToAlertOn.includes(error.code);
|
||||
|
||||
if (isLoggableAppError || isLoggableTrpcError) {
|
||||
logger.error(error, {
|
||||
method: path,
|
||||
context: {
|
||||
source: '/v2/api',
|
||||
appError: AppError.toJSON(appError),
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
responseMeta: () => {},
|
||||
});
|
||||
|
||||
const errorCodesToAlertOn = [AppErrorCode.UNKNOWN_ERROR, 'INTERNAL_SERVER_ERROR'];
|
||||
9
apps/web/src/pages/api/beta/open-api.json.ts
Normal file
9
apps/web/src/pages/api/beta/open-api.json.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
import { openApiDocument } from '@documenso/trpc/server/open-api';
|
||||
|
||||
const handler = (_req: NextApiRequest, res: NextApiResponse) => {
|
||||
res.status(200).send(openApiDocument);
|
||||
};
|
||||
|
||||
export default handler;
|
||||
@ -45,6 +45,7 @@ export default trpcNext.createNextApiHandler({
|
||||
logger.error(error, {
|
||||
method: path,
|
||||
context: {
|
||||
source: 'trpc',
|
||||
appError: AppError.toJSON(appError),
|
||||
},
|
||||
});
|
||||
|
||||
388
package-lock.json
generated
388
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@documenso/root",
|
||||
"version": "1.9.0-rc.2",
|
||||
"version": "1.9.0-rc.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@documenso/root",
|
||||
"version": "1.9.0-rc.2",
|
||||
"version": "1.9.0-rc.5",
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
"packages/*"
|
||||
@ -17,8 +17,10 @@
|
||||
"@lingui/core": "^4.11.3",
|
||||
"inngest-cli": "^0.29.1",
|
||||
"luxon": "^3.5.0",
|
||||
"mupdf": "^1.0.0",
|
||||
"next-runtime-env": "^3.2.0",
|
||||
"react": "^18"
|
||||
"react": "^18",
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.7.1",
|
||||
@ -79,7 +81,7 @@
|
||||
},
|
||||
"apps/marketing": {
|
||||
"name": "@documenso/marketing",
|
||||
"version": "1.9.0-rc.2",
|
||||
"version": "1.9.0-rc.5",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@documenso/assets": "*",
|
||||
@ -115,7 +117,7 @@
|
||||
"recharts": "^2.7.2",
|
||||
"sharp": "0.32.6",
|
||||
"typescript": "5.2.2",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lingui/loader": "^4.11.3",
|
||||
@ -492,7 +494,7 @@
|
||||
},
|
||||
"apps/web": {
|
||||
"name": "@documenso/web",
|
||||
"version": "1.9.0-rc.2",
|
||||
"version": "1.9.0-rc.5",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@documenso/api": "*",
|
||||
@ -536,10 +538,11 @@
|
||||
"recharts": "^2.7.2",
|
||||
"remeda": "^2.17.3",
|
||||
"sharp": "0.32.6",
|
||||
"trpc-openapi": "^1.2.0",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"ua-parser-js": "^1.0.37",
|
||||
"uqr": "^0.1.2",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@documenso/tailwind-config": "*",
|
||||
@ -3494,6 +3497,11 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@hapi/bourne": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz",
|
||||
"integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w=="
|
||||
},
|
||||
"node_modules/@hapi/hoek": {
|
||||
"version": "9.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||
@ -10717,15 +10725,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@trigger.dev/cli/node_modules/zod": {
|
||||
"version": "3.22.3",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz",
|
||||
"integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/@trigger.dev/core": {
|
||||
"version": "2.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@trigger.dev/core/-/core-2.3.18.tgz",
|
||||
@ -10747,14 +10746,6 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@trigger.dev/core/node_modules/zod": {
|
||||
"version": "3.22.3",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz",
|
||||
"integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/@trigger.dev/nextjs": {
|
||||
"version": "2.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@trigger.dev/nextjs/-/nextjs-2.3.18.tgz",
|
||||
@ -10818,14 +10809,6 @@
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/@trigger.dev/sdk/node_modules/zod": {
|
||||
"version": "3.22.3",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz",
|
||||
"integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/@trigger.dev/yalt": {
|
||||
"version": "2.3.18",
|
||||
"resolved": "https://registry.npmjs.org/@trigger.dev/yalt/-/yalt-2.3.18.tgz",
|
||||
@ -10842,15 +10825,6 @@
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@trigger.dev/yalt/node_modules/zod": {
|
||||
"version": "3.22.3",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz",
|
||||
"integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/@trivago/prettier-plugin-sort-imports": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz",
|
||||
@ -12007,6 +11981,18 @@
|
||||
"node": ">=6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
@ -14311,6 +14297,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/co-body": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/co-body/-/co-body-6.2.0.tgz",
|
||||
"integrity": "sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==",
|
||||
"dependencies": {
|
||||
"@hapi/bourne": "^3.0.0",
|
||||
"inflation": "^2.0.0",
|
||||
"qs": "^6.5.2",
|
||||
"raw-body": "^2.3.3",
|
||||
"type-is": "^1.6.16"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/code-block-writer": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz",
|
||||
@ -14612,6 +14613,14 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/consola": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz",
|
||||
"integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==",
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
@ -14627,6 +14636,17 @@
|
||||
"simple-wcswidth": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||
@ -14712,9 +14732,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-es": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.0.0.tgz",
|
||||
"integrity": "sha512-mWYvfOLrfEc996hlKcdABeIiPHUPC6DM2QYZdGGOvhOTbA3tjm2eBwqlJpoFdjC89NI4Qt6h0Pu06Mp+1Pj5OQ=="
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz",
|
||||
"integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="
|
||||
},
|
||||
"node_modules/copy-anything": {
|
||||
"version": "3.0.5",
|
||||
@ -14880,6 +14900,14 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/crossws": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.1.tgz",
|
||||
"integrity": "sha512-HsZgeVYaG+b5zA+9PbIPGq4+J/CJynJuearykPsXx4V/eMhyQ5EDVg3Ak2FBZtVXCiOLu/U7IiwDHTr9MA+IKw==",
|
||||
"dependencies": {
|
||||
"uncrypto": "^0.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||
@ -15587,6 +15615,11 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/defu": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
|
||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="
|
||||
},
|
||||
"node_modules/degenerator": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
|
||||
@ -15693,6 +15726,11 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/destr": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz",
|
||||
"integrity": "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ=="
|
||||
},
|
||||
"node_modules/detect-indent": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
|
||||
@ -18123,6 +18161,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/from": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
|
||||
@ -18793,6 +18839,23 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/h3": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/h3/-/h3-1.13.0.tgz",
|
||||
"integrity": "sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg==",
|
||||
"dependencies": {
|
||||
"cookie-es": "^1.2.2",
|
||||
"crossws": ">=0.2.0 <0.4.0",
|
||||
"defu": "^6.1.4",
|
||||
"destr": "^2.0.3",
|
||||
"iron-webcrypto": "^1.2.1",
|
||||
"ohash": "^1.1.4",
|
||||
"radix3": "^1.1.2",
|
||||
"ufo": "^1.5.4",
|
||||
"uncrypto": "^0.1.3",
|
||||
"unenv": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hard-rejection": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz",
|
||||
@ -19844,6 +19907,14 @@
|
||||
"integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/inflation": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/inflation/-/inflation-2.1.0.tgz",
|
||||
"integrity": "sha512-t54PPJHG1Pp7VQvxyVCJ9mBbjG3Hqryges9bXoOO6GExCPa+//i/d5GSuFtpx3ALLd7lgIAur6zrIlBQyJuMlQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/inflection": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/inflection/-/inflection-2.0.1.tgz",
|
||||
@ -20000,14 +20071,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/inngest/node_modules/zod": {
|
||||
"version": "3.22.5",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.22.5.tgz",
|
||||
"integrity": "sha512-HqnGsCdVZ2xc0qWPLdO25WnseXThh0kEYKIdV5F/hTHO75hNZFp8thxSeHhiPrHZKrFTo1SOgkAj9po5bexZlw==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/input-otp": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.2.4.tgz",
|
||||
@ -20219,6 +20282,14 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/iron-webcrypto": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz",
|
||||
"integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/brc-dd"
|
||||
}
|
||||
},
|
||||
"node_modules/is-alphabetical": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
|
||||
@ -22035,8 +22106,7 @@
|
||||
"node_modules/lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
@ -22863,6 +22933,14 @@
|
||||
"esbuild": "0.*"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/memfs": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
|
||||
@ -22917,6 +22995,14 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-refs": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-1.2.2.tgz",
|
||||
@ -22990,6 +23076,14 @@
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/micro": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/micro/-/micro-10.0.1.tgz",
|
||||
@ -23733,6 +23827,17 @@
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
@ -24139,6 +24244,12 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/mupdf": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mupdf/-/mupdf-1.0.0.tgz",
|
||||
"integrity": "sha512-AWT27abYSX5gQmWs7+jDEtmGJpWyZrqdxROpYf5BDAJBA+iYqlNztk2EMlKvuRLBzajj00kf4PtFiergDSKDTg==",
|
||||
"license": "AGPL-3.0-or-later"
|
||||
},
|
||||
"node_modules/mute-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz",
|
||||
@ -24196,7 +24307,6 @@
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@ -24665,6 +24775,11 @@
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch-native": {
|
||||
"version": "1.6.4",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.4.tgz",
|
||||
"integrity": "sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ=="
|
||||
},
|
||||
"node_modules/node-gyp": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz",
|
||||
@ -24993,6 +25108,38 @@
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-mocks-http": {
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.16.2.tgz",
|
||||
"integrity": "sha512-2Sh6YItRp1oqewZNlck3LaFp5vbyW2u51HX2p1VLxQ9U/bG90XV8JY9O7Nk+HDd6OOn/oV3nA5Tx5k4Rki0qlg==",
|
||||
"dependencies": {
|
||||
"accepts": "^1.3.7",
|
||||
"content-disposition": "^0.5.3",
|
||||
"depd": "^1.1.0",
|
||||
"fresh": "^0.5.2",
|
||||
"merge-descriptors": "^1.0.1",
|
||||
"methods": "^1.1.2",
|
||||
"mime": "^1.3.4",
|
||||
"parseurl": "^1.3.3",
|
||||
"range-parser": "^1.2.0",
|
||||
"type-is": "^1.6.18"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/express": "^4.17.21 || ^5.0.0",
|
||||
"@types/node": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/express": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.14",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
|
||||
@ -25580,6 +25727,11 @@
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ohash": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.4.tgz",
|
||||
"integrity": "sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g=="
|
||||
},
|
||||
"node_modules/oidc-token-hash": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
|
||||
@ -25681,6 +25833,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/openapi-types": {
|
||||
"version": "12.1.3",
|
||||
"resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
|
||||
"integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="
|
||||
},
|
||||
"node_modules/openapi3-ts": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/openapi3-ts/-/openapi3-ts-2.0.2.tgz",
|
||||
@ -26440,6 +26597,14 @@
|
||||
"url": "https://ko-fi.com/killymxi"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/partysocket": {
|
||||
"version": "0.0.17",
|
||||
"resolved": "https://registry.npmjs.org/partysocket/-/partysocket-0.0.17.tgz",
|
||||
@ -26595,8 +26760,7 @@
|
||||
"node_modules/pathe": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
|
||||
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="
|
||||
},
|
||||
"node_modules/pathval": {
|
||||
"version": "1.1.1",
|
||||
@ -27908,6 +28072,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/radix3": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
|
||||
"integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="
|
||||
},
|
||||
"node_modules/raf-schd": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
|
||||
@ -27946,6 +28115,14 @@
|
||||
"safe-buffer": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz",
|
||||
@ -32782,6 +32959,32 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/trpc-openapi": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/trpc-openapi/-/trpc-openapi-1.2.0.tgz",
|
||||
"integrity": "sha512-pfYoCd/3KYXWXvUPZBKJw455OOwngKN/6SIcj7Yit19OMLJ+8yVZkEvGEeg5wUSwfsiTdRsKuvqkRPXVSwV7ew==",
|
||||
"workspaces": [
|
||||
".",
|
||||
"examples/with-nextjs",
|
||||
"examples/with-express",
|
||||
"examples/with-interop",
|
||||
"examples/with-serverless",
|
||||
"examples/with-fastify",
|
||||
"examples/with-nuxtjs"
|
||||
],
|
||||
"dependencies": {
|
||||
"co-body": "^6.1.0",
|
||||
"h3": "^1.6.4",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"node-mocks-http": "^1.12.2",
|
||||
"openapi-types": "^12.1.1",
|
||||
"zod-to-json-schema": "^3.21.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@trpc/server": "^10.0.0",
|
||||
"zod": "^3.14.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-api-utils": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
|
||||
@ -33547,6 +33750,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typed-array-buffer": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz",
|
||||
@ -33665,10 +33880,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ufo": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz",
|
||||
"integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==",
|
||||
"dev": true
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz",
|
||||
"integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ=="
|
||||
},
|
||||
"node_modules/ulid": {
|
||||
"version": "2.3.0",
|
||||
@ -33703,6 +33917,11 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/uncrypto": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz",
|
||||
"integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.28.2",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.2.tgz",
|
||||
@ -33720,6 +33939,29 @@
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/unenv": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/unenv/-/unenv-1.10.0.tgz",
|
||||
"integrity": "sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==",
|
||||
"dependencies": {
|
||||
"consola": "^3.2.3",
|
||||
"defu": "^6.1.4",
|
||||
"mime": "^3.0.0",
|
||||
"node-fetch-native": "^1.6.4",
|
||||
"pathe": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/unenv/node_modules/mime": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
|
||||
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unified": {
|
||||
"version": "10.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz",
|
||||
@ -35409,9 +35651,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.23.8",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
|
||||
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
|
||||
"version": "3.24.1",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
|
||||
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
@ -35454,6 +35696,14 @@
|
||||
"@prisma/debug": "5.22.0"
|
||||
}
|
||||
},
|
||||
"node_modules/zod-to-json-schema": {
|
||||
"version": "3.24.1",
|
||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz",
|
||||
"integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==",
|
||||
"peerDependencies": {
|
||||
"zod": "^3.24.1"
|
||||
}
|
||||
},
|
||||
"node_modules/zwitch": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
|
||||
@ -35478,7 +35728,7 @@
|
||||
"superjson": "^1.13.1",
|
||||
"swagger-ui-react": "^5.11.0",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
}
|
||||
},
|
||||
"packages/api/node_modules/@ts-rest/next": {
|
||||
@ -35543,7 +35793,7 @@
|
||||
"next-auth": "4.24.5",
|
||||
"react": "^18",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
}
|
||||
},
|
||||
"packages/email": {
|
||||
@ -36752,7 +37002,7 @@
|
||||
"sharp": "0.32.6",
|
||||
"stripe": "^12.7.0",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/browser-chromium": "1.43.0",
|
||||
@ -36890,7 +37140,7 @@
|
||||
"luxon": "^3.4.0",
|
||||
"superjson": "^1.13.1",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
}
|
||||
},
|
||||
"packages/trpc/node_modules/@ts-rest/next": {
|
||||
@ -36970,7 +37220,7 @@
|
||||
"tailwind-merge": "^1.12.0",
|
||||
"tailwindcss-animate": "^1.0.5",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@documenso/tailwind-config": "*",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "1.9.0-rc.2",
|
||||
"version": "1.9.0-rc.5",
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"build:web": "turbo run build --filter=@documenso/web",
|
||||
@ -68,11 +68,14 @@
|
||||
"@lingui/core": "^4.11.3",
|
||||
"inngest-cli": "^0.29.1",
|
||||
"luxon": "^3.5.0",
|
||||
"mupdf": "^1.0.0",
|
||||
"next-runtime-env": "^3.2.0",
|
||||
"react": "^18"
|
||||
"react": "^18",
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"overrides": {
|
||||
"next": "14.2.6"
|
||||
"next": "14.2.6",
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"trigger.dev": {
|
||||
"endpointId": "documenso-app"
|
||||
|
||||
@ -25,6 +25,6 @@
|
||||
"superjson": "^1.13.1",
|
||||
"swagger-ui-react": "^5.11.0",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,13 +23,13 @@ import { getFieldsForDocument } from '@documenso/lib/server-only/field/get-field
|
||||
import { updateField } from '@documenso/lib/server-only/field/update-field';
|
||||
import { insertFormValuesInPdf } from '@documenso/lib/server-only/pdf/insert-form-values-in-pdf';
|
||||
import { deleteRecipient } from '@documenso/lib/server-only/recipient/delete-recipient';
|
||||
import { getRecipientById } from '@documenso/lib/server-only/recipient/get-recipient-by-id';
|
||||
import { getRecipientByIdV1Api } from '@documenso/lib/server-only/recipient/get-recipient-by-id-v1-api';
|
||||
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
|
||||
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
|
||||
import { updateRecipient } from '@documenso/lib/server-only/recipient/update-recipient';
|
||||
import { createTeamMemberInvites } from '@documenso/lib/server-only/team/create-team-member-invites';
|
||||
import { deleteTeamMembers } from '@documenso/lib/server-only/team/delete-team-members';
|
||||
import type { CreateDocumentFromTemplateResponse } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||
import type { TCreateDocumentFromTemplateResponse } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||
import { createDocumentFromTemplateLegacy } from '@documenso/lib/server-only/template/create-document-from-template-legacy';
|
||||
import { deleteTemplate } from '@documenso/lib/server-only/template/delete-template';
|
||||
@ -345,7 +345,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
||||
});
|
||||
}
|
||||
|
||||
const recipients = await setRecipientsForDocument({
|
||||
const { recipients } = await setRecipientsForDocument({
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
documentId: document.id,
|
||||
@ -560,7 +560,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
||||
|
||||
const templateId = Number(params.templateId);
|
||||
|
||||
let document: CreateDocumentFromTemplateResponse | null = null;
|
||||
let document: TCreateDocumentFromTemplateResponse | null = null;
|
||||
|
||||
try {
|
||||
document = await createDocumentFromTemplate({
|
||||
@ -630,7 +630,6 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
||||
token: recipient.token,
|
||||
role: recipient.role,
|
||||
signingOrder: recipient.signingOrder,
|
||||
|
||||
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`,
|
||||
})),
|
||||
},
|
||||
@ -786,7 +785,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
||||
}
|
||||
|
||||
try {
|
||||
const newRecipients = await setRecipientsForDocument({
|
||||
const { recipients: newRecipients } = await setRecipientsForDocument({
|
||||
documentId: Number(documentId),
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
@ -1000,7 +999,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
||||
throw new Error('Invalid page number');
|
||||
}
|
||||
|
||||
const recipient = await getRecipientById({
|
||||
const recipient = await getRecipientByIdV1Api({
|
||||
id: Number(recipientId),
|
||||
documentId: Number(documentId),
|
||||
}).catch(() => null);
|
||||
@ -1145,7 +1144,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
||||
};
|
||||
}
|
||||
|
||||
const recipient = await getRecipientById({
|
||||
const recipient = await getRecipientByIdV1Api({
|
||||
id: Number(recipientId),
|
||||
documentId: Number(documentId),
|
||||
}).catch(() => null);
|
||||
@ -1249,7 +1248,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
||||
};
|
||||
}
|
||||
|
||||
const recipient = await getRecipientById({
|
||||
const recipient = await getRecipientByIdV1Api({
|
||||
id: Number(field.recipientId),
|
||||
documentId: Number(documentId),
|
||||
}).catch(() => null);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import type { NextApiRequest } from 'next';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token';
|
||||
import type { Team, User } from '@documenso/prisma/client';
|
||||
|
||||
@ -22,18 +23,33 @@ export const authenticatedMiddleware = <
|
||||
const [token] = (authorization || '').split('Bearer ').filter((s) => s.length > 0);
|
||||
|
||||
if (!token) {
|
||||
throw new Error('Token was not provided for authenticated middleware');
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'API token was not provided',
|
||||
});
|
||||
}
|
||||
|
||||
const apiToken = await getApiTokenByToken({ token });
|
||||
|
||||
if (apiToken.user.disabled) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'User is disabled',
|
||||
});
|
||||
}
|
||||
|
||||
return await handler(args, apiToken.user, apiToken.team);
|
||||
} catch (_err) {
|
||||
console.log({ _err });
|
||||
} catch (err) {
|
||||
console.log({ err: err });
|
||||
|
||||
let message = 'Unauthorized';
|
||||
|
||||
if (err instanceof AppError) {
|
||||
message = err.message;
|
||||
}
|
||||
|
||||
return {
|
||||
status: 401,
|
||||
body: {
|
||||
message: 'Unauthorized',
|
||||
message,
|
||||
},
|
||||
} as const;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { DocumentStatus, TeamMemberRole } from '@documenso/prisma/client';
|
||||
import { DocumentStatus, DocumentVisibility, TeamMemberRole } from '@documenso/prisma/client';
|
||||
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
|
||||
import { seedDocuments, seedTeamDocuments } from '@documenso/prisma/seed/documents';
|
||||
import { seedTeam, seedTeamEmail, seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
@ -538,7 +539,7 @@ test('[TEAMS]: ensure recipient can see document regardless of visibility', asyn
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that members cannot see ADMIN-only documents', async ({ page }) => {
|
||||
test('[TEAMS]: check that MEMBER role cannot see ADMIN-only documents', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
|
||||
// Seed a member user
|
||||
@ -575,7 +576,46 @@ test('[TEAMS]: check that members cannot see ADMIN-only documents', async ({ pag
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that managers cannot see ADMIN-only documents', async ({ page }) => {
|
||||
test('[TEAMS]: check that MEMBER role cannot see MANAGER_AND_ABOVE-only documents', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam();
|
||||
|
||||
// Seed a member user
|
||||
const memberUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
// Seed an ADMIN-only document
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'MANAGER_AND_ABOVE',
|
||||
title: 'Manager and Above Only Document',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: memberUser.email,
|
||||
redirectPath: `/t/${team.url}/documents?status=COMPLETED`,
|
||||
});
|
||||
|
||||
// Check that the member user cannot see the ADMIN-only document
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Admin Only Document', exact: true }),
|
||||
).not.toBeVisible();
|
||||
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that MANAGER role cannot see ADMIN-only documents', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
|
||||
// Seed a manager user
|
||||
@ -612,7 +652,7 @@ test('[TEAMS]: check that managers cannot see ADMIN-only documents', async ({ pa
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that admin can see MANAGER_AND_ABOVE documents', async ({ page }) => {
|
||||
test('[TEAMS]: check that ADMIN role can see MANAGER_AND_ABOVE documents', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
|
||||
// Seed an admin user
|
||||
@ -649,6 +689,187 @@ test('[TEAMS]: check that admin can see MANAGER_AND_ABOVE documents', async ({ p
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that ADMIN role can change document visibility', async ({ page }) => {
|
||||
const team = await seedTeam({
|
||||
createTeamOptions: {
|
||||
teamGlobalSettings: {
|
||||
create: {
|
||||
documentVisibility: DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const adminUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.ADMIN,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(adminUser, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: team.teamGlobalSettings?.documentVisibility,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: adminUser.email,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
await page.getByTestId('documentVisibilitySelectValue').click();
|
||||
await page.getByLabel('Admins only').click();
|
||||
|
||||
await page.getByRole('button', { name: 'Continue' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Go Back' }).click();
|
||||
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
|
||||
|
||||
await expect(page.getByTestId('documentVisibilitySelectValue')).toContainText('Admins only');
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that MEMBER role cannot change visibility of EVERYONE documents', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamOptions: {
|
||||
teamGlobalSettings: {
|
||||
create: {
|
||||
documentVisibility: DocumentVisibility.EVERYONE,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const teamMember = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(teamMember, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: team.teamGlobalSettings?.documentVisibility,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMember.email,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
await expect(page.getByTestId('documentVisibilitySelectValue')).toHaveText('Everyone');
|
||||
await expect(page.getByTestId('documentVisibilitySelectValue')).toBeDisabled();
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that MEMBER role cannot change visibility of MANAGER_AND_ABOVE documents', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamOptions: {
|
||||
teamGlobalSettings: {
|
||||
create: {
|
||||
documentVisibility: DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const teamMember = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(teamMember, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: team.teamGlobalSettings?.documentVisibility,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMember.email,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
await expect(page.getByTestId('documentVisibilitySelectValue')).toHaveText('Managers and above');
|
||||
await expect(page.getByTestId('documentVisibilitySelectValue')).toBeDisabled();
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that MEMBER role cannot change visibility of ADMIN documents', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamOptions: {
|
||||
teamGlobalSettings: {
|
||||
create: {
|
||||
documentVisibility: DocumentVisibility.ADMIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const teamMember = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(teamMember, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: team.teamGlobalSettings?.documentVisibility,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMember.email,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
await expect(page.getByTestId('documentVisibilitySelectValue')).toHaveText('Admins only');
|
||||
await expect(page.getByTestId('documentVisibilitySelectValue')).toBeDisabled();
|
||||
});
|
||||
|
||||
test('[TEAMS]: check that MANAGER role cannot change visibility of ADMIN documents', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam({
|
||||
createTeamOptions: {
|
||||
teamGlobalSettings: {
|
||||
create: {
|
||||
documentVisibility: DocumentVisibility.ADMIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const teamManager = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MANAGER,
|
||||
});
|
||||
|
||||
const document = await seedBlankDocument(teamManager, {
|
||||
createDocumentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: team.teamGlobalSettings?.documentVisibility,
|
||||
},
|
||||
});
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamManager.email,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
await expect(page.getByTestId('documentVisibilitySelectValue')).toHaveText('Admins only');
|
||||
await expect(page.getByTestId('documentVisibilitySelectValue')).toBeDisabled();
|
||||
});
|
||||
|
||||
test('[TEAMS]: users cannot see documents from other teams', async ({ page }) => {
|
||||
// Seed two teams with documents
|
||||
const { team: teamA, teamMember2: teamAMember } = await seedTeamDocuments();
|
||||
|
||||
@ -21,6 +21,6 @@
|
||||
"next-auth": "4.24.5",
|
||||
"react": "^18",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||
import { subscriptionsContainActiveEnterprisePlan } from '@documenso/lib/utils/billing';
|
||||
import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import type { Subscription } from '@documenso/prisma/client';
|
||||
|
||||
import { getEnterprisePlanPriceIds } from '../stripe/get-enterprise-plan-prices';
|
||||
|
||||
export type IsUserEnterpriseOptions = {
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
@ -52,5 +54,11 @@ export const isUserEnterprise = async ({
|
||||
.then((user) => user.Subscription);
|
||||
}
|
||||
|
||||
return subscriptionsContainActiveEnterprisePlan(subscriptions);
|
||||
if (subscriptions.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const enterprisePlanPriceIds = await getEnterprisePlanPriceIds();
|
||||
|
||||
return subscriptionsContainsActivePlan(subscriptions, enterprisePlanPriceIds, true);
|
||||
};
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import { Column, Img, Section, Text } from '../components';
|
||||
import { TemplateDocumentImage } from './template-document-image';
|
||||
|
||||
export interface TemplateDocumentRecipientSignedProps {
|
||||
documentName: string;
|
||||
recipientName: string;
|
||||
recipientEmail: string;
|
||||
assetBaseUrl: string;
|
||||
}
|
||||
|
||||
export const TemplateDocumentRecipientSigned = ({
|
||||
documentName,
|
||||
recipientName,
|
||||
recipientEmail,
|
||||
assetBaseUrl,
|
||||
}: TemplateDocumentRecipientSignedProps) => {
|
||||
const getAssetUrl = (path: string) => {
|
||||
return new URL(path, assetBaseUrl).toString();
|
||||
};
|
||||
|
||||
const recipientReference = recipientName || recipientEmail;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TemplateDocumentImage className="mt-6" assetBaseUrl={assetBaseUrl} />
|
||||
|
||||
<Section>
|
||||
<Section className="mb-4">
|
||||
<Column align="center">
|
||||
<Text className="text-base font-semibold text-[#7AC455]">
|
||||
<Img
|
||||
src={getAssetUrl('/static/completed.png')}
|
||||
className="-mt-0.5 mr-2 inline h-7 w-7 align-middle"
|
||||
/>
|
||||
<Trans>Completed</Trans>
|
||||
</Text>
|
||||
</Column>
|
||||
</Section>
|
||||
|
||||
<Text className="text-primary mb-0 text-center text-lg font-semibold">
|
||||
<Trans>
|
||||
{recipientReference} has signed "{documentName}"
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
<Text className="mx-auto mb-6 mt-1 max-w-[80%] text-center text-base text-slate-400">
|
||||
<Trans>{recipientReference} has completed signing the document.</Trans>
|
||||
</Text>
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TemplateDocumentRecipientSigned;
|
||||
70
packages/email/templates/document-recipient-signed.tsx
Normal file
70
packages/email/templates/document-recipient-signed.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import { msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
|
||||
import { Body, Container, Head, Html, Img, Preview, Section } from '../components';
|
||||
import { useBranding } from '../providers/branding';
|
||||
import { TemplateDocumentRecipientSigned } from '../template-components/template-document-recipient-signed';
|
||||
import { TemplateFooter } from '../template-components/template-footer';
|
||||
|
||||
export interface DocumentRecipientSignedEmailTemplateProps {
|
||||
documentName?: string;
|
||||
recipientName?: string;
|
||||
recipientEmail?: string;
|
||||
assetBaseUrl?: string;
|
||||
}
|
||||
|
||||
export const DocumentRecipientSignedEmailTemplate = ({
|
||||
documentName = 'Open Source Pledge.pdf',
|
||||
recipientName = 'John Doe',
|
||||
recipientEmail = 'lucas@documenso.com',
|
||||
assetBaseUrl = 'http://localhost:3002',
|
||||
}: DocumentRecipientSignedEmailTemplateProps) => {
|
||||
const { _ } = useLingui();
|
||||
const branding = useBranding();
|
||||
|
||||
const recipientReference = recipientName || recipientEmail;
|
||||
|
||||
const previewText = msg`${recipientReference} has signed ${documentName}`;
|
||||
|
||||
const getAssetUrl = (path: string) => {
|
||||
return new URL(path, assetBaseUrl).toString();
|
||||
};
|
||||
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>{_(previewText)}</Preview>
|
||||
|
||||
<Body className="mx-auto my-auto font-sans">
|
||||
<Section className="bg-white">
|
||||
<Container className="mx-auto mb-2 mt-8 max-w-xl rounded-lg border border-solid border-slate-200 p-2 backdrop-blur-sm">
|
||||
<Section className="p-2">
|
||||
{branding.brandingEnabled && branding.brandingLogo ? (
|
||||
<Img src={branding.brandingLogo} alt="Branding Logo" className="mb-4 h-6" />
|
||||
) : (
|
||||
<Img
|
||||
src={getAssetUrl('/static/logo.png')}
|
||||
alt="Documenso Logo"
|
||||
className="mb-4 h-6"
|
||||
/>
|
||||
)}
|
||||
|
||||
<TemplateDocumentRecipientSigned
|
||||
documentName={documentName}
|
||||
recipientName={recipientName}
|
||||
recipientEmail={recipientEmail}
|
||||
assetBaseUrl={assetBaseUrl}
|
||||
/>
|
||||
</Section>
|
||||
</Container>
|
||||
|
||||
<Container className="mx-auto max-w-xl">
|
||||
<TemplateFooter />
|
||||
</Container>
|
||||
</Section>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default DocumentRecipientSignedEmailTemplate;
|
||||
@ -12,7 +12,7 @@ export function useCopyShareLink({ onSuccess, onError }: UseCopyShareLinkOptions
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
|
||||
const { mutateAsync: createOrGetShareLink, isLoading: isCreatingShareLink } =
|
||||
trpc.shareLink.createOrGetShareLink.useMutation();
|
||||
trpc.document.createOrGetShareLink.useMutation();
|
||||
|
||||
/**
|
||||
* Copy a newly created, or pre-existing share link to the user's clipboard.
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { JobClient } from './client/client';
|
||||
import { SEND_CONFIRMATION_EMAIL_JOB_DEFINITION } from './definitions/emails/send-confirmation-email';
|
||||
import { SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION } from './definitions/emails/send-recipient-signed-email';
|
||||
import { SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION } from './definitions/emails/send-rejection-emails';
|
||||
import { SEND_SIGNING_EMAIL_JOB_DEFINITION } from './definitions/emails/send-signing-email';
|
||||
import { SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION } from './definitions/emails/send-team-deleted-email';
|
||||
@ -19,6 +20,7 @@ export const jobsClient = new JobClient([
|
||||
SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION,
|
||||
SEAL_DOCUMENT_JOB_DEFINITION,
|
||||
SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION,
|
||||
SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION,
|
||||
] as const);
|
||||
|
||||
export const jobs = jobsClient;
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { sendConfirmationToken } from '../../../server-only/user/send-confirmation-token';
|
||||
import type { TSendConfirmationEmailJobDefinition } from './send-confirmation-email';
|
||||
|
||||
export const run = async ({ payload }: { payload: TSendConfirmationEmailJobDefinition }) => {
|
||||
await sendConfirmationToken({
|
||||
email: payload.email,
|
||||
force: payload.force,
|
||||
});
|
||||
};
|
||||
@ -1,6 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { sendConfirmationToken } from '../../../server-only/user/send-confirmation-token';
|
||||
import type { JobDefinition } from '../../client/_internal/job';
|
||||
|
||||
const SEND_CONFIRMATION_EMAIL_JOB_DEFINITION_ID = 'send.signup.confirmation.email';
|
||||
@ -10,6 +9,10 @@ const SEND_CONFIRMATION_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
|
||||
force: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type TSendConfirmationEmailJobDefinition = z.infer<
|
||||
typeof SEND_CONFIRMATION_EMAIL_JOB_DEFINITION_SCHEMA
|
||||
>;
|
||||
|
||||
export const SEND_CONFIRMATION_EMAIL_JOB_DEFINITION = {
|
||||
id: SEND_CONFIRMATION_EMAIL_JOB_DEFINITION_ID,
|
||||
name: 'Send Confirmation Email',
|
||||
@ -19,12 +22,11 @@ export const SEND_CONFIRMATION_EMAIL_JOB_DEFINITION = {
|
||||
schema: SEND_CONFIRMATION_EMAIL_JOB_DEFINITION_SCHEMA,
|
||||
},
|
||||
handler: async ({ payload }) => {
|
||||
await sendConfirmationToken({
|
||||
email: payload.email,
|
||||
force: payload.force,
|
||||
});
|
||||
const handler = await import('./send-confirmation-email.handler');
|
||||
|
||||
await handler.run({ payload });
|
||||
},
|
||||
} as const satisfies JobDefinition<
|
||||
typeof SEND_CONFIRMATION_EMAIL_JOB_DEFINITION_ID,
|
||||
z.infer<typeof SEND_CONFIRMATION_EMAIL_JOB_DEFINITION_SCHEMA>
|
||||
TSendConfirmationEmailJobDefinition
|
||||
>;
|
||||
|
||||
@ -0,0 +1,130 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import { DocumentRecipientSignedEmailTemplate } from '@documenso/email/templates/document-recipient-signed';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
|
||||
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
|
||||
import { type JobDefinition } from '../../client/_internal/job';
|
||||
|
||||
const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_ID = 'send.recipient.signed.email';
|
||||
|
||||
const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
|
||||
documentId: z.number(),
|
||||
recipientId: z.number(),
|
||||
});
|
||||
|
||||
export const SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION = {
|
||||
id: SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_ID,
|
||||
name: 'Send Recipient Signed Email',
|
||||
version: '1.0.0',
|
||||
trigger: {
|
||||
name: SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_ID,
|
||||
schema: SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_SCHEMA,
|
||||
},
|
||||
handler: async ({ payload, io }) => {
|
||||
const { documentId, recipientId } = payload;
|
||||
|
||||
const document = await prisma.document.findFirst({
|
||||
where: {
|
||||
id: documentId,
|
||||
Recipient: {
|
||||
some: {
|
||||
id: recipientId,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
Recipient: {
|
||||
where: {
|
||||
id: recipientId,
|
||||
},
|
||||
},
|
||||
User: true,
|
||||
documentMeta: true,
|
||||
team: {
|
||||
include: {
|
||||
teamGlobalSettings: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!document) {
|
||||
throw new Error('Document not found');
|
||||
}
|
||||
|
||||
if (document.Recipient.length === 0) {
|
||||
throw new Error('Document has no recipients');
|
||||
}
|
||||
|
||||
const isRecipientSignedEmailEnabled = extractDerivedDocumentEmailSettings(
|
||||
document.documentMeta,
|
||||
).recipientSigned;
|
||||
|
||||
if (!isRecipientSignedEmailEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [recipient] = document.Recipient;
|
||||
const { email: recipientEmail, name: recipientName } = recipient;
|
||||
const { User: owner } = document;
|
||||
|
||||
const recipientReference = recipientName || recipientEmail;
|
||||
|
||||
// Don't send notification if the owner is the one who signed
|
||||
if (owner.email === recipientEmail) {
|
||||
return;
|
||||
}
|
||||
|
||||
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
|
||||
const i18n = await getI18nInstance(document.documentMeta?.language);
|
||||
|
||||
const template = createElement(DocumentRecipientSignedEmailTemplate, {
|
||||
documentName: document.title,
|
||||
recipientName,
|
||||
recipientEmail,
|
||||
assetBaseUrl,
|
||||
});
|
||||
|
||||
await io.runTask('send-recipient-signed-email', async () => {
|
||||
const branding = document.team?.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(template, { lang: document.documentMeta?.language, branding }),
|
||||
renderEmailWithI18N(template, {
|
||||
lang: document.documentMeta?.language,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: {
|
||||
name: owner.name ?? '',
|
||||
address: owner.email,
|
||||
},
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: i18n._(msg`${recipientReference} has signed "${document.title}"`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
});
|
||||
},
|
||||
} as const satisfies JobDefinition<
|
||||
typeof SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_ID,
|
||||
z.infer<typeof SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION_SCHEMA>
|
||||
>;
|
||||
@ -0,0 +1,156 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import DocumentRejectedEmail from '@documenso/email/templates/document-rejected';
|
||||
import DocumentRejectionConfirmedEmail from '@documenso/email/templates/document-rejection-confirmed';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
|
||||
|
||||
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
|
||||
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
|
||||
import { formatDocumentsPath } from '../../../utils/teams';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
import type { TSendSigningRejectionEmailsJobDefinition } from './send-rejection-emails';
|
||||
|
||||
export const run = async ({
|
||||
payload,
|
||||
io,
|
||||
}: {
|
||||
payload: TSendSigningRejectionEmailsJobDefinition;
|
||||
io: JobRunIO;
|
||||
}) => {
|
||||
const { documentId, recipientId } = payload;
|
||||
|
||||
const [document, recipient] = await Promise.all([
|
||||
prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
},
|
||||
include: {
|
||||
User: true,
|
||||
documentMeta: true,
|
||||
team: {
|
||||
select: {
|
||||
teamEmail: true,
|
||||
name: true,
|
||||
url: true,
|
||||
teamGlobalSettings: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.recipient.findFirstOrThrow({
|
||||
where: {
|
||||
id: recipientId,
|
||||
signingStatus: SigningStatus.REJECTED,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const { documentMeta, team, User: documentOwner } = document;
|
||||
|
||||
const isEmailEnabled = extractDerivedDocumentEmailSettings(
|
||||
document.documentMeta,
|
||||
).recipientSigningRequest;
|
||||
|
||||
if (!isEmailEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const i18n = await getI18nInstance(documentMeta?.language);
|
||||
|
||||
// Send confirmation email to the recipient who rejected
|
||||
await io.runTask('send-rejection-confirmation-email', async () => {
|
||||
const recipientTemplate = createElement(DocumentRejectionConfirmedEmail, {
|
||||
recipientName: recipient.name,
|
||||
documentName: document.title,
|
||||
documentOwnerName: document.User.name || document.User.email,
|
||||
reason: recipient.rejectionReason || '',
|
||||
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
|
||||
});
|
||||
|
||||
const branding = document.team?.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(recipientTemplate, { lang: documentMeta?.language, branding }),
|
||||
renderEmailWithI18N(recipientTemplate, {
|
||||
lang: documentMeta?.language,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: {
|
||||
name: recipient.name,
|
||||
address: recipient.email,
|
||||
},
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: i18n._(msg`Document "${document.title}" - Rejection Confirmed`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
});
|
||||
|
||||
// Send notification email to document owner
|
||||
await io.runTask('send-owner-notification-email', async () => {
|
||||
const ownerTemplate = createElement(DocumentRejectedEmail, {
|
||||
recipientName: recipient.name,
|
||||
documentName: document.title,
|
||||
documentUrl: `${NEXT_PUBLIC_WEBAPP_URL()}${formatDocumentsPath(document.team?.url)}/${
|
||||
document.id
|
||||
}`,
|
||||
rejectionReason: recipient.rejectionReason || '',
|
||||
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
|
||||
});
|
||||
|
||||
const branding = document.team?.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(ownerTemplate, { lang: documentMeta?.language, branding }),
|
||||
renderEmailWithI18N(ownerTemplate, {
|
||||
lang: documentMeta?.language,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: {
|
||||
name: documentOwner.name || '',
|
||||
address: documentOwner.email,
|
||||
},
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: i18n._(msg`Document "${document.title}" - Rejected by ${recipient.name}`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
});
|
||||
|
||||
await io.runTask('update-recipient', async () => {
|
||||
await prisma.recipient.update({
|
||||
where: {
|
||||
id: recipient.id,
|
||||
},
|
||||
data: {
|
||||
sendStatus: SendStatus.SENT,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -1,21 +1,5 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import DocumentRejectedEmail from '@documenso/email/templates/document-rejected';
|
||||
import DocumentRejectionConfirmedEmail from '@documenso/email/templates/document-rejection-confirmed';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
|
||||
|
||||
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
|
||||
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
|
||||
import { formatDocumentsPath } from '../../../utils/teams';
|
||||
import { type JobDefinition } from '../../client/_internal/job';
|
||||
|
||||
const SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION_ID = 'send.signing.rejected.emails';
|
||||
@ -25,6 +9,10 @@ const SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION_SCHEMA = z.object({
|
||||
recipientId: z.number(),
|
||||
});
|
||||
|
||||
export type TSendSigningRejectionEmailsJobDefinition = z.infer<
|
||||
typeof SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION_SCHEMA
|
||||
>;
|
||||
|
||||
export const SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION = {
|
||||
id: SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION_ID,
|
||||
name: 'Send Rejection Emails',
|
||||
@ -34,136 +22,11 @@ export const SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION = {
|
||||
schema: SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION_SCHEMA,
|
||||
},
|
||||
handler: async ({ payload, io }) => {
|
||||
const { documentId, recipientId } = payload;
|
||||
const handler = await import('./send-rejection-emails.handler');
|
||||
|
||||
const [document, recipient] = await Promise.all([
|
||||
prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
},
|
||||
include: {
|
||||
User: true,
|
||||
documentMeta: true,
|
||||
team: {
|
||||
select: {
|
||||
teamEmail: true,
|
||||
name: true,
|
||||
url: true,
|
||||
teamGlobalSettings: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.recipient.findFirstOrThrow({
|
||||
where: {
|
||||
id: recipientId,
|
||||
signingStatus: SigningStatus.REJECTED,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const { documentMeta, team, User: documentOwner } = document;
|
||||
|
||||
const isEmailEnabled = extractDerivedDocumentEmailSettings(
|
||||
document.documentMeta,
|
||||
).recipientSigningRequest;
|
||||
|
||||
if (!isEmailEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const i18n = await getI18nInstance(documentMeta?.language);
|
||||
|
||||
// Send confirmation email to the recipient who rejected
|
||||
await io.runTask('send-rejection-confirmation-email', async () => {
|
||||
const recipientTemplate = createElement(DocumentRejectionConfirmedEmail, {
|
||||
recipientName: recipient.name,
|
||||
documentName: document.title,
|
||||
documentOwnerName: document.User.name || document.User.email,
|
||||
reason: recipient.rejectionReason || '',
|
||||
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
|
||||
});
|
||||
|
||||
const branding = document.team?.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(recipientTemplate, { lang: documentMeta?.language, branding }),
|
||||
renderEmailWithI18N(recipientTemplate, {
|
||||
lang: documentMeta?.language,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: {
|
||||
name: recipient.name,
|
||||
address: recipient.email,
|
||||
},
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: i18n._(msg`Document "${document.title}" - Rejection Confirmed`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
});
|
||||
|
||||
// Send notification email to document owner
|
||||
await io.runTask('send-owner-notification-email', async () => {
|
||||
const ownerTemplate = createElement(DocumentRejectedEmail, {
|
||||
recipientName: recipient.name,
|
||||
documentName: document.title,
|
||||
documentUrl: `${NEXT_PUBLIC_WEBAPP_URL()}${formatDocumentsPath(document.team?.url)}/${
|
||||
document.id
|
||||
}`,
|
||||
rejectionReason: recipient.rejectionReason || '',
|
||||
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
|
||||
});
|
||||
|
||||
const branding = document.team?.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(ownerTemplate, { lang: documentMeta?.language, branding }),
|
||||
renderEmailWithI18N(ownerTemplate, {
|
||||
lang: documentMeta?.language,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: {
|
||||
name: documentOwner.name || '',
|
||||
address: documentOwner.email,
|
||||
},
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: i18n._(msg`Document "${document.title}" - Rejected by ${recipient.name}`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
});
|
||||
|
||||
await io.runTask('update-recipient', async () => {
|
||||
await prisma.recipient.update({
|
||||
where: {
|
||||
id: recipient.id,
|
||||
},
|
||||
data: {
|
||||
sendStatus: SendStatus.SENT,
|
||||
},
|
||||
});
|
||||
});
|
||||
await handler.run({ payload, io });
|
||||
},
|
||||
} as const satisfies JobDefinition<
|
||||
typeof SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION_ID,
|
||||
z.infer<typeof SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION_SCHEMA>
|
||||
TSendSigningRejectionEmailsJobDefinition
|
||||
>;
|
||||
|
||||
@ -0,0 +1,215 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import DocumentInviteEmailTemplate from '@documenso/email/templates/document-invite';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import {
|
||||
DocumentSource,
|
||||
DocumentStatus,
|
||||
RecipientRole,
|
||||
SendStatus,
|
||||
} from '@documenso/prisma/client';
|
||||
|
||||
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
|
||||
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
|
||||
import {
|
||||
RECIPIENT_ROLES_DESCRIPTION,
|
||||
RECIPIENT_ROLE_TO_EMAIL_TYPE,
|
||||
} from '../../../constants/recipient-roles';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
|
||||
import { createDocumentAuditLogData } from '../../../utils/document-audit-logs';
|
||||
import { renderCustomEmailTemplate } from '../../../utils/render-custom-email-template';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
import type { TSendSigningEmailJobDefinition } from './send-signing-email';
|
||||
|
||||
export const run = async ({
|
||||
payload,
|
||||
io,
|
||||
}: {
|
||||
payload: TSendSigningEmailJobDefinition;
|
||||
io: JobRunIO;
|
||||
}) => {
|
||||
const { userId, documentId, recipientId, requestMetadata } = payload;
|
||||
|
||||
const [user, document, recipient] = await Promise.all([
|
||||
prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
}),
|
||||
prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
status: DocumentStatus.PENDING,
|
||||
},
|
||||
include: {
|
||||
documentMeta: true,
|
||||
team: {
|
||||
select: {
|
||||
teamEmail: true,
|
||||
name: true,
|
||||
teamGlobalSettings: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.recipient.findFirstOrThrow({
|
||||
where: {
|
||||
id: recipientId,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const { documentMeta, team } = document;
|
||||
|
||||
if (recipient.role === RecipientRole.CC) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isRecipientSigningRequestEmailEnabled = extractDerivedDocumentEmailSettings(
|
||||
document.documentMeta,
|
||||
).recipientSigningRequest;
|
||||
|
||||
if (!isRecipientSigningRequestEmailEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const customEmail = document?.documentMeta;
|
||||
const isDirectTemplate = document.source === DocumentSource.TEMPLATE_DIRECT_LINK;
|
||||
const isTeamDocument = document.teamId !== null;
|
||||
|
||||
const recipientEmailType = RECIPIENT_ROLE_TO_EMAIL_TYPE[recipient.role];
|
||||
|
||||
const { email, name } = recipient;
|
||||
const selfSigner = email === user.email;
|
||||
|
||||
const i18n = await getI18nInstance(documentMeta?.language);
|
||||
|
||||
const recipientActionVerb = i18n
|
||||
._(RECIPIENT_ROLES_DESCRIPTION[recipient.role].actionVerb)
|
||||
.toLowerCase();
|
||||
|
||||
let emailMessage = customEmail?.message || '';
|
||||
let emailSubject = i18n._(msg`Please ${recipientActionVerb} this document`);
|
||||
|
||||
if (selfSigner) {
|
||||
emailMessage = i18n._(
|
||||
msg`You have initiated the document ${`"${document.title}"`} that requires you to ${recipientActionVerb} it.`,
|
||||
);
|
||||
emailSubject = i18n._(msg`Please ${recipientActionVerb} your document`);
|
||||
}
|
||||
|
||||
if (isDirectTemplate) {
|
||||
emailMessage = i18n._(
|
||||
msg`A document was created by your direct template that requires you to ${recipientActionVerb} it.`,
|
||||
);
|
||||
emailSubject = i18n._(
|
||||
msg`Please ${recipientActionVerb} this document created by your direct template`,
|
||||
);
|
||||
}
|
||||
|
||||
if (isTeamDocument && team) {
|
||||
emailSubject = i18n._(msg`${team.name} invited you to ${recipientActionVerb} a document`);
|
||||
emailMessage = customEmail?.message ?? '';
|
||||
|
||||
if (!emailMessage) {
|
||||
emailMessage = i18n._(
|
||||
team.teamGlobalSettings?.includeSenderDetails
|
||||
? msg`${user.name} on behalf of "${team.name}" has invited you to ${recipientActionVerb} the document "${document.title}".`
|
||||
: msg`${team.name} has invited you to ${recipientActionVerb} the document "${document.title}".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const customEmailTemplate = {
|
||||
'signer.name': name,
|
||||
'signer.email': email,
|
||||
'document.name': document.title,
|
||||
};
|
||||
|
||||
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
|
||||
const signDocumentLink = `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`;
|
||||
|
||||
const template = createElement(DocumentInviteEmailTemplate, {
|
||||
documentName: document.title,
|
||||
inviterName: user.name || undefined,
|
||||
inviterEmail: isTeamDocument ? team?.teamEmail?.email || user.email : user.email,
|
||||
assetBaseUrl,
|
||||
signDocumentLink,
|
||||
customBody: renderCustomEmailTemplate(emailMessage, customEmailTemplate),
|
||||
role: recipient.role,
|
||||
selfSigner,
|
||||
isTeamInvite: isTeamDocument,
|
||||
teamName: team?.name,
|
||||
teamEmail: team?.teamEmail?.email,
|
||||
includeSenderDetails: team?.teamGlobalSettings?.includeSenderDetails,
|
||||
});
|
||||
|
||||
await io.runTask('send-signing-email', async () => {
|
||||
const branding = document.team?.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(template, { lang: documentMeta?.language, branding }),
|
||||
renderEmailWithI18N(template, {
|
||||
lang: documentMeta?.language,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: {
|
||||
name: recipient.name,
|
||||
address: recipient.email,
|
||||
},
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: renderCustomEmailTemplate(
|
||||
documentMeta?.subject || emailSubject,
|
||||
customEmailTemplate,
|
||||
),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
});
|
||||
|
||||
await io.runTask('update-recipient', async () => {
|
||||
await prisma.recipient.update({
|
||||
where: {
|
||||
id: recipient.id,
|
||||
},
|
||||
data: {
|
||||
sendStatus: SendStatus.SENT,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await io.runTask('store-audit-log', async () => {
|
||||
await prisma.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
||||
documentId: document.id,
|
||||
user,
|
||||
requestMetadata,
|
||||
data: {
|
||||
emailType: recipientEmailType,
|
||||
recipientId: recipient.id,
|
||||
recipientName: recipient.name,
|
||||
recipientEmail: recipient.email,
|
||||
recipientRole: recipient.role,
|
||||
isResending: false,
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -1,32 +1,6 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import DocumentInviteEmailTemplate from '@documenso/email/templates/document-invite';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import {
|
||||
DocumentSource,
|
||||
DocumentStatus,
|
||||
RecipientRole,
|
||||
SendStatus,
|
||||
} from '@documenso/prisma/client';
|
||||
|
||||
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
|
||||
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
|
||||
import {
|
||||
RECIPIENT_ROLES_DESCRIPTION,
|
||||
RECIPIENT_ROLE_TO_EMAIL_TYPE,
|
||||
} from '../../../constants/recipient-roles';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
|
||||
import { ZRequestMetadataSchema } from '../../../universal/extract-request-metadata';
|
||||
import { createDocumentAuditLogData } from '../../../utils/document-audit-logs';
|
||||
import { renderCustomEmailTemplate } from '../../../utils/render-custom-email-template';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
|
||||
import { type JobDefinition } from '../../client/_internal/job';
|
||||
|
||||
const SEND_SIGNING_EMAIL_JOB_DEFINITION_ID = 'send.signing.requested.email';
|
||||
@ -38,6 +12,10 @@ const SEND_SIGNING_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
|
||||
requestMetadata: ZRequestMetadataSchema.optional(),
|
||||
});
|
||||
|
||||
export type TSendSigningEmailJobDefinition = z.infer<
|
||||
typeof SEND_SIGNING_EMAIL_JOB_DEFINITION_SCHEMA
|
||||
>;
|
||||
|
||||
export const SEND_SIGNING_EMAIL_JOB_DEFINITION = {
|
||||
id: SEND_SIGNING_EMAIL_JOB_DEFINITION_ID,
|
||||
name: 'Send Signing Email',
|
||||
@ -47,185 +25,11 @@ export const SEND_SIGNING_EMAIL_JOB_DEFINITION = {
|
||||
schema: SEND_SIGNING_EMAIL_JOB_DEFINITION_SCHEMA,
|
||||
},
|
||||
handler: async ({ payload, io }) => {
|
||||
const { userId, documentId, recipientId, requestMetadata } = payload;
|
||||
const handler = await import('./send-signing-email.handler');
|
||||
|
||||
const [user, document, recipient] = await Promise.all([
|
||||
prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
}),
|
||||
prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
status: DocumentStatus.PENDING,
|
||||
},
|
||||
include: {
|
||||
documentMeta: true,
|
||||
team: {
|
||||
select: {
|
||||
teamEmail: true,
|
||||
name: true,
|
||||
teamGlobalSettings: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.recipient.findFirstOrThrow({
|
||||
where: {
|
||||
id: recipientId,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
const { documentMeta, team } = document;
|
||||
|
||||
if (recipient.role === RecipientRole.CC) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isRecipientSigningRequestEmailEnabled = extractDerivedDocumentEmailSettings(
|
||||
document.documentMeta,
|
||||
).recipientSigningRequest;
|
||||
|
||||
if (!isRecipientSigningRequestEmailEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const customEmail = document?.documentMeta;
|
||||
const isDirectTemplate = document.source === DocumentSource.TEMPLATE_DIRECT_LINK;
|
||||
const isTeamDocument = document.teamId !== null;
|
||||
|
||||
const recipientEmailType = RECIPIENT_ROLE_TO_EMAIL_TYPE[recipient.role];
|
||||
|
||||
const { email, name } = recipient;
|
||||
const selfSigner = email === user.email;
|
||||
|
||||
const i18n = await getI18nInstance(documentMeta?.language);
|
||||
|
||||
const recipientActionVerb = i18n
|
||||
._(RECIPIENT_ROLES_DESCRIPTION[recipient.role].actionVerb)
|
||||
.toLowerCase();
|
||||
|
||||
let emailMessage = customEmail?.message || '';
|
||||
let emailSubject = i18n._(msg`Please ${recipientActionVerb} this document`);
|
||||
|
||||
if (selfSigner) {
|
||||
emailMessage = i18n._(
|
||||
msg`You have initiated the document ${`"${document.title}"`} that requires you to ${recipientActionVerb} it.`,
|
||||
);
|
||||
emailSubject = i18n._(msg`Please ${recipientActionVerb} your document`);
|
||||
}
|
||||
|
||||
if (isDirectTemplate) {
|
||||
emailMessage = i18n._(
|
||||
msg`A document was created by your direct template that requires you to ${recipientActionVerb} it.`,
|
||||
);
|
||||
emailSubject = i18n._(
|
||||
msg`Please ${recipientActionVerb} this document created by your direct template`,
|
||||
);
|
||||
}
|
||||
|
||||
if (isTeamDocument && team) {
|
||||
emailSubject = i18n._(msg`${team.name} invited you to ${recipientActionVerb} a document`);
|
||||
emailMessage = customEmail?.message ?? '';
|
||||
|
||||
if (!emailMessage) {
|
||||
emailMessage = i18n._(
|
||||
team.teamGlobalSettings?.includeSenderDetails
|
||||
? msg`${user.name} on behalf of "${team.name}" has invited you to ${recipientActionVerb} the document "${document.title}".`
|
||||
: msg`${team.name} has invited you to ${recipientActionVerb} the document "${document.title}".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const customEmailTemplate = {
|
||||
'signer.name': name,
|
||||
'signer.email': email,
|
||||
'document.name': document.title,
|
||||
};
|
||||
|
||||
const assetBaseUrl = NEXT_PUBLIC_WEBAPP_URL() || 'http://localhost:3000';
|
||||
const signDocumentLink = `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`;
|
||||
|
||||
const template = createElement(DocumentInviteEmailTemplate, {
|
||||
documentName: document.title,
|
||||
inviterName: user.name || undefined,
|
||||
inviterEmail: isTeamDocument ? team?.teamEmail?.email || user.email : user.email,
|
||||
assetBaseUrl,
|
||||
signDocumentLink,
|
||||
customBody: renderCustomEmailTemplate(emailMessage, customEmailTemplate),
|
||||
role: recipient.role,
|
||||
selfSigner,
|
||||
isTeamInvite: isTeamDocument,
|
||||
teamName: team?.name,
|
||||
teamEmail: team?.teamEmail?.email,
|
||||
includeSenderDetails: team?.teamGlobalSettings?.includeSenderDetails,
|
||||
});
|
||||
|
||||
await io.runTask('send-signing-email', async () => {
|
||||
const branding = document.team?.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(template, { lang: documentMeta?.language, branding }),
|
||||
renderEmailWithI18N(template, {
|
||||
lang: documentMeta?.language,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: {
|
||||
name: recipient.name,
|
||||
address: recipient.email,
|
||||
},
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: renderCustomEmailTemplate(
|
||||
documentMeta?.subject || emailSubject,
|
||||
customEmailTemplate,
|
||||
),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
});
|
||||
|
||||
await io.runTask('update-recipient', async () => {
|
||||
await prisma.recipient.update({
|
||||
where: {
|
||||
id: recipient.id,
|
||||
},
|
||||
data: {
|
||||
sendStatus: SendStatus.SENT,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await io.runTask('store-audit-log', async () => {
|
||||
await prisma.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
||||
documentId: document.id,
|
||||
user,
|
||||
requestMetadata,
|
||||
data: {
|
||||
emailType: recipientEmailType,
|
||||
recipientId: recipient.id,
|
||||
recipientName: recipient.name,
|
||||
recipientEmail: recipient.email,
|
||||
recipientRole: recipient.role,
|
||||
isResending: false,
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
await handler.run({ payload, io });
|
||||
},
|
||||
} as const satisfies JobDefinition<
|
||||
typeof SEND_SIGNING_EMAIL_JOB_DEFINITION_ID,
|
||||
z.infer<typeof SEND_SIGNING_EMAIL_JOB_DEFINITION_SCHEMA>
|
||||
TSendSigningEmailJobDefinition
|
||||
>;
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
import { sendTeamDeleteEmail } from '../../../server-only/team/delete-team';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
import type { TSendTeamDeletedEmailJobDefinition } from './send-team-deleted-email';
|
||||
|
||||
export const run = async ({
|
||||
payload,
|
||||
io,
|
||||
}: {
|
||||
payload: TSendTeamDeletedEmailJobDefinition;
|
||||
io: JobRunIO;
|
||||
}) => {
|
||||
const { team, members } = payload;
|
||||
|
||||
for (const member of members) {
|
||||
await io.runTask(`send-team-deleted-email--${team.url}_${member.id}`, async () => {
|
||||
await sendTeamDeleteEmail({
|
||||
email: member.email,
|
||||
team,
|
||||
isOwner: member.id === team.ownerUserId,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -2,7 +2,6 @@ import { z } from 'zod';
|
||||
|
||||
import { DocumentVisibility } from '@documenso/prisma/client';
|
||||
|
||||
import { sendTeamDeleteEmail } from '../../../server-only/team/delete-team';
|
||||
import type { JobDefinition } from '../../client/_internal/job';
|
||||
|
||||
const SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_ID = 'send.team-deleted.email';
|
||||
@ -37,6 +36,10 @@ const SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
|
||||
),
|
||||
});
|
||||
|
||||
export type TSendTeamDeletedEmailJobDefinition = z.infer<
|
||||
typeof SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_SCHEMA
|
||||
>;
|
||||
|
||||
export const SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION = {
|
||||
id: SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_ID,
|
||||
name: 'Send Team Deleted Email',
|
||||
@ -46,19 +49,11 @@ export const SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION = {
|
||||
schema: SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_SCHEMA,
|
||||
},
|
||||
handler: async ({ payload, io }) => {
|
||||
const { team, members } = payload;
|
||||
const handler = await import('./send-team-deleted-email.handler');
|
||||
|
||||
for (const member of members) {
|
||||
await io.runTask(`send-team-deleted-email--${team.url}_${member.id}`, async () => {
|
||||
await sendTeamDeleteEmail({
|
||||
email: member.email,
|
||||
team,
|
||||
isOwner: member.id === team.ownerUserId,
|
||||
});
|
||||
});
|
||||
}
|
||||
await handler.run({ payload, io });
|
||||
},
|
||||
} as const satisfies JobDefinition<
|
||||
typeof SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_ID,
|
||||
z.infer<typeof SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_SCHEMA>
|
||||
TSendTeamDeletedEmailJobDefinition
|
||||
>;
|
||||
|
||||
@ -0,0 +1,105 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import TeamJoinEmailTemplate from '@documenso/email/templates/team-join';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { TeamMemberRole } from '@documenso/prisma/client';
|
||||
|
||||
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
|
||||
import { WEBAPP_BASE_URL } from '../../../constants/app';
|
||||
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
import type { TSendTeamMemberJoinedEmailJobDefinition } from './send-team-member-joined-email';
|
||||
|
||||
export const run = async ({
|
||||
payload,
|
||||
io,
|
||||
}: {
|
||||
payload: TSendTeamMemberJoinedEmailJobDefinition;
|
||||
io: JobRunIO;
|
||||
}) => {
|
||||
const team = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
id: payload.teamId,
|
||||
},
|
||||
include: {
|
||||
members: {
|
||||
where: {
|
||||
role: {
|
||||
in: [TeamMemberRole.ADMIN, TeamMemberRole.MANAGER],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
teamGlobalSettings: true,
|
||||
},
|
||||
});
|
||||
|
||||
const invitedMember = await prisma.teamMember.findFirstOrThrow({
|
||||
where: {
|
||||
id: payload.memberId,
|
||||
teamId: payload.teamId,
|
||||
},
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const member of team.members) {
|
||||
if (member.id === invitedMember.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await io.runTask(
|
||||
`send-team-member-joined-email--${invitedMember.id}_${member.id}`,
|
||||
async () => {
|
||||
const emailContent = createElement(TeamJoinEmailTemplate, {
|
||||
assetBaseUrl: WEBAPP_BASE_URL,
|
||||
baseUrl: WEBAPP_BASE_URL,
|
||||
memberName: invitedMember.user.name || '',
|
||||
memberEmail: invitedMember.user.email,
|
||||
teamName: team.name,
|
||||
teamUrl: team.url,
|
||||
});
|
||||
|
||||
const branding = team.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const lang = team.teamGlobalSettings?.documentLanguage;
|
||||
|
||||
// !: Replace with the actual language of the recipient later
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(emailContent, {
|
||||
lang,
|
||||
branding,
|
||||
}),
|
||||
renderEmailWithI18N(emailContent, {
|
||||
lang,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const i18n = await getI18nInstance(lang);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: member.user.email,
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: i18n._(msg`A new member has joined your team`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -1,18 +1,5 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import TeamJoinEmailTemplate from '@documenso/email/templates/team-join';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { TeamMemberRole } from '@documenso/prisma/client';
|
||||
|
||||
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
|
||||
import { WEBAPP_BASE_URL } from '../../../constants/app';
|
||||
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
|
||||
import type { JobDefinition } from '../../client/_internal/job';
|
||||
|
||||
const SEND_TEAM_MEMBER_JOINED_EMAIL_JOB_DEFINITION_ID = 'send.team-member-joined.email';
|
||||
@ -22,6 +9,10 @@ const SEND_TEAM_MEMBER_JOINED_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
|
||||
memberId: z.number(),
|
||||
});
|
||||
|
||||
export type TSendTeamMemberJoinedEmailJobDefinition = z.infer<
|
||||
typeof SEND_TEAM_MEMBER_JOINED_EMAIL_JOB_DEFINITION_SCHEMA
|
||||
>;
|
||||
|
||||
export const SEND_TEAM_MEMBER_JOINED_EMAIL_JOB_DEFINITION = {
|
||||
id: SEND_TEAM_MEMBER_JOINED_EMAIL_JOB_DEFINITION_ID,
|
||||
name: 'Send Team Member Joined Email',
|
||||
@ -31,88 +22,11 @@ export const SEND_TEAM_MEMBER_JOINED_EMAIL_JOB_DEFINITION = {
|
||||
schema: SEND_TEAM_MEMBER_JOINED_EMAIL_JOB_DEFINITION_SCHEMA,
|
||||
},
|
||||
handler: async ({ payload, io }) => {
|
||||
const team = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
id: payload.teamId,
|
||||
},
|
||||
include: {
|
||||
members: {
|
||||
where: {
|
||||
role: {
|
||||
in: [TeamMemberRole.ADMIN, TeamMemberRole.MANAGER],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
teamGlobalSettings: true,
|
||||
},
|
||||
});
|
||||
const handler = await import('./send-team-member-joined-email.handler');
|
||||
|
||||
const invitedMember = await prisma.teamMember.findFirstOrThrow({
|
||||
where: {
|
||||
id: payload.memberId,
|
||||
teamId: payload.teamId,
|
||||
},
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
|
||||
for (const member of team.members) {
|
||||
if (member.id === invitedMember.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await io.runTask(
|
||||
`send-team-member-joined-email--${invitedMember.id}_${member.id}`,
|
||||
async () => {
|
||||
const emailContent = createElement(TeamJoinEmailTemplate, {
|
||||
assetBaseUrl: WEBAPP_BASE_URL,
|
||||
baseUrl: WEBAPP_BASE_URL,
|
||||
memberName: invitedMember.user.name || '',
|
||||
memberEmail: invitedMember.user.email,
|
||||
teamName: team.name,
|
||||
teamUrl: team.url,
|
||||
});
|
||||
|
||||
const branding = team.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const lang = team.teamGlobalSettings?.documentLanguage;
|
||||
|
||||
// !: Replace with the actual language of the recipient later
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(emailContent, {
|
||||
lang,
|
||||
branding,
|
||||
}),
|
||||
renderEmailWithI18N(emailContent, {
|
||||
lang,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const i18n = await getI18nInstance(lang);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: member.user.email,
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: i18n._(msg`A new member has joined your team`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
await handler.run({ payload, io });
|
||||
},
|
||||
} as const satisfies JobDefinition<
|
||||
typeof SEND_TEAM_MEMBER_JOINED_EMAIL_JOB_DEFINITION_ID,
|
||||
z.infer<typeof SEND_TEAM_MEMBER_JOINED_EMAIL_JOB_DEFINITION_SCHEMA>
|
||||
TSendTeamMemberJoinedEmailJobDefinition
|
||||
>;
|
||||
|
||||
@ -0,0 +1,93 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import TeamJoinEmailTemplate from '@documenso/email/templates/team-join';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { TeamMemberRole } from '@documenso/prisma/client';
|
||||
|
||||
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
|
||||
import { WEBAPP_BASE_URL } from '../../../constants/app';
|
||||
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
import type { TSendTeamMemberLeftEmailJobDefinition } from './send-team-member-left-email';
|
||||
|
||||
export const run = async ({
|
||||
payload,
|
||||
io,
|
||||
}: {
|
||||
payload: TSendTeamMemberLeftEmailJobDefinition;
|
||||
io: JobRunIO;
|
||||
}) => {
|
||||
const team = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
id: payload.teamId,
|
||||
},
|
||||
include: {
|
||||
members: {
|
||||
where: {
|
||||
role: {
|
||||
in: [TeamMemberRole.ADMIN, TeamMemberRole.MANAGER],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
teamGlobalSettings: true,
|
||||
},
|
||||
});
|
||||
|
||||
const oldMember = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: payload.memberUserId,
|
||||
},
|
||||
});
|
||||
|
||||
for (const member of team.members) {
|
||||
await io.runTask(`send-team-member-left-email--${oldMember.id}_${member.id}`, async () => {
|
||||
const emailContent = createElement(TeamJoinEmailTemplate, {
|
||||
assetBaseUrl: WEBAPP_BASE_URL,
|
||||
baseUrl: WEBAPP_BASE_URL,
|
||||
memberName: oldMember.name || '',
|
||||
memberEmail: oldMember.email,
|
||||
teamName: team.name,
|
||||
teamUrl: team.url,
|
||||
});
|
||||
|
||||
const branding = team.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const lang = team.teamGlobalSettings?.documentLanguage;
|
||||
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(emailContent, {
|
||||
lang,
|
||||
branding,
|
||||
}),
|
||||
renderEmailWithI18N(emailContent, {
|
||||
lang,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const i18n = await getI18nInstance(lang);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: member.user.email,
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: i18n._(msg`A team member has left ${team.name}`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -1,18 +1,5 @@
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { mailer } from '@documenso/email/mailer';
|
||||
import TeamJoinEmailTemplate from '@documenso/email/templates/team-join';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { TeamMemberRole } from '@documenso/prisma/client';
|
||||
|
||||
import { getI18nInstance } from '../../../client-only/providers/i18n.server';
|
||||
import { WEBAPP_BASE_URL } from '../../../constants/app';
|
||||
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
|
||||
import type { JobDefinition } from '../../client/_internal/job';
|
||||
|
||||
const SEND_TEAM_MEMBER_LEFT_EMAIL_JOB_DEFINITION_ID = 'send.team-member-left.email';
|
||||
@ -22,6 +9,10 @@ const SEND_TEAM_MEMBER_LEFT_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
|
||||
memberUserId: z.number(),
|
||||
});
|
||||
|
||||
export type TSendTeamMemberLeftEmailJobDefinition = z.infer<
|
||||
typeof SEND_TEAM_MEMBER_LEFT_EMAIL_JOB_DEFINITION_SCHEMA
|
||||
>;
|
||||
|
||||
export const SEND_TEAM_MEMBER_LEFT_EMAIL_JOB_DEFINITION = {
|
||||
id: SEND_TEAM_MEMBER_LEFT_EMAIL_JOB_DEFINITION_ID,
|
||||
name: 'Send Team Member Left Email',
|
||||
@ -31,76 +22,11 @@ export const SEND_TEAM_MEMBER_LEFT_EMAIL_JOB_DEFINITION = {
|
||||
schema: SEND_TEAM_MEMBER_LEFT_EMAIL_JOB_DEFINITION_SCHEMA,
|
||||
},
|
||||
handler: async ({ payload, io }) => {
|
||||
const team = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
id: payload.teamId,
|
||||
},
|
||||
include: {
|
||||
members: {
|
||||
where: {
|
||||
role: {
|
||||
in: [TeamMemberRole.ADMIN, TeamMemberRole.MANAGER],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
},
|
||||
teamGlobalSettings: true,
|
||||
},
|
||||
});
|
||||
const handler = await import('./send-team-member-left-email.handler');
|
||||
|
||||
const oldMember = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: payload.memberUserId,
|
||||
},
|
||||
});
|
||||
|
||||
for (const member of team.members) {
|
||||
await io.runTask(`send-team-member-left-email--${oldMember.id}_${member.id}`, async () => {
|
||||
const emailContent = createElement(TeamJoinEmailTemplate, {
|
||||
assetBaseUrl: WEBAPP_BASE_URL,
|
||||
baseUrl: WEBAPP_BASE_URL,
|
||||
memberName: oldMember.name || '',
|
||||
memberEmail: oldMember.email,
|
||||
teamName: team.name,
|
||||
teamUrl: team.url,
|
||||
});
|
||||
|
||||
const branding = team.teamGlobalSettings
|
||||
? teamGlobalSettingsToBranding(team.teamGlobalSettings)
|
||||
: undefined;
|
||||
|
||||
const lang = team.teamGlobalSettings?.documentLanguage;
|
||||
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(emailContent, {
|
||||
lang,
|
||||
branding,
|
||||
}),
|
||||
renderEmailWithI18N(emailContent, {
|
||||
lang,
|
||||
branding,
|
||||
plainText: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const i18n = await getI18nInstance(lang);
|
||||
|
||||
await mailer.sendMail({
|
||||
to: member.user.email,
|
||||
from: {
|
||||
name: FROM_NAME,
|
||||
address: FROM_ADDRESS,
|
||||
},
|
||||
subject: i18n._(msg`A team member has left ${team.name}`),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
});
|
||||
}
|
||||
await handler.run({ payload, io });
|
||||
},
|
||||
} as const satisfies JobDefinition<
|
||||
typeof SEND_TEAM_MEMBER_LEFT_EMAIL_JOB_DEFINITION_ID,
|
||||
z.infer<typeof SEND_TEAM_MEMBER_LEFT_EMAIL_JOB_DEFINITION_SCHEMA>
|
||||
TSendTeamMemberLeftEmailJobDefinition
|
||||
>;
|
||||
|
||||
256
packages/lib/jobs/definitions/internal/seal-document.handler.ts
Normal file
256
packages/lib/jobs/definitions/internal/seal-document.handler.ts
Normal file
@ -0,0 +1,256 @@
|
||||
import { nanoid } from 'nanoid';
|
||||
import path from 'node:path';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import {
|
||||
DocumentStatus,
|
||||
RecipientRole,
|
||||
SigningStatus,
|
||||
WebhookTriggerEvents,
|
||||
} from '@documenso/prisma/client';
|
||||
import { signPdf } from '@documenso/signing';
|
||||
|
||||
import { sendCompletedEmail } from '../../../server-only/document/send-completed-email';
|
||||
import PostHogServerClient from '../../../server-only/feature-flags/get-post-hog-server-client';
|
||||
import { getCertificatePdf } from '../../../server-only/htmltopdf/get-certificate-pdf';
|
||||
import { flattenAnnotations } from '../../../server-only/pdf/flatten-annotations';
|
||||
import { flattenForm } from '../../../server-only/pdf/flatten-form';
|
||||
import { insertFieldInPDF } from '../../../server-only/pdf/insert-field-in-pdf';
|
||||
import { normalizeSignatureAppearances } from '../../../server-only/pdf/normalize-signature-appearances';
|
||||
import { triggerWebhook } from '../../../server-only/webhooks/trigger/trigger-webhook';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
|
||||
import { ZWebhookDocumentSchema } from '../../../types/webhook-payload';
|
||||
import { getFile } from '../../../universal/upload/get-file';
|
||||
import { putPdfFile } from '../../../universal/upload/put-file';
|
||||
import { fieldsContainUnsignedRequiredField } from '../../../utils/advanced-fields-helpers';
|
||||
import { createDocumentAuditLogData } from '../../../utils/document-audit-logs';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
import type { TSealDocumentJobDefinition } from './seal-document';
|
||||
|
||||
export const run = async ({
|
||||
payload,
|
||||
io,
|
||||
}: {
|
||||
payload: TSealDocumentJobDefinition;
|
||||
io: JobRunIO;
|
||||
}) => {
|
||||
const { documentId, sendEmail = true, isResealing = false, requestMetadata } = payload;
|
||||
|
||||
const document = await prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
Recipient: {
|
||||
every: {
|
||||
signingStatus: SigningStatus.SIGNED,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
documentMeta: true,
|
||||
Recipient: true,
|
||||
team: {
|
||||
select: {
|
||||
teamGlobalSettings: {
|
||||
select: {
|
||||
includeSigningCertificate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Seems silly but we need to do this in case the job is re-ran
|
||||
// after it has already run through the update task further below.
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
const documentStatus = await io.runTask('get-document-status', async () => {
|
||||
return document.status;
|
||||
});
|
||||
|
||||
// This is the same case as above.
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
const documentDataId = await io.runTask('get-document-data-id', async () => {
|
||||
return document.documentDataId;
|
||||
});
|
||||
|
||||
const documentData = await prisma.documentData.findFirst({
|
||||
where: {
|
||||
id: documentDataId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!documentData) {
|
||||
throw new Error(`Document ${document.id} has no document data`);
|
||||
}
|
||||
|
||||
const recipients = await prisma.recipient.findMany({
|
||||
where: {
|
||||
documentId: document.id,
|
||||
role: {
|
||||
not: RecipientRole.CC,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (recipients.some((recipient) => recipient.signingStatus !== SigningStatus.SIGNED)) {
|
||||
throw new Error(`Document ${document.id} has unsigned recipients`);
|
||||
}
|
||||
|
||||
const fields = await prisma.field.findMany({
|
||||
where: {
|
||||
documentId: document.id,
|
||||
},
|
||||
include: {
|
||||
Signature: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (fieldsContainUnsignedRequiredField(fields)) {
|
||||
throw new Error(`Document ${document.id} has unsigned required fields`);
|
||||
}
|
||||
|
||||
if (isResealing) {
|
||||
// If we're resealing we want to use the initial data for the document
|
||||
// so we aren't placing fields on top of eachother.
|
||||
documentData.data = documentData.initialData;
|
||||
}
|
||||
|
||||
const pdfData = await getFile(documentData);
|
||||
|
||||
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 pdfDoc = await PDFDocument.load(pdfData);
|
||||
|
||||
// Normalize and flatten layers that could cause issues with the signature
|
||||
normalizeSignatureAppearances(pdfDoc);
|
||||
flattenForm(pdfDoc);
|
||||
flattenAnnotations(pdfDoc);
|
||||
|
||||
if (certificateData) {
|
||||
const certificateDoc = await PDFDocument.load(certificateData);
|
||||
|
||||
const certificatePages = await pdfDoc.copyPages(
|
||||
certificateDoc,
|
||||
certificateDoc.getPageIndices(),
|
||||
);
|
||||
|
||||
certificatePages.forEach((page) => {
|
||||
pdfDoc.addPage(page);
|
||||
});
|
||||
}
|
||||
|
||||
for (const field of fields) {
|
||||
if (field.inserted) {
|
||||
await insertFieldInPDF(pdfDoc, field);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-flatten the form to handle our checkbox and radio fields that
|
||||
// create native arcoFields
|
||||
flattenForm(pdfDoc);
|
||||
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
const pdfBuffer = await signPdf({ pdf: Buffer.from(pdfBytes) });
|
||||
|
||||
const { name } = path.parse(document.title);
|
||||
|
||||
const documentData = await putPdfFile({
|
||||
name: `${name}_signed.pdf`,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(pdfBuffer),
|
||||
});
|
||||
|
||||
return documentData.id;
|
||||
});
|
||||
|
||||
const postHog = PostHogServerClient();
|
||||
|
||||
if (postHog) {
|
||||
postHog.capture({
|
||||
distinctId: nanoid(),
|
||||
event: 'App: Document Sealed',
|
||||
properties: {
|
||||
documentId: document.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await io.runTask('update-document', async () => {
|
||||
await prisma.$transaction(async (tx) => {
|
||||
const newData = await tx.documentData.findFirstOrThrow({
|
||||
where: {
|
||||
id: newDataId,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.document.update({
|
||||
where: {
|
||||
id: document.id,
|
||||
},
|
||||
data: {
|
||||
status: DocumentStatus.COMPLETED,
|
||||
completedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
await tx.documentData.update({
|
||||
where: {
|
||||
id: documentData.id,
|
||||
},
|
||||
data: {
|
||||
data: newData.data,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED,
|
||||
documentId: document.id,
|
||||
requestMetadata,
|
||||
user: null,
|
||||
data: {
|
||||
transactionId: nanoid(),
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
await io.runTask('send-completed-email', async () => {
|
||||
let shouldSendCompletedEmail = sendEmail && !isResealing;
|
||||
|
||||
if (isResealing && documentStatus !== DocumentStatus.COMPLETED) {
|
||||
shouldSendCompletedEmail = sendEmail;
|
||||
}
|
||||
|
||||
if (shouldSendCompletedEmail) {
|
||||
await sendCompletedEmail({ documentId, requestMetadata });
|
||||
}
|
||||
});
|
||||
|
||||
const updatedDocument = await prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: document.id,
|
||||
},
|
||||
include: {
|
||||
documentData: true,
|
||||
documentMeta: true,
|
||||
Recipient: true,
|
||||
},
|
||||
});
|
||||
|
||||
await triggerWebhook({
|
||||
event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
|
||||
data: ZWebhookDocumentSchema.parse(updatedDocument),
|
||||
userId: updatedDocument.userId,
|
||||
teamId: updatedDocument.teamId ?? undefined,
|
||||
});
|
||||
};
|
||||
@ -1,31 +1,6 @@
|
||||
import { nanoid } from 'nanoid';
|
||||
import path from 'node:path';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import {
|
||||
DocumentStatus,
|
||||
RecipientRole,
|
||||
SigningStatus,
|
||||
WebhookTriggerEvents,
|
||||
} from '@documenso/prisma/client';
|
||||
import { signPdf } from '@documenso/signing';
|
||||
|
||||
import { sendCompletedEmail } from '../../../server-only/document/send-completed-email';
|
||||
import PostHogServerClient from '../../../server-only/feature-flags/get-post-hog-server-client';
|
||||
import { getCertificatePdf } from '../../../server-only/htmltopdf/get-certificate-pdf';
|
||||
import { flattenAnnotations } from '../../../server-only/pdf/flatten-annotations';
|
||||
import { flattenForm } from '../../../server-only/pdf/flatten-form';
|
||||
import { insertFieldInPDF } from '../../../server-only/pdf/insert-field-in-pdf';
|
||||
import { normalizeSignatureAppearances } from '../../../server-only/pdf/normalize-signature-appearances';
|
||||
import { triggerWebhook } from '../../../server-only/webhooks/trigger/trigger-webhook';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../../types/document-audit-logs';
|
||||
import { ZWebhookDocumentSchema } from '../../../types/webhook-payload';
|
||||
import { ZRequestMetadataSchema } from '../../../universal/extract-request-metadata';
|
||||
import { getFile } from '../../../universal/upload/get-file';
|
||||
import { putPdfFile } from '../../../universal/upload/put-file';
|
||||
import { createDocumentAuditLogData } from '../../../utils/document-audit-logs';
|
||||
import { type JobDefinition } from '../../client/_internal/job';
|
||||
|
||||
const SEAL_DOCUMENT_JOB_DEFINITION_ID = 'internal.seal-document';
|
||||
@ -37,6 +12,8 @@ const SEAL_DOCUMENT_JOB_DEFINITION_SCHEMA = z.object({
|
||||
requestMetadata: ZRequestMetadataSchema.optional(),
|
||||
});
|
||||
|
||||
export type TSealDocumentJobDefinition = z.infer<typeof SEAL_DOCUMENT_JOB_DEFINITION_SCHEMA>;
|
||||
|
||||
export const SEAL_DOCUMENT_JOB_DEFINITION = {
|
||||
id: SEAL_DOCUMENT_JOB_DEFINITION_ID,
|
||||
name: 'Seal Document',
|
||||
@ -46,223 +23,11 @@ export const SEAL_DOCUMENT_JOB_DEFINITION = {
|
||||
schema: SEAL_DOCUMENT_JOB_DEFINITION_SCHEMA,
|
||||
},
|
||||
handler: async ({ payload, io }) => {
|
||||
const { documentId, sendEmail = true, isResealing = false, requestMetadata } = payload;
|
||||
const handler = await import('./seal-document.handler');
|
||||
|
||||
const document = await prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: documentId,
|
||||
Recipient: {
|
||||
every: {
|
||||
signingStatus: SigningStatus.SIGNED,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
documentMeta: true,
|
||||
Recipient: true,
|
||||
team: {
|
||||
select: {
|
||||
teamGlobalSettings: {
|
||||
select: {
|
||||
includeSigningCertificate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Seems silly but we need to do this in case the job is re-ran
|
||||
// after it has already run through the update task further below.
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
const documentStatus = await io.runTask('get-document-status', async () => {
|
||||
return document.status;
|
||||
});
|
||||
|
||||
// This is the same case as above.
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
const documentDataId = await io.runTask('get-document-data-id', async () => {
|
||||
return document.documentDataId;
|
||||
});
|
||||
|
||||
const documentData = await prisma.documentData.findFirst({
|
||||
where: {
|
||||
id: documentDataId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!documentData) {
|
||||
throw new Error(`Document ${document.id} has no document data`);
|
||||
}
|
||||
|
||||
const recipients = await prisma.recipient.findMany({
|
||||
where: {
|
||||
documentId: document.id,
|
||||
role: {
|
||||
not: RecipientRole.CC,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (recipients.some((recipient) => recipient.signingStatus !== SigningStatus.SIGNED)) {
|
||||
throw new Error(`Document ${document.id} has unsigned recipients`);
|
||||
}
|
||||
|
||||
const fields = await prisma.field.findMany({
|
||||
where: {
|
||||
documentId: document.id,
|
||||
},
|
||||
include: {
|
||||
Signature: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (fields.some((field) => !field.inserted)) {
|
||||
throw new Error(`Document ${document.id} has unsigned fields`);
|
||||
}
|
||||
|
||||
if (isResealing) {
|
||||
// If we're resealing we want to use the initial data for the document
|
||||
// so we aren't placing fields on top of eachother.
|
||||
documentData.data = documentData.initialData;
|
||||
}
|
||||
|
||||
const pdfData = await getFile(documentData);
|
||||
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 pdfDoc = await PDFDocument.load(pdfData);
|
||||
|
||||
// Normalize and flatten layers that could cause issues with the signature
|
||||
normalizeSignatureAppearances(pdfDoc);
|
||||
flattenForm(pdfDoc);
|
||||
flattenAnnotations(pdfDoc);
|
||||
|
||||
if (certificateData) {
|
||||
const certificateDoc = await PDFDocument.load(certificateData);
|
||||
|
||||
const certificatePages = await pdfDoc.copyPages(
|
||||
certificateDoc,
|
||||
certificateDoc.getPageIndices(),
|
||||
);
|
||||
|
||||
certificatePages.forEach((page) => {
|
||||
pdfDoc.addPage(page);
|
||||
});
|
||||
}
|
||||
|
||||
for (const field of fields) {
|
||||
await insertFieldInPDF(pdfDoc, field);
|
||||
}
|
||||
|
||||
// Re-flatten the form to handle our checkbox and radio fields that
|
||||
// create native arcoFields
|
||||
flattenForm(pdfDoc);
|
||||
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
const pdfBuffer = await signPdf({ pdf: Buffer.from(pdfBytes) });
|
||||
|
||||
const { name } = path.parse(document.title);
|
||||
|
||||
const documentData = await putPdfFile({
|
||||
name: `${name}_signed.pdf`,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(pdfBuffer),
|
||||
});
|
||||
|
||||
return documentData.id;
|
||||
});
|
||||
|
||||
const postHog = PostHogServerClient();
|
||||
|
||||
if (postHog) {
|
||||
postHog.capture({
|
||||
distinctId: nanoid(),
|
||||
event: 'App: Document Sealed',
|
||||
properties: {
|
||||
documentId: document.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await io.runTask('update-document', async () => {
|
||||
await prisma.$transaction(async (tx) => {
|
||||
const newData = await tx.documentData.findFirstOrThrow({
|
||||
where: {
|
||||
id: newDataId,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.document.update({
|
||||
where: {
|
||||
id: document.id,
|
||||
},
|
||||
data: {
|
||||
status: DocumentStatus.COMPLETED,
|
||||
completedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
await tx.documentData.update({
|
||||
where: {
|
||||
id: documentData.id,
|
||||
},
|
||||
data: {
|
||||
data: newData.data,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED,
|
||||
documentId: document.id,
|
||||
requestMetadata,
|
||||
user: null,
|
||||
data: {
|
||||
transactionId: nanoid(),
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
await io.runTask('send-completed-email', async () => {
|
||||
let shouldSendCompletedEmail = sendEmail && !isResealing;
|
||||
|
||||
if (isResealing && documentStatus !== DocumentStatus.COMPLETED) {
|
||||
shouldSendCompletedEmail = sendEmail;
|
||||
}
|
||||
|
||||
if (shouldSendCompletedEmail) {
|
||||
await sendCompletedEmail({ documentId, requestMetadata });
|
||||
}
|
||||
});
|
||||
|
||||
const updatedDocument = await prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: document.id,
|
||||
},
|
||||
include: {
|
||||
documentData: true,
|
||||
documentMeta: true,
|
||||
Recipient: true,
|
||||
},
|
||||
});
|
||||
|
||||
await triggerWebhook({
|
||||
event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
|
||||
data: ZWebhookDocumentSchema.parse(updatedDocument),
|
||||
userId: updatedDocument.userId,
|
||||
teamId: updatedDocument.teamId ?? undefined,
|
||||
});
|
||||
await handler.run({ payload, io });
|
||||
},
|
||||
} as const satisfies JobDefinition<
|
||||
typeof SEAL_DOCUMENT_JOB_DEFINITION_ID,
|
||||
z.infer<typeof SEAL_DOCUMENT_JOB_DEFINITION_SCHEMA>
|
||||
TSealDocumentJobDefinition
|
||||
>;
|
||||
|
||||
@ -121,6 +121,10 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
|
||||
throw new Error(ErrorCode.UNVERIFIED_EMAIL);
|
||||
}
|
||||
|
||||
if (user.disabled) {
|
||||
throw new Error(ErrorCode.ACCOUNT_DISABLED);
|
||||
}
|
||||
|
||||
return {
|
||||
id: Number(user.id),
|
||||
email: user.email,
|
||||
|
||||
@ -20,4 +20,5 @@ export const ErrorCode = {
|
||||
MISSING_ENCRYPTION_KEY: 'MISSING_ENCRYPTION_KEY',
|
||||
MISSING_BACKUP_CODE: 'MISSING_BACKUP_CODE',
|
||||
UNVERIFIED_EMAIL: 'UNVERIFIED_EMAIL',
|
||||
ACCOUNT_DISABLED: 'ACCOUNT_DISABLED',
|
||||
} as const;
|
||||
|
||||
@ -21,6 +21,10 @@ export const getServerComponentSession = cache(async () => {
|
||||
},
|
||||
});
|
||||
|
||||
if (user.disabled) {
|
||||
return { user: null, session: null };
|
||||
}
|
||||
|
||||
return { user, session };
|
||||
});
|
||||
|
||||
|
||||
@ -56,11 +56,11 @@
|
||||
"sharp": "0.32.6",
|
||||
"stripe": "^12.7.0",
|
||||
"ts-pattern": "^5.0.5",
|
||||
"zod": "^3.23.8"
|
||||
"zod": "3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/browser-chromium": "1.43.0",
|
||||
"@types/luxon": "^3.3.1",
|
||||
"@types/pg": "^8.11.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { Prisma } from '@documenso/prisma/client';
|
||||
import { DocumentStatus, Prisma } from '@documenso/prisma/client';
|
||||
|
||||
export type SigningVolume = {
|
||||
id: number;
|
||||
@ -43,34 +43,41 @@ export async function getSigningVolume({
|
||||
],
|
||||
});
|
||||
|
||||
const orderByClause = getOrderByClause({ sortBy, sortOrder });
|
||||
|
||||
const [subscriptions, totalCount] = await Promise.all([
|
||||
prisma.subscription.findMany({
|
||||
where: whereClause,
|
||||
include: {
|
||||
User: {
|
||||
include: {
|
||||
select: {
|
||||
name: true,
|
||||
email: true,
|
||||
Document: {
|
||||
where: {
|
||||
status: 'COMPLETED',
|
||||
status: DocumentStatus.COMPLETED,
|
||||
deletedAt: null,
|
||||
teamId: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
team: {
|
||||
include: {
|
||||
select: {
|
||||
name: true,
|
||||
document: {
|
||||
where: {
|
||||
status: 'COMPLETED',
|
||||
status: DocumentStatus.COMPLETED,
|
||||
deletedAt: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: orderByClause,
|
||||
orderBy:
|
||||
sortBy === 'name'
|
||||
? [{ User: { name: sortOrder } }, { team: { name: sortOrder } }, { createdAt: 'desc' }]
|
||||
: sortBy === 'createdAt'
|
||||
? [{ createdAt: sortOrder }]
|
||||
: undefined,
|
||||
skip: Math.max(page - 1, 0) * perPage,
|
||||
take: perPage,
|
||||
}),
|
||||
@ -82,10 +89,8 @@ export async function getSigningVolume({
|
||||
const leaderboardWithVolume: SigningVolume[] = subscriptions.map((subscription) => {
|
||||
const name =
|
||||
subscription.User?.name || subscription.team?.name || subscription.User?.email || 'Unknown';
|
||||
|
||||
const userSignedDocs = subscription.User?.Document?.length || 0;
|
||||
const teamSignedDocs = subscription.team?.document?.length || 0;
|
||||
|
||||
return {
|
||||
id: subscription.id,
|
||||
name,
|
||||
@ -95,54 +100,16 @@ export async function getSigningVolume({
|
||||
};
|
||||
});
|
||||
|
||||
if (sortBy === 'signingVolume') {
|
||||
leaderboardWithVolume.sort((a, b) => {
|
||||
return sortOrder === 'desc'
|
||||
? b.signingVolume - a.signingVolume
|
||||
: a.signingVolume - b.signingVolume;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
leaderboard: leaderboardWithVolume,
|
||||
totalPages: Math.ceil(totalCount / perPage),
|
||||
};
|
||||
}
|
||||
|
||||
function getOrderByClause(options: {
|
||||
sortBy: string;
|
||||
sortOrder: 'asc' | 'desc';
|
||||
}): Prisma.SubscriptionOrderByWithRelationInput | Prisma.SubscriptionOrderByWithRelationInput[] {
|
||||
const { sortBy, sortOrder } = options;
|
||||
|
||||
if (sortBy === 'name') {
|
||||
return [
|
||||
{
|
||||
User: {
|
||||
name: sortOrder,
|
||||
},
|
||||
},
|
||||
{
|
||||
team: {
|
||||
name: sortOrder,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (sortBy === 'createdAt') {
|
||||
return {
|
||||
createdAt: sortOrder,
|
||||
};
|
||||
}
|
||||
|
||||
// Default: sort by signing volume
|
||||
return [
|
||||
{
|
||||
User: {
|
||||
Document: {
|
||||
_count: sortOrder,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
team: {
|
||||
document: {
|
||||
_count: sortOrder,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { fieldsContainUnsignedRequiredField } from '@documenso/lib/utils/advanced-fields-helpers';
|
||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import {
|
||||
@ -8,8 +9,8 @@ import {
|
||||
RecipientRole,
|
||||
SendStatus,
|
||||
SigningStatus,
|
||||
WebhookTriggerEvents,
|
||||
} from '@documenso/prisma/client';
|
||||
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||
|
||||
import { jobs } from '../../jobs/client';
|
||||
import type { TRecipientActionAuth } from '../../types/document-auth';
|
||||
@ -85,7 +86,7 @@ export const completeDocumentWithToken = async ({
|
||||
},
|
||||
});
|
||||
|
||||
if (fields.some((field) => !field.inserted)) {
|
||||
if (fieldsContainUnsignedRequiredField(fields)) {
|
||||
throw new Error(`Recipient ${recipient.id} has unsigned fields`);
|
||||
}
|
||||
|
||||
@ -139,6 +140,14 @@ export const completeDocumentWithToken = async ({
|
||||
});
|
||||
});
|
||||
|
||||
await jobs.triggerJob({
|
||||
name: 'send.recipient.signed.email',
|
||||
payload: {
|
||||
documentId: document.id,
|
||||
recipientId: recipient.id,
|
||||
},
|
||||
});
|
||||
|
||||
const pendingRecipients = await prisma.recipient.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
'use server';
|
||||
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { normalizePdf as makeNormalizedPdf } from '@documenso/lib/server-only/pdf/normalize-pdf';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
||||
@ -8,8 +11,11 @@ import { prisma } from '@documenso/prisma';
|
||||
import { DocumentSource, DocumentVisibility, WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||
import type { Team, TeamGlobalSettings } from '@documenso/prisma/client';
|
||||
import { TeamMemberRole } from '@documenso/prisma/client';
|
||||
import { DocumentSchema } from '@documenso/prisma/generated/zod';
|
||||
|
||||
import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
|
||||
import { getFile } from '../../universal/upload/get-file';
|
||||
import { putPdfFile } from '../../universal/upload/put-file';
|
||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||
|
||||
export type CreateDocumentOptions = {
|
||||
@ -19,18 +25,24 @@ export type CreateDocumentOptions = {
|
||||
teamId?: number;
|
||||
documentDataId: string;
|
||||
formValues?: Record<string, string | number | boolean>;
|
||||
normalizePdf?: boolean;
|
||||
requestMetadata?: RequestMetadata;
|
||||
};
|
||||
|
||||
export const ZCreateDocumentResponseSchema = DocumentSchema;
|
||||
|
||||
export type TCreateDocumentResponse = z.infer<typeof ZCreateDocumentResponseSchema>;
|
||||
|
||||
export const createDocument = async ({
|
||||
userId,
|
||||
title,
|
||||
externalId,
|
||||
documentDataId,
|
||||
teamId,
|
||||
normalizePdf,
|
||||
formValues,
|
||||
requestMetadata,
|
||||
}: CreateDocumentOptions) => {
|
||||
}: CreateDocumentOptions): Promise<TCreateDocumentResponse> => {
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
@ -82,22 +94,44 @@ export const createDocument = async ({
|
||||
globalVisibility: DocumentVisibility | null | undefined,
|
||||
userRole: TeamMemberRole,
|
||||
): DocumentVisibility => {
|
||||
const defaultVisibility = globalVisibility ?? DocumentVisibility.EVERYONE;
|
||||
if (globalVisibility) {
|
||||
return globalVisibility;
|
||||
}
|
||||
|
||||
if (userRole === TeamMemberRole.ADMIN) {
|
||||
return defaultVisibility;
|
||||
return DocumentVisibility.ADMIN;
|
||||
}
|
||||
|
||||
if (userRole === TeamMemberRole.MANAGER) {
|
||||
if (defaultVisibility === DocumentVisibility.ADMIN) {
|
||||
return DocumentVisibility.MANAGER_AND_ABOVE;
|
||||
}
|
||||
return defaultVisibility;
|
||||
return DocumentVisibility.MANAGER_AND_ABOVE;
|
||||
}
|
||||
|
||||
return DocumentVisibility.EVERYONE;
|
||||
};
|
||||
|
||||
if (normalizePdf) {
|
||||
const documentData = await prisma.documentData.findFirst({
|
||||
where: {
|
||||
id: documentDataId,
|
||||
},
|
||||
});
|
||||
|
||||
if (documentData) {
|
||||
const buffer = await getFile(documentData);
|
||||
|
||||
const normalizedPdf = await makeNormalizedPdf(Buffer.from(buffer));
|
||||
|
||||
const newDocumentData = await putPdfFile({
|
||||
name: title.endsWith('.pdf') ? title : `${title}.pdf`,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(normalizedPdf),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line require-atomic-updates
|
||||
documentDataId = newDocumentData.id;
|
||||
}
|
||||
}
|
||||
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
const document = await tx.document.create({
|
||||
data: {
|
||||
|
||||
@ -1,19 +1,27 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentSource, type Prisma } from '@documenso/prisma/client';
|
||||
|
||||
import { getDocumentWhereInput } from './get-document-by-id';
|
||||
|
||||
export interface DuplicateDocumentByIdOptions {
|
||||
export interface DuplicateDocumentOptions {
|
||||
documentId: number;
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
}
|
||||
|
||||
export const duplicateDocumentById = async ({
|
||||
export const ZDuplicateDocumentResponseSchema = z.object({
|
||||
documentId: z.number(),
|
||||
});
|
||||
|
||||
export type TDuplicateDocumentResponse = z.infer<typeof ZDuplicateDocumentResponseSchema>;
|
||||
|
||||
export const duplicateDocument = async ({
|
||||
documentId,
|
||||
userId,
|
||||
teamId,
|
||||
}: DuplicateDocumentByIdOptions) => {
|
||||
}: DuplicateDocumentOptions): Promise<TDuplicateDocumentResponse> => {
|
||||
const documentWhereInput = await getDocumentWhereInput({
|
||||
documentId,
|
||||
userId,
|
||||
@ -78,5 +86,7 @@ export const duplicateDocumentById = async ({
|
||||
|
||||
const createdDocument = await prisma.document.create(createDocumentArguments);
|
||||
|
||||
return createdDocument.id;
|
||||
return {
|
||||
documentId: createdDocument.id,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { DateTime } from 'luxon';
|
||||
import { match } from 'ts-pattern';
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import type {
|
||||
@ -11,10 +12,16 @@ import type {
|
||||
User,
|
||||
} from '@documenso/prisma/client';
|
||||
import { RecipientRole, SigningStatus, TeamMemberRole } from '@documenso/prisma/client';
|
||||
import {
|
||||
DocumentSchema,
|
||||
RecipientSchema,
|
||||
TeamSchema,
|
||||
UserSchema,
|
||||
} from '@documenso/prisma/generated/zod';
|
||||
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
|
||||
|
||||
import { DocumentVisibility } from '../../types/document-visibility';
|
||||
import type { FindResultResponse } from '../../types/search-params';
|
||||
import { type FindResultResponse, ZFindResultResponse } from '../../types/search-params';
|
||||
import { maskRecipientTokensForDocument } from '../../utils/mask-recipient-tokens-for-document';
|
||||
|
||||
export type PeriodSelectorValue = '' | '7d' | '14d' | '30d';
|
||||
@ -36,6 +43,23 @@ export type FindDocumentsOptions = {
|
||||
query?: string;
|
||||
};
|
||||
|
||||
export const ZFindDocumentsResponseSchema = ZFindResultResponse.extend({
|
||||
data: DocumentSchema.extend({
|
||||
User: UserSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
}),
|
||||
Recipient: RecipientSchema.array(),
|
||||
team: TeamSchema.pick({
|
||||
id: true,
|
||||
url: true,
|
||||
}).nullable(),
|
||||
}).array(), // Todo: openapi remap.
|
||||
});
|
||||
|
||||
export type TFindDocumentsResponse = z.infer<typeof ZFindDocumentsResponseSchema>;
|
||||
|
||||
export const findDocuments = async ({
|
||||
userId,
|
||||
teamId,
|
||||
@ -48,7 +72,7 @@ export const findDocuments = async ({
|
||||
period,
|
||||
senderIds,
|
||||
query,
|
||||
}: FindDocumentsOptions) => {
|
||||
}: FindDocumentsOptions): Promise<TFindDocumentsResponse> => {
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import type { DocumentWithDetails } from '@documenso/prisma/types/document';
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import {
|
||||
DocumentDataSchema,
|
||||
DocumentMetaSchema,
|
||||
DocumentSchema,
|
||||
FieldSchema,
|
||||
RecipientSchema,
|
||||
} from '@documenso/prisma/generated/zod';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
import { getDocumentWhereInput } from './get-document-by-id';
|
||||
|
||||
export type GetDocumentWithDetailsByIdOptions = {
|
||||
@ -9,18 +18,29 @@ export type GetDocumentWithDetailsByIdOptions = {
|
||||
teamId?: number;
|
||||
};
|
||||
|
||||
export const ZGetDocumentWithDetailsByIdResponseSchema = DocumentSchema.extend({
|
||||
documentData: DocumentDataSchema,
|
||||
documentMeta: DocumentMetaSchema.nullable(),
|
||||
Recipient: RecipientSchema.array(),
|
||||
Field: FieldSchema.array(),
|
||||
});
|
||||
|
||||
export type TGetDocumentWithDetailsByIdResponse = z.infer<
|
||||
typeof ZGetDocumentWithDetailsByIdResponseSchema
|
||||
>;
|
||||
|
||||
export const getDocumentWithDetailsById = async ({
|
||||
documentId,
|
||||
userId,
|
||||
teamId,
|
||||
}: GetDocumentWithDetailsByIdOptions): Promise<DocumentWithDetails> => {
|
||||
}: GetDocumentWithDetailsByIdOptions): Promise<TGetDocumentWithDetailsByIdResponse> => {
|
||||
const documentWhereInput = await getDocumentWhereInput({
|
||||
documentId,
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
|
||||
return await prisma.document.findFirstOrThrow({
|
||||
const document = await prisma.document.findFirst({
|
||||
where: documentWhereInput,
|
||||
include: {
|
||||
documentData: true,
|
||||
@ -29,4 +49,12 @@ export const getDocumentWithDetailsById = async ({
|
||||
Field: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!document) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Document not found',
|
||||
});
|
||||
}
|
||||
|
||||
return document;
|
||||
};
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import type { z } from 'zod';
|
||||
|
||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentSchema } from '@documenso/prisma/generated/zod';
|
||||
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
||||
@ -13,12 +15,16 @@ export type MoveDocumentToTeamOptions = {
|
||||
requestMetadata?: RequestMetadata;
|
||||
};
|
||||
|
||||
export const ZMoveDocumentToTeamResponseSchema = DocumentSchema;
|
||||
|
||||
export type TMoveDocumentToTeamResponse = z.infer<typeof ZMoveDocumentToTeamResponseSchema>;
|
||||
|
||||
export const moveDocumentToTeam = async ({
|
||||
documentId,
|
||||
teamId,
|
||||
userId,
|
||||
requestMetadata,
|
||||
}: MoveDocumentToTeamOptions) => {
|
||||
}: MoveDocumentToTeamOptions): Promise<TMoveDocumentToTeamResponse> => {
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
const user = await tx.user.findUniqueOrThrow({
|
||||
where: { id: userId },
|
||||
|
||||
@ -38,7 +38,7 @@ export const resendDocument = async ({
|
||||
recipients,
|
||||
teamId,
|
||||
requestMetadata,
|
||||
}: ResendDocumentOptions) => {
|
||||
}: ResendDocumentOptions): Promise<void> => {
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
|
||||
@ -18,6 +18,7 @@ import { ZWebhookDocumentSchema } from '../../types/webhook-payload';
|
||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||
import { getFile } from '../../universal/upload/get-file';
|
||||
import { putPdfFile } from '../../universal/upload/put-file';
|
||||
import { fieldsContainUnsignedRequiredField } from '../../utils/advanced-fields-helpers';
|
||||
import { getCertificatePdf } from '../htmltopdf/get-certificate-pdf';
|
||||
import { flattenAnnotations } from '../pdf/flatten-annotations';
|
||||
import { flattenForm } from '../pdf/flatten-form';
|
||||
@ -92,8 +93,8 @@ export const sealDocument = async ({
|
||||
},
|
||||
});
|
||||
|
||||
if (fields.some((field) => !field.inserted)) {
|
||||
throw new Error(`Document ${document.id} has unsigned fields`);
|
||||
if (fieldsContainUnsignedRequiredField(fields)) {
|
||||
throw new Error(`Document ${document.id} has unsigned required fields`);
|
||||
}
|
||||
|
||||
if (isResealing) {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { putPdfFile } from '@documenso/lib/universal/upload/put-file';
|
||||
@ -11,6 +13,11 @@ import {
|
||||
SigningStatus,
|
||||
WebhookTriggerEvents,
|
||||
} from '@documenso/prisma/client';
|
||||
import {
|
||||
DocumentMetaSchema,
|
||||
DocumentSchema,
|
||||
RecipientSchema,
|
||||
} from '@documenso/prisma/generated/zod';
|
||||
|
||||
import { jobs } from '../../jobs/client';
|
||||
import { extractDerivedDocumentEmailSettings } from '../../types/document-email';
|
||||
@ -27,13 +34,20 @@ export type SendDocumentOptions = {
|
||||
requestMetadata?: RequestMetadata;
|
||||
};
|
||||
|
||||
export const ZSendDocumentResponseSchema = DocumentSchema.extend({
|
||||
documentMeta: DocumentMetaSchema.nullable(),
|
||||
Recipient: RecipientSchema.array(),
|
||||
});
|
||||
|
||||
export type TSendDocumentResponse = z.infer<typeof ZSendDocumentResponseSchema>;
|
||||
|
||||
export const sendDocument = async ({
|
||||
documentId,
|
||||
userId,
|
||||
teamId,
|
||||
sendEmail,
|
||||
requestMetadata,
|
||||
}: SendDocumentOptions) => {
|
||||
}: SendDocumentOptions): Promise<TSendDocumentResponse> => {
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
@ -211,6 +225,7 @@ export const sendDocument = async ({
|
||||
id: documentId,
|
||||
},
|
||||
include: {
|
||||
documentMeta: true,
|
||||
Recipient: true,
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use server';
|
||||
|
||||
import { match } from 'ts-pattern';
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||
@ -10,6 +11,7 @@ import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentVisibility } from '@documenso/prisma/client';
|
||||
import { DocumentStatus, TeamMemberRole } from '@documenso/prisma/client';
|
||||
import { DocumentSchema } from '@documenso/prisma/generated/zod';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
import type { TDocumentAccessAuthTypes, TDocumentActionAuthTypes } from '../../types/document-auth';
|
||||
@ -29,13 +31,17 @@ export type UpdateDocumentSettingsOptions = {
|
||||
requestMetadata?: RequestMetadata;
|
||||
};
|
||||
|
||||
export const ZUpdateDocumentSettingsResponseSchema = DocumentSchema;
|
||||
|
||||
export type TUpdateDocumentSettingsResponse = z.infer<typeof ZUpdateDocumentSettingsResponseSchema>;
|
||||
|
||||
export const updateDocumentSettings = async ({
|
||||
userId,
|
||||
teamId,
|
||||
documentId,
|
||||
data,
|
||||
requestMetadata,
|
||||
}: UpdateDocumentSettingsOptions) => {
|
||||
}: UpdateDocumentSettingsOptions): Promise<TUpdateDocumentSettingsResponse> => {
|
||||
if (!data.title && !data.globalAccessAuth && !data.globalActionAuth) {
|
||||
throw new AppError(AppErrorCode.INVALID_BODY, {
|
||||
message: 'Missing data to update',
|
||||
@ -85,39 +91,43 @@ export const updateDocumentSettings = async ({
|
||||
|
||||
if (teamId) {
|
||||
const currentUserRole = document.team?.members[0]?.role;
|
||||
const isDocumentOwner = document.userId === userId;
|
||||
const requestedVisibility = data.visibility;
|
||||
|
||||
match(currentUserRole)
|
||||
.with(TeamMemberRole.ADMIN, () => true)
|
||||
.with(TeamMemberRole.MANAGER, () => {
|
||||
const allowedVisibilities: DocumentVisibility[] = [
|
||||
DocumentVisibility.EVERYONE,
|
||||
DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
];
|
||||
if (!isDocumentOwner) {
|
||||
match(currentUserRole)
|
||||
.with(TeamMemberRole.ADMIN, () => true)
|
||||
.with(TeamMemberRole.MANAGER, () => {
|
||||
const allowedVisibilities: DocumentVisibility[] = [
|
||||
DocumentVisibility.EVERYONE,
|
||||
DocumentVisibility.MANAGER_AND_ABOVE,
|
||||
];
|
||||
|
||||
if (
|
||||
!allowedVisibilities.includes(document.visibility) ||
|
||||
(data.visibility && !allowedVisibilities.includes(data.visibility))
|
||||
) {
|
||||
if (
|
||||
!allowedVisibilities.includes(document.visibility) ||
|
||||
(requestedVisibility && !allowedVisibilities.includes(requestedVisibility))
|
||||
) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You do not have permission to update the document visibility',
|
||||
});
|
||||
}
|
||||
})
|
||||
.with(TeamMemberRole.MEMBER, () => {
|
||||
if (
|
||||
document.visibility !== DocumentVisibility.EVERYONE ||
|
||||
(requestedVisibility && requestedVisibility !== DocumentVisibility.EVERYONE)
|
||||
) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You do not have permission to update the document visibility',
|
||||
});
|
||||
}
|
||||
})
|
||||
.otherwise(() => {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You do not have permission to update the document visibility',
|
||||
message: 'You do not have permission to update the document',
|
||||
});
|
||||
}
|
||||
})
|
||||
.with(TeamMemberRole.MEMBER, () => {
|
||||
if (
|
||||
document.visibility !== DocumentVisibility.EVERYONE ||
|
||||
(data.visibility && data.visibility !== DocumentVisibility.EVERYONE)
|
||||
) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You do not have permission to update the document visibility',
|
||||
});
|
||||
}
|
||||
})
|
||||
.otherwise(() => {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You do not have permission to update the document',
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const { documentAuthOption } = extractDocumentAuthMethods({
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { FieldSchema } from '@documenso/prisma/generated/zod';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
|
||||
export type GetFieldByIdOptions = {
|
||||
userId: number;
|
||||
@ -8,13 +13,17 @@ export type GetFieldByIdOptions = {
|
||||
templateId?: number;
|
||||
};
|
||||
|
||||
export const ZGetFieldByIdResponseSchema = FieldSchema;
|
||||
|
||||
export type TGetFieldByIdResponse = z.infer<typeof ZGetFieldByIdResponseSchema>;
|
||||
|
||||
export const getFieldById = async ({
|
||||
userId,
|
||||
teamId,
|
||||
fieldId,
|
||||
documentId,
|
||||
templateId,
|
||||
}: GetFieldByIdOptions) => {
|
||||
}: GetFieldByIdOptions): Promise<TGetFieldByIdResponse> => {
|
||||
const field = await prisma.field.findFirst({
|
||||
where: {
|
||||
id: fieldId,
|
||||
@ -45,5 +54,11 @@ export const getFieldById = async ({
|
||||
},
|
||||
});
|
||||
|
||||
if (!field) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Field not found',
|
||||
});
|
||||
}
|
||||
|
||||
return field;
|
||||
};
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { isDeepEqual } from 'remeda';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
|
||||
import { validateDropdownField } from '@documenso/lib/advanced-fields-validation/validate-dropdown';
|
||||
@ -23,6 +24,7 @@ import {
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import type { Field } from '@documenso/prisma/client';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
import { FieldSchema } from '@documenso/prisma/generated/zod';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
import { canRecipientFieldsBeModified } from '../../utils/recipients';
|
||||
@ -34,12 +36,18 @@ export interface SetFieldsForDocumentOptions {
|
||||
requestMetadata?: RequestMetadata;
|
||||
}
|
||||
|
||||
export const ZSetFieldsForDocumentResponseSchema = z.object({
|
||||
fields: z.array(FieldSchema),
|
||||
});
|
||||
|
||||
export type TSetFieldsForDocumentResponse = z.infer<typeof ZSetFieldsForDocumentResponseSchema>;
|
||||
|
||||
export const setFieldsForDocument = async ({
|
||||
userId,
|
||||
documentId,
|
||||
fields,
|
||||
requestMetadata,
|
||||
}: SetFieldsForDocumentOptions): Promise<Field[]> => {
|
||||
}: SetFieldsForDocumentOptions): Promise<TSetFieldsForDocumentResponse> => {
|
||||
const document = await prisma.document.findFirst({
|
||||
where: {
|
||||
id: documentId,
|
||||
@ -75,11 +83,15 @@ export const setFieldsForDocument = async ({
|
||||
});
|
||||
|
||||
if (!document) {
|
||||
throw new Error('Document not found');
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Document not found',
|
||||
});
|
||||
}
|
||||
|
||||
if (document.completedAt) {
|
||||
throw new Error('Document already complete');
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Document already complete',
|
||||
});
|
||||
}
|
||||
|
||||
const existingFields = await prisma.field.findMany({
|
||||
@ -335,7 +347,9 @@ export const setFieldsForDocument = async ({
|
||||
return !isRemoved && !isUpdated;
|
||||
});
|
||||
|
||||
return [...filteredFields, ...persistedFields];
|
||||
return {
|
||||
fields: [...filteredFields, ...persistedFields],
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
|
||||
import { validateDropdownField } from '@documenso/lib/advanced-fields-validation/validate-dropdown';
|
||||
import { validateNumberField } from '@documenso/lib/advanced-fields-validation/validate-number';
|
||||
@ -14,6 +16,7 @@ import {
|
||||
} from '@documenso/lib/types/field-meta';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
import { FieldSchema } from '@documenso/prisma/generated/zod';
|
||||
|
||||
export type SetFieldsForTemplateOptions = {
|
||||
userId: number;
|
||||
@ -31,11 +34,17 @@ export type SetFieldsForTemplateOptions = {
|
||||
}[];
|
||||
};
|
||||
|
||||
export const ZSetFieldsForTemplateResponseSchema = z.object({
|
||||
fields: z.array(FieldSchema),
|
||||
});
|
||||
|
||||
export type TSetFieldsForTemplateResponse = z.infer<typeof ZSetFieldsForTemplateResponseSchema>;
|
||||
|
||||
export const setFieldsForTemplate = async ({
|
||||
userId,
|
||||
templateId,
|
||||
fields,
|
||||
}: SetFieldsForTemplateOptions) => {
|
||||
}: SetFieldsForTemplateOptions): Promise<TSetFieldsForTemplateResponse> => {
|
||||
const template = await prisma.template.findFirst({
|
||||
where: {
|
||||
id: templateId,
|
||||
@ -206,5 +215,7 @@ export const setFieldsForTemplate = async ({
|
||||
return !isRemoved && !isUpdated;
|
||||
});
|
||||
|
||||
return [...filteredFields, ...persistedFields];
|
||||
return {
|
||||
fields: [...filteredFields, ...persistedFields],
|
||||
};
|
||||
};
|
||||
|
||||
@ -8,6 +8,7 @@ import { validateDropdownField } from '@documenso/lib/advanced-fields-validation
|
||||
import { validateNumberField } from '@documenso/lib/advanced-fields-validation/validate-number';
|
||||
import { validateRadioField } from '@documenso/lib/advanced-fields-validation/validate-radio';
|
||||
import { validateTextField } from '@documenso/lib/advanced-fields-validation/validate-text';
|
||||
import { fromCheckboxValue } from '@documenso/lib/universal/field-checkbox';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentStatus, FieldType, SigningStatus } from '@documenso/prisma/client';
|
||||
|
||||
@ -119,7 +120,8 @@ export const signFieldWithToken = async ({
|
||||
|
||||
if (field.type === FieldType.CHECKBOX && field.fieldMeta) {
|
||||
const checkboxFieldParsedMeta = ZCheckboxFieldMeta.parse(field.fieldMeta);
|
||||
const checkboxFieldValues = value.split(',');
|
||||
const checkboxFieldValues: string[] = fromCheckboxValue(value);
|
||||
|
||||
const errors = validateCheckboxField(checkboxFieldValues, checkboxFieldParsedMeta, true);
|
||||
|
||||
if (errors.length > 0) {
|
||||
|
||||
@ -16,7 +16,6 @@ import {
|
||||
export const removeOptionalContentGroups = (document: PDFDocument) => {
|
||||
const context = document.context;
|
||||
const catalog = context.lookup(context.trailerInfo.Root);
|
||||
|
||||
if (catalog instanceof PDFDict) {
|
||||
catalog.delete(PDFName.of('OCProperties'));
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
MIN_HANDWRITING_FONT_SIZE,
|
||||
MIN_STANDARD_FONT_SIZE,
|
||||
} from '@documenso/lib/constants/pdf';
|
||||
import { fromCheckboxValue } from '@documenso/lib/universal/field-checkbox';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
import { isSignatureFieldType } from '@documenso/prisma/guards/is-signature-field';
|
||||
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
|
||||
@ -194,7 +195,7 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
||||
value: item.value.length > 0 ? item.value : `empty-value-${item.id}`,
|
||||
}));
|
||||
|
||||
const selected = field.customText.split(',');
|
||||
const selected: string[] = fromCheckboxValue(field.customText);
|
||||
|
||||
for (const [index, item] of (values ?? []).entries()) {
|
||||
const offsetY = index * 16;
|
||||
|
||||
18
packages/lib/server-only/pdf/normalize-pdf.ts
Normal file
18
packages/lib/server-only/pdf/normalize-pdf.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
|
||||
import { flattenAnnotations } from './flatten-annotations';
|
||||
import { flattenForm, removeOptionalContentGroups } from './flatten-form';
|
||||
|
||||
export const normalizePdf = async (pdf: Buffer) => {
|
||||
const pdfDoc = await PDFDocument.load(pdf).catch(() => null);
|
||||
|
||||
if (!pdfDoc) {
|
||||
return pdf;
|
||||
}
|
||||
|
||||
removeOptionalContentGroups(pdfDoc);
|
||||
flattenForm(pdfDoc);
|
||||
flattenAnnotations(pdfDoc);
|
||||
|
||||
return Buffer.from(await pdfDoc.save());
|
||||
};
|
||||
@ -0,0 +1,21 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export type GetRecipientByIdOptions = {
|
||||
id: number;
|
||||
documentId: number;
|
||||
};
|
||||
|
||||
export const getRecipientByIdV1Api = async ({ documentId, id }: GetRecipientByIdOptions) => {
|
||||
const recipient = await prisma.recipient.findFirst({
|
||||
where: {
|
||||
documentId,
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!recipient) {
|
||||
throw new Error('Recipient not found');
|
||||
}
|
||||
|
||||
return recipient;
|
||||
};
|
||||
@ -1,20 +1,54 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
|
||||
export type GetRecipientByIdOptions = {
|
||||
id: number;
|
||||
documentId: number;
|
||||
recipientId: number;
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
};
|
||||
|
||||
export const getRecipientById = async ({ documentId, id }: GetRecipientByIdOptions) => {
|
||||
/**
|
||||
* Get a recipient by ID. This will also return the recipient signing token so
|
||||
* be careful when using this.
|
||||
*/
|
||||
export const getRecipientById = async ({
|
||||
recipientId,
|
||||
userId,
|
||||
teamId,
|
||||
}: GetRecipientByIdOptions) => {
|
||||
const recipient = await prisma.recipient.findFirst({
|
||||
where: {
|
||||
documentId,
|
||||
id,
|
||||
id: recipientId,
|
||||
Document: {
|
||||
OR: [
|
||||
teamId === undefined
|
||||
? {
|
||||
userId,
|
||||
teamId: null,
|
||||
}
|
||||
: {
|
||||
teamId,
|
||||
team: {
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
include: {
|
||||
Field: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!recipient) {
|
||||
throw new Error('Recipient not found');
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Recipient not found',
|
||||
});
|
||||
}
|
||||
|
||||
return recipient;
|
||||
|
||||
@ -45,7 +45,7 @@ export const setRecipientsForDocument = async ({
|
||||
documentId,
|
||||
recipients,
|
||||
requestMetadata,
|
||||
}: SetRecipientsForDocumentOptions): Promise<Recipient[]> => {
|
||||
}: SetRecipientsForDocumentOptions) => {
|
||||
const document = await prisma.document.findFirst({
|
||||
where: {
|
||||
id: documentId,
|
||||
@ -344,7 +344,9 @@ export const setRecipientsForDocument = async ({
|
||||
return !isRemoved && !isUpdated;
|
||||
});
|
||||
|
||||
return [...filteredRecipients, ...persistedRecipients];
|
||||
return {
|
||||
recipients: [...filteredRecipients, ...persistedRecipients],
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -220,5 +220,7 @@ export const setRecipientsForTemplate = async ({
|
||||
return !isRemoved && !isUpdated;
|
||||
});
|
||||
|
||||
return [...filteredRecipients, ...persistedRecipients];
|
||||
return {
|
||||
recipients: [...filteredRecipients, ...persistedRecipients],
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { nanoid } from '@documenso/lib/universal/id';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import type { DocumentDistributionMethod } from '@documenso/prisma/client';
|
||||
@ -11,6 +13,11 @@ import {
|
||||
SigningStatus,
|
||||
WebhookTriggerEvents,
|
||||
} from '@documenso/prisma/client';
|
||||
import {
|
||||
DocumentDataSchema,
|
||||
DocumentSchema,
|
||||
RecipientSchema,
|
||||
} from '@documenso/prisma/generated/zod';
|
||||
|
||||
import type { SupportedLanguageCodes } from '../../constants/i18n';
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
@ -36,10 +43,6 @@ type FinalRecipient = Pick<
|
||||
fields: Field[];
|
||||
};
|
||||
|
||||
export type CreateDocumentFromTemplateResponse = Awaited<
|
||||
ReturnType<typeof createDocumentFromTemplate>
|
||||
>;
|
||||
|
||||
export type CreateDocumentFromTemplateOptions = {
|
||||
templateId: number;
|
||||
externalId?: string | null;
|
||||
@ -72,6 +75,15 @@ export type CreateDocumentFromTemplateOptions = {
|
||||
requestMetadata?: RequestMetadata;
|
||||
};
|
||||
|
||||
export const ZCreateDocumentFromTemplateResponseSchema = DocumentSchema.extend({
|
||||
documentData: DocumentDataSchema,
|
||||
Recipient: RecipientSchema.array(),
|
||||
});
|
||||
|
||||
export type TCreateDocumentFromTemplateResponse = z.infer<
|
||||
typeof ZCreateDocumentFromTemplateResponseSchema
|
||||
>;
|
||||
|
||||
export const createDocumentFromTemplate = async ({
|
||||
templateId,
|
||||
externalId,
|
||||
@ -80,7 +92,7 @@ export const createDocumentFromTemplate = async ({
|
||||
recipients,
|
||||
override,
|
||||
requestMetadata,
|
||||
}: CreateDocumentFromTemplateOptions) => {
|
||||
}: CreateDocumentFromTemplateOptions): Promise<TCreateDocumentFromTemplateResponse> => {
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
DIRECT_TEMPLATE_RECIPIENT_NAME,
|
||||
} from '@documenso/lib/constants/direct-templates';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import type { Recipient, TemplateDirectLink } from '@documenso/prisma/client';
|
||||
import type { Recipient } from '@documenso/prisma/client';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
|
||||
@ -21,7 +21,7 @@ export const createTemplateDirectLink = async ({
|
||||
templateId,
|
||||
userId,
|
||||
directRecipientId,
|
||||
}: CreateTemplateDirectLinkOptions): Promise<TemplateDirectLink> => {
|
||||
}: CreateTemplateDirectLinkOptions) => {
|
||||
const template = await prisma.template.findFirst({
|
||||
where: {
|
||||
id: templateId,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import type { Prisma, Template } from '@documenso/prisma/client';
|
||||
|
||||
import type { FindResultResponse } from '../../types/search-params';
|
||||
import { type FindResultResponse } from '../../types/search-params';
|
||||
|
||||
export type FindTemplatesOptions = {
|
||||
userId: number;
|
||||
@ -11,9 +11,6 @@ export type FindTemplatesOptions = {
|
||||
perPage?: number;
|
||||
};
|
||||
|
||||
export type FindTemplatesResponse = Awaited<ReturnType<typeof findTemplates>>;
|
||||
export type FindTemplateRow = FindTemplatesResponse['data'][number];
|
||||
|
||||
export const findTemplates = async ({
|
||||
userId,
|
||||
teamId,
|
||||
|
||||
@ -1,16 +1,5 @@
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import type { Prisma } from '@documenso/prisma/client';
|
||||
import {
|
||||
DocumentDataSchema,
|
||||
FieldSchema,
|
||||
RecipientSchema,
|
||||
TemplateDirectLinkSchema,
|
||||
TemplateMetaSchema,
|
||||
TemplateSchema,
|
||||
UserSchema,
|
||||
} from '@documenso/prisma/generated/zod';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
|
||||
@ -20,26 +9,7 @@ export type GetTemplateByIdOptions = {
|
||||
teamId?: number;
|
||||
};
|
||||
|
||||
export const ZGetTemplateByIdResponseSchema = TemplateSchema.extend({
|
||||
directLink: TemplateDirectLinkSchema.nullable(),
|
||||
templateDocumentData: DocumentDataSchema,
|
||||
templateMeta: TemplateMetaSchema.nullable(),
|
||||
Recipient: RecipientSchema.array(),
|
||||
Field: FieldSchema.array(),
|
||||
User: UserSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
}),
|
||||
});
|
||||
|
||||
export type TGetTemplateByIdResponse = z.infer<typeof ZGetTemplateByIdResponseSchema>;
|
||||
|
||||
export const getTemplateById = async ({
|
||||
id,
|
||||
userId,
|
||||
teamId,
|
||||
}: GetTemplateByIdOptions): Promise<TGetTemplateByIdResponse> => {
|
||||
export const getTemplateById = async ({ id, userId, teamId }: GetTemplateByIdOptions) => {
|
||||
const whereFilter: Prisma.TemplateWhereInput = {
|
||||
id,
|
||||
OR:
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
|
||||
export type MoveTemplateToTeamOptions = {
|
||||
templateId: number;
|
||||
teamId: number;
|
||||
@ -23,8 +23,7 @@ export const moveTemplateToTeam = async ({
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Template not found or already associated with a team.',
|
||||
});
|
||||
}
|
||||
@ -41,9 +40,8 @@ export const moveTemplateToTeam = async ({
|
||||
});
|
||||
|
||||
if (!team) {
|
||||
throw new TRPCError({
|
||||
code: 'FORBIDDEN',
|
||||
message: 'You are not a member of this team.',
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Team does not exist or you are not a member of this team.',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
69
packages/lib/server-only/user/disable-user.ts
Normal file
69
packages/lib/server-only/user/disable-user.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export type DisableUserOptions = {
|
||||
id: number;
|
||||
};
|
||||
|
||||
export const disableUser = async ({ id }: DisableUserOptions) => {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
include: {
|
||||
ApiToken: true,
|
||||
Webhooks: true,
|
||||
passkeys: true,
|
||||
VerificationToken: true,
|
||||
PasswordResetToken: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new AppError('There was an error disabling the user');
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.user.update({
|
||||
where: { id },
|
||||
data: { disabled: true },
|
||||
});
|
||||
|
||||
await tx.apiToken.updateMany({
|
||||
where: { userId: id },
|
||||
data: {
|
||||
expires: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
await tx.webhook.updateMany({
|
||||
where: { userId: id },
|
||||
data: {
|
||||
enabled: false,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.verificationToken.updateMany({
|
||||
where: { userId: id },
|
||||
data: {
|
||||
expires: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
await tx.passwordResetToken.updateMany({
|
||||
where: { userId: id },
|
||||
data: {
|
||||
expiry: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
await tx.passkey.deleteMany({
|
||||
where: { userId: id },
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error disabling user', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
27
packages/lib/server-only/user/enable-user.ts
Normal file
27
packages/lib/server-only/user/enable-user.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export type EnableUserOptions = {
|
||||
id: number;
|
||||
};
|
||||
|
||||
export const enableUser = async ({ id }: EnableUserOptions) => {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new AppError('There was an error enabling the user');
|
||||
}
|
||||
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
disabled: false,
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -30,11 +30,11 @@ msgstr "„{documentName}“ wurde unterschrieben"
|
||||
msgid "“{documentName}” was signed by all signers"
|
||||
msgstr "„{documentName}“ wurde von allen Unterzeichnern signiert"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:137
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:125
|
||||
msgid "{0} has invited you to {recipientActionVerb} the document \"{1}\"."
|
||||
msgstr "{0} hat Sie eingeladen, das Dokument \"{1}\" {recipientActionVerb}."
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:130
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:118
|
||||
msgid "{0} invited you to {recipientActionVerb} a document"
|
||||
msgstr "{0} hat dich eingeladen, ein Dokument {recipientActionVerb}"
|
||||
|
||||
@ -50,7 +50,7 @@ msgstr "{0} hat das Team {teamName} bei Documenso verlassen"
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} von {1} Zeile(n) ausgewählt."
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:136
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:124
|
||||
#: packages/lib/server-only/document/resend-document.tsx:137
|
||||
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
|
||||
msgstr ""
|
||||
@ -195,6 +195,22 @@ msgstr "{recipientName} {action} ein Dokument, indem Sie einen Ihrer direkten Li
|
||||
msgid "{recipientName} has rejected the document '{documentName}'"
|
||||
msgstr "{recipientName} hat das Dokument '{documentName}' abgelehnt"
|
||||
|
||||
#: packages/email/template-components/template-document-recipient-signed.tsx:49
|
||||
msgid "{recipientReference} has completed signing the document."
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-recipient-signed-email.ts:121
|
||||
msgid "{recipientReference} has signed \"{0}\""
|
||||
msgstr ""
|
||||
|
||||
#: packages/email/template-components/template-document-recipient-signed.tsx:43
|
||||
msgid "{recipientReference} has signed \"{documentName}\""
|
||||
msgstr ""
|
||||
|
||||
#: packages/email/templates/document-recipient-signed.tsx:27
|
||||
msgid "{recipientReference} has signed {documentName}"
|
||||
msgstr ""
|
||||
|
||||
#: packages/email/template-components/template-document-rejected.tsx:25
|
||||
msgid "{signerName} has rejected the document \"{documentName}\"."
|
||||
msgstr "{signerName} hat das Dokument \"{documentName}\" abgelehnt."
|
||||
@ -289,7 +305,7 @@ msgstr "<0>Konto erforderlich</0> - Der Empfänger muss angemeldet sein, um das
|
||||
msgid "<0>Require passkey</0> - The recipient must have an account and passkey configured via their settings"
|
||||
msgstr "<0>Passkey erforderlich</0> - Der Empfänger muss ein Konto haben und den Passkey über seine Einstellungen konfiguriert haben"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:122
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:110
|
||||
msgid "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
||||
msgstr "Ein Dokument wurde von deiner direkten Vorlage erstellt, das erfordert, dass du {recipientActionVerb}."
|
||||
|
||||
@ -305,7 +321,7 @@ msgstr "Ein Feld wurde entfernt"
|
||||
msgid "A field was updated"
|
||||
msgstr "Ein Feld wurde aktualisiert"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.ts:107
|
||||
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.handler.ts:98
|
||||
msgid "A new member has joined your team"
|
||||
msgstr "Ein neues Mitglied ist deinem Team beigetreten"
|
||||
|
||||
@ -329,7 +345,7 @@ msgstr "Eine Anfrage zur Verwendung Ihrer E-Mail wurde von {0} auf Documenso ini
|
||||
msgid "A team member has joined a team on Documenso"
|
||||
msgstr "Ein Teammitglied ist einem Team bei Documenso beigetreten"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-team-member-left-email.ts:96
|
||||
#: packages/lib/jobs/definitions/emails/send-team-member-left-email.handler.ts:87
|
||||
msgid "A team member has left {0}"
|
||||
msgstr "Ein Teammitglied hat {0} verlassen"
|
||||
|
||||
@ -364,12 +380,12 @@ msgstr "Übertragungsanfrage des Teams auf Documenso annehmen"
|
||||
msgid "Add a document"
|
||||
msgstr "Dokument hinzufügen"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:378
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:390
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:468
|
||||
msgid "Add a URL to redirect the user to once the document is signed"
|
||||
msgstr "Fügen Sie eine URL hinzu, um den Benutzer nach der Unterzeichnung des Dokuments weiterzuleiten"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:290
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:302
|
||||
msgid "Add an external ID to the document. This can be used to identify the document in external systems."
|
||||
msgstr "Fügen Sie dem Dokument eine externe ID hinzu. Diese kann verwendet werden, um das Dokument in externen Systemen zu identifizieren."
|
||||
|
||||
@ -414,13 +430,13 @@ msgstr "Text zum Feld hinzufügen"
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:272
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:284
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:367
|
||||
msgid "Advanced Options"
|
||||
msgstr "Erweiterte Optionen"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:576
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:414
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:415
|
||||
msgid "Advanced settings"
|
||||
msgstr "Erweiterte Einstellungen"
|
||||
|
||||
@ -472,11 +488,11 @@ msgstr "Genehmigung"
|
||||
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:"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:524
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:531
|
||||
msgid "Black"
|
||||
msgstr "Schwarz"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:538
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:545
|
||||
msgid "Blue"
|
||||
msgstr "Blau"
|
||||
|
||||
@ -492,7 +508,7 @@ msgstr "Durch die Annahme dieser Anfrage gewähren Sie <0>{teamName}</0> Zugriff
|
||||
msgid "By accepting this request, you will take responsibility for any billing items associated with this team."
|
||||
msgstr "Indem du diese Anfrage annimmst, übernimmst du die Verantwortung für alle Abrechnungspunkte, die mit diesem Team verbunden sind."
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:356
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:357
|
||||
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:58
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
@ -534,7 +550,7 @@ msgstr "Checkbox-Werte"
|
||||
msgid "Clear filters"
|
||||
msgstr "Filter löschen"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:558
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:565
|
||||
msgid "Clear Signature"
|
||||
msgstr "Unterschrift löschen"
|
||||
|
||||
@ -547,6 +563,7 @@ msgid "Close"
|
||||
msgstr "Schließen"
|
||||
|
||||
#: packages/email/template-components/template-document-completed.tsx:35
|
||||
#: packages/email/template-components/template-document-recipient-signed.tsx:37
|
||||
#: packages/email/template-components/template-document-self-signed.tsx:36
|
||||
#: packages/lib/constants/document.ts:10
|
||||
msgid "Completed"
|
||||
@ -561,8 +578,8 @@ msgstr "Abgeschlossenes Dokument"
|
||||
msgid "Configure Direct Recipient"
|
||||
msgstr "Direkten Empfänger konfigurieren"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:415
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:578
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:416
|
||||
msgid "Configure the {0} field"
|
||||
msgstr "Konfigurieren Sie das Feld {0}"
|
||||
|
||||
@ -619,13 +636,13 @@ msgstr "Konto erstellen"
|
||||
msgid "Custom Text"
|
||||
msgstr "Benutzerdefinierter Text"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:934
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:938
|
||||
#: packages/ui/primitives/document-flow/types.ts:53
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:729
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:733
|
||||
msgid "Date"
|
||||
msgstr "Datum"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:313
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:325
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:408
|
||||
msgid "Date Format"
|
||||
msgstr "Datumsformat"
|
||||
@ -642,16 +659,16 @@ msgstr "Hast du keinen Passwortwechsel angefordert? Wir helfen dir, dein Konto a
|
||||
msgid "Direct link receiver"
|
||||
msgstr "Empfänger des direkten Links"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-rejection-emails.ts:149
|
||||
#: packages/lib/jobs/definitions/emails/send-rejection-emails.handler.ts:140
|
||||
msgid "Document \"{0}\" - Rejected by {1}"
|
||||
msgstr "Dokument \"{0}\" - Abgelehnt von {1}"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-rejection-emails.ts:109
|
||||
#: packages/lib/jobs/definitions/emails/send-rejection-emails.handler.ts:100
|
||||
msgid "Document \"{0}\" - Rejection Confirmed"
|
||||
msgstr "Dokument \"{0}\" - Ablehnung Bestätigt"
|
||||
|
||||
#: packages/ui/components/document/document-global-auth-access-select.tsx:62
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:216
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:227
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:202
|
||||
msgid "Document access"
|
||||
msgstr "Dokumentenzugriff"
|
||||
@ -670,7 +687,8 @@ msgstr "Dokument storniert"
|
||||
msgid "Document completed"
|
||||
msgstr "Dokument abgeschlossen"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:168
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:208
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:286
|
||||
msgid "Document completed email"
|
||||
msgstr "E-Mail zum Abschluss des Dokuments"
|
||||
|
||||
@ -679,7 +697,7 @@ msgid "Document created"
|
||||
msgstr "Dokument erstellt"
|
||||
|
||||
#: packages/email/templates/document-created-from-direct-template.tsx:32
|
||||
#: packages/lib/server-only/template/create-document-from-direct-template.ts:574
|
||||
#: packages/lib/server-only/template/create-document-from-direct-template.ts:585
|
||||
msgid "Document created from direct template"
|
||||
msgstr "Dokument erstellt aus direkter Vorlage"
|
||||
|
||||
@ -691,7 +709,7 @@ msgstr "Dokumenterstellung"
|
||||
msgid "Document deleted"
|
||||
msgstr "Dokument gelöscht"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:207
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:247
|
||||
msgid "Document deleted email"
|
||||
msgstr "E-Mail zum Löschen des Dokuments"
|
||||
|
||||
@ -716,7 +734,7 @@ msgstr "Dokument ins Team verschoben"
|
||||
msgid "Document opened"
|
||||
msgstr "Dokument geöffnet"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:128
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:168
|
||||
msgid "Document pending email"
|
||||
msgstr "E-Mail über ausstehende Dokumente"
|
||||
|
||||
@ -757,8 +775,8 @@ msgstr "Entwurf"
|
||||
msgid "Drag & drop your PDF here."
|
||||
msgstr "Ziehen Sie Ihr PDF hierher."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1065
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:860
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1069
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:864
|
||||
msgid "Dropdown"
|
||||
msgstr "Dropdown"
|
||||
|
||||
@ -767,12 +785,12 @@ msgid "Dropdown options"
|
||||
msgstr "Dropdown-Optionen"
|
||||
|
||||
#: packages/lib/constants/document.ts:28
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:882
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:886
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:273
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:512
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:519
|
||||
#: packages/ui/primitives/document-flow/types.ts:54
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:677
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:681
|
||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:471
|
||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:478
|
||||
msgid "Email"
|
||||
@ -794,7 +812,7 @@ msgstr "E-Mail erneut gesendet"
|
||||
msgid "Email sent"
|
||||
msgstr "E-Mail gesendet"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1130
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1134
|
||||
msgid "Empty field"
|
||||
msgstr "Leeres Feld"
|
||||
|
||||
@ -807,8 +825,8 @@ msgstr "Direktlink-Signierung aktivieren"
|
||||
msgid "Enable signing order"
|
||||
msgstr "Aktiviere die Signaturreihenfolge"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:802
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:597
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:806
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:601
|
||||
msgid "Enable Typed Signatures"
|
||||
msgstr "Aktivieren Sie getippte Unterschriften"
|
||||
|
||||
@ -816,17 +834,17 @@ msgstr "Aktivieren Sie getippte Unterschriften"
|
||||
msgid "Enter password"
|
||||
msgstr "Passwort eingeben"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:257
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:258
|
||||
#: packages/ui/primitives/pdf-viewer.tsx:166
|
||||
msgid "Error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:283
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:295
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:378
|
||||
msgid "External ID"
|
||||
msgstr "Externe ID"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:258
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:259
|
||||
msgid "Failed to save settings."
|
||||
msgstr "Einstellungen konnten nicht gespeichert werden."
|
||||
|
||||
@ -896,7 +914,7 @@ msgstr "Globale Empfängerauthentifizierung"
|
||||
msgid "Go Back"
|
||||
msgstr "Zurück"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:545
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:552
|
||||
msgid "Green"
|
||||
msgstr "Grün"
|
||||
|
||||
@ -947,7 +965,7 @@ msgstr "Tritt {teamName} auf Documenso bei"
|
||||
msgid "Label"
|
||||
msgstr "Beschriftung"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:176
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:187
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:162
|
||||
msgid "Language"
|
||||
msgstr "Sprache"
|
||||
@ -983,12 +1001,12 @@ msgstr "Nachricht <0>(Optional)</0>"
|
||||
msgid "Min"
|
||||
msgstr "Min"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:908
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:912
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:299
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:550
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:556
|
||||
#: packages/ui/primitives/document-flow/types.ts:55
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:703
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:707
|
||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:506
|
||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:512
|
||||
msgid "Name"
|
||||
@ -1006,8 +1024,8 @@ msgstr "Muss unterzeichnen"
|
||||
msgid "Needs to view"
|
||||
msgstr "Muss sehen"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:693
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:516
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:697
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:520
|
||||
msgid "No recipient matching this description was found."
|
||||
msgstr "Kein passender Empfänger mit dieser Beschreibung gefunden."
|
||||
|
||||
@ -1015,8 +1033,8 @@ msgstr "Kein passender Empfänger mit dieser Beschreibung gefunden."
|
||||
msgid "No recipients"
|
||||
msgstr "Keine Empfänger"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:708
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:531
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:712
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:535
|
||||
msgid "No recipients with this role"
|
||||
msgstr "Keine Empfänger mit dieser Rolle"
|
||||
|
||||
@ -1044,9 +1062,9 @@ msgstr "Kein Wert gefunden."
|
||||
msgid "None"
|
||||
msgstr "Keine"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:986
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:990
|
||||
#: packages/ui/primitives/document-flow/types.ts:56
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:781
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:785
|
||||
msgid "Number"
|
||||
msgstr "Nummer"
|
||||
|
||||
@ -1112,15 +1130,15 @@ msgstr "Bitte {0} dein Dokument<0/>\"{documentName}\""
|
||||
msgid "Please {action} your document {documentName}"
|
||||
msgstr "Bitte {action} dein Dokument {documentName}"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:111
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:99
|
||||
msgid "Please {recipientActionVerb} this document"
|
||||
msgstr "Bitte {recipientActionVerb} dieses Dokument"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:125
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:113
|
||||
msgid "Please {recipientActionVerb} this document created by your direct template"
|
||||
msgstr "Bitte {recipientActionVerb} dieses Dokument, das von deiner direkten Vorlage erstellt wurde"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:117
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:105
|
||||
msgid "Please {recipientActionVerb} your document"
|
||||
msgstr "Bitte {recipientActionVerb} dein Dokument"
|
||||
|
||||
@ -1167,24 +1185,28 @@ msgid "Recipient"
|
||||
msgstr "Empfänger"
|
||||
|
||||
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:39
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:257
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:269
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:291
|
||||
msgid "Recipient action authentication"
|
||||
msgstr "Empfängeraktion Authentifizierung"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:89
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:129
|
||||
msgid "Recipient removed email"
|
||||
msgstr "E-Mail des entfernten Empfängers"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:50
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:51
|
||||
msgid "Recipient signed email"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:90
|
||||
msgid "Recipient signing request email"
|
||||
msgstr "E-Mail zur Unterzeichnungsanfrage des Empfängers"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:531
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:538
|
||||
msgid "Red"
|
||||
msgstr "Rot"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:371
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:383
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:461
|
||||
msgid "Redirect URL"
|
||||
msgstr "Weiterleitungs-URL"
|
||||
@ -1217,7 +1239,7 @@ msgstr "Erinnerung: Bitte {recipientActionVerb} dieses Dokument"
|
||||
msgid "Reminder: Please {recipientActionVerb} your document"
|
||||
msgstr "Erinnerung: Bitte {recipientActionVerb} dein Dokument"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1117
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1121
|
||||
msgid "Remove"
|
||||
msgstr "Entfernen"
|
||||
|
||||
@ -1245,11 +1267,11 @@ msgstr "Seien Sie versichert, Ihr Dokument ist streng vertraulich und wird niema
|
||||
msgid "Rows per page"
|
||||
msgstr "Zeilen pro Seite"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:355
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:356
|
||||
msgid "Save"
|
||||
msgstr "Speichern"
|
||||
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:893
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:897
|
||||
msgid "Save Template"
|
||||
msgstr "Vorlage speichern"
|
||||
|
||||
@ -1285,15 +1307,19 @@ msgstr "Senden"
|
||||
msgid "Send Document"
|
||||
msgstr "Dokument senden"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:158
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:198
|
||||
msgid "Send document completed email"
|
||||
msgstr "E-Mail über den Abschluss des Dokuments senden"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:197
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:276
|
||||
msgid "Send document completed email to the owner"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:237
|
||||
msgid "Send document deleted email"
|
||||
msgstr "E-Mail über das Löschen des Dokuments senden"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:118
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:158
|
||||
msgid "Send document pending email"
|
||||
msgstr "E-Mail über ausstehende Dokumente senden"
|
||||
|
||||
@ -1301,11 +1327,15 @@ msgstr "E-Mail über ausstehende Dokumente senden"
|
||||
msgid "Send documents on behalf of the team using the email address"
|
||||
msgstr "Dokumente im Namen des Teams über die E-Mail-Adresse senden"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:79
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:119
|
||||
msgid "Send recipient removed email"
|
||||
msgstr "E-Mail über entfernten Empfänger senden"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:40
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:41
|
||||
msgid "Send recipient signed email"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:80
|
||||
msgid "Send recipient signing request email"
|
||||
msgstr "E-Mail über Unterzeichnungsanfrage des Empfängers senden"
|
||||
|
||||
@ -1338,11 +1368,11 @@ msgstr "Dokument signieren"
|
||||
msgid "Sign In"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:830
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:834
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:324
|
||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||
#: packages/ui/primitives/document-flow/types.ts:49
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:625
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:629
|
||||
msgid "Signature"
|
||||
msgstr "Unterschrift"
|
||||
|
||||
@ -1366,8 +1396,8 @@ msgstr "Unterzeichner müssen eindeutige E-Mails haben"
|
||||
msgid "Signing"
|
||||
msgstr "Unterzeichnung"
|
||||
|
||||
#: packages/lib/server-only/document/send-completed-email.ts:114
|
||||
#: packages/lib/server-only/document/send-completed-email.ts:194
|
||||
#: packages/lib/server-only/document/send-completed-email.ts:119
|
||||
#: packages/lib/server-only/document/send-completed-email.ts:199
|
||||
msgid "Signing Complete!"
|
||||
msgstr "Unterzeichnung abgeschlossen!"
|
||||
|
||||
@ -1421,9 +1451,9 @@ msgstr "Team-E-Mail für {teamName} auf Documenso entfernt"
|
||||
msgid "Template title"
|
||||
msgstr "Vorlagentitel"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:960
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:964
|
||||
#: packages/ui/primitives/document-flow/types.ts:52
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:755
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:759
|
||||
msgid "Text"
|
||||
msgstr "Text"
|
||||
|
||||
@ -1515,7 +1545,7 @@ msgstr "Dies kann überschrieben werden, indem die Authentifizierungsanforderung
|
||||
msgid "This document can not be recovered, if you would like to dispute the reason for future documents please contact support."
|
||||
msgstr "Dieses Dokument kann nicht wiederhergestellt werden. Wenn du den Grund für zukünftige Dokumente anfechten möchtest, kontaktiere bitte den Support."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:764
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:768
|
||||
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
|
||||
msgstr "Dieses Dokument wurde bereits an diesen Empfänger gesendet. Sie können diesen Empfänger nicht mehr bearbeiten."
|
||||
|
||||
@ -1531,15 +1561,19 @@ msgstr "Dieses Dokument wurde mit <0>Documenso.</0> gesendet"
|
||||
msgid "This email confirms that you have rejected the document <0>\"{documentName}\"</0> sent by {documentOwnerName}."
|
||||
msgstr "Diese E-Mail bestätigt, dass Sie das Dokument <0>\"{documentName}\"</0> abgelehnt haben, das von {documentOwnerName} gesendet wurde."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:94
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:56
|
||||
msgid "This email is sent to the document owner when a recipient has signed the document."
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:134
|
||||
msgid "This email is sent to the recipient if they are removed from a pending document."
|
||||
msgstr "Diese E-Mail wird an den Empfänger gesendet, wenn er von einem ausstehenden Dokument entfernt wird."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:55
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:95
|
||||
msgid "This email is sent to the recipient requesting them to sign the document."
|
||||
msgstr "Diese E-Mail wird an den Empfänger gesendet und fordert ihn auf, das Dokument zu unterschreiben."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:133
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:173
|
||||
msgid "This email will be sent to the recipient who has just signed the document, if there are still other recipients who have not signed yet."
|
||||
msgstr "Diese E-Mail wird an den Empfänger gesendet, der das Dokument gerade unterschrieben hat, wenn es noch andere Empfänger gibt, die noch nicht unterschrieben haben."
|
||||
|
||||
@ -1551,7 +1585,7 @@ msgstr "Dieses Feld kann nicht geändert oder gelöscht werden. Wenn Sie den dir
|
||||
msgid "This is how the document will reach the recipients once the document is ready for signing."
|
||||
msgstr "So wird das Dokument die Empfänger erreichen, sobald es zum Unterschreiben bereit ist."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1097
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1101
|
||||
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
|
||||
msgstr "Dieser Empfänger kann nicht mehr bearbeitet werden, da er ein Feld unterschrieben oder das Dokument abgeschlossen hat."
|
||||
|
||||
@ -1559,29 +1593,33 @@ msgstr "Dieser Empfänger kann nicht mehr bearbeitet werden, da er ein Feld unte
|
||||
msgid "This signer has already signed the document."
|
||||
msgstr "Dieser Unterzeichner hat das Dokument bereits unterschrieben."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:212
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:252
|
||||
msgid "This will be sent to all recipients if a pending document has been deleted."
|
||||
msgstr "Dies wird an alle Empfänger gesendet, wenn ein ausstehendes Dokument gelöscht wurde."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:173
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:213
|
||||
msgid "This will be sent to all recipients once the document has been fully completed."
|
||||
msgstr "Dies wird an alle Empfänger gesendet, sobald das Dokument vollständig abgeschlossen ist."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:291
|
||||
msgid "This will be sent to the document owner once the document has been fully completed."
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:48
|
||||
msgid "This will override any global settings."
|
||||
msgstr "Dies überschreibt alle globalen Einstellungen."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:347
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:359
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:438
|
||||
msgid "Time Zone"
|
||||
msgstr "Zeitzone"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:155
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:166
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1080
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:873
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1084
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:877
|
||||
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."
|
||||
|
||||
@ -1597,7 +1635,7 @@ msgstr "Aktualisieren Sie die Rolle und fügen Sie Felder nach Bedarf für den d
|
||||
msgid "Upgrade"
|
||||
msgstr "Upgrade"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:509
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:516
|
||||
msgid "Upload Signature"
|
||||
msgstr ""
|
||||
|
||||
@ -1726,7 +1764,7 @@ msgstr "Du wurdest eingeladen, {0} auf Documenso beizutreten"
|
||||
msgid "You have been invited to join the following team"
|
||||
msgstr "Du wurdest eingeladen, dem folgenden Team beizutreten"
|
||||
|
||||
#: packages/lib/server-only/recipient/set-recipients-for-document.ts:327
|
||||
#: packages/lib/server-only/recipient/set-recipients-for-document.ts:337
|
||||
msgid "You have been removed from a document"
|
||||
msgstr "Du wurdest von einem Dokument entfernt"
|
||||
|
||||
@ -1734,7 +1772,7 @@ msgstr "Du wurdest von einem Dokument entfernt"
|
||||
msgid "You have been requested to take ownership of team {0} on Documenso"
|
||||
msgstr "Du wurdest gebeten, das Team {0} auf Documenso zu übernehmen"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:115
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:103
|
||||
#: packages/lib/server-only/document/resend-document.tsx:125
|
||||
msgid "You have initiated the document {0} that requires you to {recipientActionVerb} it."
|
||||
msgstr "Du hast das Dokument {0} initiiert, das erfordert, dass du {recipientActionVerb}."
|
||||
|
||||
@ -92,11 +92,7 @@ msgstr "{0} Empfänger(in)"
|
||||
msgid "{charactersRemaining, plural, one {1 character remaining} other {{charactersRemaining} characters remaining}}"
|
||||
msgstr "{charactersRemaining, plural, one {1 Zeichen verbleibend} other {{charactersRemaining} Zeichen verbleibend}}"
|
||||
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:55
|
||||
msgid "{formattedTeamMemberQuanity} • Monthly • Renews: {formattedDate}"
|
||||
msgstr "{formattedTeamMemberQuanity} • Monatlich • Erneuert: {formattedDate}"
|
||||
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:48
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:59
|
||||
msgid "{numberOfSeats, plural, one {# member} other {# members}}"
|
||||
msgstr "{numberOfSeats, plural, one {# Mitglied} other {# Mitglieder}}"
|
||||
|
||||
@ -254,21 +250,21 @@ msgid "Acknowledgment"
|
||||
msgstr "Bestätigung"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-dropdown.tsx:108
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/logs/document-logs-data-table.tsx:100
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/logs/document-logs-data-table.tsx:98
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx:123
|
||||
#: apps/web/src/app/(dashboard)/settings/public-profile/public-templates-data-table.tsx:164
|
||||
#: apps/web/src/app/(dashboard)/settings/security/activity/user-security-activity-data-table.tsx:118
|
||||
#: apps/web/src/app/(dashboard)/settings/security/activity/user-security-activity-data-table.tsx:116
|
||||
#: apps/web/src/app/(internal)/%5F%5Fhtmltopdf/audit-log/data-table.tsx:46
|
||||
msgid "Action"
|
||||
msgstr "Aktion"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:85
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:181
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:79
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:177
|
||||
#: apps/web/src/app/(dashboard)/templates/data-table-templates.tsx:140
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:133
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:142
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:118
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:127
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:131
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:140
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:116
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:125
|
||||
msgid "Actions"
|
||||
msgstr "Aktionen"
|
||||
|
||||
@ -491,7 +487,7 @@ msgstr "Ein Fehler ist aufgetreten, während die Teammitglieder geladen wurden.
|
||||
msgid "An error occurred while moving the document."
|
||||
msgstr "Ein Fehler ist aufgetreten, während das Dokument verschoben wurde."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:57
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:65
|
||||
msgid "An error occurred while moving the template."
|
||||
msgstr "Ein Fehler ist aufgetreten, während die Vorlage verschoben wurde."
|
||||
|
||||
@ -499,7 +495,7 @@ msgstr "Ein Fehler ist aufgetreten, während die Vorlage verschoben wurde."
|
||||
msgid "An error occurred while removing the field."
|
||||
msgstr "Ein Fehler ist beim Entfernen des Feldes aufgetreten."
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:152
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:154
|
||||
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:126
|
||||
#: apps/web/src/app/(signing)/sign/[token]/dropdown-field.tsx:137
|
||||
#: apps/web/src/app/(signing)/sign/[token]/email-field.tsx:110
|
||||
@ -522,7 +518,7 @@ msgstr "Ein Fehler ist aufgetreten, während das Dokument gesendet wurde."
|
||||
msgid "An error occurred while sending your confirmation email"
|
||||
msgstr "Beim Senden Ihrer Bestätigungs-E-Mail ist ein Fehler aufgetreten"
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:123
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:125
|
||||
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:100
|
||||
#: apps/web/src/app/(signing)/sign/[token]/dropdown-field.tsx:106
|
||||
#: apps/web/src/app/(signing)/sign/[token]/email-field.tsx:84
|
||||
@ -544,7 +540,7 @@ msgstr "Ein Fehler ist aufgetreten, während versucht wurde, eine Checkout-Sitzu
|
||||
msgid "An error occurred while updating the document settings."
|
||||
msgstr "Ein Fehler ist aufgetreten, während die Dokumenteinstellungen aktualisiert wurden."
|
||||
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:213
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:215
|
||||
msgid "An error occurred while updating the signature."
|
||||
msgstr "Ein Fehler ist aufgetreten, während die Unterschrift aktualisiert wurde."
|
||||
|
||||
@ -578,11 +574,11 @@ msgstr "Ein Fehler ist aufgetreten, während dein Dokument hochgeladen wurde."
|
||||
#: apps/web/src/components/forms/profile.tsx:87
|
||||
#: apps/web/src/components/forms/public-profile-claim-dialog.tsx:113
|
||||
#: apps/web/src/components/forms/public-profile-form.tsx:104
|
||||
#: apps/web/src/components/forms/signin.tsx:248
|
||||
#: apps/web/src/components/forms/signin.tsx:256
|
||||
#: apps/web/src/components/forms/signin.tsx:270
|
||||
#: apps/web/src/components/forms/signin.tsx:285
|
||||
#: apps/web/src/components/forms/signin.tsx:301
|
||||
#: apps/web/src/components/forms/signin.tsx:249
|
||||
#: apps/web/src/components/forms/signin.tsx:257
|
||||
#: apps/web/src/components/forms/signin.tsx:271
|
||||
#: apps/web/src/components/forms/signin.tsx:286
|
||||
#: apps/web/src/components/forms/signin.tsx:302
|
||||
#: apps/web/src/components/forms/signup.tsx:124
|
||||
#: apps/web/src/components/forms/signup.tsx:138
|
||||
#: apps/web/src/components/forms/token.tsx:143
|
||||
@ -597,11 +593,11 @@ msgstr "Es ist ein unbekannter Fehler aufgetreten"
|
||||
msgid "Any payment methods attached to this team will remain attached to this team. Please contact us if you need to update this information."
|
||||
msgstr "Alle Zahlungsmethoden, die mit diesem Team verbunden sind, bleiben diesem Team zugeordnet. Bitte kontaktiere uns, wenn du diese Informationen aktualisieren möchtest."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:225
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:221
|
||||
msgid "Any Source"
|
||||
msgstr "Jede Quelle"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:205
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:201
|
||||
msgid "Any Status"
|
||||
msgstr "Jeder Status"
|
||||
|
||||
@ -704,7 +700,7 @@ msgid "Background Color"
|
||||
msgstr "Hintergrundfarbe"
|
||||
|
||||
#: apps/web/src/components/forms/2fa/disable-authenticator-app-dialog.tsx:167
|
||||
#: apps/web/src/components/forms/signin.tsx:485
|
||||
#: apps/web/src/components/forms/signin.tsx:486
|
||||
msgid "Backup Code"
|
||||
msgstr "Backup-Code"
|
||||
|
||||
@ -721,7 +717,7 @@ msgid "Basic details"
|
||||
msgstr "Basisdetails"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/settings/billing/page.tsx:74
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:61
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:71
|
||||
#: apps/web/src/components/(dashboard)/settings/layout/desktop-nav.tsx:117
|
||||
#: apps/web/src/components/(dashboard)/settings/layout/mobile-nav.tsx:120
|
||||
#: apps/web/src/components/(teams)/settings/layout/desktop-nav.tsx:123
|
||||
@ -737,7 +733,7 @@ msgstr "Markenpräferenzen"
|
||||
msgid "Branding preferences updated"
|
||||
msgstr "Markenpräferenzen aktualisiert"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/settings/security/activity/user-security-activity-data-table.tsx:99
|
||||
#: apps/web/src/app/(dashboard)/settings/security/activity/user-security-activity-data-table.tsx:97
|
||||
#: apps/web/src/app/(internal)/%5F%5Fhtmltopdf/audit-log/data-table.tsx:48
|
||||
msgid "Browser"
|
||||
msgstr "Browser"
|
||||
@ -780,7 +776,7 @@ msgstr "Durch die Verwendung der elektronischen Unterschriftsfunktion stimmen Si
|
||||
#: apps/web/src/app/(dashboard)/settings/teams/team-email-usage.tsx:109
|
||||
#: apps/web/src/app/(dashboard)/templates/delete-template-dialog.tsx:81
|
||||
#: apps/web/src/app/(dashboard)/templates/duplicate-template-dialog.tsx:78
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:119
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:131
|
||||
#: apps/web/src/app/(dashboard)/templates/template-direct-link-dialog.tsx:472
|
||||
#: apps/web/src/app/(signing)/sign/[token]/auto-sign.tsx:220
|
||||
#: apps/web/src/app/(signing)/sign/[token]/document-action-auth-2fa.tsx:178
|
||||
@ -864,9 +860,9 @@ msgstr "Benutzername jetzt beanspruchen"
|
||||
msgid "Click here to get started"
|
||||
msgstr "Klicken Sie hier, um zu beginnen"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:78
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:76
|
||||
#: apps/web/src/app/(dashboard)/settings/public-profile/public-templates-data-table.tsx:118
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:68
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:66
|
||||
#: apps/web/src/components/document/document-history-sheet.tsx:133
|
||||
msgid "Click here to retry"
|
||||
msgstr "Klicken Sie hier, um es erneut zu versuchen"
|
||||
@ -919,7 +915,7 @@ msgstr "Unterzeichnung abschließen"
|
||||
msgid "Complete Viewing"
|
||||
msgstr "Betrachten abschließen"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:208
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:204
|
||||
#: apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx:77
|
||||
#: apps/web/src/components/formatter/document-status.tsx:28
|
||||
msgid "Completed"
|
||||
@ -1149,9 +1145,9 @@ msgstr "Erstellen Sie Ihr Konto und beginnen Sie mit dem modernen Dokumentensign
|
||||
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62
|
||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:96
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:54
|
||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:109
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:48
|
||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:63
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:105
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-information.tsx:34
|
||||
#: apps/web/src/app/(dashboard)/templates/data-table-templates.tsx:56
|
||||
#: apps/web/src/components/templates/manage-public-template-dialog.tsx:274
|
||||
@ -1168,7 +1164,7 @@ msgid "Created by"
|
||||
msgstr "Erstellt von"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/admin/documents/[id]/page.tsx:48
|
||||
#: apps/web/src/components/(teams)/tables/pending-user-teams-data-table.tsx:78
|
||||
#: apps/web/src/components/(teams)/tables/pending-user-teams-data-table.tsx:76
|
||||
msgid "Created on"
|
||||
msgstr "Erstellt am"
|
||||
|
||||
@ -1187,10 +1183,6 @@ msgstr "Aktuelles Passwort"
|
||||
msgid "Current password is incorrect."
|
||||
msgstr ""
|
||||
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:69
|
||||
msgid "Current plan: {0}"
|
||||
msgstr "Aktueller Plan: {0}"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/settings/billing/billing-plans.tsx:28
|
||||
msgid "Daily"
|
||||
msgstr "Täglich"
|
||||
@ -1199,7 +1191,7 @@ msgstr "Täglich"
|
||||
msgid "Dark Mode"
|
||||
msgstr "Dunkelmodus"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/settings/security/activity/user-security-activity-data-table.tsx:70
|
||||
#: apps/web/src/app/(dashboard)/settings/security/activity/user-security-activity-data-table.tsx:68
|
||||
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:148
|
||||
msgid "Date"
|
||||
msgstr "Datum"
|
||||
@ -1319,7 +1311,7 @@ msgstr "Konto wird gelöscht..."
|
||||
msgid "Details"
|
||||
msgstr "Einzelheiten"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/settings/security/activity/user-security-activity-data-table.tsx:75
|
||||
#: apps/web/src/app/(dashboard)/settings/security/activity/user-security-activity-data-table.tsx:73
|
||||
#: apps/web/src/app/(internal)/%5F%5Fhtmltopdf/certificate/page.tsx:242
|
||||
msgid "Device"
|
||||
msgstr "Gerät"
|
||||
@ -1334,8 +1326,8 @@ msgstr "Direkter Link"
|
||||
msgid "Direct link"
|
||||
msgstr "Direkter Link"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:160
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:231
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:156
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:227
|
||||
msgid "Direct Link"
|
||||
msgstr "Direkter Link"
|
||||
|
||||
@ -1439,11 +1431,11 @@ msgstr "Dokument abgeschlossen!"
|
||||
msgid "Document created"
|
||||
msgstr "Dokument erstellt"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:129
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:127
|
||||
msgid "Document created by <0>{0}</0>"
|
||||
msgstr "Dokument erstellt von <0>{0}</0>"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:134
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:132
|
||||
msgid "Document created using a <0>direct link</0>"
|
||||
msgstr "Dokument erstellt mit einem <0>direkten Link</0>"
|
||||
|
||||
@ -1598,7 +1590,7 @@ msgstr "Auditprotokolle herunterladen"
|
||||
msgid "Download Certificate"
|
||||
msgstr "Zertifikat herunterladen"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:214
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:210
|
||||
#: apps/web/src/components/formatter/document-status.tsx:34
|
||||
msgid "Draft"
|
||||
msgstr "Entwurf"
|
||||
@ -1668,7 +1660,7 @@ msgstr "Offenlegung der elektronischen Unterschrift"
|
||||
#: apps/web/src/components/(teams)/dialogs/update-team-email-dialog.tsx:153
|
||||
#: apps/web/src/components/forms/forgot-password.tsx:81
|
||||
#: apps/web/src/components/forms/profile.tsx:122
|
||||
#: apps/web/src/components/forms/signin.tsx:338
|
||||
#: apps/web/src/components/forms/signin.tsx:339
|
||||
#: apps/web/src/components/forms/signup.tsx:176
|
||||
msgid "Email"
|
||||
msgstr "E-Mail"
|
||||
@ -1778,12 +1770,12 @@ msgstr "Geben Sie hier Ihren Text ein"
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit/edit-template.tsx:217
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit/edit-template.tsx:256
|
||||
#: apps/web/src/app/(dashboard)/templates/duplicate-template-dialog.tsx:51
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:56
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:68
|
||||
#: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:175
|
||||
#: apps/web/src/app/(signing)/sign/[token]/auto-sign.tsx:152
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:122
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:151
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:212
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:124
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:153
|
||||
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:214
|
||||
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:99
|
||||
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:125
|
||||
#: apps/web/src/app/(signing)/sign/[token]/dropdown-field.tsx:105
|
||||
@ -1859,7 +1851,7 @@ msgid "For any questions regarding this disclosure, electronic signatures, or an
|
||||
msgstr "Für Fragen zu dieser Offenlegung, elektronischen Unterschriften oder einem verwandten Verfahren kontaktieren Sie uns bitte unter: <0>{SUPPORT_EMAIL}</0>"
|
||||
|
||||
#: apps/web/src/app/(unauthenticated)/forgot-password/page.tsx:21
|
||||
#: apps/web/src/components/forms/signin.tsx:370
|
||||
#: apps/web/src/components/forms/signin.tsx:371
|
||||
msgid "Forgot your password?"
|
||||
msgstr "Haben Sie Ihr Passwort vergessen?"
|
||||
|
||||
@ -2039,11 +2031,11 @@ msgstr "Einladung akzeptiert!"
|
||||
msgid "Invitation declined"
|
||||
msgstr "Einladung abgelehnt"
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:80
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:78
|
||||
msgid "Invitation has been deleted"
|
||||
msgstr "Einladung wurde gelöscht"
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:63
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:61
|
||||
msgid "Invitation has been resent"
|
||||
msgstr "Einladung wurde erneut gesendet"
|
||||
|
||||
@ -2063,7 +2055,7 @@ msgstr "Mitglieder einladen"
|
||||
msgid "Invite team members"
|
||||
msgstr "Teammitglieder einladen"
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:128
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:126
|
||||
msgid "Invited At"
|
||||
msgstr "Eingeladen am"
|
||||
|
||||
@ -2133,7 +2125,7 @@ msgstr "Zuletzt aktualisiert"
|
||||
msgid "Last updated at"
|
||||
msgstr "Zuletzt aktualisiert am"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:71
|
||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:69
|
||||
msgid "Last used"
|
||||
msgstr "Zuletzt verwendet"
|
||||
|
||||
@ -2142,7 +2134,7 @@ msgid "Leaderboard"
|
||||
msgstr ""
|
||||
|
||||
#: apps/web/src/components/(teams)/dialogs/leave-team-dialog.tsx:111
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:117
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:115
|
||||
msgid "Leave"
|
||||
msgstr "Verlassen"
|
||||
|
||||
@ -2175,7 +2167,7 @@ msgstr "Links generiert"
|
||||
msgid "Listening to {0}"
|
||||
msgstr "Anhören von {0}"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:100
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:98
|
||||
msgid "Load older activity"
|
||||
msgstr "Ältere Aktivitäten laden"
|
||||
|
||||
@ -2190,11 +2182,11 @@ msgid "Loading Document..."
|
||||
msgstr "Dokument wird geladen..."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/move-document-dialog.tsx:92
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:91
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:103
|
||||
msgid "Loading teams..."
|
||||
msgstr "Teams werden geladen..."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:100
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:98
|
||||
msgid "Loading..."
|
||||
msgstr "Wird geladen..."
|
||||
|
||||
@ -2204,7 +2196,7 @@ msgstr "Wird geladen..."
|
||||
msgid "Login"
|
||||
msgstr "Anmelden"
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:101
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:99
|
||||
msgid "Manage"
|
||||
msgstr "Verwalten"
|
||||
|
||||
@ -2252,7 +2244,7 @@ msgstr "Abonnement verwalten"
|
||||
msgid "Manage subscriptions"
|
||||
msgstr "Abonnements verwalten"
|
||||
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:81
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:87
|
||||
msgid "Manage team subscription."
|
||||
msgstr "Teamabonnement verwalten."
|
||||
|
||||
@ -2292,8 +2284,8 @@ msgstr "MAU (erstellt Dokument)"
|
||||
msgid "MAU (had document completed)"
|
||||
msgstr "MAU (hat Dokument abgeschlossen)"
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:90
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:113
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:88
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:111
|
||||
msgid "Member Since"
|
||||
msgstr "Mitglied seit"
|
||||
|
||||
@ -2309,6 +2301,7 @@ msgid "Modify recipients"
|
||||
msgstr "Empfänger ändern"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/settings/billing/billing-plans.tsx:30
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:54
|
||||
msgid "Monthly"
|
||||
msgstr "Monatlich"
|
||||
|
||||
@ -2321,7 +2314,7 @@ msgid "Monthly Active Users: Users that had at least one of their documents comp
|
||||
msgstr "Monatlich aktive Benutzer: Benutzer, die mindestens eines ihrer Dokumente abgeschlossen haben"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/move-document-dialog.tsx:123
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:122
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:134
|
||||
msgid "Move"
|
||||
msgstr "Verschieben"
|
||||
|
||||
@ -2329,7 +2322,7 @@ msgstr "Verschieben"
|
||||
msgid "Move Document to Team"
|
||||
msgstr "Dokument in Team verschieben"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:77
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:89
|
||||
msgid "Move Template to Team"
|
||||
msgstr "Vorlage in Team verschieben"
|
||||
|
||||
@ -2339,7 +2332,7 @@ msgid "Move to Team"
|
||||
msgstr "In Team verschieben"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/move-document-dialog.tsx:123
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:122
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:134
|
||||
msgid "Moving..."
|
||||
msgstr "Verschieben..."
|
||||
|
||||
@ -2352,7 +2345,7 @@ msgstr "Meine Vorlagen"
|
||||
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99
|
||||
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66
|
||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144
|
||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:61
|
||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:59
|
||||
#: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:287
|
||||
#: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:294
|
||||
#: apps/web/src/app/(signing)/sign/[token]/complete/claim-account.tsx:118
|
||||
@ -2367,7 +2360,7 @@ msgstr "Name"
|
||||
msgid "Need to sign documents?"
|
||||
msgstr "Müssen Dokumente signieren?"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:76
|
||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:74
|
||||
msgid "Never"
|
||||
msgstr "Niemals"
|
||||
|
||||
@ -2402,7 +2395,7 @@ msgstr "Keine aktiven Entwürfe"
|
||||
msgid "No further action is required from you at this time."
|
||||
msgstr "Es sind derzeit keine weiteren Maßnahmen Ihrerseits erforderlich."
|
||||
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:42
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:43
|
||||
msgid "No payment required"
|
||||
msgstr "Keine Zahlung erforderlich"
|
||||
|
||||
@ -2410,11 +2403,11 @@ msgstr "Keine Zahlung erforderlich"
|
||||
msgid "No public profile templates found"
|
||||
msgstr "Keine Vorlagen für das öffentliche Profil gefunden"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:108
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:106
|
||||
msgid "No recent activity"
|
||||
msgstr "Keine aktuellen Aktivitäten"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:103
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:101
|
||||
msgid "No recent documents"
|
||||
msgstr "Keine aktuellen Dokumente"
|
||||
|
||||
@ -2449,7 +2442,7 @@ msgstr "Kein Wert gefunden."
|
||||
msgid "No worries, it happens! Enter your email and we'll email you a special link to reset your password."
|
||||
msgstr "Keine Sorge, das passiert! Geben Sie Ihre E-Mail ein, und wir senden Ihnen einen speziellen Link zum Zurücksetzen Ihres Passworts."
|
||||
|
||||
#: apps/web/src/components/forms/signin.tsx:160
|
||||
#: apps/web/src/components/forms/signin.tsx:161
|
||||
msgid "Not supported"
|
||||
msgstr "Nicht unterstützt"
|
||||
|
||||
@ -2535,7 +2528,7 @@ msgstr "Geöffnet"
|
||||
msgid "Or"
|
||||
msgstr "Oder"
|
||||
|
||||
#: apps/web/src/components/forms/signin.tsx:390
|
||||
#: apps/web/src/components/forms/signin.tsx:391
|
||||
msgid "Or continue with"
|
||||
msgstr "Oder fahren Sie fort mit"
|
||||
|
||||
@ -2546,8 +2539,8 @@ msgstr "Andernfalls wird das Dokument als Entwurf erstellt."
|
||||
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:86
|
||||
#: apps/web/src/app/(internal)/%5F%5Fhtmltopdf/audit-log/page.tsx:103
|
||||
#: apps/web/src/components/(dashboard)/layout/menu-switcher.tsx:81
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:86
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:109
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:84
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:107
|
||||
msgid "Owner"
|
||||
msgstr "Besitzer"
|
||||
|
||||
@ -2555,7 +2548,7 @@ msgstr "Besitzer"
|
||||
msgid "Paid"
|
||||
msgstr "Bezahlt"
|
||||
|
||||
#: apps/web/src/components/forms/signin.tsx:435
|
||||
#: apps/web/src/components/forms/signin.tsx:436
|
||||
msgid "Passkey"
|
||||
msgstr "Passkey"
|
||||
|
||||
@ -2592,14 +2585,14 @@ msgstr "Passkeys"
|
||||
msgid "Passkeys allow you to sign in and authenticate using biometrics, password managers, etc."
|
||||
msgstr "Passkeys ermöglichen das Anmelden und die Authentifizierung mit biometrischen Daten, Passwortmanagern usw."
|
||||
|
||||
#: apps/web/src/components/forms/signin.tsx:161
|
||||
#: apps/web/src/components/forms/signin.tsx:162
|
||||
msgid "Passkeys are not supported on this browser"
|
||||
msgstr "Passkeys werden von diesem Browser nicht unterstützt"
|
||||
|
||||
#: apps/web/src/components/(dashboard)/common/command-menu.tsx:70
|
||||
#: apps/web/src/components/forms/password.tsx:128
|
||||
#: apps/web/src/components/forms/reset-password.tsx:115
|
||||
#: apps/web/src/components/forms/signin.tsx:356
|
||||
#: apps/web/src/components/forms/signin.tsx:357
|
||||
#: apps/web/src/components/forms/signup.tsx:192
|
||||
#: apps/web/src/components/forms/v2/signup.tsx:347
|
||||
msgid "Password"
|
||||
@ -2629,7 +2622,7 @@ msgid "Payment overdue"
|
||||
msgstr "Zahlung überfällig"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recipients.tsx:131
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:211
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:207
|
||||
#: apps/web/src/components/(teams)/tables/teams-member-page-data-table.tsx:82
|
||||
#: apps/web/src/components/(teams)/tables/user-settings-teams-page-data-table.tsx:77
|
||||
#: apps/web/src/components/document/document-read-only-fields.tsx:89
|
||||
@ -2746,7 +2739,7 @@ msgstr "Bitte überprüfen Sie das Dokument vor der Unterzeichnung."
|
||||
msgid "Please try again and make sure you enter the correct email address."
|
||||
msgstr "Bitte versuchen Sie es erneut und stellen Sie sicher, dass Sie die korrekte E-Mail-Adresse eingeben."
|
||||
|
||||
#: apps/web/src/components/forms/signin.tsx:203
|
||||
#: apps/web/src/components/forms/signin.tsx:204
|
||||
msgid "Please try again later or login using your normal details"
|
||||
msgstr "Bitte versuchen Sie es später erneut oder melden Sie sich mit Ihren normalen Daten an"
|
||||
|
||||
@ -2858,17 +2851,17 @@ msgstr "Der Grund muss weniger als 500 Zeichen lang sein"
|
||||
msgid "Reauthentication is required to sign this field"
|
||||
msgstr "Eine erneute Authentifizierung ist erforderlich, um dieses Feld zu unterschreiben"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:57
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:55
|
||||
#: apps/web/src/app/(dashboard)/settings/security/page.tsx:130
|
||||
msgid "Recent activity"
|
||||
msgstr "Aktuelle Aktivitäten"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:47
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:45
|
||||
msgid "Recent documents"
|
||||
msgstr "Neueste Dokumente"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:69
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:120
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:63
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:116
|
||||
#: apps/web/src/app/(dashboard)/templates/template-direct-link-dialog.tsx:280
|
||||
msgid "Recipient"
|
||||
msgstr "Empfänger"
|
||||
@ -2929,8 +2922,8 @@ msgstr "Haben Sie Ihr Passwort vergessen? <0>Einloggen</0>"
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/team-email-dropdown.tsx:89
|
||||
#: apps/web/src/components/(teams)/dialogs/remove-team-email-dialog.tsx:159
|
||||
#: apps/web/src/components/(teams)/tables/pending-user-teams-data-table-actions.tsx:54
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:166
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:167
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:164
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:165
|
||||
#: apps/web/src/components/forms/avatar-image.tsx:166
|
||||
msgid "Remove"
|
||||
msgstr "Entfernen"
|
||||
@ -2939,10 +2932,14 @@ msgstr "Entfernen"
|
||||
msgid "Remove team email"
|
||||
msgstr "Team-E-Mail entfernen"
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:164
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:162
|
||||
msgid "Remove team member"
|
||||
msgstr "Teammitglied entfernen"
|
||||
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:63
|
||||
msgid "Renews: {formattedDate}"
|
||||
msgstr ""
|
||||
|
||||
#: apps/web/src/components/forms/password.tsx:144
|
||||
#: apps/web/src/components/forms/reset-password.tsx:131
|
||||
msgid "Repeat Password"
|
||||
@ -2957,7 +2954,7 @@ msgid "Reseal document"
|
||||
msgstr "Dokument wieder versiegeln"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/_action-items/resend-document.tsx:118
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:154
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:152
|
||||
msgid "Resend"
|
||||
msgstr "Erneut senden"
|
||||
|
||||
@ -3035,9 +3032,9 @@ msgstr "Zugriff widerrufen"
|
||||
#: apps/web/src/app/(dashboard)/templates/template-direct-link-dialog.tsx:283
|
||||
#: apps/web/src/components/(teams)/dialogs/invite-team-member-dialog.tsx:318
|
||||
#: apps/web/src/components/(teams)/dialogs/update-team-member-dialog.tsx:163
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:82
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:123
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:105
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:80
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:121
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:103
|
||||
msgid "Role"
|
||||
msgstr "Rolle"
|
||||
|
||||
@ -3094,7 +3091,7 @@ msgid "Select"
|
||||
msgstr "Auswählen"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/move-document-dialog.tsx:87
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:86
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:98
|
||||
msgid "Select a team"
|
||||
msgstr "Wählen Sie ein Team aus"
|
||||
|
||||
@ -3102,7 +3099,7 @@ msgstr "Wählen Sie ein Team aus"
|
||||
msgid "Select a team to move this document to. This action cannot be undone."
|
||||
msgstr "Wählen Sie ein Team aus, um dieses Dokument dorthin zu verschieben. Diese Aktion kann nicht rückgängig gemacht werden."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:80
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:92
|
||||
msgid "Select a team to move this template to. This action cannot be undone."
|
||||
msgstr "Wählen Sie ein Team aus, um diese Vorlage dorthin zu verschieben. Diese Aktion kann nicht rückgängig gemacht werden."
|
||||
|
||||
@ -3134,7 +3131,7 @@ msgstr "Im Namen des Teams senden"
|
||||
msgid "Send reminder"
|
||||
msgstr "Erinnerung senden"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:65
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:59
|
||||
msgid "Sender"
|
||||
msgstr "Absender"
|
||||
|
||||
@ -3233,8 +3230,8 @@ msgid "Sign Here"
|
||||
msgstr "Hier unterzeichnen"
|
||||
|
||||
#: apps/web/src/app/not-found.tsx:29
|
||||
#: apps/web/src/components/forms/signin.tsx:383
|
||||
#: apps/web/src/components/forms/signin.tsx:510
|
||||
#: apps/web/src/components/forms/signin.tsx:384
|
||||
#: apps/web/src/components/forms/signin.tsx:511
|
||||
msgid "Sign In"
|
||||
msgstr "Einloggen"
|
||||
|
||||
@ -3319,8 +3316,8 @@ msgstr "Unterzeichnungszertifikat"
|
||||
msgid "Signing certificate provided by"
|
||||
msgstr "Unterzeichnungszertifikat bereitgestellt von"
|
||||
|
||||
#: apps/web/src/components/forms/signin.tsx:383
|
||||
#: apps/web/src/components/forms/signin.tsx:510
|
||||
#: apps/web/src/components/forms/signin.tsx:384
|
||||
#: apps/web/src/components/forms/signin.tsx:511
|
||||
msgid "Signing in..."
|
||||
msgstr "Anmeldung..."
|
||||
|
||||
@ -3396,8 +3393,8 @@ msgstr "Website Einstellungen"
|
||||
#: apps/web/src/components/(teams)/dialogs/remove-team-email-dialog.tsx:64
|
||||
#: apps/web/src/components/(teams)/dialogs/remove-team-email-dialog.tsx:83
|
||||
#: apps/web/src/components/(teams)/tables/pending-user-teams-data-table-actions.tsx:33
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:68
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:85
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:66
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:83
|
||||
#: apps/web/src/components/(teams)/team-billing-portal-button.tsx:29
|
||||
msgid "Something went wrong"
|
||||
msgstr "Etwas ist schiefgelaufen"
|
||||
@ -3439,7 +3436,7 @@ msgstr "Entschuldigung, wir konnten die Prüfprotokolle nicht herunterladen. Bit
|
||||
msgid "Sorry, we were unable to download the certificate. Please try again later."
|
||||
msgstr "Entschuldigung, wir konnten das Zertifikat nicht herunterladen. Bitte versuchen Sie es später erneut."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:138
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:134
|
||||
msgid "Source"
|
||||
msgstr "Quelle"
|
||||
|
||||
@ -3449,8 +3446,8 @@ msgstr "Statistiken"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:81
|
||||
#: apps/web/src/app/(dashboard)/admin/subscriptions/page.tsx:32
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:79
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:130
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:73
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:126
|
||||
#: apps/web/src/app/(internal)/%5F%5Fhtmltopdf/audit-log/page.tsx:93
|
||||
#: apps/web/src/components/(teams)/tables/team-billing-invoices-data-table.tsx:73
|
||||
msgid "Status"
|
||||
@ -3492,8 +3489,8 @@ msgstr "Abonnements"
|
||||
#: apps/web/src/components/(teams)/dialogs/update-team-member-dialog.tsx:92
|
||||
#: apps/web/src/components/(teams)/forms/update-team-form.tsx:67
|
||||
#: apps/web/src/components/(teams)/tables/pending-user-teams-data-table-actions.tsx:27
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:62
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:79
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:60
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:77
|
||||
#: apps/web/src/components/forms/public-profile-form.tsx:80
|
||||
#: apps/web/src/components/templates/manage-public-template-dialog.tsx:133
|
||||
#: apps/web/src/components/templates/manage-public-template-dialog.tsx:170
|
||||
@ -3512,8 +3509,8 @@ msgstr "Systemanforderungen"
|
||||
msgid "System Theme"
|
||||
msgstr "Systemthema"
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:65
|
||||
#: apps/web/src/components/(teams)/tables/pending-user-teams-data-table.tsx:64
|
||||
#: apps/web/src/components/(teams)/tables/current-user-teams-data-table.tsx:63
|
||||
#: apps/web/src/components/(teams)/tables/pending-user-teams-data-table.tsx:62
|
||||
msgid "Team"
|
||||
msgstr "Team"
|
||||
|
||||
@ -3559,8 +3556,8 @@ msgstr "Teameinladung"
|
||||
msgid "Team invitations have been sent."
|
||||
msgstr "Teameinladungen wurden gesendet."
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:109
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:86
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:107
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:84
|
||||
msgid "Team Member"
|
||||
msgstr "Teammitglied"
|
||||
|
||||
@ -3634,10 +3631,10 @@ msgstr "Teams"
|
||||
msgid "Teams restricted"
|
||||
msgstr "Teams beschränkt"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit/template-edit-page-view.tsx:63
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit/template-edit-page-view.tsx:64
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:39
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:148
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:228
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:144
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:224
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view.tsx:148
|
||||
#: apps/web/src/components/(teams)/dialogs/invite-team-member-dialog.tsx:408
|
||||
#: apps/web/src/components/templates/manage-public-template-dialog.tsx:271
|
||||
@ -3664,10 +3661,14 @@ msgstr "Vorlage ist von Deinem öffentlichen Profil entfernt worden."
|
||||
msgid "Template has been updated."
|
||||
msgstr "Vorlage wurde aktualisiert."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:48
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:50
|
||||
msgid "Template moved"
|
||||
msgstr "Vorlage verschoben"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:62
|
||||
msgid "Template not found or already associated with a team."
|
||||
msgstr ""
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/edit/edit-template.tsx:246
|
||||
msgid "Template saved"
|
||||
msgstr "Vorlage gespeichert"
|
||||
@ -3795,7 +3796,7 @@ msgstr "Die Teamübertragungsanfrage an <0>{0}</0> ist abgelaufen."
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never existed."
|
||||
msgstr "Das Team, das Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:49
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:51
|
||||
msgid "The template has been successfully moved to the selected team."
|
||||
msgstr "Die Vorlage wurde erfolgreich in das ausgewählte Team verschoben."
|
||||
|
||||
@ -3884,11 +3885,11 @@ msgstr "Dieses Dokument wurde von allen Empfängern unterschrieben"
|
||||
msgid "This document is currently a draft and has not been sent"
|
||||
msgstr "Dieses Dokument ist momentan ein Entwurf und wurde nicht gesendet"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:152
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:148
|
||||
msgid "This document was created by you or a team member using the template above."
|
||||
msgstr "Dieses Dokument wurde von Ihnen oder einem Teammitglied unter Verwendung der oben genannten Vorlage erstellt."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:164
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:160
|
||||
msgid "This document was created using a direct link."
|
||||
msgstr "Dieses Dokument wurde mit einem direkten Link erstellt."
|
||||
|
||||
@ -3908,7 +3909,7 @@ msgstr "Dieser Link ist ungültig oder abgelaufen. Bitte kontaktieren Sie Ihr Te
|
||||
msgid "This passkey has already been registered."
|
||||
msgstr "Dieser Zugangsschlüssel wurde bereits registriert."
|
||||
|
||||
#: apps/web/src/components/forms/signin.tsx:200
|
||||
#: apps/web/src/components/forms/signin.tsx:201
|
||||
msgid "This passkey is not configured for this application. Please login and add one in the user settings."
|
||||
msgstr "Dieser Passkey ist für diese Anwendung nicht konfiguriert. Bitte melden Sie sich an und fügen Sie einen in den Benutzereinstellungen hinzu."
|
||||
|
||||
@ -3916,7 +3917,7 @@ msgstr "Dieser Passkey ist für diese Anwendung nicht konfiguriert. Bitte melden
|
||||
msgid "This price includes minimum 5 seats."
|
||||
msgstr "Dieser Preis beinhaltet mindestens 5 Plätze."
|
||||
|
||||
#: apps/web/src/components/forms/signin.tsx:202
|
||||
#: apps/web/src/components/forms/signin.tsx:203
|
||||
msgid "This session has expired. Please try again."
|
||||
msgstr "Diese Sitzung ist abgelaufen. Bitte versuchen Sie es erneut."
|
||||
|
||||
@ -3949,7 +3950,7 @@ msgstr "Dieser Benutzername ist bereits vergeben"
|
||||
msgid "This username is already taken"
|
||||
msgstr "Dieser Benutzername ist bereits vergeben"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/logs/document-logs-data-table.tsx:73
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/logs/document-logs-data-table.tsx:71
|
||||
#: apps/web/src/app/(internal)/%5F%5Fhtmltopdf/audit-log/data-table.tsx:44
|
||||
msgid "Time"
|
||||
msgstr "Zeit"
|
||||
@ -3963,8 +3964,8 @@ msgid "Time Zone"
|
||||
msgstr "Zeitzone"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:67
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:60
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:115
|
||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:54
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-documents-table.tsx:111
|
||||
#: apps/web/src/app/(dashboard)/templates/data-table-templates.tsx:61
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
@ -4098,7 +4099,7 @@ msgstr "Zwei-Faktor-Authentifizierung"
|
||||
msgid "Two factor authentication recovery codes are used to access your account in the event that you lose access to your authenticator app."
|
||||
msgstr "Wiederherstellungscodes für die Zwei-Faktor-Authentifizierung werden verwendet, um auf Ihr Konto zuzugreifen, falls Sie den Zugang zu Ihrer Authentifizierungs-App verlieren."
|
||||
|
||||
#: apps/web/src/components/forms/signin.tsx:448
|
||||
#: apps/web/src/components/forms/signin.tsx:449
|
||||
msgid "Two-Factor Authentication"
|
||||
msgstr "Zwei-Faktor-Authentifizierung"
|
||||
|
||||
@ -4155,7 +4156,7 @@ msgstr "Direkter Zugriff auf die Vorlage kann nicht erstellt werden. Bitte versu
|
||||
msgid "Unable to decline this team invitation at this time."
|
||||
msgstr "Zurzeit kann diese Teameinladung nicht abgelehnt werden."
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:86
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:84
|
||||
msgid "Unable to delete invitation. Please try again."
|
||||
msgstr "Einladung kann nicht gelöscht werden. Bitte versuchen Sie es erneut."
|
||||
|
||||
@ -4171,12 +4172,12 @@ msgstr "Zwei-Faktor-Authentifizierung kann nicht deaktiviert werden"
|
||||
msgid "Unable to join this team at this time."
|
||||
msgstr "Zurzeit kann diesem Team nicht beigetreten werden."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:72
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-recent-activity.tsx:70
|
||||
#: apps/web/src/components/document/document-history-sheet.tsx:127
|
||||
msgid "Unable to load document history"
|
||||
msgstr "Kann den Dokumentverlauf nicht laden"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:62
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:60
|
||||
msgid "Unable to load documents"
|
||||
msgstr "Dokumente können nicht geladen werden"
|
||||
|
||||
@ -4192,7 +4193,7 @@ msgstr "Derzeit ist es nicht möglich, die E-Mail-Verifizierung zu entfernen. Bi
|
||||
msgid "Unable to remove team email at this time. Please try again."
|
||||
msgstr "Das Team-E-Mail kann zurzeit nicht entfernt werden. Bitte versuchen Sie es erneut."
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:69
|
||||
#: apps/web/src/components/(teams)/tables/team-member-invites-data-table.tsx:67
|
||||
msgid "Unable to resend invitation. Please try again."
|
||||
msgstr "Einladung kann nicht erneut gesendet werden. Bitte versuchen Sie es erneut."
|
||||
|
||||
@ -4209,8 +4210,8 @@ msgstr "Passwort kann nicht zurückgesetzt werden"
|
||||
msgid "Unable to setup two-factor authentication"
|
||||
msgstr "Zwei-Faktor-Authentifizierung kann nicht eingerichtet werden"
|
||||
|
||||
#: apps/web/src/components/forms/signin.tsx:247
|
||||
#: apps/web/src/components/forms/signin.tsx:255
|
||||
#: apps/web/src/components/forms/signin.tsx:248
|
||||
#: apps/web/src/components/forms/signin.tsx:256
|
||||
msgid "Unable to sign in"
|
||||
msgstr "Anmeldung nicht möglich"
|
||||
|
||||
@ -4227,6 +4228,7 @@ msgstr "Unvollendet"
|
||||
#: apps/web/src/app/(internal)/%5F%5Fhtmltopdf/certificate/page.tsx:262
|
||||
#: apps/web/src/app/(internal)/%5F%5Fhtmltopdf/certificate/page.tsx:273
|
||||
#: apps/web/src/app/(internal)/%5F%5Fhtmltopdf/certificate/page.tsx:284
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:55
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
@ -4267,7 +4269,7 @@ msgstr "Profil aktualisieren"
|
||||
msgid "Update Recipient"
|
||||
msgstr "Empfänger aktualisieren"
|
||||
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:146
|
||||
#: apps/web/src/components/(teams)/tables/team-members-data-table.tsx:144
|
||||
msgid "Update role"
|
||||
msgstr "Rolle aktualisieren"
|
||||
|
||||
@ -4336,12 +4338,12 @@ msgid "Use"
|
||||
msgstr "Verwenden"
|
||||
|
||||
#: apps/web/src/components/forms/2fa/disable-authenticator-app-dialog.tsx:187
|
||||
#: apps/web/src/components/forms/signin.tsx:505
|
||||
#: apps/web/src/components/forms/signin.tsx:506
|
||||
msgid "Use Authenticator"
|
||||
msgstr "Authenticator verwenden"
|
||||
|
||||
#: apps/web/src/components/forms/2fa/disable-authenticator-app-dialog.tsx:185
|
||||
#: apps/web/src/components/forms/signin.tsx:503
|
||||
#: apps/web/src/components/forms/signin.tsx:504
|
||||
msgid "Use Backup Code"
|
||||
msgstr "Backup-Code verwenden"
|
||||
|
||||
@ -4349,7 +4351,7 @@ msgstr "Backup-Code verwenden"
|
||||
msgid "Use Template"
|
||||
msgstr "Vorlage verwenden"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/logs/document-logs-data-table.tsx:78
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/logs/document-logs-data-table.tsx:76
|
||||
#: apps/web/src/app/(internal)/%5F%5Fhtmltopdf/audit-log/data-table.tsx:45
|
||||
msgid "User"
|
||||
msgstr "Benutzer"
|
||||
@ -4435,7 +4437,7 @@ msgstr "Alle Dokumente anzeigen, die an Ihr Konto gesendet wurden"
|
||||
msgid "View all recent security activity related to your account."
|
||||
msgstr "Sehen Sie sich alle aktuellen Sicherheitsaktivitäten in Ihrem Konto an."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:157
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:155
|
||||
msgid "View all related documents"
|
||||
msgstr "Alle verwandten Dokumente anzeigen"
|
||||
|
||||
@ -4459,7 +4461,7 @@ msgstr "Dokumente ansehen, die mit dieser E-Mail verknüpft sind"
|
||||
msgid "View invites"
|
||||
msgstr "Einladungen ansehen"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:95
|
||||
#: apps/web/src/app/(dashboard)/templates/[id]/template-page-view-recent-activity.tsx:93
|
||||
msgid "View more"
|
||||
msgstr "Mehr anzeigen"
|
||||
|
||||
@ -4594,9 +4596,9 @@ msgid "We encountered an unknown error while attempting to save your details. Pl
|
||||
msgstr "Wir sind auf einen unbekannten Fehler gestoßen, während wir versucht haben, Ihre Daten zu speichern. Bitte versuchen Sie es später erneut."
|
||||
|
||||
#: apps/web/src/components/forms/profile.tsx:89
|
||||
#: apps/web/src/components/forms/signin.tsx:272
|
||||
#: apps/web/src/components/forms/signin.tsx:287
|
||||
#: apps/web/src/components/forms/signin.tsx:303
|
||||
#: apps/web/src/components/forms/signin.tsx:273
|
||||
#: apps/web/src/components/forms/signin.tsx:288
|
||||
#: apps/web/src/components/forms/signin.tsx:304
|
||||
msgid "We encountered an unknown error while attempting to sign you In. Please try again later."
|
||||
msgstr "Wir sind auf einen unbekannten Fehler gestoßen, während wir versucht haben, Sie anzumelden. Bitte versuchen Sie es später erneut."
|
||||
|
||||
@ -4797,6 +4799,7 @@ msgid "Write about yourself"
|
||||
msgstr "Schreiben Sie über sich selbst"
|
||||
|
||||
#: apps/web/src/app/(dashboard)/settings/billing/billing-plans.tsx:31
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:53
|
||||
msgid "Yearly"
|
||||
msgstr "Jährlich"
|
||||
|
||||
@ -4841,6 +4844,10 @@ msgstr "Sie aktualisieren derzeit <0>{teamMemberName}.</0>"
|
||||
msgid "You are currently updating the <0>{passkeyName}</0> passkey."
|
||||
msgstr "Sie aktualisieren derzeit den <0>{passkeyName}</0> Passkey."
|
||||
|
||||
#: apps/web/src/app/(dashboard)/templates/move-template-dialog.tsx:64
|
||||
msgid "You are not a member of this team."
|
||||
msgstr ""
|
||||
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/error.tsx:28
|
||||
msgid "You are not authorized to view this page."
|
||||
msgstr "Sie sind nicht berechtigt, diese Seite anzuzeigen."
|
||||
@ -4965,7 +4972,7 @@ msgstr "Sie haben {teamMemberName} aktualisiert."
|
||||
msgid "You have verified your email address for <0>{0}</0>."
|
||||
msgstr "Sie haben Ihre E-Mail-Adresse für <0>{0}</0> bestätigt."
|
||||
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:82
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:88
|
||||
msgid "You must be an admin of this team to manage billing."
|
||||
msgstr "Sie müssen Administrator dieses Teams sein, um die Abrechnung zu verwalten."
|
||||
|
||||
@ -5119,7 +5126,7 @@ msgstr "Ihr Wiederherstellungscode wurde in die Zwischenablage kopiert."
|
||||
msgid "Your recovery codes are listed below. Please store them in a safe place."
|
||||
msgstr "Ihre Wiederherstellungscodes sind unten aufgeführt. Bitte bewahren Sie sie an einem sicheren Ort auf."
|
||||
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:62
|
||||
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/billing/page.tsx:72
|
||||
msgid "Your subscription is currently active."
|
||||
msgstr "Ihr Abonnement ist derzeit aktiv."
|
||||
|
||||
|
||||
@ -25,11 +25,11 @@ msgstr "“{documentName}” has been signed"
|
||||
msgid "“{documentName}” was signed by all signers"
|
||||
msgstr "“{documentName}” was signed by all signers"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:137
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:125
|
||||
msgid "{0} has invited you to {recipientActionVerb} the document \"{1}\"."
|
||||
msgstr "{0} has invited you to {recipientActionVerb} the document \"{1}\"."
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:130
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:118
|
||||
msgid "{0} invited you to {recipientActionVerb} a document"
|
||||
msgstr "{0} invited you to {recipientActionVerb} a document"
|
||||
|
||||
@ -45,7 +45,7 @@ msgstr "{0} left the team {teamName} on Documenso"
|
||||
msgid "{0} of {1} row(s) selected."
|
||||
msgstr "{0} of {1} row(s) selected."
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:136
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:124
|
||||
#: packages/lib/server-only/document/resend-document.tsx:137
|
||||
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
|
||||
msgstr "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
|
||||
@ -190,6 +190,22 @@ msgstr "{recipientName} {action} a document by using one of your direct links"
|
||||
msgid "{recipientName} has rejected the document '{documentName}'"
|
||||
msgstr "{recipientName} has rejected the document '{documentName}'"
|
||||
|
||||
#: packages/email/template-components/template-document-recipient-signed.tsx:49
|
||||
msgid "{recipientReference} has completed signing the document."
|
||||
msgstr "{recipientReference} has completed signing the document."
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-recipient-signed-email.ts:121
|
||||
msgid "{recipientReference} has signed \"{0}\""
|
||||
msgstr "{recipientReference} has signed \"{0}\""
|
||||
|
||||
#: packages/email/template-components/template-document-recipient-signed.tsx:43
|
||||
msgid "{recipientReference} has signed \"{documentName}\""
|
||||
msgstr "{recipientReference} has signed \"{documentName}\""
|
||||
|
||||
#: packages/email/templates/document-recipient-signed.tsx:27
|
||||
msgid "{recipientReference} has signed {documentName}"
|
||||
msgstr "{recipientReference} has signed {documentName}"
|
||||
|
||||
#: packages/email/template-components/template-document-rejected.tsx:25
|
||||
msgid "{signerName} has rejected the document \"{documentName}\"."
|
||||
msgstr "{signerName} has rejected the document \"{documentName}\"."
|
||||
@ -284,7 +300,7 @@ msgstr "<0>Require account</0> - The recipient must be signed in to view the doc
|
||||
msgid "<0>Require passkey</0> - The recipient must have an account and passkey configured via their settings"
|
||||
msgstr "<0>Require passkey</0> - The recipient must have an account and passkey configured via their settings"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:122
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:110
|
||||
msgid "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
||||
msgstr "A document was created by your direct template that requires you to {recipientActionVerb} it."
|
||||
|
||||
@ -300,7 +316,7 @@ msgstr "A field was removed"
|
||||
msgid "A field was updated"
|
||||
msgstr "A field was updated"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.ts:107
|
||||
#: packages/lib/jobs/definitions/emails/send-team-member-joined-email.handler.ts:98
|
||||
msgid "A new member has joined your team"
|
||||
msgstr "A new member has joined your team"
|
||||
|
||||
@ -324,7 +340,7 @@ msgstr "A request to use your email has been initiated by {0} on Documenso"
|
||||
msgid "A team member has joined a team on Documenso"
|
||||
msgstr "A team member has joined a team on Documenso"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-team-member-left-email.ts:96
|
||||
#: packages/lib/jobs/definitions/emails/send-team-member-left-email.handler.ts:87
|
||||
msgid "A team member has left {0}"
|
||||
msgstr "A team member has left {0}"
|
||||
|
||||
@ -359,12 +375,12 @@ msgstr "Accept team transfer request on Documenso"
|
||||
msgid "Add a document"
|
||||
msgstr "Add a document"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:378
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:390
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:468
|
||||
msgid "Add a URL to redirect the user to once the document is signed"
|
||||
msgstr "Add a URL to redirect the user to once the document is signed"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:290
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:302
|
||||
msgid "Add an external ID to the document. This can be used to identify the document in external systems."
|
||||
msgstr "Add an external ID to the document. This can be used to identify the document in external systems."
|
||||
|
||||
@ -409,13 +425,13 @@ msgstr "Add text to the field"
|
||||
msgid "Admin"
|
||||
msgstr "Admin"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:272
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:284
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:367
|
||||
msgid "Advanced Options"
|
||||
msgstr "Advanced Options"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:576
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:414
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:415
|
||||
msgid "Advanced settings"
|
||||
msgstr "Advanced settings"
|
||||
|
||||
@ -467,11 +483,11 @@ msgstr "Approving"
|
||||
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:"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:524
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:531
|
||||
msgid "Black"
|
||||
msgstr "Black"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:538
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:545
|
||||
msgid "Blue"
|
||||
msgstr "Blue"
|
||||
|
||||
@ -487,7 +503,7 @@ msgstr "By accepting this request, you will be granting <0>{teamName}</0> access
|
||||
msgid "By accepting this request, you will take responsibility for any billing items associated with this team."
|
||||
msgstr "By accepting this request, you will take responsibility for any billing items associated with this team."
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:356
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:357
|
||||
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx:58
|
||||
msgid "Cancel"
|
||||
msgstr "Cancel"
|
||||
@ -529,7 +545,7 @@ msgstr "Checkbox values"
|
||||
msgid "Clear filters"
|
||||
msgstr "Clear filters"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:558
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:565
|
||||
msgid "Clear Signature"
|
||||
msgstr "Clear Signature"
|
||||
|
||||
@ -542,6 +558,7 @@ msgid "Close"
|
||||
msgstr "Close"
|
||||
|
||||
#: packages/email/template-components/template-document-completed.tsx:35
|
||||
#: packages/email/template-components/template-document-recipient-signed.tsx:37
|
||||
#: packages/email/template-components/template-document-self-signed.tsx:36
|
||||
#: packages/lib/constants/document.ts:10
|
||||
msgid "Completed"
|
||||
@ -556,8 +573,8 @@ msgstr "Completed Document"
|
||||
msgid "Configure Direct Recipient"
|
||||
msgstr "Configure Direct Recipient"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:577
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:415
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:578
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:416
|
||||
msgid "Configure the {0} field"
|
||||
msgstr "Configure the {0} field"
|
||||
|
||||
@ -614,13 +631,13 @@ msgstr "Create account"
|
||||
msgid "Custom Text"
|
||||
msgstr "Custom Text"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:934
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:938
|
||||
#: packages/ui/primitives/document-flow/types.ts:53
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:729
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:733
|
||||
msgid "Date"
|
||||
msgstr "Date"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:313
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:325
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:408
|
||||
msgid "Date Format"
|
||||
msgstr "Date Format"
|
||||
@ -637,16 +654,16 @@ msgstr "Didn't request a password change? We are here to help you secure your ac
|
||||
msgid "Direct link receiver"
|
||||
msgstr "Direct link receiver"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-rejection-emails.ts:149
|
||||
#: packages/lib/jobs/definitions/emails/send-rejection-emails.handler.ts:140
|
||||
msgid "Document \"{0}\" - Rejected by {1}"
|
||||
msgstr "Document \"{0}\" - Rejected by {1}"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-rejection-emails.ts:109
|
||||
#: packages/lib/jobs/definitions/emails/send-rejection-emails.handler.ts:100
|
||||
msgid "Document \"{0}\" - Rejection Confirmed"
|
||||
msgstr "Document \"{0}\" - Rejection Confirmed"
|
||||
|
||||
#: packages/ui/components/document/document-global-auth-access-select.tsx:62
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:216
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:227
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:202
|
||||
msgid "Document access"
|
||||
msgstr "Document access"
|
||||
@ -665,7 +682,8 @@ msgstr "Document Cancelled"
|
||||
msgid "Document completed"
|
||||
msgstr "Document completed"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:168
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:208
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:286
|
||||
msgid "Document completed email"
|
||||
msgstr "Document completed email"
|
||||
|
||||
@ -674,7 +692,7 @@ msgid "Document created"
|
||||
msgstr "Document created"
|
||||
|
||||
#: packages/email/templates/document-created-from-direct-template.tsx:32
|
||||
#: packages/lib/server-only/template/create-document-from-direct-template.ts:574
|
||||
#: packages/lib/server-only/template/create-document-from-direct-template.ts:585
|
||||
msgid "Document created from direct template"
|
||||
msgstr "Document created from direct template"
|
||||
|
||||
@ -686,7 +704,7 @@ msgstr "Document Creation"
|
||||
msgid "Document deleted"
|
||||
msgstr "Document deleted"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:207
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:247
|
||||
msgid "Document deleted email"
|
||||
msgstr "Document deleted email"
|
||||
|
||||
@ -711,7 +729,7 @@ msgstr "Document moved to team"
|
||||
msgid "Document opened"
|
||||
msgstr "Document opened"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:128
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:168
|
||||
msgid "Document pending email"
|
||||
msgstr "Document pending email"
|
||||
|
||||
@ -752,8 +770,8 @@ msgstr "Draft"
|
||||
msgid "Drag & drop your PDF here."
|
||||
msgstr "Drag & drop your PDF here."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1065
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:860
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1069
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:864
|
||||
msgid "Dropdown"
|
||||
msgstr "Dropdown"
|
||||
|
||||
@ -762,12 +780,12 @@ msgid "Dropdown options"
|
||||
msgstr "Dropdown options"
|
||||
|
||||
#: packages/lib/constants/document.ts:28
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:882
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:886
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:273
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:512
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:519
|
||||
#: packages/ui/primitives/document-flow/types.ts:54
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:677
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:681
|
||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:471
|
||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:478
|
||||
msgid "Email"
|
||||
@ -789,7 +807,7 @@ msgstr "Email resent"
|
||||
msgid "Email sent"
|
||||
msgstr "Email sent"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1130
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1134
|
||||
msgid "Empty field"
|
||||
msgstr "Empty field"
|
||||
|
||||
@ -802,8 +820,8 @@ msgstr "Enable Direct Link Signing"
|
||||
msgid "Enable signing order"
|
||||
msgstr "Enable signing order"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:802
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:597
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:806
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:601
|
||||
msgid "Enable Typed Signatures"
|
||||
msgstr "Enable Typed Signatures"
|
||||
|
||||
@ -811,17 +829,17 @@ msgstr "Enable Typed Signatures"
|
||||
msgid "Enter password"
|
||||
msgstr "Enter password"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:257
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:258
|
||||
#: packages/ui/primitives/pdf-viewer.tsx:166
|
||||
msgid "Error"
|
||||
msgstr "Error"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:283
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:295
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:378
|
||||
msgid "External ID"
|
||||
msgstr "External ID"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:258
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:259
|
||||
msgid "Failed to save settings."
|
||||
msgstr "Failed to save settings."
|
||||
|
||||
@ -891,7 +909,7 @@ msgstr "Global recipient action authentication"
|
||||
msgid "Go Back"
|
||||
msgstr "Go Back"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:545
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:552
|
||||
msgid "Green"
|
||||
msgstr "Green"
|
||||
|
||||
@ -942,7 +960,7 @@ msgstr "Join {teamName} on Documenso"
|
||||
msgid "Label"
|
||||
msgstr "Label"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:176
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:187
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:162
|
||||
msgid "Language"
|
||||
msgstr "Language"
|
||||
@ -978,12 +996,12 @@ msgstr "Message <0>(Optional)</0>"
|
||||
msgid "Min"
|
||||
msgstr "Min"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:908
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:912
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:299
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:550
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:556
|
||||
#: packages/ui/primitives/document-flow/types.ts:55
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:703
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:707
|
||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:506
|
||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:512
|
||||
msgid "Name"
|
||||
@ -1001,8 +1019,8 @@ msgstr "Needs to sign"
|
||||
msgid "Needs to view"
|
||||
msgstr "Needs to view"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:693
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:516
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:697
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:520
|
||||
msgid "No recipient matching this description was found."
|
||||
msgstr "No recipient matching this description was found."
|
||||
|
||||
@ -1010,8 +1028,8 @@ msgstr "No recipient matching this description was found."
|
||||
msgid "No recipients"
|
||||
msgstr "No recipients"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:708
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:531
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:712
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:535
|
||||
msgid "No recipients with this role"
|
||||
msgstr "No recipients with this role"
|
||||
|
||||
@ -1039,9 +1057,9 @@ msgstr "No value found."
|
||||
msgid "None"
|
||||
msgstr "None"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:986
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:990
|
||||
#: packages/ui/primitives/document-flow/types.ts:56
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:781
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:785
|
||||
msgid "Number"
|
||||
msgstr "Number"
|
||||
|
||||
@ -1107,15 +1125,15 @@ msgstr "Please {0} your document<0/>\"{documentName}\""
|
||||
msgid "Please {action} your document {documentName}"
|
||||
msgstr "Please {action} your document {documentName}"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:111
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:99
|
||||
msgid "Please {recipientActionVerb} this document"
|
||||
msgstr "Please {recipientActionVerb} this document"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:125
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:113
|
||||
msgid "Please {recipientActionVerb} this document created by your direct template"
|
||||
msgstr "Please {recipientActionVerb} this document created by your direct template"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:117
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:105
|
||||
msgid "Please {recipientActionVerb} your document"
|
||||
msgstr "Please {recipientActionVerb} your document"
|
||||
|
||||
@ -1162,24 +1180,28 @@ msgid "Recipient"
|
||||
msgstr "Recipient"
|
||||
|
||||
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:39
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:257
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:269
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:291
|
||||
msgid "Recipient action authentication"
|
||||
msgstr "Recipient action authentication"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:89
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:129
|
||||
msgid "Recipient removed email"
|
||||
msgstr "Recipient removed email"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:50
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:51
|
||||
msgid "Recipient signed email"
|
||||
msgstr "Recipient signed email"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:90
|
||||
msgid "Recipient signing request email"
|
||||
msgstr "Recipient signing request email"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:531
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:538
|
||||
msgid "Red"
|
||||
msgstr "Red"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:371
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:383
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:461
|
||||
msgid "Redirect URL"
|
||||
msgstr "Redirect URL"
|
||||
@ -1212,7 +1234,7 @@ msgstr "Reminder: Please {recipientActionVerb} this document"
|
||||
msgid "Reminder: Please {recipientActionVerb} your document"
|
||||
msgstr "Reminder: Please {recipientActionVerb} your document"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1117
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1121
|
||||
msgid "Remove"
|
||||
msgstr "Remove"
|
||||
|
||||
@ -1240,11 +1262,11 @@ msgstr "Rest assured, your document is strictly confidential and will never be s
|
||||
msgid "Rows per page"
|
||||
msgstr "Rows per page"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:355
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx:356
|
||||
msgid "Save"
|
||||
msgstr "Save"
|
||||
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:893
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:897
|
||||
msgid "Save Template"
|
||||
msgstr "Save Template"
|
||||
|
||||
@ -1280,15 +1302,19 @@ msgstr "Send"
|
||||
msgid "Send Document"
|
||||
msgstr "Send Document"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:158
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:198
|
||||
msgid "Send document completed email"
|
||||
msgstr "Send document completed email"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:197
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:276
|
||||
msgid "Send document completed email to the owner"
|
||||
msgstr "Send document completed email to the owner"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:237
|
||||
msgid "Send document deleted email"
|
||||
msgstr "Send document deleted email"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:118
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:158
|
||||
msgid "Send document pending email"
|
||||
msgstr "Send document pending email"
|
||||
|
||||
@ -1296,11 +1322,15 @@ msgstr "Send document pending email"
|
||||
msgid "Send documents on behalf of the team using the email address"
|
||||
msgstr "Send documents on behalf of the team using the email address"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:79
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:119
|
||||
msgid "Send recipient removed email"
|
||||
msgstr "Send recipient removed email"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:40
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:41
|
||||
msgid "Send recipient signed email"
|
||||
msgstr "Send recipient signed email"
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:80
|
||||
msgid "Send recipient signing request email"
|
||||
msgstr "Send recipient signing request email"
|
||||
|
||||
@ -1333,11 +1363,11 @@ msgstr "Sign Document"
|
||||
msgid "Sign In"
|
||||
msgstr "Sign In"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:830
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:834
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:324
|
||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||
#: packages/ui/primitives/document-flow/types.ts:49
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:625
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:629
|
||||
msgid "Signature"
|
||||
msgstr "Signature"
|
||||
|
||||
@ -1361,8 +1391,8 @@ msgstr "Signers must have unique emails"
|
||||
msgid "Signing"
|
||||
msgstr "Signing"
|
||||
|
||||
#: packages/lib/server-only/document/send-completed-email.ts:114
|
||||
#: packages/lib/server-only/document/send-completed-email.ts:194
|
||||
#: packages/lib/server-only/document/send-completed-email.ts:119
|
||||
#: packages/lib/server-only/document/send-completed-email.ts:199
|
||||
msgid "Signing Complete!"
|
||||
msgstr "Signing Complete!"
|
||||
|
||||
@ -1416,9 +1446,9 @@ msgstr "Team email removed for {teamName} on Documenso"
|
||||
msgid "Template title"
|
||||
msgstr "Template title"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:960
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:964
|
||||
#: packages/ui/primitives/document-flow/types.ts:52
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:755
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:759
|
||||
msgid "Text"
|
||||
msgstr "Text"
|
||||
|
||||
@ -1510,7 +1540,7 @@ msgstr "This can be overriden by setting the authentication requirements directl
|
||||
msgid "This document can not be recovered, if you would like to dispute the reason for future documents please contact support."
|
||||
msgstr "This document can not be recovered, if you would like to dispute the reason for future documents please contact support."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:764
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:768
|
||||
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
|
||||
msgstr "This document has already been sent to this recipient. You can no longer edit this recipient."
|
||||
|
||||
@ -1526,15 +1556,19 @@ msgstr "This document was sent using <0>Documenso.</0>"
|
||||
msgid "This email confirms that you have rejected the document <0>\"{documentName}\"</0> sent by {documentOwnerName}."
|
||||
msgstr "This email confirms that you have rejected the document <0>\"{documentName}\"</0> sent by {documentOwnerName}."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:94
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:56
|
||||
msgid "This email is sent to the document owner when a recipient has signed the document."
|
||||
msgstr "This email is sent to the document owner when a recipient has signed the document."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:134
|
||||
msgid "This email is sent to the recipient if they are removed from a pending document."
|
||||
msgstr "This email is sent to the recipient if they are removed from a pending document."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:55
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:95
|
||||
msgid "This email is sent to the recipient requesting them to sign the document."
|
||||
msgstr "This email is sent to the recipient requesting them to sign the document."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:133
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:173
|
||||
msgid "This email will be sent to the recipient who has just signed the document, if there are still other recipients who have not signed yet."
|
||||
msgstr "This email will be sent to the recipient who has just signed the document, if there are still other recipients who have not signed yet."
|
||||
|
||||
@ -1546,7 +1580,7 @@ msgstr "This field cannot be modified or deleted. When you share this template's
|
||||
msgid "This is how the document will reach the recipients once the document is ready for signing."
|
||||
msgstr "This is how the document will reach the recipients once the document is ready for signing."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1097
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1101
|
||||
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
|
||||
msgstr "This recipient can no longer be modified as they have signed a field, or completed the document."
|
||||
|
||||
@ -1554,29 +1588,33 @@ msgstr "This recipient can no longer be modified as they have signed a field, or
|
||||
msgid "This signer has already signed the document."
|
||||
msgstr "This signer has already signed the document."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:212
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:252
|
||||
msgid "This will be sent to all recipients if a pending document has been deleted."
|
||||
msgstr "This will be sent to all recipients if a pending document has been deleted."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:173
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:213
|
||||
msgid "This will be sent to all recipients once the document has been fully completed."
|
||||
msgstr "This will be sent to all recipients once the document has been fully completed."
|
||||
|
||||
#: packages/ui/components/document/document-email-checkboxes.tsx:291
|
||||
msgid "This will be sent to the document owner once the document has been fully completed."
|
||||
msgstr "This will be sent to the document owner once the document has been fully completed."
|
||||
|
||||
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:48
|
||||
msgid "This will override any global settings."
|
||||
msgstr "This will override any global settings."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:347
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:359
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx:438
|
||||
msgid "Time Zone"
|
||||
msgstr "Time Zone"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:155
|
||||
#: packages/ui/primitives/document-flow/add-settings.tsx:166
|
||||
msgid "Title"
|
||||
msgstr "Title"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1080
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:873
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1084
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:877
|
||||
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."
|
||||
|
||||
@ -1592,7 +1630,7 @@ msgstr "Update the role and add fields as required for the direct recipient. The
|
||||
msgid "Upgrade"
|
||||
msgstr "Upgrade"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:509
|
||||
#: packages/ui/primitives/signature-pad/signature-pad.tsx:516
|
||||
msgid "Upload Signature"
|
||||
msgstr "Upload Signature"
|
||||
|
||||
@ -1721,7 +1759,7 @@ msgstr "You have been invited to join {0} on Documenso"
|
||||
msgid "You have been invited to join the following team"
|
||||
msgstr "You have been invited to join the following team"
|
||||
|
||||
#: packages/lib/server-only/recipient/set-recipients-for-document.ts:327
|
||||
#: packages/lib/server-only/recipient/set-recipients-for-document.ts:337
|
||||
msgid "You have been removed from a document"
|
||||
msgstr "You have been removed from a document"
|
||||
|
||||
@ -1729,7 +1767,7 @@ msgstr "You have been removed from a document"
|
||||
msgid "You have been requested to take ownership of team {0} on Documenso"
|
||||
msgstr "You have been requested to take ownership of team {0} on Documenso"
|
||||
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.ts:115
|
||||
#: packages/lib/jobs/definitions/emails/send-signing-email.handler.ts:103
|
||||
#: packages/lib/server-only/document/resend-document.tsx:125
|
||||
msgid "You have initiated the document {0} that requires you to {recipientActionVerb} it."
|
||||
msgstr "You have initiated the document {0} that requires you to {recipientActionVerb} it."
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user