Compare commits

...

9 Commits

31 changed files with 628 additions and 313 deletions

View File

@ -2,8 +2,8 @@ declare namespace NodeJS {
export interface ProcessEnv {
NEXT_PUBLIC_WEBAPP_URL?: string;
NEXT_PUBLIC_MARKETING_URL?: string;
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?:string;
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?: string;
NEXT_PRIVATE_DATABASE_URL: string;
NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID: string;

View File

@ -2,7 +2,7 @@ declare namespace NodeJS {
export interface ProcessEnv {
NEXT_PUBLIC_WEBAPP_URL?: string;
NEXT_PUBLIC_MARKETING_URL?: string;
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?:string;
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?: string;
NEXT_PRIVATE_DATABASE_URL: string;

View File

@ -349,11 +349,13 @@ export const EditDocumentForm = ({
<AddSignersFormPartial
key={recipients.length}
documentFlow={documentFlow.signers}
document={document}
recipients={recipients}
fields={fields}
isDocumentEnterprise={isDocumentEnterprise}
onSubmit={onAddSignersFormSubmit}
isDocumentPdfLoaded={isDocumentPdfLoaded}
teamId={team?.id}
/>
<AddFieldsFormPartial

View File

@ -266,6 +266,7 @@ export const EditTemplateForm = ({
onSubmit={onAddTemplatePlaceholderFormSubmit}
isEnterprise={isEnterprise}
isDocumentPdfLoaded={isDocumentPdfLoaded}
template={template}
/>
<AddTemplateFieldsFormPartial

View File

@ -1,5 +1,8 @@
import { z } from 'zod';
export const ZBaseEmbedDataSchema = z.object({
css: z.string().optional().transform(value => value || undefined),
css: z
.string()
.optional()
.transform((value) => value || undefined),
});

View File

@ -18,15 +18,18 @@ export const EmbedDocumentCompleted = ({ name, signature }: EmbedDocumentComplet
<div className="mt-8 w-full max-w-md">
<SigningCard3D
className='w-full mx-auto'
className="mx-auto w-full"
name={name || 'Documenso'}
signature={signature}
signingCelebrationImage={signingCelebration}
/>
</div>
<p className="mt-8 max-w-[50ch] text-center text-muted-foreground text-sm">
<Trans>The document is now completed, please follow any instructions provided within the parent application.</Trans>
<p className="text-muted-foreground mt-8 max-w-[50ch] text-center text-sm">
<Trans>
The document is now completed, please follow any instructions provided within the parent
application.
</Trans>
</p>
</div>
);

View File

@ -6,6 +6,7 @@ import { useSearchParams } from 'next/navigation';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
import { DateTime } from 'luxon';
import { useThrottleFn } from '@documenso/lib/client-only/hooks/use-throttle-fn';
@ -14,7 +15,7 @@ import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones';
import { validateFieldsInserted } from '@documenso/lib/utils/fields';
import type { DocumentMeta, Recipient, TemplateMeta } from '@documenso/prisma/client';
import { FieldType, type DocumentData, type Field } from '@documenso/prisma/client';
import { type DocumentData, type Field, FieldType } from '@documenso/prisma/client';
import { trpc } from '@documenso/trpc/react';
import type {
TRemovedSignedFieldWithTokenMutationSchema,
@ -34,7 +35,6 @@ import type { DirectTemplateLocalField } from '~/app/(recipient)/d/[token]/sign-
import { useRequiredSigningContext } from '~/app/(signing)/sign/[token]/provider';
import { Logo } from '~/components/branding/logo';
import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
import { EmbedClientLoading } from '../../client-loading';
import { EmbedDocumentCompleted } from '../../completed';
import { EmbedDocumentFields } from '../../document-fields';
@ -307,7 +307,7 @@ export const EmbedDirectTemplateClientPage = ({
<div className="relative mx-auto flex min-h-[100dvh] max-w-screen-lg flex-col items-center justify-center p-6">
{(!hasFinishedInit || !hasDocumentLoaded) && <EmbedClientLoading />}
<div className="relative flex flex-col md:flex-row w-full gap-x-6 gap-y-12">
<div className="relative flex w-full flex-col gap-x-6 gap-y-12 md:flex-row">
{/* Viewer */}
<div className="flex-1">
<LazyPDFViewer
@ -318,26 +318,26 @@ export const EmbedDirectTemplateClientPage = ({
{/* Widget */}
<div
className="group/document-widget fixed md:sticky md:top-4 left-0 w-full bottom-8 px-6 md:px-0 z-50 md:z-auto md:w-[350px] flex-shrink-0 h-fit"
className="group/document-widget fixed bottom-8 left-0 z-50 h-fit w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0"
data-expanded={isExpanded || undefined}
>
<div className="w-full border-border bg-widget flex md:min-h-[min(calc(100dvh-2rem),48rem)] h-fit flex-col rounded-xl border px-4 py-4 md:py-6">
<div className="border-border bg-widget flex h-fit w-full flex-col rounded-xl border px-4 py-4 md:min-h-[min(calc(100dvh-2rem),48rem)] md:py-6">
{/* Header */}
<div>
<div className="flex items-center justify-between gap-x-2">
<h3 className="text-foreground text-xl md:text-2xl font-semibold">
<h3 className="text-foreground text-xl font-semibold md:text-2xl">
<Trans>Sign document</Trans>
</h3>
<Button variant="outline" className="h-8 w-8 p-0 md:hidden">
{isExpanded ? (
<LucideChevronDown
className="h-5 w-5 text-muted-foreground"
className="text-muted-foreground h-5 w-5"
onClick={() => setIsExpanded(false)}
/>
) : (
<LucideChevronUp
className="h-5 w-5 text-muted-foreground"
className="text-muted-foreground h-5 w-5"
onClick={() => setIsExpanded(true)}
/>
)}
@ -354,7 +354,7 @@ export const EmbedDirectTemplateClientPage = ({
</div>
{/* Form */}
<div className="-mx-2 px-2 hidden group-data-[expanded]/document-widget:block md:block">
<div className="-mx-2 hidden px-2 group-data-[expanded]/document-widget:block md:block">
<div className="flex flex-1 flex-col gap-y-4">
<div>
<Label htmlFor="full-name">
@ -408,9 +408,9 @@ export const EmbedDirectTemplateClientPage = ({
</div>
</div>
<div className="flex-1 hidden group-data-[expanded]/document-widget:block md:block" />
<div className="hidden flex-1 group-data-[expanded]/document-widget:block md:block" />
<div className="w-full grid-cols-2 items-center mt-4 hidden group-data-[expanded]/document-widget:grid md:grid">
<div className="mt-4 hidden w-full grid-cols-2 items-center group-data-[expanded]/document-widget:grid md:grid">
{pendingFields.length > 0 ? (
<Button className="col-start-2" onClick={() => onNextFieldClick()}>
<Trans>Next</Trans>

View File

@ -1,3 +1,3 @@
export default function EmbedDirectTemplateNotFound() {
return <div>Not Found</div>
return <div>Not Found</div>;
}

View File

@ -73,11 +73,7 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem
const fields = template.Field.filter((field) => field.recipientId === directTemplateRecipientId);
return (
<SigningProvider
email={user?.email}
fullName={user?.name}
signature={user?.signature}
>
<SigningProvider email={user?.email} fullName={user?.name} signature={user?.signature}>
<DocumentAuthProvider
documentAuthOptions={template.authOptions}
recipient={recipient}

View File

@ -13,7 +13,7 @@ import {
ZTextFieldMeta,
} from '@documenso/lib/types/field-meta';
import type { DocumentMeta, Recipient, TemplateMeta } from '@documenso/prisma/client';
import { FieldType, type Field } from '@documenso/prisma/client';
import { type Field, FieldType } from '@documenso/prisma/client';
import type { FieldWithSignatureAndFieldMeta } from '@documenso/prisma/types/field-with-signature-and-fieldmeta';
import type {
TRemovedSignedFieldWithTokenMutationSchema,

View File

@ -1,5 +1,7 @@
export const EmbedPaywall = () => {
return <div>
<h1>Paywall</h1>
</div>
}
return (
<div>
<h1>Paywall</h1>
</div>
);
};

View File

@ -1,15 +1,17 @@
'use client';
import { useEffect, useState } from 'react';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
import { useThrottleFn } from '@documenso/lib/client-only/hooks/use-throttle-fn';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { validateFieldsInserted } from '@documenso/lib/utils/fields';
import type { DocumentMeta, Recipient, TemplateMeta } from '@documenso/prisma/client';
import { type DocumentData, type Field } from '@documenso/prisma/client';
import { trpc } from '@documenso/trpc/react';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { useEffect, useState } from 'react';
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
@ -20,9 +22,9 @@ import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
import { useRequiredSigningContext } from '~/app/(signing)/sign/[token]/provider';
import { Logo } from '~/components/branding/logo';
import { EmbedClientLoading } from '../../client-loading';
import { EmbedDocumentCompleted } from '../../completed';
import { EmbedDocumentFields } from '../../document-fields';
@ -185,7 +187,7 @@ export const EmbedSignDocumentClientPage = ({
<div className="relative mx-auto flex min-h-[100dvh] max-w-screen-lg flex-col items-center justify-center p-6">
{(!hasFinishedInit || !hasDocumentLoaded) && <EmbedClientLoading />}
<div className="relative flex flex-col md:flex-row w-full gap-x-6 gap-y-12">
<div className="relative flex w-full flex-col gap-x-6 gap-y-12 md:flex-row">
{/* Viewer */}
<div className="flex-1">
<LazyPDFViewer
@ -196,26 +198,26 @@ export const EmbedSignDocumentClientPage = ({
{/* Widget */}
<div
className="group/document-widget fixed md:sticky md:top-4 left-0 w-full bottom-8 px-6 md:px-0 z-50 md:z-auto md:w-[350px] flex-shrink-0 h-fit"
className="group/document-widget fixed bottom-8 left-0 z-50 h-fit w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0"
data-expanded={isExpanded || undefined}
>
<div className="w-full border-border bg-widget flex flex-col rounded-xl border px-4 py-4 md:py-6">
<div className="border-border bg-widget flex w-full flex-col rounded-xl border px-4 py-4 md:py-6">
{/* Header */}
<div>
<div className="flex items-center justify-between gap-x-2">
<h3 className="text-foreground text-xl md:text-2xl font-semibold">
<h3 className="text-foreground text-xl font-semibold md:text-2xl">
<Trans>Sign document</Trans>
</h3>
<Button variant="outline" className="h-8 w-8 p-0 md:hidden">
{isExpanded ? (
<LucideChevronDown
className="h-5 w-5 text-muted-foreground"
className="text-muted-foreground h-5 w-5"
onClick={() => setIsExpanded(false)}
/>
) : (
<LucideChevronUp
className="h-5 w-5 text-muted-foreground"
className="text-muted-foreground h-5 w-5"
onClick={() => setIsExpanded(true)}
/>
)}
@ -232,7 +234,7 @@ export const EmbedSignDocumentClientPage = ({
</div>
{/* Form */}
<div className="-mx-2 px-2 hidden group-data-[expanded]/document-widget:block md:block">
<div className="-mx-2 hidden px-2 group-data-[expanded]/document-widget:block md:block">
<div className="flex flex-1 flex-col gap-y-4">
<div>
<Label htmlFor="full-name">
@ -285,9 +287,9 @@ export const EmbedSignDocumentClientPage = ({
</div>
</div>
<div className="flex-1 hidden group-data-[expanded]/document-widget:block md:block" />
<div className="hidden flex-1 group-data-[expanded]/document-widget:block md:block" />
<div className="w-full grid-cols-2 items-center mt-4 hidden group-data-[expanded]/document-widget:grid md:grid">
<div className="mt-4 hidden w-full grid-cols-2 items-center group-data-[expanded]/document-widget:grid md:grid">
{pendingFields.length > 0 ? (
<Button className="col-start-2" onClick={() => onNextFieldClick()}>
<Trans>Next</Trans>

View File

@ -1,3 +1,3 @@
export default function EmbedDirectTemplateNotFound() {
return <div>Not Found</div>
return <div>Not Found</div>;
}

View File

@ -4,8 +4,12 @@ import { match } from 'ts-pattern';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { getServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
import { DocumentStatus } from '@documenso/prisma/client';
import { DocumentAuthProvider } from '~/app/(signing)/sign/[token]/document-auth-provider';
import { SigningProvider } from '~/app/(signing)/sign/[token]/provider';
@ -13,10 +17,6 @@ import { SigningProvider } from '~/app/(signing)/sign/[token]/provider';
import { EmbedAuthenticateView } from '../../authenticate';
import { EmbedPaywall } from '../../paywall';
import { EmbedSignDocumentClientPage } from './client';
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { DocumentStatus } from '@documenso/prisma/client';
export type EmbedSignDocumentPageProps = {
params: {
@ -66,7 +66,12 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen
.exhaustive();
if (!isAccessAuthValid) {
return <EmbedAuthenticateView email={user?.email || recipient.email} returnTo={`/embed/direct/${token}`} />;
return (
<EmbedAuthenticateView
email={user?.email || recipient.email}
returnTo={`/embed/direct/${token}`}
/>
);
}
return (

View File

@ -81,7 +81,7 @@ async function middleware(req: NextRequest): Promise<NextResponse> {
// Allow third parties to iframe the document.
res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.headers.set('Access-Control-Allow-Origin', '*');
res.headers.set('Content-Security-Policy', "frame-ancestors *");
res.headers.set('Content-Security-Policy', 'frame-ancestors *');
res.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
res.headers.set('X-Content-Type-Options', 'nosniff');
res.headers.set('X-Frame-Options', 'ALLOW-ALL');

View File

@ -3,19 +3,19 @@ import { useCallback, useRef, useState } from 'react';
type ThrottleOptions = {
leading?: boolean;
trailing?: boolean;
}
};
export function useThrottleFn<T extends (...args: unknown[]) => unknown>(
fn: T,
ms = 500,
options: ThrottleOptions = {}
options: ThrottleOptions = {},
): [(...args: Parameters<T>) => void, boolean, () => void] {
const [isThrottling, setIsThrottling] = useState(false);
const $isThrottling = useRef(false);
const $timeout = useRef<NodeJS.Timeout | null>(null);
const $lastArgs = useRef<Parameters<T> | null>(null);
const { leading = true, trailing = true } = options;
const $setIsThrottling = useCallback((value: boolean) => {
@ -44,7 +44,7 @@ export function useThrottleFn<T extends (...args: unknown[]) => unknown>(
$lastArgs.current = args;
}
},
[fn, ms, leading, trailing, $setIsThrottling]
[fn, ms, leading, trailing, $setIsThrottling],
);
const cancel = useCallback(() => {

View File

@ -5,7 +5,8 @@ export const APP_DOCUMENT_UPLOAD_SIZE_LIMIT =
export const NEXT_PUBLIC_WEBAPP_URL = () => env('NEXT_PUBLIC_WEBAPP_URL');
export const NEXT_PUBLIC_MARKETING_URL = () => env('NEXT_PUBLIC_MARKETING_URL');
export const NEXT_PRIVATE_INTERNAL_WEBAPP_URL = process.env.NEXT_PRIVATE_INTERNAL_WEBAPP_URL ?? NEXT_PUBLIC_WEBAPP_URL();
export const NEXT_PRIVATE_INTERNAL_WEBAPP_URL =
process.env.NEXT_PRIVATE_INTERNAL_WEBAPP_URL ?? NEXT_PUBLIC_WEBAPP_URL();
export const IS_APP_MARKETING = process.env.NEXT_PUBLIC_PROJECT === 'marketing';
export const IS_APP_WEB = process.env.NEXT_PUBLIC_PROJECT === 'web';

View File

@ -5,7 +5,11 @@ import { getToken } from 'next-auth/jwt';
import { LOCAL_FEATURE_FLAGS } from '@documenso/lib/constants/feature-flags';
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client';
import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL, NEXT_PRIVATE_INTERNAL_WEBAPP_URL } from '../../constants/app';
import {
NEXT_PRIVATE_INTERNAL_WEBAPP_URL,
NEXT_PUBLIC_MARKETING_URL,
NEXT_PUBLIC_WEBAPP_URL,
} from '../../constants/app';
import { extractDistinctUserId, mapJwtToFlagProperties } from './get';
/**

View File

@ -7,7 +7,11 @@ import { getToken } from 'next-auth/jwt';
import { LOCAL_FEATURE_FLAGS, extractPostHogConfig } from '@documenso/lib/constants/feature-flags';
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client';
import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL, NEXT_PRIVATE_INTERNAL_WEBAPP_URL } from '../../constants/app';
import {
NEXT_PRIVATE_INTERNAL_WEBAPP_URL,
NEXT_PUBLIC_MARKETING_URL,
NEXT_PUBLIC_WEBAPP_URL,
} from '../../constants/app';
/**
* Evaluate a single feature flag based on the current user if possible.
@ -67,7 +71,7 @@ export default async function handleFeatureFlagGet(req: Request) {
if (origin.startsWith(NEXT_PUBLIC_MARKETING_URL() ?? 'http://localhost:3001')) {
res.headers.set('Access-Control-Allow-Origin', origin);
}
if (origin.startsWith(NEXT_PRIVATE_INTERNAL_WEBAPP_URL ?? 'http://localhost:3000')) {
res.headers.set('Access-Control-Allow-Origin', origin);
}

View File

@ -0,0 +1,54 @@
import { prisma } from '@documenso/prisma';
import { SendStatus } from '@documenso/prisma/client';
export type DeleteRecipientForTemplateOptions = {
templateId: number;
recipientId: number;
userId: number;
teamId?: number;
};
export const deleteRecipientFromTemplate = async ({
templateId,
recipientId,
userId,
teamId,
}: DeleteRecipientForTemplateOptions) => {
const recipient = await prisma.recipient.findFirst({
where: {
id: recipientId,
Template: {
id: templateId,
...(teamId
? {
team: {
id: teamId,
members: {
some: {
userId,
},
},
},
}
: {
userId,
teamId: null,
}),
},
},
});
if (!recipient) {
throw new Error('Recipient not found');
}
if (recipient.sendStatus !== SendStatus.NOT_SENT) {
throw new Error('Can not delete a recipient that has already been sent a document');
}
return await prisma.recipient.delete({
where: {
id: recipient.id,
},
});
};

View File

@ -1,12 +1,12 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-07-24 13:01+1000\n"
"POT-Creation-Date: 2024-08-22 05:16+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: de\n"
"Project-Id-Version: documenso-app\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-09-05 06:04\n"
"Last-Translator: \n"
@ -82,19 +82,19 @@ msgstr "Weitere Option hinzufügen"
msgid "Add another value"
msgstr "Weiteren Wert hinzufügen"
#: packages/ui/primitives/document-flow/add-signers.tsx:359
#: packages/ui/primitives/document-flow/add-signers.tsx:459
msgid "Add myself"
msgstr "Mich selbst hinzufügen"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:369
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:450
msgid "Add Myself"
msgstr "Mich hinzufügen"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:355
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:436
msgid "Add Placeholder Recipient"
msgstr "Platzhalterempfänger hinzufügen"
#: packages/ui/primitives/document-flow/add-signers.tsx:348
#: packages/ui/primitives/document-flow/add-signers.tsx:448
msgid "Add Signer"
msgstr "Unterzeichner hinzufügen"
@ -154,8 +154,8 @@ msgid "Cancel"
msgstr "Abbrechen"
#: packages/ui/primitives/document-flow/add-signers.tsx:164
msgid "Cannot remove signer"
msgstr "Unterzeichner kann nicht entfernt werden"
#~ msgid "Cannot remove signer"
#~ msgstr "Unterzeichner kann nicht entfernt werden"
#: packages/lib/constants/recipient-roles.ts:17
msgid "Cc"
@ -230,7 +230,7 @@ msgstr "Datum"
msgid "Date Format"
msgstr "Datumsformat"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:312
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:393
msgid "Direct link receiver"
msgstr "Empfänger des direkten Links"
@ -263,11 +263,11 @@ msgstr "Dropdown-Optionen"
#: packages/ui/primitives/document-flow/add-fields.tsx:776
#: packages/ui/primitives/document-flow/add-signature.tsx:272
#: packages/ui/primitives/document-flow/add-signers.tsx:232
#: packages/ui/primitives/document-flow/add-signers.tsx:239
#: packages/ui/primitives/document-flow/add-signers.tsx:330
#: packages/ui/primitives/document-flow/add-signers.tsx:337
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:210
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:217
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:296
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:303
msgid "Email"
msgstr "E-Mail"
@ -378,10 +378,10 @@ msgstr "Min"
#: packages/ui/primitives/document-flow/add-fields.tsx:802
#: packages/ui/primitives/document-flow/add-signature.tsx:298
#: packages/ui/primitives/document-flow/add-signers.tsx:265
#: packages/ui/primitives/document-flow/add-signers.tsx:364
#: packages/ui/primitives/template-flow/add-template-fields.tsx:658
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:245
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:251
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:327
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:333
msgid "Name"
msgstr "Name"
@ -560,8 +560,8 @@ msgstr "Unterschriftenkarte teilen"
msgid "Share the Link"
msgstr "Link teilen"
#: packages/ui/primitives/document-flow/add-signers.tsx:377
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:387
#: packages/ui/primitives/document-flow/add-signers.tsx:477
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:468
msgid "Show advanced settings"
msgstr "Erweiterte Einstellungen anzeigen"
@ -686,13 +686,13 @@ msgstr "Dieses Dokument wurde bereits an diesen Empfänger gesendet. Sie können
msgid "This document is password protected. Please enter the password to view the document."
msgstr "Dieses Dokument ist durch ein Passwort geschützt. Bitte geben Sie das Passwort ein, um das Dokument anzusehen."
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:315
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:396
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
msgstr "Dieses Feld kann nicht geändert oder gelöscht werden. Wenn Sie den direkten Link dieser Vorlage teilen oder zu Ihrem öffentlichen Profil hinzufügen, kann jeder, der darauf zugreift, seinen Namen und seine E-Mail-Adresse eingeben und die ihm zugewiesenen Felder ausfüllen."
#: packages/ui/primitives/document-flow/add-signers.tsx:165
msgid "This signer has already received the document."
msgstr "Dieser Unterzeichner hat das Dokument bereits erhalten."
#~ msgid "This signer has already received the document."
#~ msgstr "Dieser Unterzeichner hat das Dokument bereits erhalten."
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:48
msgid "This will override any global settings."

File diff suppressed because one or more lines are too long

View File

@ -1,38 +1,33 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-07-24 13:01+1000\n"
"POT-Creation-Date: 2024-08-22 05:16+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: de\n"
"Project-Id-Version: documenso-app\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-09-05 06:04\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: documenso-app\n"
"X-Crowdin-Project-ID: 694691\n"
"X-Crowdin-Language: de\n"
"X-Crowdin-File: marketing.po\n"
"X-Crowdin-File-ID: 6\n"
"Language-Team: \n"
"Plural-Forms: \n"
#: apps/marketing/src/app/(marketing)/blog/page.tsx:45
msgid "{0}"
msgstr "{0}"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:100
msgid "5 standard documents per month"
msgstr "5 Standarddokumente pro Monat"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:198
msgid "5 Users Included"
msgstr "5 Benutzer inbegriffen"
msgstr ""
#: apps/marketing/src/components/(marketing)/faster-smarter-beautiful-bento.tsx:34
msgid "A 10x better signing experience."
msgstr "Eine 10x bessere Signaturerfahrung."
msgstr ""
#: apps/marketing/src/app/(marketing)/singleplayer/client.tsx:51
msgid "Add document"
@ -40,142 +35,138 @@ msgstr "Dokument hinzufügen"
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:201
msgid "Add More Users for {0}"
msgstr "Mehr Benutzer hinzufügen für {0}"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:165
msgid "All our metrics, finances, and learnings are public. We believe in transparency and want to share our journey with you. You can read more about why here: <0>Announcing Open Metrics</0>"
msgstr "Alle unsere Kennzahlen, Finanzen und Erkenntnisse sind öffentlich. Wir glauben an Transparenz und möchten unsere Reise mit Ihnen teilen. Mehr erfahren Sie hier: <0>Ankündigung Offene Kennzahlen</0>"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/funding-raised.tsx:58
#: apps/marketing/src/app/(marketing)/open/funding-raised.tsx:65
msgid "Amount Raised"
msgstr "Erhobener Betrag"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:145
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:189
msgid "API Access"
msgstr "API-Zugriff"
msgstr ""
#: apps/marketing/src/components/(marketing)/faster-smarter-beautiful-bento.tsx:67
msgid "Beautiful."
msgstr "Schön."
msgstr ""
#: apps/marketing/src/components/(marketing)/faster-smarter-beautiful-bento.tsx:69
msgid "Because signing should be celebrated. Thats why we care about the smallest detail in our product."
msgstr "Weil Unterschriften gefeiert werden sollten. Deshalb kümmern wir uns um jedes kleinste Detail in unserem Produkt."
msgstr ""
#: apps/marketing/src/components/(marketing)/footer.tsx:35
#: apps/marketing/src/components/(marketing)/header.tsx:57
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:36
msgid "Blog"
msgstr "Blog"
msgstr ""
#: apps/marketing/src/components/(marketing)/open-build-template-bento.tsx:64
msgid "Build on top."
msgstr "Aufbauen oben drauf."
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:163
msgid "Can I use Documenso commercially?"
msgstr "Kann ich Documenso kommerziell nutzen?"
msgstr ""
#: apps/marketing/src/components/(marketing)/footer.tsx:42
msgid "Careers"
msgstr "Karrieren"
msgstr ""
#: apps/marketing/src/components/(marketing)/footer.tsx:36
msgid "Changelog"
msgstr "Änderungsprotokoll"
msgstr ""
#: apps/marketing/src/components/(marketing)/open-build-template-bento.tsx:85
msgid "Choose a template from the community app store. Or submit your own template for others to use."
msgstr "Wählen Sie eine Vorlage aus dem Community-App-Store. Oder reichen Sie Ihre eigene Vorlage ein, damit andere sie benutzen können."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:219
msgid "Community"
msgstr "Gemeinschaft"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/monthly-completed-documents-chart.tsx:55
msgid "Completed Documents"
msgstr "Fertige Dokumente"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/monthly-completed-documents-chart.tsx:33
msgid "Completed Documents per Month"
msgstr "Fertige Dokumente pro Monat"
msgstr ""
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:65
msgid "Connections"
msgstr "Verbindungen"
msgstr ""
#: apps/marketing/src/components/(marketing)/enterprise.tsx:35
msgid "Contact Us"
msgstr "Kontaktiere uns"
msgstr ""
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:67
msgid "Create connections and automations with Zapier and more to integrate with your favorite tools."
msgstr "Erstellen Sie Verbindungen und Automatisierungen mit Zapier und mehr, um sich mit Ihren Lieblingstools zu integrieren."
msgstr ""
#: apps/marketing/src/components/(marketing)/call-to-action.tsx:23
msgid "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
msgstr "Erstellen Sie Ihr Konto und beginnen Sie mit der Nutzung modernster Dokumentensignaturen. Offene und schöne Signaturen sind zum Greifen nah."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/tooltip.tsx:35
msgid "Customers with an Active Subscriptions."
msgstr "Kunden mit einer aktiven Abonnements."
msgstr ""
#: apps/marketing/src/components/(marketing)/open-build-template-bento.tsx:33
msgid "Customise and expand."
msgstr "Anpassen und erweitern."
msgstr ""
#: apps/marketing/src/components/(marketing)/footer.tsx:38
msgid "Design"
msgstr "Design"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:44
msgid "Designed for every stage of your journey."
msgstr "Entwickelt für jede Phase Ihrer Reise."
msgstr ""
#: apps/marketing/src/components/(marketing)/carousel.tsx:40
msgid "Direct Link"
msgstr "Direktlink"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:181
msgid "Documenso is a community effort to create an open and vibrant ecosystem around a tool, everybody is free to use and adapt. By being truly open we want to create trusted infrastructure for the future of the internet."
msgstr "Documenso ist eine Gemeinschaftsanstrengung, um ein offenes und lebendiges Ökosystem um ein Werkzeug zu schaffen, das jeder frei nutzen und anpassen kann. Indem wir wirklich offen sind, wollen wir vertrauenswürdige Infrastruktur für die Zukunft des Internets schaffen."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/typefully.tsx:28
msgid "Documenso on X"
msgstr "Documenso auf X"
msgstr ""
#: apps/marketing/src/components/(marketing)/hero.tsx:104
msgid "Document signing,<0/>finally open source."
msgstr "Unterschriften,<0/>endlich Open Source."
msgstr ""
#: apps/marketing/src/components/(marketing)/footer.tsx:33
#: apps/marketing/src/components/(marketing)/header.tsx:50
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:28
msgid "Documentation"
msgstr "Dokumentation"
msgstr ""
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:110
msgid "Easily embed Documenso into your product. Simply copy and paste our react widget into your application."
msgstr "Betten Sie Documenso ganz einfach in Ihr Produkt ein. Kopieren und fügen Sie einfach unser React-Widget in Ihre Anwendung ein."
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:42
#~ msgid "Easy Sharing (Soon)."
#~ msgstr "Easy Sharing (Soon)."
msgstr ""
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:46
msgid "Easy Sharing."
msgstr "Einfaches Teilen."
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:148
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:192
msgid "Email and Discord Support"
msgstr "E-Mail- und Discord-Support"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/team-members.tsx:43
msgid "Engagement"
msgstr "Beteiligung"
msgstr ""
#: apps/marketing/src/app/(marketing)/singleplayer/client.tsx:64
msgid "Enter your details."
@ -183,43 +174,43 @@ msgstr "Geben Sie Ihre Details ein."
#: apps/marketing/src/components/(marketing)/enterprise.tsx:16
msgid "Enterprise Compliance, License or Technical Needs?"
msgstr "Enterprise-Konformität, Lizenz- oder technische Bedürfnisse?"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:128
msgid "Everything you need for a great signing experience."
msgstr "Alles, was Sie für ein großartiges Signaturerlebnis benötigen."
msgstr ""
#: apps/marketing/src/components/(marketing)/faster-smarter-beautiful-bento.tsx:45
msgid "Fast."
msgstr "Schnell."
msgstr ""
#: apps/marketing/src/components/(marketing)/faster-smarter-beautiful-bento.tsx:36
msgid "Faster, smarter and more beautiful."
msgstr "Schneller, intelligenter und schöner."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:210
msgid "Finances"
msgstr "Finanzen"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/typefully.tsx:38
msgid "Follow us on X"
msgstr "Folgen Sie uns auf X"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:172
msgid "For companies looking to scale across multiple teams."
msgstr "Für Unternehmen, die über mehrere Teams skalieren möchten."
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:85
msgid "For small teams and individuals with basic needs."
msgstr "Für kleine Teams und Einzelpersonen mit grundlegenden Bedürfnissen."
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:80
msgid "Free"
msgstr "Kostenlos"
msgstr ""
#: apps/marketing/src/app/(marketing)/blog/page.tsx:26
msgid "From the blog"
msgstr "Aus dem Blog"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/data.ts:9
#: apps/marketing/src/app/(marketing)/open/data.ts:17
@ -227,227 +218,222 @@ msgstr "Aus dem Blog"
#: apps/marketing/src/app/(marketing)/open/data.ts:41
#: apps/marketing/src/app/(marketing)/open/data.ts:49
msgid "Full-Time"
msgstr "Vollzeit"
msgstr ""
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:87
msgid "Get paid (Soon)."
msgstr "Lassen Sie sich bezahlen (Bald)."
msgstr ""
#: apps/marketing/src/components/(marketing)/call-to-action.tsx:31
msgid "Get started"
msgstr "Loslegen"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:75
msgid "Get Started"
msgstr "Loslegen"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:47
msgid "Get started today."
msgstr "Fangen Sie heute an."
msgstr ""
#: apps/marketing/src/app/(marketing)/blog/page.tsx:30
msgid "Get the latest news from Documenso, including product updates, team announcements and more!"
msgstr "Erhalten Sie die neuesten Nachrichten von Documenso, einschließlich Produkt-Updates, Team-Ankündigungen und mehr!"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:233
msgid "GitHub: Total Merged PRs"
msgstr "GitHub: Gesamte PRs zusammengeführt"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:251
msgid "GitHub: Total Open Issues"
msgstr "GitHub: Gesamte offene Issues"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:225
msgid "GitHub: Total Stars"
msgstr "GitHub: Gesamtanzahl Sterne"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/salary-bands.tsx:23
msgid "Global Salary Bands"
msgstr "Globale Gehaltsbänder"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:261
msgid "Growth"
msgstr "Wachstum"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:134
msgid "How can I contribute?"
msgstr "Wie kann ich beitragen?"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:105
msgid "How do you handle my data?"
msgstr "Wie gehen Sie mit meinen Daten um?"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:118
msgid "Individual"
msgstr "Einzelperson"
msgstr ""
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:89
msgid "Integrated payments with Stripe so you dont have to worry about getting paid."
msgstr "Integrierte Zahlungen mit Stripe, sodass Sie sich keine Sorgen ums Bezahlen machen müssen."
msgstr ""
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:35
msgid "Integrates with all your favourite tools."
msgstr "Integriert sich mit all Ihren Lieblingstools."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:289
msgid "Is there more?"
msgstr "Gibt es mehr?"
msgstr ""
#: apps/marketing/src/components/(marketing)/open-build-template-bento.tsx:44
msgid "Its up to you. Either clone our repository or rely on our easy to use hosting solution."
msgstr "Es liegt an Ihnen. Entweder klonen Sie unser Repository oder nutzen unsere einfach zu bedienende Hosting-Lösung."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/team-members.tsx:49
msgid "Join Date"
msgstr "Eintrittsdatum"
msgstr ""
#: apps/marketing/src/components/(marketing)/call-to-action.tsx:19
msgid "Join the Open Signing Movement"
msgstr "Treten Sie der Open Signing-Bewegung bei"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/team-members.tsx:46
msgid "Location"
msgstr "Standort"
msgstr ""
#: apps/marketing/src/components/(marketing)/open-build-template-bento.tsx:66
msgid "Make it your own through advanced customization and adjustability."
msgstr "Machen Sie es zu Ihrem eigenen durch erweiterte Anpassung und Einstellbarkeit."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:199
msgid "Merged PR's"
msgstr "Zusammengeführte PRs"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:234
msgid "Merged PRs"
msgstr "Zusammengeführte PRs"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:40
msgid "Monthly"
msgstr "Monatlich"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/team-members.tsx:34
msgid "Name"
msgstr "Name"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/monthly-new-users-chart.tsx:30
#: apps/marketing/src/app/(marketing)/open/monthly-new-users-chart.tsx:43
#: apps/marketing/src/app/(marketing)/open/monthly-new-users-chart.tsx:52
msgid "New Users"
msgstr "Neue Benutzer"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:106
msgid "No credit card required"
msgstr "Keine Kreditkarte erforderlich"
msgstr ""
#: apps/marketing/src/components/(marketing)/callout.tsx:29
#: apps/marketing/src/components/(marketing)/hero.tsx:125
msgid "No Credit Card required"
msgstr "Keine Kreditkarte erforderlich"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:61
msgid "None of these work for you? Try self-hosting!"
msgstr "Keines dieser Angebote passt zu Ihnen? Versuchen Sie das Selbst-Hosting!"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:194
#: apps/marketing/src/app/(marketing)/open/page.tsx:252
msgid "Open Issues"
msgstr "Offene Issues"
msgstr ""
#: apps/marketing/src/components/(marketing)/open-build-template-bento.tsx:42
msgid "Open Source or Hosted."
msgstr "Open Source oder Hosted."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:161
#: apps/marketing/src/components/(marketing)/footer.tsx:37
#: apps/marketing/src/components/(marketing)/header.tsx:64
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:40
msgid "Open Startup"
msgstr "Offenes Startup"
msgstr ""
#: apps/marketing/src/components/(marketing)/footer.tsx:41
msgid "OSS Friends"
msgstr "OSS-Freunde"
msgstr ""
#: apps/marketing/src/components/(marketing)/faster-smarter-beautiful-bento.tsx:91
msgid "Our custom templates come with smart rules that can help you save time and energy."
msgstr "Unsere benutzerdefinierten Vorlagen verfügen über intelligente Regeln, die Ihnen Zeit und Energie sparen können."
msgstr ""
#: apps/marketing/src/components/(marketing)/enterprise.tsx:20
msgid "Our Enterprise License is great for large organizations looking to switch to Documenso for all their signing needs. It's available for our cloud offering as well as self-hosted setups and offers a wide range of compliance and Adminstration Features."
msgstr "Unsere Enterprise-Lizenz ist ideal für große Organisationen, die auf Documenso für all ihre Signaturanforderungen umsteigen möchten. Sie ist sowohl für unser Cloud-Angebot als auch für selbstgehostete Setups verfügbar und bietet eine breite Palette an Compliance- und Verwaltungsfunktionen."
#: apps/marketing/src/components/(marketing)/enterprise.tsx:20
#~ msgid "Our Enterprise License is great large organizations looking to switch to Documenso for all their signing needs. It's availible for our cloud offering as well as self-hosted setups and offer a wide range of compliance and Adminstration Features."
#~ msgstr "Our Enterprise License is great large organizations looking to switch to Documenso for all their signing needs. It's availible for our cloud offering as well as self-hosted setups and offer a wide range of compliance and Adminstration Features."
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:65
msgid "Our self-hosted option is great for small teams and individuals who need a simple solution. You can use our docker based setup to get started in minutes. Take control with full customizability and data ownership."
msgstr "Unsere selbstgehostete Option ist ideal für kleine Teams und Einzelpersonen, die eine einfache Lösung benötigen. Sie können unser docker-basiertes Setup verwenden, um in wenigen Minuten loszulegen. Übernehmen Sie die Kontrolle mit vollständiger Anpassbarkeit und Datenhoheit."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/data.ts:25
msgid "Part-Time"
msgstr "Teilzeit"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:151
msgid "Premium Profile Name"
msgstr "Premium Profilname"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:40
#: apps/marketing/src/components/(marketing)/footer.tsx:31
#: apps/marketing/src/components/(marketing)/header.tsx:42
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:24
msgid "Pricing"
msgstr "Preise"
msgstr ""
#: apps/marketing/src/components/(marketing)/footer.tsx:43
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:53
msgid "Privacy"
msgstr "Datenschutz"
msgstr ""
#: apps/marketing/src/components/(marketing)/carousel.tsx:58
msgid "Profile"
msgstr "Profil"
msgstr ""
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:108
msgid "React Widget (Soon)."
msgstr "React Widget (Demnächst)."
msgstr ""
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:48
msgid "Receive your personal link to share with everyone you care about."
msgstr "Erhalten Sie Ihren persönlichen Link zum Teilen mit allen, die Ihnen wichtig sind."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/team-members.tsx:37
msgid "Role"
msgstr "Rolle"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/salary-bands.tsx:37
#: apps/marketing/src/app/(marketing)/open/team-members.tsx:40
msgid "Salary"
msgstr "Gehalt"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:62
msgid "Save $60 or $120"
msgstr "Sparen Sie $60 oder $120"
msgstr ""
#: apps/marketing/src/components/(marketing)/i18n-switcher.tsx:47
#~ msgid "Search languages..."
#~ msgstr "Sprachen suchen..."
#~ msgstr "Sprachen suchen...>>>>>>> main"
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:109
msgid "Securely. Our data centers are located in Frankfurt (Germany), giving us the best local privacy laws. We are very aware of the sensitive nature of our data and follow best practices to ensure the security and integrity of the data entrusted to us."
msgstr "Sicher. Unsere Rechenzentren befinden sich in Frankfurt (Deutschland) und bieten uns die besten lokalen Datenschutzgesetze. Uns ist die sensible Natur unserer Daten sehr bewusst und wir folgen bewährten Praktiken, um die Sicherheit und Integrität der uns anvertrauten Daten zu gewährleisten."
msgstr ""
#: apps/marketing/src/components/(marketing)/share-connect-paid-widget-bento.tsx:37
msgid "Send, connect, receive and embed everywhere."
msgstr "Überall senden, verbinden, empfangen und einbetten."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/salary-bands.tsx:34
msgid "Seniority"
msgstr "Dienstalter"
msgstr ""
#: apps/marketing/src/components/(marketing)/footer.tsx:39
msgid "Shop"
msgstr "Shop"
msgstr ""
#: apps/marketing/src/app/(marketing)/singleplayer/client.tsx:63
msgid "Sign"
@ -456,115 +442,115 @@ msgstr "Signieren"
#: apps/marketing/src/components/(marketing)/header.tsx:72
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:61
msgid "Sign in"
msgstr "Anmelden"
msgstr ""
#: apps/marketing/src/components/(marketing)/header.tsx:77
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:57
msgid "Sign up"
msgstr "Registrieren"
msgstr ""
#: apps/marketing/src/components/(marketing)/carousel.tsx:22
msgid "Signing Process"
msgstr "Signaturprozess"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:94
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:136
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:180
msgid "Signup Now"
msgstr "Jetzt registrieren"
msgstr ""
#: apps/marketing/src/components/(marketing)/faster-smarter-beautiful-bento.tsx:89
msgid "Smart."
msgstr "Intelligent."
msgstr ""
#: apps/marketing/src/components/(marketing)/hero.tsx:132
msgid "Star on GitHub"
msgstr "Auf GitHub favorisieren"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:226
msgid "Stars"
msgstr "Favoriten"
msgstr ""
#: apps/marketing/src/components/(marketing)/footer.tsx:40
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:44
msgid "Status"
msgstr "Status"
msgstr ""
#: apps/marketing/src/components/(marketing)/footer.tsx:34
#: apps/marketing/src/components/(marketing)/mobile-navigation.tsx:48
msgid "Support"
msgstr "Support"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/team-members.tsx:26
msgid "Team"
msgstr "Team"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:195
msgid "Team Inbox"
msgstr "Team-Posteingang"
msgstr ""
#: apps/marketing/src/components/(marketing)/carousel.tsx:28
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:162
msgid "Teams"
msgstr "Teams"
msgstr ""
#: apps/marketing/src/components/(marketing)/open-build-template-bento.tsx:83
msgid "Template Store (Soon)."
msgstr "Vorlagen-Shop (Demnächst)."
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:138
msgid "That's awesome. You can take a look at the current <0>Issues</0> and join our <1>Discord Community</1> to keep up to date, on what the current priorities are. In any case, we are an open community and welcome all input, technical and non-technical ❤️"
msgstr "Das ist großartig. Sie können sich die aktuellen <0>Issues</0> ansehen und unserer <1>Discord-Community</1> beitreten, um auf dem neuesten Stand zu bleiben, was die aktuellen Prioritäten sind. In jedem Fall sind wir eine offene Gemeinschaft und begrüßen jegliche Beiträge, technische und nicht-technische ❤️"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:293
msgid "This page is evolving as we learn what makes a great signing company. We'll update it when we have more to share."
msgstr "Diese Seite entwickelt sich weiter, während wir lernen, was ein großartiges Signing-Unternehmen ausmacht. Wir werden sie aktualisieren, wenn wir mehr zu teilen haben."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/salary-bands.tsx:31
msgid "Title"
msgstr "Titel"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/total-signed-documents-chart.tsx:30
#: apps/marketing/src/app/(marketing)/open/total-signed-documents-chart.tsx:55
msgid "Total Completed Documents"
msgstr "Insgesamt Abgeschlossene Dokumente"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/page.tsx:267
#: apps/marketing/src/app/(marketing)/open/page.tsx:268
msgid "Total Customers"
msgstr "Insgesamt Kunden"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/funding-raised.tsx:29
msgid "Total Funding Raised"
msgstr "Insgesamt Finanzierungsvolumen"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/monthly-total-users-chart.tsx:30
#: apps/marketing/src/app/(marketing)/open/monthly-total-users-chart.tsx:43
#: apps/marketing/src/app/(marketing)/open/monthly-total-users-chart.tsx:52
msgid "Total Users"
msgstr "Gesamtanzahl der Benutzer"
msgstr ""
#: apps/marketing/src/components/(marketing)/open-build-template-bento.tsx:31
msgid "Truly your own."
msgstr "Wirklich Ihr Eigenes."
msgstr ""
#: apps/marketing/src/components/(marketing)/callout.tsx:27
#: apps/marketing/src/components/(marketing)/hero.tsx:123
msgid "Try our Free Plan"
msgstr "Probieren Sie unseren Gratisplan aus"
msgstr ""
#: apps/marketing/src/app/(marketing)/open/typefully.tsx:20
msgid "Twitter Stats"
msgstr "Twitter-Statistiken"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:142
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:186
msgid "Unlimited Documents per Month"
msgstr "Unbegrenzte Dokumente pro Monat"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:103
msgid "Up to 10 recipients per document"
msgstr "Bis zu 10 Empfänger pro Dokument"
msgstr ""
#: apps/marketing/src/app/(marketing)/singleplayer/client.tsx:52
msgid "Upload a document and add fields."
@ -572,48 +558,52 @@ msgstr "Laden Sie ein Dokument hoch und fügen Sie Felder hinzu."
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:123
msgid "Using our hosted version is the easiest way to get started, you can simply subscribe and start signing your documents. We take care of the infrastructure, so you can focus on your business. Additionally, when using our hosted version you benefit from our trusted signing certificates which helps you to build trust with your customers."
msgstr "Die Nutzung unserer gehosteten Version ist der einfachste Weg, um zu starten. Sie können einfach abonnieren und mit der Unterzeichnung Ihrer Dokumente beginnen. Wir kümmern uns um die Infrastruktur, damit Sie sich auf Ihr Geschäft konzentrieren können. Zudem profitieren Sie bei der Nutzung unserer gehosteten Version von unseren vertrauenswürdigen Signaturzertifikaten, die Ihnen helfen, Vertrauen bei Ihren Kunden aufzubauen."
msgstr ""
#: apps/marketing/src/app/(marketing)/open/typefully.tsx:33
msgid "View all stats"
msgstr "Alle Statistiken anzeigen"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:195
msgid "We are happy to assist you at <0>support@documenso.com</0> or <1>in our Discord-Support-Channel</1> please message either Lucas or Timur to get added to the channel if you are not already a member."
msgstr "Wir helfen Ihnen gerne unter <0>support@documenso.com</0> oder <1>in unserem Discord-Support-Kanal</1>. Bitte senden Sie Lucas oder Timur eine Nachricht, um dem Kanal beizutreten, falls Sie noch kein Mitglied sind."
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:89
msgid "What is the difference between the plans?"
msgstr "Was ist der Unterschied zwischen den Plänen?"
msgstr ""
#: apps/marketing/src/components/(marketing)/faster-smarter-beautiful-bento.tsx:47
msgid "When it comes to sending or receiving a contract, you can count on lightning-fast speeds."
msgstr "Wenn es um das Senden oder Empfangen eines Vertrags geht, können Sie auf blitzschnelle Geschwindigkeiten zählen."
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:191
msgid "Where can I get support?"
msgstr "Wo kann ich Unterstützung bekommen?"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:177
msgid "Why should I prefer Documenso over DocuSign or some other signing tool?"
msgstr "Warum sollte ich Documenso gegenüber DocuSign oder einem anderen Signatur-Tool bevorzugen?"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:119
msgid "Why should I use your hosting service?"
msgstr "Warum sollte ich Ihren Hosting-Service nutzen?"
msgstr ""
#: apps/marketing/src/components/(marketing)/pricing-table.tsx:60
msgid "Yearly"
msgstr "Jährlich"
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:167
msgid "Yes! Documenso is offered under the GNU AGPL V3 open source license. This means you can use it for free and even modify it to fit your needs, as long as you publish your changes under the same license."
msgstr "Ja! Documenso wird unter der GNU AGPL V3 Open-Source-Lizenz angeboten. Das bedeutet, dass Sie es kostenlos nutzen und sogar an Ihre Bedürfnisse anpassen können, solange Sie Ihre Änderungen unter derselben Lizenz veröffentlichen."
msgstr ""
#: apps/marketing/src/app/(marketing)/pricing/page.tsx:93
msgid "You can self-host Documenso for free or use our ready-to-use hosted version. The hosted version comes with additional support, painless scalability and more. Early adopters will get access to all features we build this year, for no additional cost! Forever! Yes, that includes multiple users per account later. If you want Documenso for your enterprise, we are happy to talk about your needs."
msgstr "Sie können Documenso kostenlos selbst hosten oder unsere sofort einsatzbereite gehostete Version nutzen. Die gehostete Version bietet zusätzlichen Support, schmerzfreie Skalierbarkeit und mehr. Frühzeitige Anwender erhalten in diesem Jahr Zugriff auf alle Funktionen, die wir entwickeln, ohne zusätzliche Kosten! Für immer! Ja, das beinhaltet später mehrere Benutzer pro Konto. Wenn Sie Documenso für Ihr Unternehmen möchten, sprechen wir gerne über Ihre Bedürfnisse."
msgstr ""
#: apps/marketing/src/components/(marketing)/carousel.tsx:265
msgid "Your browser does not support the video tag."
msgstr "Ihr Browser unterstützt das Video-Tag nicht."
msgstr ""
#: apps/marketing/src/components/(marketing)/carousel.tsx:265
#~ msgid "Your browser does not support the video tag.<<<<<<< HEAD"
#~ msgstr "Ihr Browser unterstützt das Video-Tag nicht.>>>>>>> main"

View File

@ -1,12 +1,12 @@
msgid ""
msgstr ""
"POT-Creation-Date: 2024-07-24 13:01+1000\n"
"POT-Creation-Date: 2024-08-22 05:16+0000\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
"Language: de\n"
"Project-Id-Version: documenso-app\n"
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-09-05 06:04\n"
"Last-Translator: \n"
@ -754,7 +754,7 @@ msgstr "Klicken Sie, um den Signatur-Link zu kopieren, um ihn an den Empfänger
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:175
#: apps/web/src/app/(signing)/sign/[token]/form.tsx:107
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:435
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:312
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:314
msgid "Click to insert field"
msgstr "Klicken Sie, um das Feld einzufügen"
@ -772,7 +772,7 @@ msgstr "Schließen"
#: apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx:58
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:425
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:302
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:304
#: apps/web/src/components/forms/v2/signup.tsx:522
msgid "Complete"
msgstr "Vollständig"
@ -1405,7 +1405,7 @@ msgstr "Webhook bearbeiten"
#: apps/web/src/app/(recipient)/d/[token]/configure-direct-template.tsx:118
#: apps/web/src/app/(signing)/sign/[token]/email-field.tsx:126
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:376
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:254
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:256
#: apps/web/src/components/(teams)/dialogs/add-team-email-dialog.tsx:169
#: apps/web/src/components/(teams)/dialogs/update-team-email-dialog.tsx:153
#: apps/web/src/components/forms/forgot-password.tsx:81
@ -1587,7 +1587,7 @@ msgstr "Haben Sie Ihr Passwort vergessen?"
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:326
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:193
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:361
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:239
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:241
#: apps/web/src/components/forms/profile.tsx:110
#: apps/web/src/components/forms/v2/signup.tsx:300
msgid "Full Name"
@ -2030,7 +2030,7 @@ msgid "New Template"
msgstr "Neue Vorlage"
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:416
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:293
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:295
#: apps/web/src/components/forms/v2/signup.tsx:509
msgid "Next"
msgstr "Nächster"
@ -2733,7 +2733,7 @@ msgid "Sign as<0>{0} <1>({1})</1></0>"
msgstr "Unterzeichnen als<0>{0} <1>({1})</1></0>"
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:329
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:207
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:209
msgid "Sign document"
msgstr "Dokument unterschreiben"
@ -2762,7 +2762,7 @@ msgid "Sign Out"
msgstr "Ausloggen"
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:350
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:228
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:230
msgid "Sign the document to complete the process."
msgstr "Unterschreiben Sie das Dokument, um den Vorgang abzuschließen."
@ -2789,7 +2789,7 @@ msgstr "Registrieren mit OIDC"
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:197
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:227
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:391
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:268
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:270
#: apps/web/src/components/forms/profile.tsx:132
msgid "Signature"
msgstr "Unterschrift"
@ -2857,7 +2857,7 @@ msgstr "Website Einstellungen"
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/team-email-dropdown.tsx:39
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:61
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:243
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:123
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:125
#: apps/web/src/components/(teams)/dialogs/create-team-checkout-dialog.tsx:50
#: apps/web/src/components/(teams)/dialogs/create-team-checkout-dialog.tsx:99
#: apps/web/src/components/(teams)/dialogs/invite-team-member-dialog.tsx:210
@ -4023,7 +4023,7 @@ msgstr "Wir konnten die Zwei-Faktor-Authentifizierung für Ihr Konto nicht einri
#: apps/web/src/app/(recipient)/d/[token]/direct-template.tsx:119
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:245
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:125
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:127
msgid "We were unable to submit this document at this time. Please try again later."
msgstr "Wir konnten dieses Dokument zurzeit nicht einreichen. Bitte versuchen Sie es später erneut."

View File

@ -77,19 +77,19 @@ msgstr "Add another option"
msgid "Add another value"
msgstr "Add another value"
#: packages/ui/primitives/document-flow/add-signers.tsx:359
#: packages/ui/primitives/document-flow/add-signers.tsx:459
msgid "Add myself"
msgstr "Add myself"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:369
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:450
msgid "Add Myself"
msgstr "Add Myself"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:355
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:436
msgid "Add Placeholder Recipient"
msgstr "Add Placeholder Recipient"
#: packages/ui/primitives/document-flow/add-signers.tsx:348
#: packages/ui/primitives/document-flow/add-signers.tsx:448
msgid "Add Signer"
msgstr "Add Signer"
@ -149,8 +149,8 @@ msgid "Cancel"
msgstr "Cancel"
#: packages/ui/primitives/document-flow/add-signers.tsx:164
msgid "Cannot remove signer"
msgstr "Cannot remove signer"
#~ msgid "Cannot remove signer"
#~ msgstr "Cannot remove signer"
#: packages/lib/constants/recipient-roles.ts:17
msgid "Cc"
@ -225,7 +225,7 @@ msgstr "Date"
msgid "Date Format"
msgstr "Date Format"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:312
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:393
msgid "Direct link receiver"
msgstr "Direct link receiver"
@ -258,11 +258,11 @@ msgstr "Dropdown options"
#: packages/ui/primitives/document-flow/add-fields.tsx:776
#: packages/ui/primitives/document-flow/add-signature.tsx:272
#: packages/ui/primitives/document-flow/add-signers.tsx:232
#: packages/ui/primitives/document-flow/add-signers.tsx:239
#: packages/ui/primitives/document-flow/add-signers.tsx:330
#: packages/ui/primitives/document-flow/add-signers.tsx:337
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:210
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:217
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:296
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:303
msgid "Email"
msgstr "Email"
@ -373,10 +373,10 @@ msgstr "Min"
#: packages/ui/primitives/document-flow/add-fields.tsx:802
#: packages/ui/primitives/document-flow/add-signature.tsx:298
#: packages/ui/primitives/document-flow/add-signers.tsx:265
#: packages/ui/primitives/document-flow/add-signers.tsx:364
#: packages/ui/primitives/template-flow/add-template-fields.tsx:658
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:245
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:251
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:327
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:333
msgid "Name"
msgstr "Name"
@ -555,8 +555,8 @@ msgstr "Share Signature Card"
msgid "Share the Link"
msgstr "Share the Link"
#: packages/ui/primitives/document-flow/add-signers.tsx:377
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:387
#: packages/ui/primitives/document-flow/add-signers.tsx:477
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:468
msgid "Show advanced settings"
msgstr "Show advanced settings"
@ -681,13 +681,13 @@ msgstr "This document has already been sent to this recipient. You can no longer
msgid "This document is password protected. Please enter the password to view the document."
msgstr "This document is password protected. Please enter the password to view the document."
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:315
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx:396
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
msgstr "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
#: packages/ui/primitives/document-flow/add-signers.tsx:165
msgid "This signer has already received the document."
msgstr "This signer has already received the document."
#~ msgid "This signer has already received the document."
#~ msgstr "This signer has already received the document."
#: packages/ui/components/recipient/recipient-action-auth-select.tsx:48
msgid "This will override any global settings."

File diff suppressed because one or more lines are too long

View File

@ -749,7 +749,7 @@ msgstr "Click to copy signing link for sending to recipient"
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:175
#: apps/web/src/app/(signing)/sign/[token]/form.tsx:107
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:435
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:312
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:314
msgid "Click to insert field"
msgstr "Click to insert field"
@ -767,7 +767,7 @@ msgstr "Close"
#: apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx:58
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:425
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:302
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:304
#: apps/web/src/components/forms/v2/signup.tsx:522
msgid "Complete"
msgstr "Complete"
@ -1402,7 +1402,7 @@ msgstr "Edit webhook"
#: apps/web/src/app/(recipient)/d/[token]/configure-direct-template.tsx:118
#: apps/web/src/app/(signing)/sign/[token]/email-field.tsx:126
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:376
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:254
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:256
#: apps/web/src/components/(teams)/dialogs/add-team-email-dialog.tsx:169
#: apps/web/src/components/(teams)/dialogs/update-team-email-dialog.tsx:153
#: apps/web/src/components/forms/forgot-password.tsx:81
@ -1586,7 +1586,7 @@ msgstr "Forgot your password?"
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:326
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:193
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:361
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:239
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:241
#: apps/web/src/components/forms/profile.tsx:110
#: apps/web/src/components/forms/v2/signup.tsx:300
msgid "Full Name"
@ -2029,7 +2029,7 @@ msgid "New Template"
msgstr "New Template"
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:416
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:293
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:295
#: apps/web/src/components/forms/v2/signup.tsx:509
msgid "Next"
msgstr "Next"
@ -2732,7 +2732,7 @@ msgid "Sign as<0>{0} <1>({1})</1></0>"
msgstr "Sign as<0>{0} <1>({1})</1></0>"
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:329
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:207
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:209
msgid "Sign document"
msgstr "Sign document"
@ -2761,7 +2761,7 @@ msgid "Sign Out"
msgstr "Sign Out"
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:350
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:228
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:230
msgid "Sign the document to complete the process."
msgstr "Sign the document to complete the process."
@ -2788,7 +2788,7 @@ msgstr "Sign Up with OIDC"
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:197
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:227
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:391
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:268
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:270
#: apps/web/src/components/forms/profile.tsx:132
msgid "Signature"
msgstr "Signature"
@ -2856,7 +2856,7 @@ msgstr "Site Settings"
#: apps/web/src/app/(teams)/t/[teamUrl]/settings/team-email-dropdown.tsx:39
#: apps/web/src/app/(unauthenticated)/verify-email/[token]/page.tsx:61
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:243
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:123
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:125
#: apps/web/src/components/(teams)/dialogs/create-team-checkout-dialog.tsx:50
#: apps/web/src/components/(teams)/dialogs/create-team-checkout-dialog.tsx:99
#: apps/web/src/components/(teams)/dialogs/invite-team-member-dialog.tsx:210
@ -4022,7 +4022,7 @@ msgstr "We were unable to setup two-factor authentication for your account. Plea
#: apps/web/src/app/(recipient)/d/[token]/direct-template.tsx:119
#: apps/web/src/app/embed/direct/[[...url]]/client.tsx:245
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:125
#: apps/web/src/app/embed/sign/[[...url]]/client.tsx:127
msgid "We were unable to submit this document at this time. Please try again later."
msgstr "We were unable to submit this document at this time. Please try again later."

View File

@ -1,6 +1,8 @@
import { TRPCError } from '@trpc/server';
import { completeDocumentWithToken } from '@documenso/lib/server-only/document/complete-document-with-token';
import { deleteRecipient } from '@documenso/lib/server-only/recipient/delete-recipient';
import { deleteRecipientFromTemplate } from '@documenso/lib/server-only/recipient/delete-recipient-from-template';
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
import { setRecipientsForTemplate } from '@documenso/lib/server-only/recipient/set-recipients-for-template';
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
@ -10,6 +12,8 @@ import {
ZAddSignersMutationSchema,
ZAddTemplateSignersMutationSchema,
ZCompleteDocumentWithTokenMutationSchema,
ZRemoveSignerMutationSchema,
ZRemoveTemplateSignerMutationSchema,
} from './schema';
export const recipientRouter = router({
@ -70,6 +74,51 @@ export const recipientRouter = router({
}
}),
removeTemplateSigner: authenticatedProcedure
.input(ZRemoveTemplateSignerMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { templateId, recipientId, teamId } = input;
const userId = ctx.user.id;
return await deleteRecipientFromTemplate({
userId,
templateId,
teamId,
recipientId,
});
} catch (e) {
console.error(e);
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We were unable to remove the recipient. Please try again later.',
});
}
}),
removeSigner: authenticatedProcedure
.input(ZRemoveSignerMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { documentId, teamId, recipientId } = input;
return await deleteRecipient({
userId: ctx.user.id,
documentId,
teamId,
recipientId,
requestMetadata: extractNextApiRequestMetadata(ctx.req),
});
} catch (err) {
console.error(err);
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We were unable to set this field. Please try again later.',
});
}
}),
completeDocumentWithToken: procedure
.input(ZCompleteDocumentWithTokenMutationSchema)
.mutation(async ({ input, ctx }) => {

View File

@ -58,6 +58,14 @@ export const ZAddTemplateSignersMutationSchema = z
export type TAddTemplateSignersMutationSchema = z.infer<typeof ZAddTemplateSignersMutationSchema>;
export const ZRemoveSignerMutationSchema = z.object({
documentId: z.number(),
teamId: z.number().optional(),
recipientId: z.number(),
});
export type TRemoveSignerMutationSchema = z.infer<typeof ZRemoveSignerMutationSchema>;
export const ZCompleteDocumentWithTokenMutationSchema = z.object({
token: z.string(),
documentId: z.number(),
@ -67,3 +75,13 @@ export const ZCompleteDocumentWithTokenMutationSchema = z.object({
export type TCompleteDocumentWithTokenMutationSchema = z.infer<
typeof ZCompleteDocumentWithTokenMutationSchema
>;
export const ZRemoveTemplateSignerMutationSchema = z.object({
templateId: z.number(),
teamId: z.number().optional(),
recipientId: z.number(),
});
export type TRemoveTemplateSignerMutationSchema = z.infer<
typeof ZRemoveTemplateSignerMutationSchema
>;

View File

@ -2,6 +2,8 @@
import React, { useId, useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
@ -11,10 +13,13 @@ import { useSession } from 'next-auth/react';
import { useFieldArray, useForm } from 'react-hook-form';
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
import { ZRecipientAuthOptionsSchema } from '@documenso/lib/types/document-auth';
import { nanoid } from '@documenso/lib/universal/id';
import type { Field, Recipient } from '@documenso/prisma/client';
import { RecipientRole, SendStatus } from '@documenso/prisma/client';
import type { DocumentWithDetails } from '@documenso/prisma/types/document';
import { trpc } from '@documenso/trpc/react';
import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animate-generic-fade-in-out';
import { RecipientActionAuthSelect } from '@documenso/ui/components/recipient/recipient-action-auth-select';
import { RecipientRoleSelect } from '@documenso/ui/components/recipient/recipient-role-select';
@ -41,32 +46,70 @@ import type { DocumentFlowStep } from './types';
export type AddSignersFormProps = {
documentFlow: DocumentFlowStep;
document: DocumentWithDetails;
recipients: Recipient[];
fields: Field[];
isDocumentEnterprise: boolean;
onSubmit: (_data: TAddSignersFormSchema) => void;
isDocumentPdfLoaded: boolean;
teamId?: number;
};
export const AddSignersFormPartial = ({
documentFlow,
document,
recipients,
fields,
isDocumentEnterprise,
onSubmit,
isDocumentPdfLoaded,
teamId,
}: AddSignersFormProps) => {
const { _ } = useLingui();
const { toast } = useToast();
const { remaining } = useLimits();
const { data: session } = useSession();
const router = useRouter();
const user = session?.user;
const initialId = useId();
const utils = trpc.useUtils();
const { currentStep, totalSteps, previousStep } = useStep();
const { mutateAsync: addSigners } = trpc.recipient.addSigners.useMutation({
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
onSuccess: (newRecipients) => {
utils.document.getDocumentWithDetailsById.setData(
{
id: document.id,
teamId,
},
(oldData) => ({ ...(oldData || document), Recipient: newRecipients }),
);
},
});
const { mutateAsync: deleteSigner } = trpc.recipient.removeSigner.useMutation({
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
onSuccess: (deletedRecipient) => {
utils.document.getDocumentWithDetailsById.setData(
{
id: document.id,
teamId,
},
(oldData) => {
if (!oldData) return document;
return {
...oldData,
Recipient: oldData.Recipient.filter((r) => r.id !== deletedRecipient.id),
};
},
);
},
});
const form = useForm<TAddSignersFormSchema>({
resolver: zodResolver(ZAddSignersFormSchema),
defaultValues: {
@ -156,22 +199,11 @@ export const AddSignersFormPartial = ({
});
};
const onRemoveSigner = (index: number) => {
const signer = signers[index];
if (hasBeenSentToRecipientId(signer.nativeId)) {
toast({
title: _(msg`Cannot remove signer`),
description: _(msg`This signer has already received the document.`),
variant: 'destructive',
});
return;
}
removeSigner(index);
};
/*
The self-signer is automatically saved on blur
since the email input is focused after adding the self-signer.
When the user clicks outside the input, the self-signer is saved in the db.
*/
const onAddSelfSigner = () => {
if (emptySignerIndex !== -1) {
setValue(`signers.${emptySignerIndex}.name`, user?.name ?? '');
@ -193,6 +225,72 @@ export const AddSignersFormPartial = ({
}
};
const handleOnBlur = async (index: number) => {
try {
const currentSigner = form.getValues(`signers.${index}`);
if (!currentSigner.email) {
return;
}
await addSigners({
documentId: document.id,
teamId: teamId,
signers: form.getValues('signers').map((signer) => ({
...signer,
actionAuth: signer.actionAuth || null,
})),
});
const isNewSigner = !currentSigner.nativeId;
toast({
title: isNewSigner ? 'Signer added' : 'Signer updated',
description: isNewSigner
? 'The signer has been added to the document.'
: 'The signer information has been updated.',
});
router.refresh();
} catch (e) {
console.error(e);
toast({
title: 'Error',
description: 'An error occurred while updating the document recipient.',
variant: 'destructive',
});
}
};
const handleRemoveSigner = async (index: number) => {
const signer = signers[index];
if (hasBeenSentToRecipientId(signer.nativeId)) {
toast({
title: 'Cannot remove signer',
description: 'This signer has already received the document.',
variant: 'destructive',
});
return;
}
removeSigner(index);
if (signer.nativeId) {
await deleteSigner({
documentId: document.id,
teamId: teamId,
recipientId: signer.nativeId,
});
toast({
title: 'Signer removed',
description: 'The signer has been removed from the document.',
});
}
};
return (
<>
<DocumentFlowFormContainerHeader
@ -240,6 +338,7 @@ export const AddSignersFormPartial = ({
{...field}
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
onKeyDown={onKeyDown}
onBlur={() => void handleOnBlur(index)}
/>
</FormControl>
@ -266,6 +365,7 @@ export const AddSignersFormPartial = ({
{...field}
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
onKeyDown={onKeyDown}
onBlur={() => void handleOnBlur(index)}
/>
</FormControl>
@ -319,7 +419,7 @@ export const AddSignersFormPartial = ({
hasBeenSentToRecipientId(signer.nativeId) ||
signers.length === 1
}
onClick={() => onRemoveSigner(index)}
onClick={() => void handleRemoveSigner(index)}
>
<Trash className="h-5 w-5" />
</button>

View File

@ -2,6 +2,8 @@
import React, { useEffect, useId, useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
@ -10,11 +12,14 @@ import { Link2Icon, Plus, Trash } from 'lucide-react';
import { useSession } from 'next-auth/react';
import { useFieldArray, useForm } from 'react-hook-form';
import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
import { ZRecipientAuthOptionsSchema } from '@documenso/lib/types/document-auth';
import { nanoid } from '@documenso/lib/universal/id';
import { generateRecipientPlaceholder } from '@documenso/lib/utils/templates';
import type { TemplateDirectLink } from '@documenso/prisma/client';
import { type Field, type Recipient, RecipientRole } from '@documenso/prisma/client';
import type { TemplateWithDetails } from '@documenso/prisma/types/template';
import { trpc } from '@documenso/trpc/react';
import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animate-generic-fade-in-out';
import { RecipientActionAuthSelect } from '@documenso/ui/components/recipient/recipient-action-auth-select';
import { RecipientRoleSelect } from '@documenso/ui/components/recipient/recipient-role-select';
@ -22,6 +27,7 @@ import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message';
import { Input } from '@documenso/ui/primitives/input';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { Checkbox } from '../checkbox';
import {
@ -46,6 +52,7 @@ export type AddTemplatePlaceholderRecipientsFormProps = {
templateDirectLink: TemplateDirectLink | null;
isEnterprise: boolean;
isDocumentPdfLoaded: boolean;
template: TemplateWithDetails;
onSubmit: (_data: TAddTemplatePlacholderRecipientsFormSchema) => void;
};
@ -57,7 +64,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
fields,
isDocumentPdfLoaded,
onSubmit,
template,
}: AddTemplatePlaceholderRecipientsFormProps) => {
const { toast } = useToast();
const router = useRouter();
const initialId = useId();
const { _ } = useLingui();
@ -71,6 +81,38 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
const { currentStep, totalSteps, previousStep } = useStep();
const utils = trpc.useUtils();
const { mutateAsync: addTemplateSigners } = trpc.recipient.addTemplateSigners.useMutation({
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
onSuccess: (newData) => {
utils.template.getTemplateWithDetailsById.setData(
{
id: template.id,
},
(oldData) => ({ ...(oldData || template), ...newData }),
);
},
});
const { mutateAsync: removeTemplateSigner } = trpc.recipient.removeTemplateSigner.useMutation({
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
onSuccess: (deletedRecipient) => {
utils.template.getTemplateWithDetailsById.setData(
{
id: template.id,
},
(oldData) => {
if (!oldData) return template;
return {
...oldData,
recipients: oldData.Recipient.filter((r) => r.id !== deletedRecipient.id),
};
},
);
},
});
const generateDefaultFormSigners = () => {
if (recipients.length === 0) {
return [
@ -158,10 +200,6 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
setPlaceholderRecipientCount((count) => count + 1);
};
const onRemoveSigner = (index: number) => {
removeSigner(index);
};
const isSignerDirectRecipient = (
signer: TAddTemplatePlacholderRecipientsFormSchema['signers'][number],
): boolean => {
@ -171,6 +209,54 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
);
};
const handleOnBlur = async (index: number) => {
try {
const currentSigner = form.getValues(`signers.${index}`);
if (!currentSigner.email) {
return;
}
await addTemplateSigners({
templateId: template.id,
teamId: template.teamId ?? undefined,
signers: form.getValues('signers'),
});
router.refresh();
} catch (e) {
console.error(e);
toast({
title: 'Error',
description: 'An error occurred while updating the template recipient.',
variant: 'destructive',
});
}
};
const handleRemoveSigner = async (index: number) => {
const signer = signers[index];
if (!signer) {
return;
}
removeSigner(index);
if (signer.nativeId) {
await removeTemplateSigner({
templateId: template.id,
teamId: template.teamId ?? undefined,
recipientId: signer.nativeId,
});
toast({
title: 'Signer removed',
description: 'The signer has been removed from the document.',
});
}
};
return (
<>
<DocumentFlowFormContainerHeader
@ -216,12 +302,8 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
type="email"
placeholder={_(msg`Email`)}
{...field}
disabled={
field.disabled ||
isSubmitting ||
signers[index].email === user?.email ||
isSignerDirectRecipient(signer)
}
disabled={field.disabled || isSubmitting}
onBlur={() => void handleOnBlur(index)}
/>
</FormControl>
@ -250,12 +332,8 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
<Input
placeholder={_(msg`Name`)}
{...field}
disabled={
field.disabled ||
isSubmitting ||
signers[index].email === user?.email ||
isSignerDirectRecipient(signer)
}
disabled={field.disabled || isSubmitting}
onBlur={() => void handleOnBlur(index)}
/>
</FormControl>
@ -291,7 +369,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
<FormControl>
<RecipientRoleSelect
{...field}
onValueChange={field.onChange}
onValueChange={(value) => {
field.onChange(value);
void handleOnBlur(index);
}}
disabled={isSubmitting}
hideCCRecipients={isSignerDirectRecipient(signer)}
/>
@ -325,7 +406,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
type="button"
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center justify-center text-slate-500 hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
disabled={isSubmitting || signers.length === 1}
onClick={() => onRemoveSigner(index)}
onClick={() => void handleRemoveSigner(index)}
>
<Trash className="h-5 w-5" />
</button>