Compare commits

...

25 Commits

Author SHA1 Message Date
2d73b38f7c chore: add translations 2025-11-11 20:04:01 +00:00
3d0e3c6e8e fix: update dropzone to create envelopes (#2168) 2025-11-11 20:49:42 +11:00
936d8d90b3 fix: envelope auth not being passed (#2167) 2025-11-11 13:05:05 +11:00
c6b08d8594 feat: org insights (#1937) 2025-11-11 12:09:58 +11:00
575634e326 fix: add pluralization to document details (#2130) 2025-11-11 10:54:12 +11:00
c66eda4aae chore: add message context in user organisation table (#2138) 2025-11-11 10:44:20 +11:00
ef52b35f79 chore: change message in dropzone wrappers (#2137) 2025-11-11 10:43:22 +11:00
95a647034a chore: add message context in envelope download dialog (#2136) 2025-11-11 10:42:30 +11:00
34dba0b6ff chore: fix message formatting (#2135) 2025-11-11 10:41:54 +11:00
fccd97e124 fix: add pluralization to 2fa code template (#2131) 2025-11-11 10:39:05 +11:00
3dbbcefddf fix: add pluralization to passkey page (#2129) 2025-11-11 10:37:54 +11:00
2aea3c4de0 fix: rename envelope buttons (#2161) 2025-11-10 22:21:34 +11:00
ff44ffbc03 v2.0.6 2025-11-10 19:08:43 +11:00
441842d2bd fix: use correct token for embeded template files (#2160) 2025-11-10 19:08:08 +11:00
ca0b83579f fix: auto insert prefilled text and number fields (#2157) 2025-11-10 18:04:21 +11:00
6c0d1da91e fix(input): prevent mobile zoom on input focus (#2079) 2025-11-10 12:43:06 +11:00
805982f3e8 fix: envelope cc issues (#2158) 2025-11-10 11:42:57 +11:00
e2f5e570cf fix: envelope direct template (#2156) 2025-11-09 22:23:13 +11:00
9fd9613076 feat: add additional field options (#2154) 2025-11-08 23:40:03 +11:00
0977c16e33 v2.0.5 2025-11-08 16:03:59 +11:00
88d5a636c3 fix: show legacy ids on template and document view page (#2153)
<img width="557" height="455" alt="image"
src="https://github.com/user-attachments/assets/7b669f4a-c6c5-4fdc-bf10-da0def7b0b3f"
/>
2025-11-08 16:03:26 +11:00
1e6292b1d9 v2.0.4 2025-11-08 13:58:11 +11:00
d65866156d fix: remove parallel steps (#2152) 2025-11-08 13:57:26 +11:00
fe8915162f v2.0.3 2025-11-08 12:53:50 +11:00
37a2634aca feat: support optimizeParallelism for inngest jobs (#2151) 2025-11-08 12:53:13 +11:00
121 changed files with 4311 additions and 1622 deletions

View File

@ -336,7 +336,7 @@ export const EnvelopeDistributeDialog = ({
<Trans>Message</Trans>{' '}
<span className="text-muted-foreground">(Optional)</span>
<Tooltip>
<TooltipTrigger>
<TooltipTrigger type="button">
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground p-4">

View File

@ -176,7 +176,7 @@ export const EnvelopeDownloadDialog = ({
{!isDownloadingState[generateDownloadKey(item.id, 'original')] && (
<DownloadIcon className="mr-2 h-4 w-4" />
)}
<Trans>Original</Trans>
<Trans context="Original document (adjective)">Original</Trans>
</Button>
{envelopeStatus === DocumentStatus.COMPLETED && (
@ -190,7 +190,7 @@ export const EnvelopeDownloadDialog = ({
{!isDownloadingState[generateDownloadKey(item.id, 'signed')] && (
<DownloadIcon className="mr-2 h-4 w-4" />
)}
<Trans>Signed</Trans>
<Trans context="Signed document (adjective)">Signed</Trans>
</Button>
)}
</div>

View File

@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Plural, Trans } from '@lingui/react/macro';
import type * as DialogPrimitive from '@radix-ui/react-dialog';
import { startRegistration } from '@simplewebauthn/browser';
import { KeyRoundIcon } from 'lucide-react';
@ -209,7 +209,11 @@ export const PasskeyCreateDialog = ({ trigger, onSuccess, ...props }: PasskeyCre
))
.with('TOO_MANY_PASSKEYS', () => (
<AlertDescription>
<Trans>You cannot have more than {MAXIMUM_PASSKEYS} passkeys.</Trans>
<Plural
value={MAXIMUM_PASSKEYS}
one="You cannot have more than # passkey."
other="You cannot have more than # passkeys."
/>
</AlertDescription>
))
.with('InvalidStateError', () => (

View File

@ -1,5 +1,4 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { Trans } from '@lingui/react/macro';
import { createCallable } from 'react-call';
@ -28,49 +27,71 @@ import {
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
const createNumberFieldSchema = (fieldMeta: TNumberFieldMeta) => {
let schema = z.coerce.number({
invalid_type_error: msg`Please enter a valid number`.id,
});
const { numberFormat, minValue, maxValue } = fieldMeta;
if (typeof minValue === 'number') {
schema = schema.min(minValue);
}
if (typeof maxValue === 'number') {
schema = schema.max(maxValue);
}
if (numberFormat) {
const foundRegex = numberFormatValues.find((item) => item.value === numberFormat)?.regex;
if (!foundRegex) {
return schema;
}
return schema.refine(
(value) => {
return foundRegex.test(value.toString());
},
{
message: msg`Number needs to be formatted as ${numberFormat}`.id,
},
);
}
return schema;
};
export type SignFieldNumberDialogProps = {
fieldMeta: TNumberFieldMeta;
};
export const SignFieldNumberDialog = createCallable<SignFieldNumberDialogProps, number | null>(
export const SignFieldNumberDialog = createCallable<SignFieldNumberDialogProps, string | null>(
({ call, fieldMeta }) => {
const { t } = useLingui();
// Needs to be inside dialog for translation purposes.
const createNumberFieldSchema = (fieldMeta: TNumberFieldMeta) => {
const { numberFormat, minValue, maxValue } = fieldMeta;
if (numberFormat) {
const foundRegex = numberFormatValues.find((item) => item.value === numberFormat)?.regex;
if (foundRegex) {
return z.string().refine(
(value) => {
return foundRegex.test(value.toString());
},
{
message: t`Number needs to be formatted as ${numberFormat}`,
},
);
}
}
// Not gong to work with min/max numbers + number format
// Since currently doesn't work in V1 going to ignore for now.
return z.string().superRefine((value, ctx) => {
const isValidNumber = /^[0-9,.]+$/.test(value.toString());
if (!isValidNumber) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: t`Please enter a valid number`,
});
return;
}
if (typeof minValue === 'number' && parseFloat(value) < minValue) {
ctx.addIssue({
code: z.ZodIssueCode.too_small,
minimum: minValue,
inclusive: true,
type: 'number',
});
return;
}
if (typeof maxValue === 'number' && parseFloat(value) > maxValue) {
ctx.addIssue({
code: z.ZodIssueCode.too_big,
maximum: maxValue,
inclusive: true,
type: 'number',
});
return;
}
});
};
const ZSignFieldNumberFormSchema = z.object({
number: createNumberFieldSchema(fieldMeta),
});

View File

@ -96,7 +96,7 @@ export const TemplateCreateDialog = ({ folderId }: TemplateCreateDialogProps) =>
<DialogTrigger asChild>
<Button className="cursor-pointer" disabled={!user.emailVerified}>
<FilePlus className="-ml-1 mr-2 h-4 w-4" />
<Trans>New Template</Trans>
<Trans>Template (Legacy)</Trans>
</Button>
</DialogTrigger>

View File

@ -265,7 +265,7 @@ export const TemplateDirectLinkDialog = ({
{remaining.directTemplates !== 0 && (
<DialogFooter className="mx-auto mt-4">
<Button type="button" onClick={() => setCurrentStep('SELECT_RECIPIENT')}>
<Trans> Enable direct link signing</Trans>
<Trans>Enable direct link signing</Trans>
</Button>
</DialogFooter>
)}

View File

@ -336,7 +336,7 @@ export const EmbedDirectTemplateClientPage = ({
<div className="flex-1">
<PDFViewer
envelopeItem={envelopeItems[0]}
token={token}
token={recipient.token}
version="signed"
onDocumentLoad={() => setHasDocumentLoaded(true)}
/>

View File

@ -0,0 +1,49 @@
import { useTransition } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import type { DateRange } from '@documenso/lib/types/search-params';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
type DateRangeFilterProps = {
currentRange: DateRange;
};
export const DateRangeFilter = ({ currentRange }: DateRangeFilterProps) => {
const { _ } = useLingui();
const [isPending, startTransition] = useTransition();
const updateSearchParams = useUpdateSearchParams();
const handleRangeChange = (value: string) => {
startTransition(() => {
updateSearchParams({
dateRange: value as DateRange,
page: 1,
});
});
};
return (
<div className="flex items-center gap-2">
<Select value={currentRange} onValueChange={handleRangeChange} disabled={isPending}>
<SelectTrigger className="w-48">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="last30days">{_(msg`Last 30 Days`)}</SelectItem>
<SelectItem value="last90days">{_(msg`Last 90 Days`)}</SelectItem>
<SelectItem value="lastYear">{_(msg`Last Year`)}</SelectItem>
<SelectItem value="allTime">{_(msg`All Time`)}</SelectItem>
</SelectContent>
</Select>
</div>
);
};

View File

@ -7,6 +7,7 @@ import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
type TDateFieldMeta as DateFieldMeta,
FIELD_DEFAULT_GENERIC_ALIGN,
ZDateFieldMeta,
} from '@documenso/lib/types/field-meta';
import { Form } from '@documenso/ui/primitives/form/form';
@ -39,7 +40,7 @@ export const EditorFieldDateForm = ({
mode: 'onChange',
defaultValues: {
fontSize: value.fontSize || DEFAULT_FIELD_FONT_SIZE,
textAlign: value.textAlign || 'left',
textAlign: value.textAlign ?? FIELD_DEFAULT_GENERIC_ALIGN,
},
});

View File

@ -7,6 +7,7 @@ import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
type TEmailFieldMeta as EmailFieldMeta,
FIELD_DEFAULT_GENERIC_ALIGN,
ZEmailFieldMeta,
} from '@documenso/lib/types/field-meta';
import { Form } from '@documenso/ui/primitives/form/form';
@ -39,7 +40,7 @@ export const EditorFieldEmailForm = ({
mode: 'onChange',
defaultValues: {
fontSize: value.fontSize || DEFAULT_FIELD_FONT_SIZE,
textAlign: value.textAlign || 'left',
textAlign: value.textAlign ?? FIELD_DEFAULT_GENERIC_ALIGN,
},
});

View File

@ -3,6 +3,10 @@ import { useEffect } from 'react';
import { Trans, useLingui } from '@lingui/react/macro';
import { type Control, useFormContext } from 'react-hook-form';
import { FIELD_MIN_LINE_HEIGHT } from '@documenso/lib/types/field-meta';
import { FIELD_MAX_LINE_HEIGHT } from '@documenso/lib/types/field-meta';
import { FIELD_MIN_LETTER_SPACING } from '@documenso/lib/types/field-meta';
import { FIELD_MAX_LETTER_SPACING } from '@documenso/lib/types/field-meta';
import { cn } from '@documenso/ui/lib/utils';
import { Checkbox } from '@documenso/ui/primitives/checkbox';
import {
@ -107,6 +111,119 @@ export const EditorGenericTextAlignField = ({
);
};
export const EditorGenericVerticalAlignField = ({
formControl,
className,
}: {
formControl: FormControlType;
className?: string;
}) => {
const { t } = useLingui();
return (
<FormField
control={formControl}
name="verticalAlign"
render={({ field }) => (
<FormItem className={className}>
<FormLabel>
<Trans>Vertical Align</Trans>
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<SelectTrigger>
<SelectValue placeholder={t`Select vertical align`} />
</SelectTrigger>
<SelectContent>
<SelectItem value="top">
<Trans>Top</Trans>
</SelectItem>
<SelectItem value="middle">
<Trans>Middle</Trans>
</SelectItem>
<SelectItem value="bottom">
<Trans>Bottom</Trans>
</SelectItem>
</SelectContent>
</Select>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
};
export const EditorGenericLineHeightField = ({
formControl,
className,
}: {
formControl: FormControlType;
className?: string;
}) => {
const { t } = useLingui();
return (
<FormField
control={formControl}
name="lineHeight"
render={({ field }) => (
<FormItem className={className}>
<FormLabel>
<Trans>Line Height</Trans>
</FormLabel>
<FormControl>
<Input
type="number"
min={FIELD_MIN_LINE_HEIGHT}
max={FIELD_MAX_LINE_HEIGHT}
className="bg-background"
placeholder={t`Line height`}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
};
export const EditorGenericLetterSpacingField = ({
formControl,
className,
}: {
formControl: FormControlType;
className?: string;
}) => {
const { t } = useLingui();
return (
<FormField
control={formControl}
name="letterSpacing"
render={({ field }) => (
<FormItem className={className}>
<FormLabel>
<Trans>Letter Spacing</Trans>
</FormLabel>
<FormControl>
<Input
type="number"
min={FIELD_MIN_LETTER_SPACING}
max={FIELD_MAX_LETTER_SPACING}
className="bg-background"
placeholder={t`Letter spacing`}
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
};
export const EditorGenericRequiredField = ({
formControl,
className,

View File

@ -6,6 +6,7 @@ import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
FIELD_DEFAULT_GENERIC_ALIGN,
type TInitialsFieldMeta as InitialsFieldMeta,
ZInitialsFieldMeta,
} from '@documenso/lib/types/field-meta';
@ -39,7 +40,7 @@ export const EditorFieldInitialsForm = ({
mode: 'onChange',
defaultValues: {
fontSize: value.fontSize || DEFAULT_FIELD_FONT_SIZE,
textAlign: value.textAlign || 'left',
textAlign: value.textAlign ?? FIELD_DEFAULT_GENERIC_ALIGN,
},
});

View File

@ -6,6 +6,7 @@ import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
FIELD_DEFAULT_GENERIC_ALIGN,
type TNameFieldMeta as NameFieldMeta,
ZNameFieldMeta,
} from '@documenso/lib/types/field-meta';
@ -39,7 +40,7 @@ export const EditorFieldNameForm = ({
mode: 'onChange',
defaultValues: {
fontSize: value.fontSize || DEFAULT_FIELD_FONT_SIZE,
textAlign: value.textAlign || 'left',
textAlign: value.textAlign ?? FIELD_DEFAULT_GENERIC_ALIGN,
},
});

View File

@ -6,6 +6,11 @@ import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
FIELD_DEFAULT_GENERIC_ALIGN,
FIELD_DEFAULT_GENERIC_VERTICAL_ALIGN,
FIELD_DEFAULT_LETTER_SPACING,
FIELD_DEFAULT_LINE_HEIGHT,
type TNumberFieldMeta as NumberFieldMeta,
ZNumberFieldMeta,
} from '@documenso/lib/types/field-meta';
@ -31,9 +36,12 @@ import { Separator } from '@documenso/ui/primitives/separator';
import {
EditorGenericFontSizeField,
EditorGenericLabelField,
EditorGenericLetterSpacingField,
EditorGenericLineHeightField,
EditorGenericReadOnlyField,
EditorGenericRequiredField,
EditorGenericTextAlignField,
EditorGenericVerticalAlignField,
} from './editor-field-generic-field-forms';
const ZNumberFieldFormSchema = ZNumberFieldMeta.pick({
@ -43,6 +51,9 @@ const ZNumberFieldFormSchema = ZNumberFieldMeta.pick({
numberFormat: true,
fontSize: true,
textAlign: true,
lineHeight: true,
letterSpacing: true,
verticalAlign: true,
required: true,
readOnly: true,
minValue: true,
@ -99,8 +110,11 @@ export const EditorFieldNumberForm = ({
placeholder: value.placeholder || '',
value: value.value || '',
numberFormat: value.numberFormat || null,
fontSize: value.fontSize || 14,
textAlign: value.textAlign || 'left',
fontSize: value.fontSize || DEFAULT_FIELD_FONT_SIZE,
textAlign: value.textAlign ?? FIELD_DEFAULT_GENERIC_ALIGN,
lineHeight: value.lineHeight ?? FIELD_DEFAULT_LINE_HEIGHT,
letterSpacing: value.letterSpacing ?? FIELD_DEFAULT_LETTER_SPACING,
verticalAlign: value.verticalAlign ?? FIELD_DEFAULT_GENERIC_VERTICAL_ALIGN,
required: value.required || false,
readOnly: value.readOnly || false,
minValue: value.minValue,
@ -118,6 +132,10 @@ export const EditorFieldNumberForm = ({
useEffect(() => {
const validatedFormValues = ZNumberFieldFormSchema.safeParse(formValues);
if (formValues.readOnly && !formValues.value) {
void form.trigger('value');
}
if (validatedFormValues.success) {
onValueChange({
type: 'number',
@ -130,10 +148,12 @@ export const EditorFieldNumberForm = ({
<Form {...form}>
<form>
<fieldset className="flex flex-col gap-2">
<div className="flex w-full flex-row gap-x-4">
<EditorGenericFontSizeField className="w-full" formControl={form.control} />
<EditorGenericFontSizeField className="w-full" formControl={form.control} />
<div className="flex w-full flex-row gap-x-4">
<EditorGenericTextAlignField className="w-full" formControl={form.control} />
<EditorGenericVerticalAlignField className="w-full" formControl={form.control} />
</div>
<EditorGenericLabelField formControl={form.control} />
@ -204,6 +224,12 @@ export const EditorFieldNumberForm = ({
)}
/>
<div className="flex w-full flex-row gap-x-4">
<EditorGenericLineHeightField className="w-full" formControl={form.control} />
<EditorGenericLetterSpacingField className="w-full" formControl={form.control} />
</div>
<div className="mt-1">
<EditorGenericRequiredField formControl={form.control} />
</div>

View File

@ -5,11 +5,8 @@ import { Trans } from '@lingui/react/macro';
import { useForm, useWatch } from 'react-hook-form';
import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
type TSignatureFieldMeta,
ZSignatureFieldMeta,
} from '@documenso/lib/types/field-meta';
import { DEFAULT_SIGNATURE_TEXT_FONT_SIZE } from '@documenso/lib/constants/pdf';
import { type TSignatureFieldMeta, ZSignatureFieldMeta } from '@documenso/lib/types/field-meta';
import { Form } from '@documenso/ui/primitives/form/form';
import { EditorGenericFontSizeField } from './editor-field-generic-field-forms';
@ -35,7 +32,7 @@ export const EditorFieldSignatureForm = ({
resolver: zodResolver(ZSignatureFieldFormSchema),
mode: 'onChange',
defaultValues: {
fontSize: value.fontSize || DEFAULT_FIELD_FONT_SIZE,
fontSize: value.fontSize || DEFAULT_SIGNATURE_TEXT_FONT_SIZE,
},
});

View File

@ -3,11 +3,16 @@ import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import { useForm, useWatch } from 'react-hook-form';
import { z } from 'zod';
import type { z } from 'zod';
import {
DEFAULT_FIELD_FONT_SIZE,
FIELD_DEFAULT_GENERIC_ALIGN,
FIELD_DEFAULT_GENERIC_VERTICAL_ALIGN,
FIELD_DEFAULT_LETTER_SPACING,
FIELD_DEFAULT_LINE_HEIGHT,
type TTextFieldMeta as TextFieldMeta,
ZTextFieldMeta,
} from '@documenso/lib/types/field-meta';
import {
Form,
@ -22,32 +27,36 @@ import { Textarea } from '@documenso/ui/primitives/textarea';
import {
EditorGenericFontSizeField,
EditorGenericLetterSpacingField,
EditorGenericLineHeightField,
EditorGenericReadOnlyField,
EditorGenericRequiredField,
EditorGenericTextAlignField,
EditorGenericVerticalAlignField,
} from './editor-field-generic-field-forms';
const ZTextFieldFormSchema = z
.object({
label: z.string().optional(),
placeholder: z.string().optional(),
text: z.string().optional(),
characterLimit: z.coerce.number().min(0).optional(),
fontSize: z.coerce.number().min(8).max(96).optional(),
textAlign: z.enum(['left', 'center', 'right']).optional(),
required: z.boolean().optional(),
readOnly: z.boolean().optional(),
})
.refine(
(data) => {
// A read-only field must have text
return !data.readOnly || (data.text && data.text.length > 0);
},
{
message: 'A read-only field must have text',
path: ['text'],
},
);
const ZTextFieldFormSchema = ZTextFieldMeta.pick({
label: true,
placeholder: true,
text: true,
characterLimit: true,
fontSize: true,
textAlign: true,
lineHeight: true,
letterSpacing: true,
verticalAlign: true,
required: true,
readOnly: true,
}).refine(
(data) => {
// A read-only field must have text
return !data.readOnly || (data.text && data.text.length > 0);
},
{
message: 'A read-only field must have text',
path: ['text'],
},
);
type TTextFieldFormSchema = z.infer<typeof ZTextFieldFormSchema>;
@ -73,7 +82,10 @@ export const EditorFieldTextForm = ({
text: value.text || '',
characterLimit: value.characterLimit || 0,
fontSize: value.fontSize || DEFAULT_FIELD_FONT_SIZE,
textAlign: value.textAlign || 'left',
textAlign: value.textAlign ?? FIELD_DEFAULT_GENERIC_ALIGN,
lineHeight: value.lineHeight ?? FIELD_DEFAULT_LINE_HEIGHT,
letterSpacing: value.letterSpacing ?? FIELD_DEFAULT_LETTER_SPACING,
verticalAlign: value.verticalAlign ?? FIELD_DEFAULT_GENERIC_VERTICAL_ALIGN,
required: value.required || false,
readOnly: value.readOnly || false,
},
@ -89,6 +101,10 @@ export const EditorFieldTextForm = ({
useEffect(() => {
const validatedFormValues = ZTextFieldFormSchema.safeParse(formValues);
if (formValues.readOnly && !formValues.text) {
void form.trigger('text');
}
if (validatedFormValues.success) {
onValueChange({
type: 'text',
@ -101,10 +117,12 @@ export const EditorFieldTextForm = ({
<Form {...form}>
<form>
<fieldset className="flex flex-col gap-2">
<div className="flex w-full flex-row gap-x-4">
<EditorGenericFontSizeField className="w-full" formControl={form.control} />
<EditorGenericFontSizeField className="w-full" formControl={form.control} />
<div className="flex w-full flex-row gap-x-4">
<EditorGenericTextAlignField className="w-full" formControl={form.control} />
<EditorGenericVerticalAlignField className="w-full" formControl={form.control} />
</div>
<FormField
@ -182,17 +200,16 @@ export const EditorFieldTextForm = ({
</FormLabel>
<FormControl>
<Input
type="number"
min={0}
className="bg-background"
placeholder={t`Field character limit`}
placeholder={t`Character limit`}
{...field}
value={field.value || ''}
onChange={(e) => {
field.onChange(e);
const values = form.getValues();
const characterLimit = parseInt(e.target.value, 10) || 0;
field.onChange(characterLimit || '');
const textValue = values.text || '';
if (characterLimit > 0 && textValue.length > characterLimit) {
@ -206,6 +223,12 @@ export const EditorFieldTextForm = ({
)}
/>
<div className="flex w-full flex-row gap-x-4">
<EditorGenericLineHeightField className="w-full" formControl={form.control} />
<EditorGenericLetterSpacingField className="w-full" formControl={form.control} />
</div>
<div className="mt-1">
<EditorGenericRequiredField formControl={form.control} />
</div>

View File

@ -184,10 +184,10 @@ export const DocumentSigningPageViewV2 = () => {
</div>
)}
<div className="embed--DocumentWidgetFooter">
<div className="embed--DocumentWidgetFooter mt-auto">
{/* Footer of left sidebar. */}
{!isEmbed && (
<div className="mt-auto px-4">
<div className="px-4">
<Button asChild variant="ghost" className="w-full justify-start">
<Link to="/">
<ArrowLeftIcon className="mr-2 h-4 w-4" />

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { Trans } from '@lingui/react/macro';
import { type DocumentData, DocumentStatus, type EnvelopeItem } from '@prisma/client';
import { type DocumentData, DocumentStatus, type EnvelopeItem, EnvelopeType } from '@prisma/client';
import { DownloadIcon } from 'lucide-react';
import { DateTime } from 'luxon';
@ -100,7 +100,14 @@ export const DocumentCertificateQRView = ({
)}
{internalVersion === 2 ? (
<EnvelopeRenderProvider envelope={{ envelopeItems }} token={token}>
<EnvelopeRenderProvider
envelope={{
envelopeItems,
status: DocumentStatus.COMPLETED,
type: EnvelopeType.DOCUMENT,
}}
token={token}
>
<DocumentCertificateQrV2
title={title}
recipientCount={recipientCount}
@ -130,7 +137,7 @@ export const DocumentCertificateQRView = ({
envelopeItems={envelopeItems}
token={token}
trigger={
<Button type="button" variant="outline" className="flex-1">
<Button type="button" variant="outline" className="w-fit">
<DownloadIcon className="mr-2 h-5 w-5" />
<Trans>Download</Trans>
</Button>
@ -189,7 +196,7 @@ const DocumentCertificateQrV2 = ({
envelopeItems={envelopeItems}
token={token}
trigger={
<Button type="button" variant="outline" className="flex-1">
<Button type="button" variant="outline" className="w-fit">
<DownloadIcon className="mr-2 h-5 w-5" />
<Trans>Download</Trans>
</Button>

View File

@ -7,6 +7,7 @@ import { DateTime } from 'luxon';
import { useIsMounted } from '@documenso/lib/client-only/hooks/use-is-mounted';
import type { TEnvelope } from '@documenso/lib/types/envelope';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
export type DocumentPageViewInformationProps = {
userId: number;
@ -40,6 +41,10 @@ export const DocumentPageViewInformation = ({
.setLocale(i18n.locales?.[0] || i18n.locale)
.toRelative(),
},
{
description: msg`Document ID (Legacy)`,
value: mapSecondaryIdToDocumentId(envelope.secondaryId),
},
];
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMounted, envelope, userId]);

View File

@ -3,6 +3,7 @@ import { useMemo, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { useNavigate, useParams } from 'react-router';
import { match } from 'ts-pattern';
@ -17,7 +18,7 @@ import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react';
import type { TCreateDocumentPayloadSchema } from '@documenso/trpc/server/document-router/create-document.types';
import { cn } from '@documenso/ui/lib/utils';
import { DocumentDropzone } from '@documenso/ui/primitives/document-upload';
import { DocumentUploadButton as DocumentUploadButtonPrimitive } from '@documenso/ui/primitives/document-upload-button';
import {
Tooltip,
TooltipContent,
@ -28,11 +29,11 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
import { useCurrentTeam } from '~/providers/team';
export type DocumentUploadButtonProps = {
export type DocumentUploadButtonLegacyProps = {
className?: string;
};
export const DocumentUploadButton = ({ className }: DocumentUploadButtonProps) => {
export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLegacyProps) => {
const { _ } = useLingui();
const { toast } = useToast();
const { user } = useSession();
@ -144,12 +145,14 @@ export const DocumentUploadButton = ({ className }: DocumentUploadButtonProps) =
<Tooltip>
<TooltipTrigger asChild>
<div>
<DocumentDropzone
<DocumentUploadButtonPrimitive
loading={isLoading}
disabled={remaining.documents === 0 || !user.emailVerified}
disabledMessage={disabledMessage}
onDrop={async (files) => onFileDrop(files[0])}
onDropRejected={onFileDropRejected}
type={EnvelopeType.DOCUMENT}
internalVersion="1"
/>
</div>
</TooltipTrigger>

View File

@ -616,13 +616,14 @@ export default function EnvelopeEditorFieldsPageRenderer() {
transform: 'translateX(-50%)',
zIndex: 50,
}}
className="text-muted-foreground grid w-max grid-cols-5 gap-x-1 gap-y-0.5 rounded-md border bg-white p-1 shadow-sm"
// Don't use darkmode for this component, it should look the same for both light/dark modes.
className="grid w-max grid-cols-5 gap-x-1 gap-y-0.5 rounded-md border border-gray-300 bg-white p-1 text-gray-500 shadow-sm"
>
{fieldButtonList.map((field) => (
<button
key={field.type}
onClick={() => createFieldFromPendingTemplate(pendingFieldCreation, field.type)}
className="hover:text-foreground col-span-1 w-full flex-shrink-0 rounded-sm px-2 py-1 text-xs hover:bg-gray-100"
className="col-span-1 w-full flex-shrink-0 rounded-sm px-2 py-1 text-xs hover:bg-gray-100 hover:text-gray-600"
>
{t(field.name)}
</button>

View File

@ -2,7 +2,7 @@ import { lazy, useEffect, useMemo, useState } from 'react';
import { faker } from '@faker-js/faker/locale/en';
import { Trans } from '@lingui/react/macro';
import { FieldType } from '@prisma/client';
import { FieldType, SigningStatus } from '@prisma/client';
import { FileTextIcon } from 'lucide-react';
import { match } from 'ts-pattern';
@ -201,7 +201,10 @@ export const EnvelopeEditorPreviewPage = () => {
envelope={envelope}
token={undefined}
fields={fieldsWithPlaceholders}
recipients={envelope.recipients}
recipients={envelope.recipients.map((recipient) => ({
...recipient,
signingStatus: SigningStatus.SIGNED,
}))}
overrideSettings={{
mode: 'export',
}}

View File

@ -212,7 +212,7 @@ export const EnvelopeEditorRecipientForm = () => {
);
const hasDocumentBeenSent = recipients.some(
(recipient) => recipient.sendStatus === SendStatus.SENT,
(recipient) => recipient.role !== RecipientRole.CC && recipient.sendStatus === SendStatus.SENT,
);
const canRecipientBeModified = (recipientId?: number) => {

View File

@ -49,7 +49,7 @@ export const EnvelopeEditorUploadPage = () => {
const organisation = useCurrentOrganisation();
const { t } = useLingui();
const { envelope, setLocalEnvelope, relativePath } = useCurrentEnvelopeEditor();
const { envelope, setLocalEnvelope, relativePath, editorFields } = useCurrentEnvelopeEditor();
const { maximumEnvelopeItemCount, remaining } = useLimits();
const { toast } = useToast();
@ -165,9 +165,17 @@ export const EnvelopeEditorUploadPage = () => {
const onFileDelete = (envelopeItemId: string) => {
setLocalFiles((prev) => prev.filter((uploadingFile) => uploadingFile.id !== envelopeItemId));
const fieldsWithoutDeletedItem = envelope.fields.filter(
(field) => field.envelopeItemId !== envelopeItemId,
);
setLocalEnvelope({
envelopeItems: envelope.envelopeItems.filter((item) => item.id !== envelopeItemId),
fields: envelope.fields.filter((field) => field.envelopeItemId !== envelopeItemId),
});
// Reset editor fields.
editorFields.resetForm(fieldsWithoutDeletedItem);
};
/**

View File

@ -1,7 +1,7 @@
import { useEffect, useMemo } from 'react';
import { useLingui } from '@lingui/react/macro';
import { type Recipient, SigningStatus } from '@prisma/client';
import { DocumentStatus, type Recipient, SigningStatus } from '@prisma/client';
import type Konva from 'konva';
import { usePageRenderer } from '@documenso/lib/client-only/hooks/use-page-renderer';
@ -19,6 +19,7 @@ export default function EnvelopeGenericPageRenderer() {
const { i18n } = useLingui();
const {
envelopeStatus,
currentEnvelopeItem,
fields,
recipients,
@ -42,6 +43,10 @@ export default function EnvelopeGenericPageRenderer() {
const { _className, scale } = pageContext;
const localPageFields = useMemo((): GenericLocalField[] => {
if (envelopeStatus === DocumentStatus.COMPLETED) {
return [];
}
return fields
.filter(
(field) =>
@ -54,11 +59,20 @@ export default function EnvelopeGenericPageRenderer() {
throw new Error(`Recipient not found for field ${field.id}`);
}
const isInserted = recipient.signingStatus === SigningStatus.SIGNED && field.inserted;
return {
...field,
inserted: isInserted,
customText: isInserted ? field.customText : '',
recipient,
};
});
})
.filter(
({ inserted, fieldMeta, recipient }) =>
(recipient.signingStatus === SigningStatus.SIGNED ? inserted : true) ||
fieldMeta?.readOnly,
);
}, [fields, pageContext.pageNumber, currentEnvelopeItem?.id, recipients]);
const unsafeRenderFieldOnLayer = (field: GenericLocalField) => {
@ -67,12 +81,8 @@ export default function EnvelopeGenericPageRenderer() {
return;
}
const { recipient } = field;
const fieldTranslations = getClientSideFieldTranslations(i18n);
const isInserted = recipient.signingStatus === SigningStatus.SIGNED && field.inserted;
renderField({
scale,
pageLayer: pageLayer.current,
@ -83,7 +93,6 @@ export default function EnvelopeGenericPageRenderer() {
height: Number(field.height),
positionX: Number(field.positionX),
positionY: Number(field.positionY),
customText: isInserted ? field.customText : '',
fieldMeta: field.fieldMeta,
signature: {
signatureImageAsBase64: '',
@ -95,7 +104,7 @@ export default function EnvelopeGenericPageRenderer() {
pageHeight: unscaledViewport.height,
color: getRecipientColorKey(field.recipientId),
editable: false,
mode: overrideSettings?.mode ?? 'sign',
mode: overrideSettings?.mode ?? 'edit',
});
};

View File

@ -1,7 +1,14 @@
import { useEffect, useMemo } from 'react';
import { Trans, useLingui } from '@lingui/react/macro';
import { type Field, FieldType, RecipientRole, type Signature } from '@prisma/client';
import {
type Field,
FieldType,
type Recipient,
RecipientRole,
type Signature,
SigningStatus,
} from '@prisma/client';
import type Konva from 'konva';
import type { KonvaEventObject } from 'konva/lib/Node';
import { match } from 'ts-pattern';
@ -12,6 +19,7 @@ import { useOptionalSession } from '@documenso/lib/client-only/providers/session
import { DIRECT_TEMPLATE_RECIPIENT_EMAIL } from '@documenso/lib/constants/direct-templates';
import { isBase64Image } from '@documenso/lib/constants/signatures';
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
import type { TEnvelope } from '@documenso/lib/types/envelope';
import { ZFullFieldSchema } from '@documenso/lib/types/field';
import { createSpinner } from '@documenso/lib/universal/field-renderer/field-generic-items';
import { renderField } from '@documenso/lib/universal/field-renderer/render-field';
@ -19,6 +27,7 @@ import { isFieldUnsignedAndRequired } from '@documenso/lib/utils/advanced-fields
import { getClientSideFieldTranslations } from '@documenso/lib/utils/fields';
import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
import type { TSignEnvelopeFieldValue } from '@documenso/trpc/server/envelope-router/sign-envelope-field.types';
import { EnvelopeRecipientFieldTooltip } from '@documenso/ui/components/document/envelope-recipient-field-tooltip';
import { EnvelopeFieldToolTip } from '@documenso/ui/components/field/envelope-field-tooltip';
import type { TRecipientColor } from '@documenso/ui/lib/recipient-colors';
import { useToast } from '@documenso/ui/primitives/use-toast';
@ -36,6 +45,10 @@ import { handleTextFieldClick } from '~/utils/field-signing/text-field';
import { useRequiredDocumentSigningAuthContext } from '../document-signing/document-signing-auth-provider';
import { useRequiredEnvelopeSigningContext } from '../document-signing/envelope-signing-provider';
type GenericLocalField = TEnvelope['fields'][number] & {
recipient: Pick<Recipient, 'id' | 'name' | 'email' | 'signingStatus'>;
};
export default function EnvelopeSignerPageRenderer() {
const { t, i18n } = useLingui();
const { currentEnvelopeItem, setRenderError } = useCurrentEnvelopeRender();
@ -91,6 +104,36 @@ export default function EnvelopeSignerPageRenderer() {
);
}, [recipientFields, selectedAssistantRecipientFields, pageContext.pageNumber]);
/**
* Returns fields that have been fully signed by other recipients for this specific
* page.
*/
const localPageOtherRecipientFields = useMemo((): GenericLocalField[] => {
const signedRecipients = envelope.recipients.filter(
(recipient) => recipient.signingStatus === SigningStatus.SIGNED,
);
return signedRecipients.flatMap((recipient) => {
return recipient.fields
.filter(
(field) =>
field.page === pageContext.pageNumber &&
field.envelopeItemId === currentEnvelopeItem?.id &&
(field.inserted || field.fieldMeta?.readOnly),
)
.map((field) => ({
...field,
recipient: {
id: recipient.id,
name: recipient.name,
email: recipient.email,
signingStatus: recipient.signingStatus,
role: recipient.role,
},
}));
});
}, [envelope.recipients, pageContext.pageNumber]);
const unsafeRenderFieldOnLayer = (unparsedField: Field & { signature?: Signature | null }) => {
if (!pageLayer.current) {
console.error('Layer not loaded yet');
@ -376,6 +419,46 @@ export default function EnvelopeSignerPageRenderer() {
}
};
const renderFields = () => {
if (!pageLayer.current) {
console.error('Layer not loaded yet');
return;
}
// Render current recipient fields.
for (const field of localPageFields) {
renderFieldOnLayer(field);
}
// Render other recipient signed and inserted fields.
for (const field of localPageOtherRecipientFields) {
try {
renderField({
scale,
pageLayer: pageLayer.current,
field: {
renderId: field.id.toString(),
...field,
width: Number(field.width),
height: Number(field.height),
positionX: Number(field.positionX),
positionY: Number(field.positionY),
fieldMeta: field.fieldMeta,
},
translations: getClientSideFieldTranslations(i18n),
pageWidth: unscaledViewport.width,
pageHeight: unscaledViewport.height,
color: 'readOnly',
editable: false,
mode: 'sign',
});
} catch (err) {
console.error('Unable to render one or more fields belonging to other recipients.');
console.error(err);
}
}
};
const signField = async (
fieldId: number,
payload: TSignEnvelopeFieldValue,
@ -412,11 +495,7 @@ export default function EnvelopeSignerPageRenderer() {
* Initialize the Konva page canvas and all fields and interactions.
*/
const createPageCanvas = (currentStage: Konva.Stage, currentPageLayer: Konva.Layer) => {
// Render the fields.
for (const field of localPageFields) {
renderFieldOnLayer(field);
}
renderFields();
currentPageLayer.batchDraw();
};
@ -428,9 +507,7 @@ export default function EnvelopeSignerPageRenderer() {
return;
}
localPageFields.forEach((field) => {
renderFieldOnLayer(field);
});
renderFields();
pageLayer.current.batchDraw();
}, [localPageFields, showPendingFieldTooltip, fullName, signature, email]);
@ -446,9 +523,7 @@ export default function EnvelopeSignerPageRenderer() {
// Rerender the whole page.
pageLayer.current.destroyChildren();
localPageFields.forEach((field) => {
renderFieldOnLayer(field);
});
renderFields();
pageLayer.current.batchDraw();
}, [selectedAssistantRecipient]);
@ -475,6 +550,15 @@ export default function EnvelopeSignerPageRenderer() {
</EnvelopeFieldToolTip>
)}
{localPageOtherRecipientFields.map((field) => (
<EnvelopeRecipientFieldTooltip
key={field.id}
field={field}
showFieldStatus={true}
showRecipientTooltip={true}
/>
))}
{/* The element Konva will inject it's canvas into. */}
<div className="konva-container absolute inset-0 z-10 w-full" ref={konvaContainer}></div>

View File

@ -75,14 +75,12 @@ export const EnvelopeSignerCompleteDialog = () => {
accessAuthOptions?: TRecipientAccessAuth,
) => {
try {
const payload = {
await completeDocument({
token: recipient.token,
documentId: mapSecondaryIdToDocumentId(envelope.secondaryId),
authOptions: accessAuthOptions,
accessAuthOptions,
...(nextSigner?.email && nextSigner?.name ? { nextSigner } : {}),
};
await completeDocument(payload);
});
analytics.capture('App: Recipient has completed signing', {
signerId: recipient.id,

View File

@ -1,10 +1,15 @@
import { type ReactNode, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { useLingui } from '@lingui/react/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { Loader } from 'lucide-react';
import { ErrorCode, type FileRejection, useDropzone } from 'react-dropzone';
import {
ErrorCode as DropzoneErrorCode,
ErrorCode,
type FileRejection,
useDropzone,
} from 'react-dropzone';
import { Link, useNavigate, useParams } from 'react-router';
import { match } from 'ts-pattern';
@ -16,21 +21,26 @@ import { APP_DOCUMENT_UPLOAD_SIZE_LIMIT, IS_BILLING_ENABLED } from '@documenso/l
import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { megabytesToBytes } from '@documenso/lib/universal/unit-convertions';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { formatDocumentsPath, formatTemplatesPath } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react';
import type { TCreateDocumentPayloadSchema } from '@documenso/trpc/server/document-router/create-document.types';
import type { TCreateEnvelopePayload } from '@documenso/trpc/server/envelope-router/create-envelope.types';
import { cn } from '@documenso/ui/lib/utils';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { useCurrentTeam } from '~/providers/team';
export interface DocumentDropZoneWrapperProps {
export interface EnvelopeDropZoneWrapperProps {
children: ReactNode;
type: EnvelopeType;
className?: string;
}
export const DocumentDropZoneWrapper = ({ children, className }: DocumentDropZoneWrapperProps) => {
const { _ } = useLingui();
export const EnvelopeDropZoneWrapper = ({
children,
type,
className,
}: EnvelopeDropZoneWrapperProps) => {
const { t } = useLingui();
const { toast } = useToast();
const { user } = useSession();
const { folderId } = useParams();
@ -47,13 +57,13 @@ export const DocumentDropZoneWrapper = ({ children, className }: DocumentDropZon
TIME_ZONES.find((timezone) => timezone === Intl.DateTimeFormat().resolvedOptions().timeZone) ??
DEFAULT_DOCUMENT_TIME_ZONE;
const { quota, remaining, refreshLimits } = useLimits();
const { quota, remaining, refreshLimits, maximumEnvelopeItemCount } = useLimits();
const { mutateAsync: createDocument } = trpc.document.create.useMutation();
const { mutateAsync: createEnvelope } = trpc.envelope.create.useMutation();
const isUploadDisabled = remaining.documents === 0 || !user.emailVerified;
const onFileDrop = async (file: File) => {
const onFileDrop = async (files: File[]) => {
if (isUploadDisabled && IS_BILLING_ENABLED()) {
await navigate(`/o/${organisation.url}/settings/billing`);
return;
@ -63,51 +73,67 @@ export const DocumentDropZoneWrapper = ({ children, className }: DocumentDropZon
setIsLoading(true);
const payload = {
title: file.name,
timezone: userTimezone,
folderId: folderId ?? undefined,
} satisfies TCreateDocumentPayloadSchema;
folderId,
type,
title: files[0].name,
meta: {
timezone: userTimezone,
},
} satisfies TCreateEnvelopePayload;
const formData = new FormData();
formData.append('payload', JSON.stringify(payload));
formData.append('file', file);
const { envelopeId: id } = await createDocument(formData);
for (const file of files) {
formData.append('files', file);
}
const { id } = await createEnvelope(formData);
void refreshLimits();
toast({
title: _(msg`Document uploaded`),
description: _(msg`Your document has been uploaded successfully.`),
title: type === EnvelopeType.DOCUMENT ? t`Document uploaded` : t`Template uploaded`,
description:
type === EnvelopeType.DOCUMENT
? t`Your document has been uploaded successfully.`
: t`Your template has been uploaded successfully.`,
duration: 5000,
});
analytics.capture('App: Document Uploaded', {
userId: user.id,
documentId: id,
timestamp: new Date().toISOString(),
});
if (type === EnvelopeType.DOCUMENT) {
analytics.capture('App: Document Uploaded', {
userId: user.id,
documentId: id,
timestamp: new Date().toISOString(),
});
}
await navigate(`${formatDocumentsPath(team.url)}/${id}/edit`);
const pathPrefix =
type === EnvelopeType.DOCUMENT
? formatDocumentsPath(team.url)
: formatTemplatesPath(team.url);
await navigate(`${pathPrefix}/${id}/edit`);
} catch (err) {
const error = AppError.parseError(err);
const errorMessage = match(error.code)
.with('INVALID_DOCUMENT_FILE', () => msg`You cannot upload encrypted PDFs`)
.with('INVALID_DOCUMENT_FILE', () => t`You cannot upload encrypted PDFs`)
.with(
AppErrorCode.LIMIT_EXCEEDED,
() => msg`You have reached your document limit for this month. Please upgrade your plan.`,
() => t`You have reached your document limit for this month. Please upgrade your plan.`,
)
.with(
'ENVELOPE_ITEM_LIMIT_EXCEEDED',
() => msg`You have reached the limit of the number of files per envelope`,
() => t`You have reached the limit of the number of files per envelope`,
)
.otherwise(() => msg`An error occurred while uploading your document.`);
.otherwise(() => t`An error occurred during upload.`);
toast({
title: _(msg`Error`),
description: _(errorMessage),
title: t`Error`,
description: errorMessage,
variant: 'destructive',
duration: 7500,
});
@ -121,6 +147,20 @@ export const DocumentDropZoneWrapper = ({ children, className }: DocumentDropZon
return;
}
const maxItemsReached = fileRejections.some((fileRejection) =>
fileRejection.errors.some((error) => error.code === DropzoneErrorCode.TooManyFiles),
);
if (maxItemsReached) {
toast({
title: t`You cannot upload more than ${maximumEnvelopeItemCount} items per envelope.`,
duration: 5000,
variant: 'destructive',
});
return;
}
// Since users can only upload only one file (no multi-upload), we only handle the first file rejection
const { file, errors } = fileRejections[0];
@ -148,14 +188,14 @@ export const DocumentDropZoneWrapper = ({ children, className }: DocumentDropZon
const description = (
<>
<span className="font-medium">
{file.name} <Trans>couldn't be uploaded:</Trans>
<Trans>{file.name} couldn't be uploaded:</Trans>
</span>
{errorNodes}
</>
);
toast({
title: _(msg`Upload failed`),
title: t`Upload failed`,
description,
duration: 5000,
variant: 'destructive',
@ -165,17 +205,11 @@ export const DocumentDropZoneWrapper = ({ children, className }: DocumentDropZon
accept: {
'application/pdf': ['.pdf'],
},
//disabled: isUploadDisabled,
multiple: false,
multiple: true,
maxSize: megabytesToBytes(APP_DOCUMENT_UPLOAD_SIZE_LIMIT),
onDrop: ([acceptedFile]) => {
if (acceptedFile) {
void onFileDrop(acceptedFile);
}
},
onDropRejected: (fileRejections) => {
onFileDropRejected(fileRejections);
},
maxFiles: maximumEnvelopeItemCount,
onDrop: (files) => void onFileDrop(files),
onDropRejected: onFileDropRejected,
noClick: true,
noDragEventsBubbling: true,
});
@ -189,7 +223,11 @@ export const DocumentDropZoneWrapper = ({ children, className }: DocumentDropZon
<div className="bg-muted/60 fixed left-0 top-0 z-[9999] h-full w-full backdrop-blur-[4px]">
<div className="pointer-events-none flex h-full w-full flex-col items-center justify-center">
<h2 className="text-foreground text-2xl font-semibold">
<Trans>Upload Document</Trans>
{type === EnvelopeType.DOCUMENT ? (
<Trans>Upload Document</Trans>
) : (
<Trans>Upload Template</Trans>
)}
</h2>
<p className="text-muted-foreground text-md mt-4">
@ -224,7 +262,7 @@ export const DocumentDropZoneWrapper = ({ children, className }: DocumentDropZon
<div className="pointer-events-none flex h-1/2 w-full flex-col items-center justify-center">
<Loader className="text-primary h-12 w-12 animate-spin" />
<p className="text-foreground mt-8 font-medium">
<Trans>Uploading document...</Trans>
<Trans>Uploading</Trans>
</p>
</div>
</div>

View File

@ -18,7 +18,7 @@ import { formatDocumentsPath, formatTemplatesPath } from '@documenso/lib/utils/t
import { trpc } from '@documenso/trpc/react';
import type { TCreateEnvelopePayload } from '@documenso/trpc/server/envelope-router/create-envelope.types';
import { cn } from '@documenso/ui/lib/utils';
import { DocumentDropzone } from '@documenso/ui/primitives/document-upload';
import { DocumentUploadButton } from '@documenso/ui/primitives/document-upload-button';
import {
Tooltip,
TooltipContent,
@ -175,13 +175,14 @@ export const EnvelopeUploadButton = ({ className, type, folderId }: EnvelopeUplo
<Tooltip>
<TooltipTrigger asChild>
<div>
<DocumentDropzone
<DocumentUploadButton
loading={isLoading}
disabled={remaining.documents === 0 || !user.emailVerified}
disabledMessage={disabledMessage}
onDrop={onFileDrop}
onDropRejected={onFileDropRejected}
type="envelope"
type={type}
internalVersion="2"
maxFiles={maximumEnvelopeItemCount}
/>
</div>

View File

@ -6,7 +6,6 @@ import { FolderIcon, HomeIcon } from 'lucide-react';
import { Link } from 'react-router';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { IS_ENVELOPES_ENABLED } from '@documenso/lib/constants/app';
import { formatDocumentsPath, formatTemplatesPath } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react';
import { type TFolderWithSubfolders } from '@documenso/trpc/server/folder-router/schema';
@ -17,11 +16,11 @@ import { FolderDeleteDialog } from '~/components/dialogs/folder-delete-dialog';
import { FolderMoveDialog } from '~/components/dialogs/folder-move-dialog';
import { FolderUpdateDialog } from '~/components/dialogs/folder-update-dialog';
import { TemplateCreateDialog } from '~/components/dialogs/template-create-dialog';
import { DocumentUploadButton } from '~/components/general/document/document-upload-button';
import { DocumentUploadButtonLegacy } from '~/components/general/document/document-upload-button-legacy';
import { FolderCard, FolderCardEmpty } from '~/components/general/folder/folder-card';
import { useCurrentTeam } from '~/providers/team';
import { EnvelopeUploadButton } from '../document/envelope-upload-button';
import { EnvelopeUploadButton } from '../envelope/envelope-upload-button';
export type FolderGridProps = {
type: FolderType;
@ -99,14 +98,12 @@ export const FolderGrid = ({ type, parentId }: FolderGridProps) => {
</div>
<div className="flex gap-4 sm:flex-row sm:justify-end">
{(IS_ENVELOPES_ENABLED || organisation.organisationClaim.flags.allowEnvelopes) && (
<EnvelopeUploadButton type={type} folderId={parentId || undefined} />
)}
<EnvelopeUploadButton type={type} folderId={parentId || undefined} />
{type === FolderType.DOCUMENT ? (
<DocumentUploadButton />
<DocumentUploadButtonLegacy /> // If you delete this, delete the component as well.
) : (
<TemplateCreateDialog folderId={parentId ?? undefined} />
<TemplateCreateDialog folderId={parentId ?? undefined} /> // If you delete this, delete the component as well.
)}
<FolderCreateDialog type={type} />

View File

@ -1,171 +0,0 @@
import { type ReactNode, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { Loader } from 'lucide-react';
import { ErrorCode, type FileRejection, useDropzone } from 'react-dropzone';
import { useNavigate, useParams } from 'react-router';
import { match } from 'ts-pattern';
import { APP_DOCUMENT_UPLOAD_SIZE_LIMIT } from '@documenso/lib/constants/app';
import { megabytesToBytes } from '@documenso/lib/universal/unit-convertions';
import { formatTemplatesPath } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react';
import type { TCreateTemplatePayloadSchema } from '@documenso/trpc/server/template-router/schema';
import { cn } from '@documenso/ui/lib/utils';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { useCurrentTeam } from '~/providers/team';
export interface TemplateDropZoneWrapperProps {
children: ReactNode;
className?: string;
}
export const TemplateDropZoneWrapper = ({ children, className }: TemplateDropZoneWrapperProps) => {
const { _ } = useLingui();
const { toast } = useToast();
const { folderId } = useParams();
const team = useCurrentTeam();
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(false);
const { mutateAsync: createTemplate } = trpc.template.createTemplate.useMutation();
const onFileDrop = async (file: File) => {
try {
setIsLoading(true);
const payload = {
title: file.name,
folderId: folderId ?? undefined,
} satisfies TCreateTemplatePayloadSchema;
const formData = new FormData();
formData.append('payload', JSON.stringify(payload));
formData.append('file', file);
const { envelopeId: id } = await createTemplate(formData);
toast({
title: _(msg`Template uploaded`),
description: _(
msg`Your template has been uploaded successfully. You will be redirected to the template page.`,
),
duration: 5000,
});
await navigate(`${formatTemplatesPath(team.url)}/${id}/edit`);
} catch {
toast({
title: _(msg`Something went wrong`),
description: _(msg`Please try again later.`),
variant: 'destructive',
});
} finally {
setIsLoading(false);
}
};
const onFileDropRejected = (fileRejections: FileRejection[]) => {
if (!fileRejections.length) {
return;
}
// Since users can only upload only one file (no multi-upload), we only handle the first file rejection
const { file, errors } = fileRejections[0];
if (!errors.length) {
return;
}
const errorNodes = errors.map((error, index) => (
<span key={index} className="block">
{match(error.code)
.with(ErrorCode.FileTooLarge, () => (
<Trans>File is larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB</Trans>
))
.with(ErrorCode.FileInvalidType, () => <Trans>Only PDF files are allowed</Trans>)
.with(ErrorCode.FileTooSmall, () => <Trans>File is too small</Trans>)
.with(ErrorCode.TooManyFiles, () => (
<Trans>Only one file can be uploaded at a time</Trans>
))
.otherwise(() => (
<Trans>Unknown error</Trans>
))}
</span>
));
const description = (
<>
<span className="font-medium">
{file.name} <Trans>couldn't be uploaded:</Trans>
</span>
{errorNodes}
</>
);
toast({
title: _(msg`Upload failed`),
description,
duration: 5000,
variant: 'destructive',
});
};
const { getRootProps, getInputProps, isDragActive } = useDropzone({
accept: {
'application/pdf': ['.pdf'],
},
//disabled: isUploadDisabled,
multiple: false,
maxSize: megabytesToBytes(APP_DOCUMENT_UPLOAD_SIZE_LIMIT),
onDrop: ([acceptedFile]) => {
if (acceptedFile) {
void onFileDrop(acceptedFile);
}
},
onDropRejected: (fileRejections) => {
onFileDropRejected(fileRejections);
},
noClick: true,
noDragEventsBubbling: true,
});
return (
<div {...getRootProps()} className={cn('relative min-h-screen', className)}>
<input {...getInputProps()} />
{children}
{isDragActive && (
<div className="bg-muted/60 fixed left-0 top-0 z-[9999] h-full w-full backdrop-blur-[4px]">
<div className="pointer-events-none flex h-full w-full flex-col items-center justify-center">
<h2 className="text-foreground text-2xl font-semibold">
<Trans>Upload Template</Trans>
</h2>
<p className="text-muted-foreground text-md mt-4">
<Trans>Drag and drop your PDF file here</Trans>
</p>
</div>
</div>
)}
{isLoading && (
<div className="bg-muted/30 absolute inset-0 z-50 backdrop-blur-[2px]">
<div className="pointer-events-none flex h-1/2 w-full flex-col items-center justify-center">
<Loader className="text-primary h-12 w-12 animate-spin" />
<p className="text-foreground mt-8 font-medium">
<Trans>Uploading template...</Trans>
</p>
</div>
</div>
)}
</div>
);
};

View File

@ -7,11 +7,13 @@ import type { User } from '@prisma/client';
import { DateTime } from 'luxon';
import { useIsMounted } from '@documenso/lib/client-only/hooks/use-is-mounted';
import { mapSecondaryIdToTemplateId } from '@documenso/lib/utils/envelope';
export type TemplatePageViewInformationProps = {
userId: number;
template: {
userId: number;
secondaryId: string;
createdAt: Date;
updatedAt: Date;
user: Pick<User, 'id' | 'name' | 'email'>;
@ -43,6 +45,10 @@ export const TemplatePageViewInformation = ({
.setLocale(i18n.locales?.[0] || i18n.locale)
.toRelative(),
},
{
description: msg`Template ID (Legacy)`,
value: mapSecondaryIdToTemplateId(template.secondaryId),
},
];
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isMounted, template, userId]);

View File

@ -2,40 +2,49 @@ import { useEffect, useMemo, useState, useTransition } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { ChevronDownIcon, ChevronUpIcon, ChevronsUpDown, Loader } from 'lucide-react';
import { Link } from 'react-router';
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import type { DateRange } from '@documenso/lib/types/search-params';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
import { DataTable } from '@documenso/ui/primitives/data-table';
import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination';
import { Input } from '@documenso/ui/primitives/input';
export type SigningVolume = {
id: number;
export type OrganisationOverview = {
id: string;
name: string;
signingVolume: number;
createdAt: Date;
planId: string;
customerId: string;
subscriptionStatus?: string;
isActive?: boolean;
teamCount?: number;
memberCount?: number;
};
type LeaderboardTableProps = {
signingVolume: SigningVolume[];
type OrganisationOverviewTableProps = {
organisations: OrganisationOverview[];
totalPages: number;
perPage: number;
page: number;
sortBy: 'name' | 'createdAt' | 'signingVolume';
sortOrder: 'asc' | 'desc';
dateRange: DateRange;
};
export const AdminLeaderboardTable = ({
signingVolume,
export const AdminOrganisationOverviewTable = ({
organisations,
totalPages,
perPage,
page,
sortBy,
sortOrder,
}: LeaderboardTableProps) => {
dateRange,
}: OrganisationOverviewTableProps) => {
const { _, i18n } = useLingui();
const [isPending, startTransition] = useTransition();
@ -67,17 +76,16 @@ export const AdminLeaderboardTable = ({
cell: ({ row }) => {
return (
<div>
<a
className="text-primary underline"
href={`https://dashboard.stripe.com/subscriptions/${row.original.planId}`}
target="_blank"
<Link
className="hover:underline"
to={`/admin/organisation-insights/${row.original.id}?dateRange=${dateRange}`}
>
{row.getValue('name')}
</a>
</Link>
</div>
);
},
size: 250,
size: 240,
},
{
header: () => (
@ -85,7 +93,9 @@ export const AdminLeaderboardTable = ({
className="flex cursor-pointer items-center"
onClick={() => handleColumnSort('signingVolume')}
>
{_(msg`Signing Volume`)}
<span className="whitespace-nowrap">
<Trans>Document Volume</Trans>
</span>
{sortBy === 'signingVolume' ? (
sortOrder === 'asc' ? (
<ChevronUpIcon className="ml-2 h-4 w-4" />
@ -99,6 +109,23 @@ export const AdminLeaderboardTable = ({
),
accessorKey: 'signingVolume',
cell: ({ row }) => <div>{Number(row.getValue('signingVolume'))}</div>,
size: 160,
},
{
header: () => {
return <Trans>Teams</Trans>;
},
accessorKey: 'teamCount',
cell: ({ row }) => <div>{Number(row.original.teamCount) || 0}</div>,
size: 120,
},
{
header: () => {
return <Trans>Members</Trans>;
},
accessorKey: 'memberCount',
cell: ({ row }) => <div>{Number(row.original.memberCount) || 0}</div>,
size: 160,
},
{
header: () => {
@ -107,7 +134,9 @@ export const AdminLeaderboardTable = ({
className="flex cursor-pointer items-center"
onClick={() => handleColumnSort('createdAt')}
>
{_(msg`Created`)}
<span className="whitespace-nowrap">
<Trans>Created</Trans>
</span>
{sortBy === 'createdAt' ? (
sortOrder === 'asc' ? (
<ChevronUpIcon className="ml-2 h-4 w-4" />
@ -121,10 +150,11 @@ export const AdminLeaderboardTable = ({
);
},
accessorKey: 'createdAt',
cell: ({ row }) => i18n.date(row.original.createdAt),
cell: ({ row }) => i18n.date(new Date(row.original.createdAt)),
size: 120,
},
] satisfies DataTableColumnDef<SigningVolume>[];
}, [sortOrder, sortBy]);
] satisfies DataTableColumnDef<OrganisationOverview>[];
}, [sortOrder, sortBy, dateRange]);
useEffect(() => {
startTransition(() => {
@ -169,13 +199,13 @@ export const AdminLeaderboardTable = ({
<Input
className="my-6 flex flex-row gap-4"
type="text"
placeholder={_(msg`Search by name or email`)}
placeholder={_(msg`Search by organisation name`)}
value={searchString}
onChange={handleChange}
/>
<DataTable
columns={columns}
data={signingVolume}
data={organisations}
perPage={perPage}
currentPage={page}
totalPages={totalPages}

View File

@ -93,13 +93,31 @@ export const AdminOrganisationsTable = ({
),
},
{
header: t`Status`,
id: 'role',
header: t`Role`,
cell: ({ row }) => (
<Badge variant="neutral">
{row.original.owner.id === memberUserId ? t`Owner` : t`Member`}
</Badge>
),
},
{
id: 'billingStatus',
header: t`Status`,
cell: ({ row }) => {
const subscription = row.original.subscription;
const isPaid = subscription && subscription.status === 'ACTIVE';
return (
<div
className={`inline-flex items-center rounded-full px-2 py-1 text-xs font-medium ${
isPaid ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'
}`}
>
{isPaid ? t`Paid` : t`Free`}
</div>
);
},
},
{
header: t`Subscription`,
cell: ({ row }) =>
@ -168,7 +186,7 @@ export const AdminOrganisationsTable = ({
onPaginationChange={onPaginationChange}
columnVisibility={{
owner: showOwnerColumn,
status: memberUserId !== undefined,
role: memberUserId !== undefined,
}}
error={{
enable: isLoadingError,

View File

@ -0,0 +1,287 @@
import { useTransition } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Building2, Loader, TrendingUp, Users } from 'lucide-react';
import { Link } from 'react-router';
import { useNavigation } from 'react-router';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import type { OrganisationDetailedInsights } from '@documenso/lib/server-only/admin/get-organisation-detailed-insights';
import type { DateRange } from '@documenso/lib/types/search-params';
import type { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
import { Button } from '@documenso/ui/primitives/button';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
import { DataTable } from '@documenso/ui/primitives/data-table';
import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination';
import { DateRangeFilter } from '~/components/filters/date-range-filter';
import { DocumentStatus } from '~/components/general/document/document-status';
type OrganisationInsightsTableProps = {
insights: OrganisationDetailedInsights;
page: number;
perPage: number;
dateRange: DateRange;
view: 'teams' | 'users' | 'documents';
};
export const OrganisationInsightsTable = ({
insights,
page,
perPage,
dateRange,
view,
}: OrganisationInsightsTableProps) => {
const { _, i18n } = useLingui();
const [isPending, startTransition] = useTransition();
const navigation = useNavigation();
const updateSearchParams = useUpdateSearchParams();
const isLoading = isPending || navigation.state === 'loading';
const onPaginationChange = (newPage: number, newPerPage: number) => {
startTransition(() => {
updateSearchParams({
page: newPage,
perPage: newPerPage,
});
});
};
const handleViewChange = (newView: 'teams' | 'users' | 'documents') => {
startTransition(() => {
updateSearchParams({
view: newView,
page: 1,
});
});
};
const teamsColumns = [
{
header: _(msg`Team Name`),
accessorKey: 'name',
cell: ({ row }) => <span className="block max-w-full truncate">{row.getValue('name')}</span>,
size: 240,
},
{
header: _(msg`Members`),
accessorKey: 'memberCount',
cell: ({ row }) => Number(row.getValue('memberCount')),
size: 120,
},
{
header: _(msg`Documents`),
accessorKey: 'documentCount',
cell: ({ row }) => Number(row.getValue('documentCount')),
size: 140,
},
{
header: _(msg`Created`),
accessorKey: 'createdAt',
cell: ({ row }) => i18n.date(new Date(row.getValue('createdAt'))),
size: 160,
},
] satisfies DataTableColumnDef<(typeof insights.teams)[number]>[];
const usersColumns = [
{
header: () => <span className="whitespace-nowrap">{_(msg`Name`)}</span>,
accessorKey: 'name',
cell: ({ row }) => (
<Link
className="block max-w-full truncate hover:underline"
to={`/admin/users/${row.original.id}`}
>
{(row.getValue('name') as string) || (row.getValue('email') as string)}
</Link>
),
size: 220,
},
{
header: () => <span className="whitespace-nowrap">{_(msg`Email`)}</span>,
accessorKey: 'email',
cell: ({ row }) => <span className="block max-w-full truncate">{row.getValue('email')}</span>,
size: 260,
},
{
header: () => <span className="whitespace-nowrap">{_(msg`Documents Created`)}</span>,
accessorKey: 'documentCount',
cell: ({ row }) => Number(row.getValue('documentCount')),
size: 180,
},
{
header: () => <span className="whitespace-nowrap">{_(msg`Documents Completed`)}</span>,
accessorKey: 'signedDocumentCount',
cell: ({ row }) => Number(row.getValue('signedDocumentCount')),
size: 180,
},
{
header: () => <span className="whitespace-nowrap">{_(msg`Joined`)}</span>,
accessorKey: 'createdAt',
cell: ({ row }) => i18n.date(new Date(row.getValue('createdAt'))),
size: 160,
},
] satisfies DataTableColumnDef<(typeof insights.users)[number]>[];
const documentsColumns = [
{
header: () => <span className="whitespace-nowrap">{_(msg`Title`)}</span>,
accessorKey: 'title',
cell: ({ row }) => (
<Link
className="block max-w-[200px] truncate hover:underline"
to={`/admin/documents/${row.original.id}`}
title={row.getValue('title') as string}
>
{row.getValue('title')}
</Link>
),
size: 200,
},
{
header: () => <span className="whitespace-nowrap">{_(msg`Status`)}</span>,
accessorKey: 'status',
cell: ({ row }) => (
<DocumentStatus status={row.getValue('status') as ExtendedDocumentStatus} />
),
size: 120,
},
{
header: () => <span className="whitespace-nowrap">{_(msg`Team`)}</span>,
accessorKey: 'teamName',
cell: ({ row }) => (
<span className="block max-w-[150px] truncate" title={row.getValue('teamName') as string}>
{row.getValue('teamName')}
</span>
),
size: 150,
},
{
header: () => <span className="whitespace-nowrap">{_(msg`Created`)}</span>,
accessorKey: 'createdAt',
cell: ({ row }) => i18n.date(new Date(row.getValue('createdAt'))),
size: 140,
},
{
header: () => <span className="whitespace-nowrap">{_(msg`Completed`)}</span>,
accessorKey: 'completedAt',
cell: ({ row }) => {
const completedAt = row.getValue('completedAt') as Date | null;
return completedAt ? i18n.date(new Date(completedAt)) : '-';
},
size: 140,
},
] satisfies DataTableColumnDef<(typeof insights.documents)[number]>[];
const getCurrentData = (): unknown[] => {
switch (view) {
case 'teams':
return insights.teams;
case 'users':
return insights.users;
case 'documents':
return insights.documents;
default:
return [];
}
};
const getCurrentColumns = (): DataTableColumnDef<unknown>[] => {
switch (view) {
case 'teams':
return teamsColumns as unknown as DataTableColumnDef<unknown>[];
case 'users':
return usersColumns as unknown as DataTableColumnDef<unknown>[];
case 'documents':
return documentsColumns as unknown as DataTableColumnDef<unknown>[];
default:
return [];
}
};
return (
<div className="relative">
{insights.summary && (
<div className="mb-6 grid grid-cols-2 gap-4 md:grid-cols-3">
<SummaryCard icon={Building2} title={_(msg`Teams`)} value={insights.summary.totalTeams} />
<SummaryCard icon={Users} title={_(msg`Members`)} value={insights.summary.totalMembers} />
<SummaryCard
icon={TrendingUp}
title={_(msg`Documents Completed`)}
value={insights.summary.volumeThisPeriod}
/>
</div>
)}
<div className="mb-6 flex items-center justify-between">
<div className="flex gap-2">
<Button
variant={view === 'teams' ? 'default' : 'outline'}
onClick={() => handleViewChange('teams')}
disabled={isLoading}
>
{_(msg`Teams`)}
</Button>
<Button
variant={view === 'users' ? 'default' : 'outline'}
onClick={() => handleViewChange('users')}
disabled={isLoading}
>
{_(msg`Users`)}
</Button>
<Button
variant={view === 'documents' ? 'default' : 'outline'}
onClick={() => handleViewChange('documents')}
disabled={isLoading}
>
{_(msg`Documents`)}
</Button>
</div>
<DateRangeFilter currentRange={dateRange} />
</div>
<div className={view === 'documents' ? 'overflow-hidden' : undefined}>
<DataTable<unknown, unknown>
columns={getCurrentColumns()}
data={getCurrentData()}
perPage={perPage}
currentPage={page}
totalPages={insights.totalPages}
onPaginationChange={onPaginationChange}
>
{(table) => <DataTablePagination additionalInformation="VisibleCount" table={table} />}
</DataTable>
</div>
{isLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" />
</div>
)}
</div>
);
};
const SummaryCard = ({
icon: Icon,
title,
value,
subtitle,
}: {
icon: React.ComponentType<{ className?: string }>;
title: string;
value: number;
subtitle?: string;
}) => (
<div className="bg-card flex items-start gap-x-2 rounded-lg border px-4 py-3">
<Icon className="text-muted-foreground h-4 w-4 items-start" />
<div className="-mt-0.5 space-y-2">
<p className="text-muted-foreground text-sm font-medium">{title}</p>
<p className="text-2xl font-bold">{value}</p>
{subtitle && <p className="text-muted-foreground text-xs">{subtitle}</p>}
</div>
</div>
);

View File

@ -56,7 +56,14 @@ export const UserOrganisationsTable = () => {
avatarFallback={row.original.name.slice(0, 1).toUpperCase()}
primaryText={
<span className="text-foreground/80 font-semibold">
{isPersonalLayoutMode ? _(msg`Personal`) : row.original.name}
{isPersonalLayoutMode
? _(
msg({
message: `Personal`,
context: `Personal organisation (adjective)`,
}),
)
: row.original.name}
</span>
}
secondaryText={

View File

@ -88,14 +88,12 @@ export default function Layout({ loaderData, params, matches }: Route.ComponentP
? {
heading: msg`Organisation not found`,
subHeading: msg`404 Organisation not found`,
message: msg`The organisation you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The organisation you are looking for may have been removed, renamed or may have never existed.`,
}
: {
heading: msg`Team not found`,
subHeading: msg`404 Team not found`,
message: msg`The team you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The team you are looking for may have been removed, renamed or may have never existed.`,
},
}}
primaryButton={

View File

@ -114,13 +114,13 @@ export default function AdminLayout() {
variant="ghost"
className={cn(
'justify-start md:w-full',
pathname?.startsWith('/admin/leaderboard') && 'bg-secondary',
pathname?.startsWith('/admin/organisation-insights') && 'bg-secondary',
)}
asChild
>
<Link to="/admin/leaderboard">
<Link to="/admin/organisation-insights">
<Trophy className="mr-2 h-5 w-5" />
<Trans>Leaderboard</Trans>
<Trans>Organisation Insights</Trans>
</Link>
</Button>
@ -128,7 +128,7 @@ export default function AdminLayout() {
variant="ghost"
className={cn(
'justify-start md:w-full',
pathname?.startsWith('/admin/banner') && 'bg-secondary',
pathname?.startsWith('/admin/site-settings') && 'bg-secondary',
)}
asChild
>

View File

@ -1,77 +0,0 @@
import { Trans } from '@lingui/react/macro';
import { getSigningVolume } from '@documenso/lib/server-only/admin/get-signing-volume';
import {
AdminLeaderboardTable,
type SigningVolume,
} from '~/components/tables/admin-leaderboard-table';
import type { Route } from './+types/leaderboard';
export async function loader({ request }: Route.LoaderArgs) {
const url = new URL(request.url);
const rawSortBy = url.searchParams.get('sortBy') || 'signingVolume';
const rawSortOrder = url.searchParams.get('sortOrder') || 'desc';
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const sortOrder = (['asc', 'desc'].includes(rawSortOrder) ? rawSortOrder : 'desc') as
| 'asc'
| 'desc';
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const sortBy = (
['name', 'createdAt', 'signingVolume'].includes(rawSortBy) ? rawSortBy : 'signingVolume'
) as 'name' | 'createdAt' | 'signingVolume';
const page = Number(url.searchParams.get('page')) || 1;
const perPage = Number(url.searchParams.get('perPage')) || 10;
const search = url.searchParams.get('search') || '';
const { leaderboard, totalPages } = await getSigningVolume({
search,
page,
perPage,
sortBy,
sortOrder,
});
const typedSigningVolume: SigningVolume[] = leaderboard.map((item) => ({
...item,
name: item.name || '',
createdAt: item.createdAt || new Date(),
}));
return {
signingVolume: typedSigningVolume,
totalPages,
page,
perPage,
sortBy,
sortOrder,
};
}
export default function Leaderboard({ loaderData }: Route.ComponentProps) {
const { signingVolume, totalPages, page, perPage, sortBy, sortOrder } = loaderData;
return (
<div>
<div className="flex items-center">
<h2 className="text-4xl font-semibold">
<Trans>Signing Volume</Trans>
</h2>
</div>
<div className="mt-8">
<AdminLeaderboardTable
signingVolume={signingVolume}
totalPages={totalPages}
page={page}
perPage={perPage}
sortBy={sortBy}
sortOrder={sortOrder}
/>
</div>
</div>
);
}

View File

@ -0,0 +1,59 @@
import { getOrganisationDetailedInsights } from '@documenso/lib/server-only/admin/get-organisation-detailed-insights';
import type { DateRange } from '@documenso/lib/types/search-params';
import { getAdminOrganisation } from '@documenso/trpc/server/admin-router/get-admin-organisation';
import { OrganisationInsightsTable } from '~/components/tables/organisation-insights-table';
import type { Route } from './+types/organisation-insights.$id';
export async function loader({ params, request }: Route.LoaderArgs) {
const { id } = params;
const url = new URL(request.url);
const page = Number(url.searchParams.get('page')) || 1;
const perPage = Number(url.searchParams.get('perPage')) || 10;
const dateRange = (url.searchParams.get('dateRange') || 'last30days') as DateRange;
const view = (url.searchParams.get('view') || 'teams') as 'teams' | 'users' | 'documents';
const [insights, organisation] = await Promise.all([
getOrganisationDetailedInsights({
organisationId: id,
page,
perPage,
dateRange,
view,
}),
getAdminOrganisation({ organisationId: id }),
]);
return {
organisationId: id,
organisationName: organisation.name,
insights,
page,
perPage,
dateRange,
view,
};
}
export default function OrganisationInsights({ loaderData }: Route.ComponentProps) {
const { insights, page, perPage, dateRange, view, organisationName } = loaderData;
return (
<div>
<div className="flex items-center justify-between">
<h2 className="text-4xl font-semibold">{organisationName}</h2>
</div>
<div className="mt-8">
<OrganisationInsightsTable
insights={insights}
page={page}
perPage={perPage}
dateRange={dateRange}
view={view}
/>
</div>
</div>
);
}

View File

@ -0,0 +1,91 @@
import { Trans } from '@lingui/react/macro';
import { getOrganisationInsights } from '@documenso/lib/server-only/admin/get-signing-volume';
import type { DateRange } from '@documenso/lib/types/search-params';
import { DateRangeFilter } from '~/components/filters/date-range-filter';
import {
AdminOrganisationOverviewTable,
type OrganisationOverview,
} from '~/components/tables/admin-organisation-overview-table';
import type { Route } from './+types/organisation-insights._index';
export async function loader({ request }: Route.LoaderArgs) {
const url = new URL(request.url);
const rawSortBy = url.searchParams.get('sortBy') || 'signingVolume';
const rawSortOrder = url.searchParams.get('sortOrder') || 'desc';
const isSortOrder = (value: string): value is 'asc' | 'desc' =>
value === 'asc' || value === 'desc';
const isSortBy = (value: string): value is 'name' | 'createdAt' | 'signingVolume' =>
value === 'name' || value === 'createdAt' || value === 'signingVolume';
const sortOrder: 'asc' | 'desc' = isSortOrder(rawSortOrder) ? rawSortOrder : 'desc';
const sortBy: 'name' | 'createdAt' | 'signingVolume' = isSortBy(rawSortBy)
? rawSortBy
: 'signingVolume';
const page = Number(url.searchParams.get('page')) || 1;
const perPage = Number(url.searchParams.get('perPage')) || 10;
const search = url.searchParams.get('search') || '';
const dateRange = (url.searchParams.get('dateRange') || 'last30days') as DateRange;
const { organisations, totalPages } = await getOrganisationInsights({
search,
page,
perPage,
sortBy,
sortOrder,
dateRange,
});
const typedOrganisations: OrganisationOverview[] = organisations.map((item) => ({
id: String(item.id),
name: item.name || '',
signingVolume: item.signingVolume,
createdAt: item.createdAt || new Date(),
customerId: item.customerId || '',
subscriptionStatus: item.subscriptionStatus,
teamCount: item.teamCount || 0,
memberCount: item.memberCount || 0,
}));
return {
organisations: typedOrganisations,
totalPages,
page,
perPage,
sortBy,
sortOrder,
dateRange,
};
}
export default function Organisations({ loaderData }: Route.ComponentProps) {
const { organisations, totalPages, page, perPage, sortBy, sortOrder, dateRange } = loaderData;
return (
<div>
<div className="flex items-center justify-between">
<h2 className="text-4xl font-semibold">
<Trans>Organisation Insights</Trans>
</h2>
<DateRangeFilter currentRange={dateRange} />
</div>
<div className="mt-8">
<AdminOrganisationOverviewTable
organisations={organisations}
totalPages={totalPages}
page={page}
perPage={perPage}
sortBy={sortBy}
sortOrder={sortOrder}
dateRange={dateRange}
/>
</div>
</div>
);
}

View File

@ -142,8 +142,7 @@ export default function OrganisationGroupSettingsPage({ params }: Route.Componen
404: {
heading: msg`Organisation not found`,
subHeading: msg`404 Organisation not found`,
message: msg`The organisation you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The organisation you are looking for may have been removed, renamed or may have never existed.`,
},
}}
primaryButton={

View File

@ -59,8 +59,7 @@ export default function UserPage({ params }: { params: { id: number } }) {
404: {
heading: msg`User not found`,
subHeading: msg`404 User not found`,
message: msg`The user you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The user you are looking for may have been removed, renamed or may have never existed.`,
},
}}
primaryButton={

View File

@ -117,8 +117,7 @@ export default function OrganisationEmailDomainSettingsPage({ params }: Route.Co
404: {
heading: msg`Email domain not found`,
subHeading: msg`404 Email domain not found`,
message: msg`The email domain you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The email domain you are looking for may have been removed, renamed or may have never existed.`,
},
}}
primaryButton={

View File

@ -89,8 +89,7 @@ export default function OrganisationGroupSettingsPage({ params }: Route.Componen
404: {
heading: msg`Organisation group not found`,
subHeading: msg`404 Organisation group not found`,
message: msg`The organisation group you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The organisation group you are looking for may have been removed, renamed or may have never existed.`,
},
}}
primaryButton={

View File

@ -60,8 +60,7 @@ export default function Layout() {
404: {
heading: msg`Team not found`,
subHeading: msg`404 Team not found`,
message: msg`The team you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The team you are looking for may have been removed, renamed or may have never existed.`,
},
}}
primaryButton={

View File

@ -71,8 +71,7 @@ export default function DocumentPage({ params }: Route.ComponentProps) {
404: {
heading: msg`Not found`,
subHeading: msg`404 Not found`,
message: msg`The document you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The document you are looking for may have been removed, renamed or may have never existed.`,
},
}}
primaryButton={
@ -127,7 +126,11 @@ export default function DocumentPage({ params }: Route.ComponentProps) {
position="bottom"
>
<span>
<Trans>{envelope.recipients.length} Recipient(s)</Trans>
<Plural
value={envelope.recipients.length}
one="# Recipient"
other="# Recipients"
/>
</span>
</StackAvatarsWithTooltip>
</div>

View File

@ -82,8 +82,7 @@ export default function EnvelopeEditorPage({ params }: Route.ComponentProps) {
404: {
heading: msg`Not found`,
subHeading: msg`404 Not found`,
message: msg`The document you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The document you are looking for may have been removed, renamed or may have never existed.`,
},
}}
primaryButton={

View File

@ -1,6 +1,7 @@
import { useEffect, useMemo, useState } from 'react';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { FolderType, OrganisationType } from '@prisma/client';
import { useParams, useSearchParams } from 'react-router';
import { Link } from 'react-router';
@ -18,9 +19,9 @@ import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/av
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
import { DocumentMoveToFolderDialog } from '~/components/dialogs/document-move-to-folder-dialog';
import { DocumentDropZoneWrapper } from '~/components/general/document/document-drop-zone-wrapper';
import { DocumentSearch } from '~/components/general/document/document-search';
import { DocumentStatus } from '~/components/general/document/document-status';
import { EnvelopeDropZoneWrapper } from '~/components/general/envelope/envelope-drop-zone-wrapper';
import { FolderGrid } from '~/components/general/folder/folder-grid';
import { PeriodSelector } from '~/components/general/period-selector';
import { DocumentsTable } from '~/components/tables/documents-table';
@ -108,9 +109,8 @@ export default function DocumentsPage() {
}
}, [data?.stats]);
// Todo: Envelopes - Change the dropzone wrapper to create to V2 documents after we're ready.
return (
<DocumentDropZoneWrapper>
<EnvelopeDropZoneWrapper type={EnvelopeType.DOCUMENT}>
<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
<FolderGrid type={FolderType.DOCUMENT} parentId={folderId ?? null} />
@ -210,6 +210,6 @@ export default function DocumentsPage() {
/>
)}
</div>
</DocumentDropZoneWrapper>
</EnvelopeDropZoneWrapper>
);
}

View File

@ -109,8 +109,7 @@ export default function WebhookPage({ params }: Route.ComponentProps) {
404: {
heading: msg`Webhook not found`,
subHeading: msg`404 Webhook not found`,
message: msg`The webhook you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The webhook you are looking for may have been removed, renamed or may have never existed.`,
},
}}
primaryButton={

View File

@ -66,8 +66,7 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
404: {
heading: msg`Not found`,
subHeading: msg`404 Not found`,
message: msg`The template you are looking for may have been removed, renamed or may have never
existed.`,
message: msg`The template you are looking for may have been removed, renamed or may have never existed.`,
},
}}
primaryButton={

View File

@ -1,4 +1,5 @@
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { Bird } from 'lucide-react';
import { useParams, useSearchParams } from 'react-router';
@ -8,8 +9,8 @@ import { formatDocumentsPath, formatTemplatesPath } from '@documenso/lib/utils/t
import { trpc } from '@documenso/trpc/react';
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
import { EnvelopeDropZoneWrapper } from '~/components/general/envelope/envelope-drop-zone-wrapper';
import { FolderGrid } from '~/components/general/folder/folder-grid';
import { TemplateDropZoneWrapper } from '~/components/general/template/template-drop-zone-wrapper';
import { TemplatesTable } from '~/components/tables/templates-table';
import { useCurrentTeam } from '~/providers/team';
import { appMetaTags } from '~/utils/meta';
@ -37,7 +38,7 @@ export default function TemplatesPage() {
});
return (
<TemplateDropZoneWrapper>
<EnvelopeDropZoneWrapper type={EnvelopeType.TEMPLATE}>
<div className="mx-auto max-w-screen-xl px-4 md:px-8">
<FolderGrid type={FolderType.TEMPLATE} parentId={folderId ?? null} />
@ -85,6 +86,6 @@ export default function TemplatesPage() {
</div>
</div>
</div>
</TemplateDropZoneWrapper>
</EnvelopeDropZoneWrapper>
);
}

View File

@ -8,7 +8,7 @@ import { SignFieldNumberDialog } from '~/components/dialogs/sign-field-number-di
type HandleNumberFieldClickOptions = {
field: TFieldNumber;
number: number | null;
number: string | null;
};
export const handleNumberFieldClick = async (

View File

@ -106,5 +106,5 @@
"vite-plugin-babel-macros": "^1.0.6",
"vite-tsconfig-paths": "^5.1.4"
},
"version": "2.0.2"
"version": "2.0.6"
}

Binary file not shown.

6
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "@documenso/root",
"version": "2.0.2",
"version": "2.0.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@documenso/root",
"version": "2.0.2",
"version": "2.0.6",
"workspaces": [
"apps/*",
"packages/*"
@ -100,7 +100,7 @@
},
"apps/remix": {
"name": "@documenso/remix",
"version": "2.0.2",
"version": "2.0.6",
"dependencies": {
"@cantoo/pdf-lib": "^2.5.2",
"@documenso/api": "*",

View File

@ -1,6 +1,6 @@
{
"private": true,
"version": "2.0.2",
"version": "2.0.6",
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev --filter=@documenso/remix",

View File

@ -1,4 +1,6 @@
import { FieldType } from '@prisma/client';
import fs from 'node:fs';
import path from 'node:path';
import type { TFieldAndMeta } from '@documenso/lib/types/field-meta';
import { toCheckboxCustomText } from '@documenso/lib/utils/fields';
@ -13,11 +15,66 @@ export type FieldTestData = TFieldAndMeta & {
signature?: string;
};
const columnWidth = 19.125;
const rowHeight = 6.7;
export const signatureBase64Demo = `data:image/png;base64,${fs.readFileSync(
path.join(__dirname, '../../../packages/assets/', 'logo_icon.png'),
'base64',
)}`;
const alignmentGridStartX = 31;
const alignmentGridStartY = 19.02;
const columnWidth = 19.125;
const fullColumnWidth = 57.37499999999998;
const rowHeight = 6.7;
const rowPadding = 0;
const calculatePositionPageOne = (
row: number,
column: number,
width: 'full' | 'column' = 'column',
) => {
const alignmentGridStartX = 31;
const alignmentGridStartY = 19;
return {
height: rowHeight,
width: width === 'full' ? fullColumnWidth : columnWidth,
positionX: alignmentGridStartX + (column ?? 0) * columnWidth,
positionY: alignmentGridStartY + row * (rowHeight + rowPadding),
};
};
const calculatePositionPageTwo = (
row: number,
column: number,
width: 'full' | 'column' = 'column',
) => {
const alignmentGridStartX = 31;
const alignmentGridStartY = 16.35;
return {
height: rowHeight,
width: width === 'full' ? fullColumnWidth : columnWidth,
positionX: alignmentGridStartX + (column ?? 0) * columnWidth,
positionY: alignmentGridStartY + row * (rowHeight + rowPadding),
};
};
const calculatePositionPageThree = (
row: number,
column: number,
width: 'full' | 'column' = 'column',
rowQuantity: number = 1,
) => {
const alignmentGridStartX = 31;
const alignmentGridStartY = 16.4;
const rowHeight = 6.8;
return {
height: rowHeight * rowQuantity,
width: width === 'full' ? fullColumnWidth : columnWidth,
positionX: alignmentGridStartX + (column ?? 0) * columnWidth,
positionY: alignmentGridStartY + row * (rowHeight + rowPadding),
};
};
export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
/**
@ -31,10 +88,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'email',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(0, 0),
customText: 'admin@documenso.com',
},
{
@ -44,10 +98,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'email',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(0, 1),
customText: 'admin@documenso.com',
},
{
@ -58,10 +109,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'email',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(0, 2),
customText: 'admin@documenso.com',
},
/**
@ -75,10 +123,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'name',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(1, 0),
customText: 'John Doe',
},
{
@ -88,10 +133,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'name',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(1, 1),
customText: 'John Doe',
},
{
@ -102,10 +144,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'name',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(1, 2),
customText: 'John Doe',
},
/**
@ -119,10 +158,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'date',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(2, 0),
customText: '123456789',
},
{
@ -132,10 +168,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'date',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(2, 1),
customText: '123456789',
},
{
@ -146,10 +179,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'date',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(2, 2),
customText: '123456789',
},
/**
@ -163,10 +193,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'text',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(3, 0),
customText: '123456789',
},
{
@ -176,10 +203,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'text',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(3, 1),
customText: '123456789',
},
{
@ -190,10 +214,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'text',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(3, 2),
customText: '123456789',
},
/**
@ -207,10 +228,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'number',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(4, 0),
customText: '123456789',
},
{
@ -220,10 +238,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'number',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(4, 1),
customText: '123456789',
},
{
@ -234,10 +249,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'number',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(4, 2),
customText: '123456789',
},
/**
@ -251,10 +263,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'initials',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(5, 0),
customText: 'JD',
},
{
@ -264,10 +273,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'initials',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(5, 1),
customText: 'JD',
},
{
@ -278,10 +284,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'initials',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(5, 2),
customText: 'JD',
},
/**
@ -299,10 +302,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
],
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(6, 0),
customText: '0',
},
{
@ -312,15 +312,12 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'radio',
values: [
{ id: 1, checked: false, value: 'Option 1' },
{ id: 2, checked: true, value: 'Option 2' },
{ id: 2, checked: false, value: 'Option 2' },
],
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
customText: '2',
...calculatePositionPageOne(6, 1),
customText: '',
},
{
type: FieldType.RADIO,
@ -330,15 +327,12 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'radio',
values: [
{ id: 1, checked: false, value: 'Option 1' },
{ id: 2, checked: false, value: 'Option 2' },
{ id: 2, checked: true, value: 'Option 2' },
],
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
customText: '',
...calculatePositionPageOne(6, 2),
customText: '1',
},
/**
* Row 8 Checkbox
@ -355,10 +349,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
],
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(7, 0),
customText: toCheckboxCustomText([0]),
},
{
@ -368,15 +359,12 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'checkbox',
values: [
{ id: 1, checked: false, value: 'Option 1' },
{ id: 2, checked: true, value: 'Option 2' },
{ id: 2, checked: false, value: 'Option 2' },
],
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
customText: toCheckboxCustomText([1]),
...calculatePositionPageOne(7, 1),
customText: '',
},
{
type: FieldType.CHECKBOX,
@ -386,15 +374,12 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'checkbox',
values: [
{ id: 1, checked: false, value: 'Option 1' },
{ id: 2, checked: false, value: 'Option 2' },
{ id: 2, checked: true, value: 'Option 2' },
],
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
customText: '',
...calculatePositionPageOne(7, 2),
customText: toCheckboxCustomText([1]),
},
/**
* Row 8 Dropdown
@ -407,10 +392,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'dropdown',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(8, 0),
customText: 'Option 1',
},
{
@ -420,10 +402,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'dropdown',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(8, 1),
customText: 'Option 1',
},
{
@ -434,10 +413,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'dropdown',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(8, 2),
customText: 'Option 1',
},
/**
@ -450,10 +426,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'signature',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(9, 0),
customText: '',
signature: 'My Signature',
},
@ -463,10 +436,7 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'signature',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(9, 1),
customText: '',
signature: 'My Signature',
},
@ -477,22 +447,295 @@ export const ALIGNMENT_TEST_FIELDS: FieldTestData[] = [
type: 'signature',
},
page: 1,
height: rowHeight,
width: columnWidth,
positionX: 0,
positionY: 0,
...calculatePositionPageOne(9, 2),
customText: '',
signature: 'My Signature',
},
/**
* @@@@@@@@@@@@@@@@@@@@@@@
*
* PAGE 2
*
* @@@@@@@@@@@@@@@@@@@@@@@
*/
// TEXT GRID ROW 1
{
type: FieldType.TEXT,
fieldMeta: {
textAlign: 'left',
type: 'text',
verticalAlign: 'top',
},
page: 2,
...calculatePositionPageTwo(0, 0),
customText: 'SOME TEXT',
},
{
type: FieldType.TEXT,
fieldMeta: {
textAlign: 'center',
type: 'text',
verticalAlign: 'top',
},
page: 2,
...calculatePositionPageTwo(0, 1),
customText: 'SOME TEXT',
},
{
type: FieldType.TEXT,
fieldMeta: {
textAlign: 'right',
type: 'text',
verticalAlign: 'top',
},
page: 2,
...calculatePositionPageTwo(0, 2),
customText: 'SOME TEXT',
},
// TEXT GRID ROW 2
{
type: FieldType.TEXT,
fieldMeta: {
textAlign: 'left',
type: 'text',
verticalAlign: 'middle',
},
page: 2,
...calculatePositionPageTwo(1, 0),
customText: 'SOME TEXT',
},
{
type: FieldType.TEXT,
fieldMeta: {
textAlign: 'center',
type: 'text',
verticalAlign: 'middle',
},
page: 2,
...calculatePositionPageTwo(1, 1),
customText: 'SOME TEXT',
},
{
type: FieldType.TEXT,
fieldMeta: {
textAlign: 'right',
type: 'text',
verticalAlign: 'middle',
},
page: 2,
...calculatePositionPageTwo(1, 2),
customText: 'SOME TEXT',
},
// TEXT GRID ROW 3
{
type: FieldType.TEXT,
fieldMeta: {
textAlign: 'left',
type: 'text',
verticalAlign: 'bottom',
},
page: 2,
...calculatePositionPageTwo(2, 0),
customText: 'SOME TEXT',
},
{
type: FieldType.TEXT,
fieldMeta: {
textAlign: 'center',
type: 'text',
verticalAlign: 'bottom',
},
page: 2,
...calculatePositionPageTwo(2, 1),
customText: 'SOME TEXT',
},
{
type: FieldType.TEXT,
fieldMeta: {
textAlign: 'right',
type: 'text',
verticalAlign: 'bottom',
},
page: 2,
...calculatePositionPageTwo(2, 2),
customText: 'SOME TEXT',
},
// NUMBER GRID ROW 1
{
type: FieldType.NUMBER,
fieldMeta: {
textAlign: 'left',
type: 'number',
verticalAlign: 'top',
},
page: 2,
...calculatePositionPageTwo(3, 0),
customText: '123456789123456789',
},
{
type: FieldType.NUMBER,
fieldMeta: {
textAlign: 'center',
type: 'number',
verticalAlign: 'top',
},
page: 2,
...calculatePositionPageTwo(3, 1),
customText: '123456789123456789',
},
{
type: FieldType.NUMBER,
fieldMeta: {
textAlign: 'right',
type: 'number',
verticalAlign: 'top',
},
page: 2,
...calculatePositionPageTwo(3, 2),
customText: '123456789123456789',
},
// NUMBER GRID ROW 2
{
type: FieldType.NUMBER,
fieldMeta: {
textAlign: 'left',
type: 'number',
verticalAlign: 'middle',
},
page: 2,
...calculatePositionPageTwo(4, 0),
customText: '123456789123456789',
},
{
type: FieldType.NUMBER,
fieldMeta: {
textAlign: 'center',
type: 'number',
verticalAlign: 'middle',
},
page: 2,
...calculatePositionPageTwo(4, 1),
customText: '123456789123456789',
},
{
type: FieldType.NUMBER,
fieldMeta: {
textAlign: 'right',
type: 'number',
verticalAlign: 'middle',
},
page: 2,
...calculatePositionPageTwo(4, 2),
customText: '123456789123456789',
},
// NUMBER GRID ROW 3
{
type: FieldType.NUMBER,
fieldMeta: {
textAlign: 'left',
type: 'number',
verticalAlign: 'bottom',
},
page: 2,
...calculatePositionPageTwo(5, 0),
customText: '123456789123456789',
},
{
type: FieldType.NUMBER,
fieldMeta: {
textAlign: 'center',
type: 'number',
verticalAlign: 'bottom',
},
page: 2,
...calculatePositionPageTwo(5, 1),
customText: '123456789123456789',
},
{
type: FieldType.NUMBER,
fieldMeta: {
textAlign: 'right',
type: 'number',
verticalAlign: 'bottom',
},
page: 2,
...calculatePositionPageTwo(5, 2),
customText: '123456789123456789',
},
// Text combing
{
type: FieldType.TEXT,
fieldMeta: {
type: 'text',
verticalAlign: 'middle',
letterSpacing: 32,
characterLimit: 9,
},
page: 2,
...calculatePositionPageTwo(6, 0, 'full'),
positionX: calculatePositionPageTwo(6, 0, 'full').positionX + 1.75,
width: calculatePositionPageTwo(6, 0, 'full').width + 1.75,
customText: 'HEY HEY 1',
},
// Number combing
{
type: FieldType.NUMBER,
fieldMeta: {
type: 'number',
verticalAlign: 'middle',
letterSpacing: 32,
},
page: 2,
...calculatePositionPageTwo(7, 0, 'full'),
positionX: calculatePositionPageTwo(7, 0, 'full').positionX + 1.75,
width: calculatePositionPageTwo(7, 0, 'full').width + 1.75,
customText: '123456789',
},
/**
* @@@@@@@@@@@@@@@@@@@@@@@
*
* PAGE 2 TEXT MULTILINE
*
* @@@@@@@@@@@@@@@@@@@@@@@
*/
{
type: FieldType.TEXT,
fieldMeta: {
verticalAlign: 'top',
textAlign: 'left',
lineHeight: 2.24,
type: 'text',
},
page: 3,
...calculatePositionPageThree(0, 0, 'full', 3),
customText:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
},
{
type: FieldType.TEXT,
fieldMeta: {
verticalAlign: 'middle',
textAlign: 'center',
lineHeight: 2.24,
type: 'text',
},
page: 3,
...calculatePositionPageThree(3, 0, 'full', 3),
customText:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
},
{
type: FieldType.TEXT,
fieldMeta: {
verticalAlign: 'bottom',
textAlign: 'right',
lineHeight: 2.24,
type: 'text',
},
page: 3,
...calculatePositionPageThree(6, 0, 'full', 3),
customText:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
},
] as const;
export const formatAlignmentTestFields = ALIGNMENT_TEST_FIELDS.map((field, index) => {
const row = Math.floor(index / 3);
const column = index % 3;
return {
...field,
positionX: alignmentGridStartX + column * columnWidth,
positionY: alignmentGridStartY + row * rowHeight,
};
});

View File

@ -7,6 +7,7 @@ import {
} from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
import type { FieldTestData } from './field-alignment-pdf';
import { signatureBase64Demo } from './field-alignment-pdf';
const columnWidth = 20.1;
const fullColumnWidth = 75.8;
@ -37,7 +38,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
page: 2,
...calculatePosition(0, 0),
customText: '',
signature: 'My Signature',
signature: signatureBase64Demo,
},
{
type: FieldType.SIGNATURE,
@ -47,7 +48,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
page: 2,
...calculatePosition(1, 0),
customText: '',
signature: 'My Signature',
signature: signatureBase64Demo,
},
{
type: FieldType.SIGNATURE,
@ -67,7 +68,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
page: 2,
...calculatePosition(3, 0),
customText: '',
signature: 'My Signature',
signature: 'My Signature super overflow maybe',
},
/**
@ -80,7 +81,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
},
page: 3,
...calculatePosition(0, 0, 'full'),
customText: '123456789',
customText: 'Hello world, this is some random text that I have written here',
},
{
type: FieldType.TEXT,
@ -89,7 +90,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
},
page: 3,
...calculatePosition(1, 0),
customText: '123456789123456789123456789123456789',
customText: 'Some text that should overflow correctly',
},
{
type: FieldType.TEXT,
@ -109,7 +110,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
},
page: 3,
...calculatePosition(3, 0),
customText: '123456789',
customText: 'Input should have a placeholder text when clicked',
},
{
type: FieldType.TEXT,
@ -119,7 +120,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
},
page: 3,
...calculatePosition(3, 1),
customText: '123456789',
customText: 'Should have a label during editing and signing',
},
{
type: FieldType.TEXT,
@ -129,7 +130,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
},
page: 3,
...calculatePosition(3, 2),
customText: '123456789',
customText: '',
},
{
type: FieldType.TEXT,
@ -139,20 +140,19 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
},
page: 3,
...calculatePosition(4, 0),
customText: '123456789',
customText: 'This is a required field',
},
{
type: FieldType.TEXT,
fieldMeta: {
type: 'text',
readOnly: true,
text: 'Readonly Value',
text: 'Some Readonly Value',
},
page: 3,
...calculatePosition(4, 1),
customText: 'Readonly Value',
customText: '',
},
/**
* PAGE 4 NUMBER
*/
@ -220,7 +220,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
type: FieldType.NUMBER,
fieldMeta: {
type: 'number',
value: '123',
value: '123456789',
},
page: 4,
...calculatePosition(3, 2),
@ -241,10 +241,11 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
fieldMeta: {
type: 'number',
readOnly: true,
value: '123456789',
},
page: 4,
...calculatePosition(4, 1),
customText: '123456789',
customText: '',
},
/**
@ -272,8 +273,8 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
type: 'radio',
values: [
{ id: 1, checked: false, value: 'Option 1' },
{ id: 2, checked: true, value: 'Option 2' },
{ id: 3, checked: false, value: 'Option 3' },
{ id: 2, checked: false, value: 'Option 2' },
{ id: 3, checked: true, value: 'Option 3' },
],
},
page: 5,
@ -285,6 +286,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
fieldMeta: {
direction: 'vertical',
type: 'radio',
required: true,
values: [
{ id: 1, checked: false, value: 'Option 1' },
{ id: 2, checked: false, value: 'Option 2' },
@ -293,17 +295,18 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
},
page: 5,
...calculatePosition(2, 0),
customText: '',
customText: '2',
},
{
type: FieldType.RADIO,
fieldMeta: {
direction: 'vertical',
type: 'radio',
readOnly: true,
values: [
{ id: 1, checked: false, value: 'Option 1' },
{ id: 2, checked: false, value: 'Option 2' },
{ id: 3, checked: false, value: 'Option 3' },
{ id: 3, checked: true, value: 'Option 3' },
],
},
page: 5,
@ -338,7 +341,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
values: [
{ id: 1, checked: false, value: 'Option 1' },
{ id: 2, checked: true, value: 'Option 2' },
{ id: 2, checked: true, value: 'Option 3' },
{ id: 3, checked: false, value: 'Option 3' },
],
},
page: 6,
@ -358,7 +361,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
},
page: 6,
...calculatePosition(2, 0),
customText: '',
customText: toCheckboxCustomText([2]),
},
{
type: FieldType.CHECKBOX,
@ -368,7 +371,7 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
readOnly: true,
values: [
{ id: 1, checked: false, value: 'Option 1' },
{ id: 2, checked: false, value: 'Option 2' },
{ id: 2, checked: true, value: 'Option 2' },
],
},
page: 6,
@ -445,11 +448,11 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
fieldMeta: {
values: [{ value: 'Option 1' }, { value: 'Option 2' }],
type: 'dropdown',
defaultValue: 'Option 1',
defaultValue: 'Option 2',
},
page: 7,
...calculatePosition(1, 0),
customText: 'Option 1',
customText: 'Option 2',
},
{
type: FieldType.DROPDOWN,
@ -460,13 +463,14 @@ export const FIELD_META_TEST_FIELDS: FieldTestData[] = [
},
page: 7,
...calculatePosition(2, 0),
customText: 'Option 1',
customText: 'Option 3',
},
{
type: FieldType.DROPDOWN,
fieldMeta: {
values: [{ value: 'Option 1' }, { value: 'Option 2' }, { value: 'Option 3' }],
type: 'dropdown',
defaultValue: 'Option 1',
readOnly: true,
},
page: 7,

View File

@ -27,7 +27,7 @@ import type { TCreateEnvelopeRecipientsRequest } from '@documenso/trpc/server/en
import type { TGetEnvelopeResponse } from '@documenso/trpc/server/envelope-router/get-envelope.types';
import type { TUpdateEnvelopeRequest } from '@documenso/trpc/server/envelope-router/update-envelope.types';
import { formatAlignmentTestFields } from '../../../constants/field-alignment-pdf';
import { ALIGNMENT_TEST_FIELDS } from '../../../constants/field-alignment-pdf';
import { FIELD_META_TEST_FIELDS } from '../../../constants/field-meta-pdf';
const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL();
@ -490,7 +490,7 @@ test.describe('API V2 Envelopes', () => {
// Step 6: Create fields for first PDF (alignment fields)
const alignmentFieldsRequest = {
envelopeId: createdEnvelope.id,
data: formatAlignmentTestFields.map((field) => ({
data: ALIGNMENT_TEST_FIELDS.map((field) => ({
recipientId,
envelopeItemId: alignmentItem.id,
type: field.type,
@ -547,7 +547,7 @@ test.describe('API V2 Envelopes', () => {
expect(finalEnvelope.envelopeItems.length).toBe(2);
expect(finalEnvelope.recipients.length).toBe(1);
expect(finalEnvelope.fields.length).toBe(
formatAlignmentTestFields.length + FIELD_META_TEST_FIELDS.length,
ALIGNMENT_TEST_FIELDS.length + FIELD_META_TEST_FIELDS.length,
);
expect(finalEnvelope.title).toBe('Envelope Full Field Test');
expect(finalEnvelope.type).toBe(EnvelopeType.DOCUMENT);

View File

@ -21,7 +21,7 @@ import pixelMatch from 'pixelmatch';
import { PNG } from 'pngjs';
import type { TestInfo } from '@playwright/test';
import { expect, test } from '@playwright/test';
import { DocumentStatus } from '@prisma/client';
import { DocumentStatus, EnvelopeType } from '@prisma/client';
import fs from 'node:fs';
import path from 'node:path';
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.js';
@ -29,26 +29,218 @@ import { getEnvelopeItemPdfUrl } from '@documenso/lib/utils/envelope-download';
import { prisma } from '@documenso/prisma';
import { seedAlignmentTestDocument } from '@documenso/prisma/seed/initial-seed';
import { seedUser } from '@documenso/prisma/seed/users';
import { apiSignin } from '../fixtures/authentication';
import type {
TCreateEnvelopePayload,
TCreateEnvelopeResponse,
} from '../../../trpc/server/envelope-router/create-envelope.types';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../lib/constants/app';
import { createApiToken } from '../../../lib/server-only/public-api/create-api-token';
import { RecipientRole } from '../../../prisma/generated/types';
import { FIELD_META_TEST_FIELDS } from '../../constants/field-meta-pdf';
import { ALIGNMENT_TEST_FIELDS } from '../../constants/field-alignment-pdf';
import type { TDistributeEnvelopeRequest } from '../../../trpc/server/envelope-router/distribute-envelope.types';
import { isBase64Image } from '../../../lib/constants/signatures';
const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL();
const baseUrl = `${WEBAPP_BASE_URL}/api/v2`;
test.describe.configure({ mode: 'parallel', timeout: 60000 });
test.skip('field placement visual regression', async ({ page }, testInfo) => {
const { user, team } = await seedUser();
const envelope = await seedAlignmentTestDocument({
userId: user.id,
teamId: team.id,
recipientName: user.name || '',
recipientEmail: user.email,
insertFields: true,
status: DocumentStatus.PENDING,
test.skip('seed alignment test document', async ({ page }) => {
const user = await prisma.user.findFirstOrThrow({
where: {
email: 'example@documenso.com',
},
include: {
ownedOrganisations: {
include: {
teams: true,
},
},
},
});
const token = envelope.recipients[0].token;
const userId = user.id;
const teamId = user.ownedOrganisations[0].teams[0].id;
const signUrl = `/sign/${token}`;
await seedAlignmentTestDocument({
userId,
teamId,
recipientName: user.name || '',
recipientEmail: user.email,
insertFields: false,
status: DocumentStatus.DRAFT,
});
});
test('field placement visual regression', async ({ page, request }, testInfo) => {
const { user, team } = await seedUser();
const { token } = await createApiToken({
userId: user.id,
teamId: team.id,
tokenName: 'test',
expiresIn: null,
});
// Step 1: Create initial envelope with Prisma (with first envelope item)
const alignmentPdf = fs.readFileSync(
path.join(__dirname, '../../../../assets/field-font-alignment.pdf'),
);
const fieldMetaPdf = fs.readFileSync(path.join(__dirname, '../../../../assets/field-meta.pdf'));
const formData = new FormData();
const fieldMetaFields = FIELD_META_TEST_FIELDS.map((field) => ({
identifier: 'field-meta',
type: field.type,
page: field.page,
positionX: field.positionX,
positionY: field.positionY,
width: field.width,
height: field.height,
fieldMeta: field.fieldMeta,
}));
const alignmentFields = ALIGNMENT_TEST_FIELDS.map((field) => ({
identifier: 'alignment-pdf',
type: field.type,
page: field.page,
positionX: field.positionX,
positionY: field.positionY,
width: field.width,
height: field.height,
fieldMeta: field.fieldMeta,
}));
const createEnvelopePayload: TCreateEnvelopePayload = {
type: EnvelopeType.DOCUMENT,
title: 'Envelope Full Field Test',
recipients: [
{
email: user.email,
name: user.name || '',
role: RecipientRole.SIGNER,
fields: [...fieldMetaFields, ...alignmentFields],
},
],
};
formData.append('payload', JSON.stringify(createEnvelopePayload));
formData.append('files', new File([alignmentPdf], 'alignment-pdf', { type: 'application/pdf' }));
formData.append('files', new File([fieldMetaPdf], 'field-meta', { type: 'application/pdf' }));
const createEnvelopeRequest = await request.post(`${baseUrl}/envelope/create`, {
headers: { Authorization: `Bearer ${token}` },
multipart: formData,
});
expect(createEnvelopeRequest.ok()).toBeTruthy();
expect(createEnvelopeRequest.status()).toBe(200);
const { id: createdEnvelopeId }: TCreateEnvelopeResponse = await createEnvelopeRequest.json();
const envelope = await prisma.envelope.findUniqueOrThrow({
where: {
id: createdEnvelopeId,
},
include: {
recipients: true,
envelopeItems: true,
},
});
const recipientId = envelope.recipients[0].id;
const alignmentItem = envelope.envelopeItems.find((item: { order: number }) => item.order === 1);
const fieldMetaItem = envelope.envelopeItems.find((item: { order: number }) => item.order === 2);
expect(recipientId).toBeDefined();
expect(alignmentItem).toBeDefined();
expect(fieldMetaItem).toBeDefined();
if (!alignmentItem || !fieldMetaItem) {
throw new Error('Envelope items not found');
}
const distributeEnvelopeRequest = await request.post(`${baseUrl}/envelope/distribute`, {
headers: { Authorization: `Bearer ${token}` },
data: {
envelopeId: envelope.id,
} satisfies TDistributeEnvelopeRequest,
});
expect(distributeEnvelopeRequest.ok()).toBeTruthy();
const uninsertedFields = await prisma.field.findMany({
where: {
envelopeId: envelope.id,
inserted: false,
},
include: {
envelopeItem: {
select: {
title: true,
},
},
},
});
await Promise.all(
uninsertedFields.map(async (field) => {
let foundField = ALIGNMENT_TEST_FIELDS.find(
(f) =>
field.page === f.page &&
field.envelopeItem.title === 'alignment-pdf' &&
Number(field.positionX).toFixed(2) === f.positionX.toFixed(2) &&
Number(field.positionY).toFixed(2) === f.positionY.toFixed(2) &&
Number(field.width).toFixed(2) === f.width.toFixed(2) &&
Number(field.height).toFixed(2) === f.height.toFixed(2),
);
if (!foundField) {
foundField = FIELD_META_TEST_FIELDS.find(
(f) =>
field.page === f.page &&
field.envelopeItem.title === 'field-meta' &&
Number(field.positionX).toFixed(2) === f.positionX.toFixed(2) &&
Number(field.positionY).toFixed(2) === f.positionY.toFixed(2) &&
Number(field.width).toFixed(2) === f.width.toFixed(2) &&
Number(field.height).toFixed(2) === f.height.toFixed(2),
);
}
if (!foundField) {
throw new Error('Field not found');
}
await prisma.field.update({
where: {
id: field.id,
},
data: {
inserted: true,
customText: foundField.customText,
signature: foundField.signature
? {
create: {
recipientId: envelope.recipients[0].id,
signatureImageAsBase64: isBase64Image(foundField.signature)
? foundField.signature
: null,
typedSignature: isBase64Image(foundField.signature) ? null : foundField.signature,
},
}
: undefined,
},
});
}),
);
const recipientToken = envelope.recipients[0].token;
const signUrl = `/sign/${recipientToken}`;
await apiSignin({
page,
@ -97,7 +289,7 @@ test.skip('field placement visual regression', async ({ page }, testInfo) => {
const documentUrl = getEnvelopeItemPdfUrl({
type: 'download',
envelopeItem: item,
token,
token: recipientToken,
version: 'signed',
});
@ -289,7 +481,7 @@ const compareSignedPdfWithImages = async ({
// Expect the certificate to NOT be blank. Since the storedImage is blank.
expect.soft(comparison).toBeGreaterThan(20000);
} else {
expect.soft(comparison).toEqual(0);
expect.soft(comparison).toBeLessThan(2);
}
}
};

View File

@ -81,7 +81,7 @@ test('[TEAMS]: can create a document inside a document folder', async ({ page })
redirectPath: `/t/${team.url}/documents/f/${teamFolder.id}`,
});
const fileInput = page.locator('input[type="file"]').nth(1);
const fileInput = page.locator('input[type="file"]').nth(2);
await fileInput.waitFor({ state: 'attached' });
await fileInput.setInputFiles(
@ -368,7 +368,7 @@ test('[TEAMS]: can create a template inside a template folder', async ({ page })
await expect(page.getByText('Team Client Templates')).toBeVisible();
await page.getByRole('button', { name: 'New Template' }).click();
await page.getByRole('button', { name: 'Template (Legacy)' }).click();
await page.getByText('Upload Template Document').click();
@ -842,7 +842,7 @@ test('[TEAMS]: documents inherit folder visibility', async ({ page }) => {
// Upload document.
const [fileChooser] = await Promise.all([
page.waitForEvent('filechooser'),
page.getByRole('button', { name: 'Upload Document' }).click(),
page.getByRole('button', { name: 'Document (Legacy)' }).click(),
]);
await fileChooser.setFiles(

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -1,4 +1,4 @@
import { Trans } from '@lingui/react/macro';
import { Plural, Trans } from '@lingui/react/macro';
import { Heading, Img, Section, Text } from '../components';
@ -46,7 +46,11 @@ export const TemplateAccessAuth2FA = ({
</Section>
<Text className="mt-4 text-center text-sm text-slate-600">
<Trans>This code will expire in {expiresInMinutes} minutes.</Trans>
<Plural
value={expiresInMinutes}
one="This code will expire in # minute."
other="This code will expire in # minutes."
/>
</Text>
<Text className="mt-4 text-center text-sm text-slate-500">

View File

@ -11,7 +11,7 @@ export const validateNumberField = (
const { minValue, maxValue, readOnly, required, numberFormat, fontSize } = fieldMeta || {};
if (numberFormat) {
if (numberFormat && value.length > 0) {
const foundRegex = numberFormatValues.find((item) => item.value === numberFormat)?.regex;
if (!foundRegex) {

View File

@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import type { Recipient } from '@prisma/client';
import type { Field, Recipient } from '@prisma/client';
import { FieldType } from '@prisma/client';
import { useFieldArray, useForm } from 'react-hook-form';
import { z } from 'zod';
@ -63,6 +63,8 @@ type UseEditorFieldsResponse = {
// Selected recipient
selectedRecipient: Recipient | null;
setSelectedRecipient: (recipientId: number | null) => void;
resetForm: (fields?: Field[]) => void;
};
export const useEditorFields = ({
@ -72,24 +74,30 @@ export const useEditorFields = ({
const [selectedFieldFormId, setSelectedFieldFormId] = useState<string | null>(null);
const [selectedRecipientId, setSelectedRecipientId] = useState<number | null>(null);
const generateDefaultValues = (fields?: Field[]) => {
const formFields = (fields || envelope.fields).map(
(field): TLocalField => ({
id: field.id,
formId: nanoid(),
envelopeItemId: field.envelopeItemId,
page: field.page,
type: field.type,
positionX: Number(field.positionX),
positionY: Number(field.positionY),
width: Number(field.width),
height: Number(field.height),
recipientId: field.recipientId,
fieldMeta: field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : undefined,
}),
);
return {
fields: formFields,
};
};
const form = useForm<TEditorFieldsFormSchema>({
defaultValues: {
fields: envelope.fields.map(
(field): TLocalField => ({
id: field.id,
formId: nanoid(),
envelopeItemId: field.envelopeItemId,
page: field.page,
type: field.type,
positionX: Number(field.positionX),
positionY: Number(field.positionY),
width: Number(field.width),
height: Number(field.height),
recipientId: field.recipientId,
fieldMeta: field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : undefined,
}),
),
},
defaultValues: generateDefaultValues(),
resolver: zodResolver(ZEditorFieldsFormSchema),
});
@ -272,6 +280,10 @@ export const useEditorFields = ({
setSelectedRecipientId(foundRecipient?.id ?? null);
};
const resetForm = (fields?: Field[]) => {
form.reset(generateDefaultValues(fields));
};
return {
// Core state
localFields,
@ -295,6 +307,8 @@ export const useEditorFields = ({
// Selected recipient
selectedRecipient,
setSelectedRecipient,
resetForm,
};
};

View File

@ -107,6 +107,10 @@ export function usePageRenderer(renderFunction: RenderFunction) {
stage: stage.current,
pageLayer: pageLayer.current,
});
void document.fonts.ready.then(function () {
pageLayer.current?.batchDraw();
});
});
return () => {

View File

@ -30,6 +30,8 @@ type EnvelopeRenderItem = TEnvelope['envelopeItems'][number];
type EnvelopeRenderProviderValue = {
getPdfBuffer: (envelopeItemId: string) => FileData | null;
envelopeItems: EnvelopeRenderItem[];
envelopeStatus: TEnvelope['status'];
envelopeType: TEnvelope['type'];
currentEnvelopeItem: EnvelopeRenderItem | null;
setCurrentEnvelopeItem: (envelopeItemId: string) => void;
fields: Field[];
@ -44,7 +46,7 @@ type EnvelopeRenderProviderValue = {
interface EnvelopeRenderProviderProps {
children: React.ReactNode;
envelope: Pick<TEnvelope, 'envelopeItems'>;
envelope: Pick<TEnvelope, 'envelopeItems' | 'status' | 'type'>;
/**
* Optional fields which are passed down to renderers for custom rendering needs.
@ -100,7 +102,7 @@ export const EnvelopeRenderProvider = ({
// Indexed by documentDataId.
const [files, setFiles] = useState<Record<string, FileData>>({});
const [currentItem, setItem] = useState<EnvelopeRenderItem | null>(null);
const [currentItem, setCurrentItem] = useState<EnvelopeRenderItem | null>(null);
const [renderError, setRenderError] = useState<boolean>(false);
@ -163,11 +165,15 @@ export const EnvelopeRenderProvider = ({
const setCurrentEnvelopeItem = (envelopeItemId: string) => {
const foundItem = envelope.envelopeItems.find((item) => item.id === envelopeItemId);
setItem(foundItem ?? null);
setCurrentItem(foundItem ?? null);
};
// Set the selected item to the first item if none is set.
useEffect(() => {
if (currentItem && !envelopeItems.some((item) => item.id === currentItem.id)) {
setCurrentItem(null);
}
if (!currentItem && envelopeItems.length > 0) {
setCurrentEnvelopeItem(envelopeItems[0].id);
}
@ -203,6 +209,8 @@ export const EnvelopeRenderProvider = ({
value={{
getPdfBuffer,
envelopeItems,
envelopeStatus: envelope.status,
envelopeType: envelope.type,
currentEnvelopeItem: currentItem,
setCurrentEnvelopeItem,
fields: fields ?? [],

View File

@ -15,5 +15,3 @@ export const API_V2_BETA_URL = '/api/v2-beta';
export const API_V2_URL = '/api/v2';
export const SUPPORT_EMAIL = env('NEXT_PUBLIC_SUPPORT_EMAIL') ?? 'support@documenso.com';
export const IS_ENVELOPES_ENABLED = env('NEXT_PUBLIC_FEATURE_ENVELOPES_ENABLED') === 'true';

View File

@ -32,6 +32,7 @@ export type JobDefinition<Name extends string = string, Schema = any> = {
name: string;
version: string;
enabled?: boolean;
optimizeParallelism?: boolean;
trigger: {
name: Name;
schema?: z.ZodType<Schema>;

View File

@ -40,6 +40,7 @@ export class InngestJobProvider extends BaseJobProvider {
{
id: job.id,
name: job.name,
optimizeParallelism: job.optimizeParallelism ?? false,
},
{
event: job.trigger.name,

View File

@ -189,11 +189,44 @@ export const run = async ({
settings,
});
const decoratePromises: Array<Promise<{ oldDocumentDataId: string; newDocumentDataId: string }>> =
[];
// !: The commented out code is our desired implementation but we're seemingly
// !: running into issues with inngest parallelism in production.
// !: Until this is resolved we will do this sequentially which is slower but
// !: will actually work.
// const decoratePromises: Array<Promise<{ oldDocumentDataId: string; newDocumentDataId: string }>> =
// [];
// for (const envelopeItem of envelopeItems) {
// const task = io.runTask(`decorate-${envelopeItem.id}`, async () => {
// const envelopeItemFields = envelope.envelopeItems.find(
// (item) => item.id === envelopeItem.id,
// )?.field;
// if (!envelopeItemFields) {
// throw new Error(`Envelope item fields not found for envelope item ${envelopeItem.id}`);
// }
// return decorateAndSignPdf({
// envelope,
// envelopeItem,
// envelopeItemFields,
// isRejected,
// rejectionReason,
// certificateData,
// auditLogData,
// });
// });
// decoratePromises.push(task);
// }
// const newDocumentData = await Promise.all(decoratePromises);
// TODO: Remove once parallelization is working
const newDocumentData: Array<{ oldDocumentDataId: string; newDocumentDataId: string }> = [];
for (const envelopeItem of envelopeItems) {
const task = io.runTask(`decorate-${envelopeItem.id}`, async () => {
const result = await io.runTask(`decorate-${envelopeItem.id}`, async () => {
const envelopeItemFields = envelope.envelopeItems.find(
(item) => item.id === envelopeItem.id,
)?.field;
@ -213,11 +246,9 @@ export const run = async ({
});
});
decoratePromises.push(task);
newDocumentData.push(result);
}
const newDocumentData = await Promise.all(decoratePromises);
const postHog = PostHogServerClient();
if (postHog) {

View File

@ -18,6 +18,7 @@ export const SEAL_DOCUMENT_JOB_DEFINITION = {
id: SEAL_DOCUMENT_JOB_DEFINITION_ID,
name: 'Seal Document',
version: '1.0.0',
optimizeParallelism: true,
trigger: {
name: SEAL_DOCUMENT_JOB_DEFINITION_ID,
schema: SEAL_DOCUMENT_JOB_DEFINITION_SCHEMA,

View File

@ -0,0 +1,363 @@
import type { DocumentStatus } from '@prisma/client';
import { EnvelopeType } from '@prisma/client';
import type { DateRange } from '@documenso/lib/types/search-params';
import { kyselyPrisma, sql } from '@documenso/prisma';
export type OrganisationSummary = {
totalTeams: number;
totalMembers: number;
totalDocuments: number;
activeDocuments: number;
completedDocuments: number;
volumeThisPeriod: number;
volumeAllTime: number;
};
export type OrganisationDetailedInsights = {
teams: TeamInsights[];
users: UserInsights[];
documents: DocumentInsights[];
totalPages: number;
summary?: OrganisationSummary;
};
export type TeamInsights = {
id: number;
name: string;
memberCount: number;
documentCount: number;
createdAt: Date;
};
export type UserInsights = {
id: number;
name: string;
email: string;
documentCount: number;
signedDocumentCount: number;
createdAt: Date;
};
export type DocumentInsights = {
id: string;
title: string;
status: DocumentStatus;
teamName: string;
createdAt: Date;
completedAt: Date | null;
};
export type GetOrganisationDetailedInsightsOptions = {
organisationId: string;
page?: number;
perPage?: number;
dateRange?: DateRange;
view: 'teams' | 'users' | 'documents';
};
export async function getOrganisationDetailedInsights({
organisationId,
page = 1,
perPage = 10,
dateRange = 'last30days',
view,
}: GetOrganisationDetailedInsightsOptions): Promise<OrganisationDetailedInsights> {
const offset = Math.max(page - 1, 0) * perPage;
const now = new Date();
let createdAtFrom: Date | null = null;
switch (dateRange) {
case 'last30days': {
createdAtFrom = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
break;
}
case 'last90days': {
createdAtFrom = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
break;
}
case 'lastYear': {
createdAtFrom = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
break;
}
case 'allTime':
default:
createdAtFrom = null;
break;
}
const summaryData = await getOrganisationSummary(organisationId, createdAtFrom);
const viewData = await (async () => {
switch (view) {
case 'teams':
return await getTeamInsights(organisationId, offset, perPage, createdAtFrom);
case 'users':
return await getUserInsights(organisationId, offset, perPage, createdAtFrom);
case 'documents':
return await getDocumentInsights(organisationId, offset, perPage, createdAtFrom);
default:
throw new Error(`Invalid view: ${view}`);
}
})();
return {
...viewData,
summary: summaryData,
};
}
async function getTeamInsights(
organisationId: string,
offset: number,
perPage: number,
createdAtFrom: Date | null,
): Promise<OrganisationDetailedInsights> {
const teamsQuery = kyselyPrisma.$kysely
.selectFrom('Team as t')
.leftJoin('Envelope as e', (join) =>
join
.onRef('t.id', '=', 'e.teamId')
.on('e.deletedAt', 'is', null)
.on('e.type', '=', sql.lit(EnvelopeType.DOCUMENT)),
)
.leftJoin('TeamGroup as tg', 'tg.teamId', 't.id')
.leftJoin('OrganisationGroup as og', 'og.id', 'tg.organisationGroupId')
.leftJoin('OrganisationGroupMember as ogm', 'ogm.groupId', 'og.id')
.leftJoin('OrganisationMember as om', 'om.id', 'ogm.organisationMemberId')
.where('t.organisationId', '=', organisationId)
.select([
't.id as id',
't.name as name',
't.createdAt as createdAt',
sql<number>`COUNT(DISTINCT om."userId")`.as('memberCount'),
(createdAtFrom
? sql<number>`COUNT(DISTINCT CASE WHEN e.id IS NOT NULL AND e."createdAt" >= ${createdAtFrom} THEN e.id END)`
: sql<number>`COUNT(DISTINCT e.id)`
).as('documentCount'),
])
.groupBy(['t.id', 't.name', 't.createdAt'])
.orderBy('documentCount', 'desc')
.limit(perPage)
.offset(offset);
const countQuery = kyselyPrisma.$kysely
.selectFrom('Team as t')
.where('t.organisationId', '=', organisationId)
.select(({ fn }) => [fn.countAll().as('count')]);
const [teams, countResult] = await Promise.all([teamsQuery.execute(), countQuery.execute()]);
const count = Number(countResult[0]?.count || 0);
return {
teams: teams as TeamInsights[],
users: [],
documents: [],
totalPages: Math.ceil(Number(count) / perPage),
};
}
async function getUserInsights(
organisationId: string,
offset: number,
perPage: number,
createdAtFrom: Date | null,
): Promise<OrganisationDetailedInsights> {
const usersBase = kyselyPrisma.$kysely
.selectFrom('OrganisationMember as om')
.innerJoin('User as u', 'u.id', 'om.userId')
.where('om.organisationId', '=', organisationId)
.leftJoin('Envelope as e', (join) =>
join
.onRef('e.userId', '=', 'u.id')
.on('e.deletedAt', 'is', null)
.on('e.type', '=', sql.lit(EnvelopeType.DOCUMENT)),
)
.leftJoin('Team as td', (join) =>
join.onRef('td.id', '=', 'e.teamId').on('td.organisationId', '=', organisationId),
)
.leftJoin('Recipient as r', (join) =>
join.onRef('r.email', '=', 'u.email').on('r.signedAt', 'is not', null),
)
.leftJoin('Envelope as se', (join) =>
join
.onRef('se.id', '=', 'r.envelopeId')
.on('se.deletedAt', 'is', null)
.on('se.type', '=', sql.lit(EnvelopeType.DOCUMENT)),
)
.leftJoin('Team as ts', (join) =>
join.onRef('ts.id', '=', 'se.teamId').on('ts.organisationId', '=', organisationId),
);
const usersQuery = usersBase
.select([
'u.id as id',
'u.name as name',
'u.email as email',
'u.createdAt as createdAt',
(createdAtFrom
? sql<number>`COUNT(DISTINCT CASE WHEN e.id IS NOT NULL AND td.id IS NOT NULL AND e."createdAt" >= ${createdAtFrom} THEN e.id END)`
: sql<number>`COUNT(DISTINCT CASE WHEN td.id IS NOT NULL THEN e.id END)`
).as('documentCount'),
(createdAtFrom
? sql<number>`COUNT(DISTINCT CASE WHEN e.id IS NOT NULL AND td.id IS NOT NULL AND e.status = 'COMPLETED' AND e."createdAt" >= ${createdAtFrom} THEN e.id END)`
: sql<number>`COUNT(DISTINCT CASE WHEN e.id IS NOT NULL AND td.id IS NOT NULL AND e.status = 'COMPLETED' THEN e.id END)`
).as('signedDocumentCount'),
])
.groupBy(['u.id', 'u.name', 'u.email', 'u.createdAt'])
.orderBy('u.createdAt', 'desc')
.limit(perPage)
.offset(offset);
const countQuery = kyselyPrisma.$kysely
.selectFrom('OrganisationMember as om')
.innerJoin('User as u', 'u.id', 'om.userId')
.where('om.organisationId', '=', organisationId)
.select(({ fn }) => [fn.countAll().as('count')]);
const [users, countResult] = await Promise.all([usersQuery.execute(), countQuery.execute()]);
const count = Number(countResult[0]?.count || 0);
return {
teams: [],
users: users as UserInsights[],
documents: [],
totalPages: Math.ceil(Number(count) / perPage),
};
}
async function getDocumentInsights(
organisationId: string,
offset: number,
perPage: number,
createdAtFrom: Date | null,
): Promise<OrganisationDetailedInsights> {
let documentsQuery = kyselyPrisma.$kysely
.selectFrom('Envelope as e')
.innerJoin('Team as t', 'e.teamId', 't.id')
.where('t.organisationId', '=', organisationId)
.where('e.deletedAt', 'is', null)
.where(() => sql`e.type = ${EnvelopeType.DOCUMENT}::"EnvelopeType"`);
if (createdAtFrom) {
documentsQuery = documentsQuery.where('e.createdAt', '>=', createdAtFrom);
}
documentsQuery = documentsQuery
.select([
'e.id as id',
'e.title as title',
'e.status as status',
'e.createdAt as createdAt',
'e.completedAt as completedAt',
't.name as teamName',
])
.orderBy('e.createdAt', 'desc')
.limit(perPage)
.offset(offset);
let countQuery = kyselyPrisma.$kysely
.selectFrom('Envelope as e')
.innerJoin('Team as t', 'e.teamId', 't.id')
.where('t.organisationId', '=', organisationId)
.where('e.deletedAt', 'is', null)
.where(() => sql`e.type = ${EnvelopeType.DOCUMENT}::"EnvelopeType"`);
if (createdAtFrom) {
countQuery = countQuery.where('e.createdAt', '>=', createdAtFrom);
}
countQuery = countQuery.select(({ fn }) => [fn.countAll().as('count')]);
const [documents, countResult] = await Promise.all([
documentsQuery.execute(),
countQuery.execute(),
]);
const count = Number((countResult[0] as { count: number })?.count || 0);
return {
teams: [],
users: [],
documents: documents.map((doc) => ({
...doc,
id: String((doc as { id: number }).id),
})) as DocumentInsights[],
totalPages: Math.ceil(Number(count) / perPage),
};
}
async function getOrganisationSummary(
organisationId: string,
createdAtFrom: Date | null,
): Promise<OrganisationSummary> {
const summaryQuery = kyselyPrisma.$kysely
.selectFrom('Organisation as o')
.where('o.id', '=', organisationId)
.select([
sql<number>`(SELECT COUNT(DISTINCT t2.id) FROM "Team" AS t2 WHERE t2."organisationId" = o.id)`.as(
'totalTeams',
),
sql<number>`(SELECT COUNT(DISTINCT om2."userId") FROM "OrganisationMember" AS om2 WHERE om2."organisationId" = o.id)`.as(
'totalMembers',
),
sql<number>`(
SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id AND e2."deletedAt" IS NULL AND e2.type = 'DOCUMENT'
)`.as('totalDocuments'),
sql<number>`(
SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id AND e2."deletedAt" IS NULL AND e2.type = 'DOCUMENT' AND e2.status IN ('DRAFT', 'PENDING')
)`.as('activeDocuments'),
sql<number>`(
SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id AND e2."deletedAt" IS NULL AND e2.type = 'DOCUMENT' AND e2.status = 'COMPLETED'
)`.as('completedDocuments'),
(createdAtFrom
? sql<number>`(
SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id
AND e2."deletedAt" IS NULL
AND e2.type = 'DOCUMENT'
AND e2.status = 'COMPLETED'
AND e2."createdAt" >= ${createdAtFrom}
)`
: sql<number>`(
SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id
AND e2."deletedAt" IS NULL
AND e2.type = 'DOCUMENT'
AND e2.status = 'COMPLETED'
)`
).as('volumeThisPeriod'),
sql<number>`(
SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id AND e2."deletedAt" IS NULL AND e2.type = 'DOCUMENT' AND e2.status = 'COMPLETED'
)`.as('volumeAllTime'),
]);
const result = await summaryQuery.executeTakeFirst();
return {
totalTeams: Number(result?.totalTeams || 0),
totalMembers: Number(result?.totalMembers || 0),
totalDocuments: Number(result?.totalDocuments || 0),
activeDocuments: Number(result?.activeDocuments || 0),
completedDocuments: Number(result?.completedDocuments || 0),
volumeThisPeriod: Number(result?.volumeThisPeriod || 0),
volumeAllTime: Number(result?.volumeAllTime || 0),
};
}

View File

@ -1,13 +1,17 @@
import { DocumentStatus, EnvelopeType, SubscriptionStatus } from '@prisma/client';
import { DocumentStatus, EnvelopeType } from '@prisma/client';
import type { DateRange } from '@documenso/lib/types/search-params';
import { kyselyPrisma, sql } from '@documenso/prisma';
export type SigningVolume = {
export type OrganisationInsights = {
id: number;
name: string;
signingVolume: number;
createdAt: Date;
planId: string;
customerId: string | null;
subscriptionStatus?: string;
teamCount?: number;
memberCount?: number;
};
export type GetSigningVolumeOptions = {
@ -28,28 +32,26 @@ export async function getSigningVolume({
const offset = Math.max(page - 1, 0) * perPage;
let findQuery = kyselyPrisma.$kysely
.selectFrom('Subscription as s')
.innerJoin('Organisation as o', 's.organisationId', 'o.id')
.selectFrom('Organisation as o')
.leftJoin('Team as t', 'o.id', 't.organisationId')
.leftJoin('Envelope as e', (join) =>
join
.onRef('t.id', '=', 'e.teamId')
.on('e.status', '=', sql.lit(DocumentStatus.COMPLETED))
.on('e.deletedAt', 'is', null),
.on('e.deletedAt', 'is', null)
.on('e.type', '=', sql.lit(EnvelopeType.DOCUMENT)),
)
.where(sql`s.status = ${SubscriptionStatus.ACTIVE}::"SubscriptionStatus"`)
.where((eb) =>
eb.or([eb('o.name', 'ilike', `%${search}%`), eb('t.name', 'ilike', `%${search}%`)]),
)
.where('e.type', '=', EnvelopeType.DOCUMENT)
.select([
's.id as id',
's.createdAt as createdAt',
's.planId as planId',
'o.id as id',
'o.createdAt as createdAt',
'o.customerId as customerId',
sql<string>`COALESCE(o.name, 'Unknown')`.as('name'),
sql<number>`COUNT(DISTINCT e.id)`.as('signingVolume'),
])
.groupBy(['s.id', 'o.name']);
.groupBy(['o.id', 'o.name', 'o.customerId']);
switch (sortBy) {
case 'name':
@ -68,19 +70,127 @@ export async function getSigningVolume({
findQuery = findQuery.limit(perPage).offset(offset);
const countQuery = kyselyPrisma.$kysely
.selectFrom('Subscription as s')
.innerJoin('Organisation as o', 's.organisationId', 'o.id')
.selectFrom('Organisation as o')
.leftJoin('Team as t', 'o.id', 't.organisationId')
.where(sql`s.status = ${SubscriptionStatus.ACTIVE}::"SubscriptionStatus"`)
.where((eb) =>
eb.or([eb('o.name', 'ilike', `%${search}%`), eb('t.name', 'ilike', `%${search}%`)]),
)
.select(({ fn }) => [fn.countAll().as('count')]);
.select(() => [sql<number>`COUNT(DISTINCT o.id)`.as('count')]);
const [results, [{ count }]] = await Promise.all([findQuery.execute(), countQuery.execute()]);
return {
leaderboard: results,
organisations: results,
totalPages: Math.ceil(Number(count) / perPage),
};
}
export type GetOrganisationInsightsOptions = GetSigningVolumeOptions & {
dateRange?: DateRange;
startDate?: Date;
endDate?: Date;
};
export async function getOrganisationInsights({
search = '',
page = 1,
perPage = 10,
sortBy = 'signingVolume',
sortOrder = 'desc',
dateRange = 'last30days',
startDate,
endDate,
}: GetOrganisationInsightsOptions) {
const offset = Math.max(page - 1, 0) * perPage;
const now = new Date();
let dateCondition = sql`1=1`;
if (startDate && endDate) {
dateCondition = sql`e."createdAt" >= ${startDate} AND e."createdAt" <= ${endDate}`;
} else {
switch (dateRange) {
case 'last30days': {
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
dateCondition = sql`e."createdAt" >= ${thirtyDaysAgo}`;
break;
}
case 'last90days': {
const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
dateCondition = sql`e."createdAt" >= ${ninetyDaysAgo}`;
break;
}
case 'lastYear': {
const oneYearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
dateCondition = sql`e."createdAt" >= ${oneYearAgo}`;
break;
}
case 'allTime':
default:
dateCondition = sql`1=1`;
break;
}
}
let findQuery = kyselyPrisma.$kysely
.selectFrom('Organisation as o')
.leftJoin('Team as t', 'o.id', 't.organisationId')
.leftJoin('Envelope as e', (join) =>
join
.onRef('t.id', '=', 'e.teamId')
.on('e.status', '=', sql.lit(DocumentStatus.COMPLETED))
.on('e.deletedAt', 'is', null)
.on('e.type', '=', sql.lit(EnvelopeType.DOCUMENT)),
)
.leftJoin('OrganisationMember as om', 'o.id', 'om.organisationId')
.leftJoin('Subscription as s', 'o.id', 's.organisationId')
.where((eb) =>
eb.or([eb('o.name', 'ilike', `%${search}%`), eb('t.name', 'ilike', `%${search}%`)]),
)
.select([
'o.id as id',
'o.createdAt as createdAt',
'o.customerId as customerId',
sql<string>`COALESCE(o.name, 'Unknown')`.as('name'),
sql<number>`COUNT(DISTINCT CASE WHEN e.id IS NOT NULL AND ${dateCondition} THEN e.id END)`.as(
'signingVolume',
),
sql<number>`GREATEST(COUNT(DISTINCT t.id), 1)`.as('teamCount'),
sql<number>`COUNT(DISTINCT om."userId")`.as('memberCount'),
sql<string>`CASE WHEN s.status IS NOT NULL THEN s.status ELSE NULL END`.as(
'subscriptionStatus',
),
])
.groupBy(['o.id', 'o.name', 'o.customerId', 's.status']);
switch (sortBy) {
case 'name':
findQuery = findQuery.orderBy('name', sortOrder);
break;
case 'createdAt':
findQuery = findQuery.orderBy('createdAt', sortOrder);
break;
case 'signingVolume':
findQuery = findQuery.orderBy('signingVolume', sortOrder);
break;
default:
findQuery = findQuery.orderBy('signingVolume', 'desc');
}
findQuery = findQuery.limit(perPage).offset(offset);
const countQuery = kyselyPrisma.$kysely
.selectFrom('Organisation as o')
.leftJoin('Team as t', 'o.id', 't.organisationId')
.where((eb) =>
eb.or([eb('o.name', 'ilike', `%${search}%`), eb('t.name', 'ilike', `%${search}%`)]),
)
.select(() => [sql<number>`COUNT(DISTINCT o.id)`.as('count')]);
const [results, [{ count }]] = await Promise.all([findQuery.execute(), countQuery.execute()]);
return {
organisations: results,
totalPages: Math.ceil(Number(count) / perPage),
};
}

View File

@ -19,7 +19,7 @@ import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { jobs } from '../../jobs/client';
import type { TRecipientAccessAuth, TRecipientActionAuth } from '../../types/document-auth';
import type { TRecipientAccessAuth } from '../../types/document-auth';
import { DocumentAuth } from '../../types/document-auth';
import {
ZWebhookDocumentSchema,
@ -37,7 +37,6 @@ export type CompleteDocumentWithTokenOptions = {
token: string;
id: EnvelopeIdOptions;
userId?: number;
authOptions?: TRecipientActionAuth;
accessAuthOptions?: TRecipientAccessAuth;
requestMetadata?: RequestMetadata;
nextSigner?: {

View File

@ -1,4 +1,4 @@
import type { DocumentData, Envelope, EnvelopeItem } from '@prisma/client';
import type { DocumentData, Envelope, EnvelopeItem, Field } from '@prisma/client';
import {
DocumentSigningOrder,
DocumentStatus,
@ -24,7 +24,9 @@ import {
ZCheckboxFieldMeta,
ZDropdownFieldMeta,
ZFieldAndMetaSchema,
ZNumberFieldMeta,
ZRadioFieldMeta,
ZTextFieldMeta,
} from '../../types/field-meta';
import {
ZWebhookDocumentSchema,
@ -182,80 +184,19 @@ export const sendDocument = async ({
// Validate and autoinsert fields for V2 envelopes.
if (envelope.internalVersion === 2) {
for (const unknownField of envelope.fields) {
const parsedField = ZFieldAndMetaSchema.safeParse(unknownField);
const recipient = envelope.recipients.find((r) => r.id === unknownField.recipientId);
if (parsedField.error) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'One or more fields have invalid metadata. Error: ' + parsedField.error.message,
if (!recipient) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Recipient not found',
});
}
const field = parsedField.data;
const fieldId = unknownField.id;
const fieldToAutoInsert = extractFieldAutoInsertValues(unknownField);
if (field.type === FieldType.RADIO) {
const { values = [] } = ZRadioFieldMeta.parse(field.fieldMeta);
const checkedItemIndex = values.findIndex((value) => value.checked);
if (checkedItemIndex !== -1) {
fieldsToAutoInsert.push({
fieldId,
customText: toRadioCustomText(checkedItemIndex),
});
}
}
if (field.type === FieldType.DROPDOWN) {
const { defaultValue, values = [] } = ZDropdownFieldMeta.parse(field.fieldMeta);
if (defaultValue && values.some((value) => value.value === defaultValue)) {
fieldsToAutoInsert.push({
fieldId,
customText: defaultValue,
});
}
}
if (field.type === FieldType.CHECKBOX) {
const {
values = [],
validationRule,
validationLength,
} = ZCheckboxFieldMeta.parse(field.fieldMeta);
const checkedIndices: number[] = [];
values.forEach((value, i) => {
if (value.checked) {
checkedIndices.push(i);
}
});
let isValid = true;
if (validationRule && validationLength) {
const validation = checkboxValidationSigns.find((sign) => sign.label === validationRule);
if (!validation) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Invalid checkbox validation rule',
});
}
isValid = validateCheckboxLength(
checkedIndices.length,
validation.value,
validationLength,
);
}
if (isValid && checkedIndices.length > 0) {
fieldsToAutoInsert.push({
fieldId,
customText: toCheckboxCustomText(checkedIndices),
});
}
// Only auto-insert fields if the recipient has not been sent the document yet.
if (fieldToAutoInsert && recipient.sendStatus !== SendStatus.SENT) {
fieldsToAutoInsert.push(fieldToAutoInsert);
}
}
}
@ -275,6 +216,7 @@ export const sendDocument = async ({
if (envelope.internalVersion === 2) {
const autoInsertedFields = await Promise.all(
fieldsToAutoInsert.map(async (field) => {
// Warning: Only auto-insert fields if the recipient has not been sent the document yet.
return await tx.field.update({
where: {
id: field.fieldId,
@ -387,3 +329,113 @@ const injectFormValuesIntoDocument = async (
},
});
};
/**
* Extracts the auto insertion values for a given field.
*
* If field is not auto insertable, returns `null`.
*/
export const extractFieldAutoInsertValues = (
unknownField: Field,
): { fieldId: number; customText: string } | null => {
const parsedField = ZFieldAndMetaSchema.safeParse(unknownField);
if (parsedField.error) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'One or more fields have invalid metadata. Error: ' + parsedField.error.message,
});
}
const field = parsedField.data;
const fieldId = unknownField.id;
// Auto insert text fields with prefilled values.
if (field.type === FieldType.TEXT) {
const { text } = ZTextFieldMeta.parse(field.fieldMeta);
if (text) {
return {
fieldId,
customText: text,
};
}
}
// Auto insert number fields with prefilled values.
if (field.type === FieldType.NUMBER) {
const { value } = ZNumberFieldMeta.parse(field.fieldMeta);
if (value) {
return {
fieldId,
customText: value,
};
}
}
// Auto insert radio fields with the pre-checked value.
if (field.type === FieldType.RADIO) {
const { values = [] } = ZRadioFieldMeta.parse(field.fieldMeta);
const checkedItemIndex = values.findIndex((value) => value.checked);
if (checkedItemIndex !== -1) {
return {
fieldId,
customText: toRadioCustomText(checkedItemIndex),
};
}
}
// Auto insert dropdown fields with the default value.
if (field.type === FieldType.DROPDOWN) {
const { defaultValue, values = [] } = ZDropdownFieldMeta.parse(field.fieldMeta);
if (defaultValue && values.some((value) => value.value === defaultValue)) {
return {
fieldId,
customText: defaultValue,
};
}
}
// Auto insert checkbox fields with the pre-checked values.
if (field.type === FieldType.CHECKBOX) {
const {
values = [],
validationRule,
validationLength,
} = ZCheckboxFieldMeta.parse(field.fieldMeta);
const checkedIndices: number[] = [];
values.forEach((value, i) => {
if (value.checked) {
checkedIndices.push(i);
}
});
let isValid = true;
if (validationRule && validationLength) {
const validation = checkboxValidationSigns.find((sign) => sign.label === validationRule);
if (!validation) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {
message: 'Invalid checkbox validation rule',
});
}
isValid = validateCheckboxLength(checkedIndices.length, validation.value, validationLength);
}
if (isValid && checkedIndices.length > 0) {
return {
fieldId,
customText: toCheckboxCustomText(checkedIndices),
};
}
}
return null;
};

View File

@ -6,6 +6,7 @@ import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { DocumentAccessAuth, type TDocumentAuthMethods } from '../../types/document-auth';
import { extractDocumentAuthMethods } from '../../utils/document-auth';
import { extractFieldAutoInsertValues } from '../document/send-document';
import { getTeamSettings } from '../team/get-team-settings';
import type { EnvelopeForSigningResponse } from './get-envelope-for-recipient-signing';
import { ZEnvelopeForSigningResponse } from './get-envelope-for-recipient-signing';
@ -144,6 +145,19 @@ export const getEnvelopeForDirectTemplateSigning = async ({
recipient: {
...recipient,
directToken: envelope.directLink?.token || '',
fields: recipient.fields.map((field) => {
const autoInsertValue = extractFieldAutoInsertValues(field);
if (!autoInsertValue) {
return field;
}
return {
...field,
inserted: true,
customText: autoInsertValue.customText,
};
}),
},
recipientSignature: null,
isRecipientsTurn: true,

View File

@ -11,7 +11,7 @@ import UserSchema from '@documenso/prisma/generated/zod/modelSchema/UserSchema';
import { AppError, AppErrorCode } from '../../errors/app-error';
import type { TDocumentAuthMethods } from '../../types/document-auth';
import { ZFieldSchema } from '../../types/field';
import { ZEnvelopeFieldSchema, ZFieldSchema } from '../../types/field';
import { ZRecipientLiteSchema } from '../../types/recipient';
import { isRecipientAuthorized } from '../document/is-recipient-authorized';
import { getTeamSettings } from '../team/get-team-settings';
@ -63,9 +63,11 @@ export const ZEnvelopeForSigningResponse = z.object({
rejectionReason: true,
})
.extend({
fields: ZFieldSchema.omit({
documentId: true,
templateId: true,
fields: ZEnvelopeFieldSchema.extend({
signature: SignatureSchema.pick({
signatureImageAsBase64: true,
typedSignature: true,
}).nullish(),
}).array(),
})
.array(),

View File

@ -129,7 +129,7 @@ export const setFieldsForTemplate = async ({
if (field.type === FieldType.NUMBER && field.fieldMeta) {
const numberFieldParsedMeta = ZNumberFieldMeta.parse(field.fieldMeta);
const errors = validateNumberField(
String(numberFieldParsedMeta.value),
String(numberFieldParsedMeta.value || ''),
numberFieldParsedMeta,
);
if (errors.length > 0) {

View File

@ -88,11 +88,13 @@ export const addUserToOrganisation = async ({
organisationId,
organisationGroups,
organisationMemberRole,
bypassEmail = false,
}: {
userId: number;
organisationId: string;
organisationGroups: OrganisationGroup[];
organisationMemberRole: OrganisationMemberRole;
bypassEmail?: boolean;
}) => {
const organisationGroupToUse = organisationGroups.find(
(group) =>
@ -122,13 +124,15 @@ export const addUserToOrganisation = async ({
},
});
await jobs.triggerJob({
name: 'send.organisation-member-joined.email',
payload: {
organisationId,
memberUserId: userId,
},
});
if (!bypassEmail) {
await jobs.triggerJob({
name: 'send.organisation-member-joined.email',
payload: {
organisationId,
memberUserId: userId,
},
});
}
},
{ timeout: 30_000 },
);

View File

@ -32,10 +32,8 @@ export const insertFieldInPDFV2 = async ({
const stage = new Konva.Stage({ width: pageWidth, height: pageHeight });
const layer = new Konva.Layer();
const insertedFields = fields.filter((field) => field.inserted);
// Render the fields onto the layer.
for (const field of insertedFields) {
for (const field of fields) {
renderField({
scale: 1,
field: {

View File

@ -215,6 +215,12 @@ export const createDocumentFromDirectTemplate = async ({
const fieldsToProcess = directTemplateRecipient.fields.filter((templateField) => {
const signedFieldValue = signedFieldValues.find((value) => value.fieldId === templateField.id);
// Custom logic for V2 to include all fields, since v1 excludes read only
// and prefilled fields.
if (directTemplateEnvelope.internalVersion === 2) {
return true;
}
// Include if it's required or has a signed value
return isRequiredField(templateField) || signedFieldValue !== undefined;
});
@ -468,19 +474,28 @@ export const createDocumentFromDirectTemplate = async ({
signingOrder: directTemplateRecipient.signingOrder,
fields: {
createMany: {
data: directTemplateNonSignatureFields.map(({ templateField, customText }) => ({
envelopeId: createdEnvelope.id,
envelopeItemId: oldEnvelopeItemToNewEnvelopeItemIdMap[templateField.envelopeItemId],
type: templateField.type,
page: templateField.page,
positionX: templateField.positionX,
positionY: templateField.positionY,
width: templateField.width,
height: templateField.height,
customText: customText ?? '',
inserted: true,
fieldMeta: templateField.fieldMeta || Prisma.JsonNull,
})),
data: directTemplateNonSignatureFields.map(({ templateField, customText }) => {
let inserted = true;
// Custom logic for V2 to only insert if values exist.
if (directTemplateEnvelope.internalVersion === 2) {
inserted = customText !== '';
}
return {
envelopeId: createdEnvelope.id,
envelopeItemId: oldEnvelopeItemToNewEnvelopeItemIdMap[templateField.envelopeItemId],
type: templateField.type,
page: templateField.page,
positionX: templateField.positionX,
positionY: templateField.positionY,
width: templateField.width,
height: templateField.height,
customText: customText ?? '',
inserted,
fieldMeta: templateField.fieldMeta || Prisma.JsonNull,
};
}),
},
},
},

View File

@ -8,7 +8,7 @@ msgstr ""
"Language: de\n"
"Project-Id-Version: documenso-app\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-11-07 03:40\n"
"PO-Revision-Date: 2025-11-11 02:33\n"
"Last-Translator: \n"
"Language-Team: German\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@ -18,10 +18,6 @@ msgstr ""
"X-Crowdin-File: web.po\n"
"X-Crowdin-File-ID: 8\n"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid " Enable direct link signing"
msgstr " Direktlink-Signierung aktivieren"
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid ".PDF documents accepted (max {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB)"
msgstr ".PDF-Dokumente akzeptiert (max. {APP_DOCUMENT_UPLOAD_SIZE_LIMIT} MB)"
@ -98,6 +94,11 @@ msgstr "{0, plural, one {# Ordner} other {# Ordner}}"
msgid "{0, plural, one {# recipient} other {# recipients}}"
msgstr "{0, plural, one {# Empfänger} other {# Empfänger}}"
#. placeholder {0}: envelope.recipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0, plural, one {# Recipient} other {# Recipients}}"
msgstr "{0, plural, one {# Empfänger} other {# Empfänger}}"
#. placeholder {0}: org.teams.length
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
msgid "{0, plural, one {# team} other {# teams}}"
@ -151,6 +152,12 @@ msgstr "{0, plural, one {Warte auf 1 Empfänger} other {Warte auf # Empfänger}}
msgid "{0}"
msgstr "{0}"
#. placeholder {0}: file.name
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "{0} couldn't be uploaded:"
msgstr "{0} konnte nicht hochgeladen werden:"
#. placeholder {0}: team.name
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
msgid "{0} direct signing templates"
@ -170,7 +177,7 @@ msgstr "{0} hat dich eingeladen, ein Dokument {recipientActionVerb}"
#. placeholder {0}: remaining.documents
#. placeholder {1}: quota.documents
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "{0} of {1} documents remaining this month."
msgstr "{0} von {1} Dokumenten verbleibend in diesem Monat."
@ -188,11 +195,6 @@ msgstr "{0} von {1} Zeile(n) ausgewählt."
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
msgstr "{0} im Namen von \"{1}\" hat Sie eingeladen, das Dokument \"{2}\" {recipientActionVerb}."
#. placeholder {0}: envelope.recipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0} Recipient(s)"
msgstr "{0} Empfänger(in)"
#. placeholder {0}: organisation.name
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "{0} Teams"
@ -206,6 +208,10 @@ msgstr "{browserInfo} auf {os}"
msgid "{charactersRemaining, plural, one {1 character remaining} other {{charactersRemaining} characters remaining}}"
msgstr "{charactersRemaining, plural, one {1 Zeichen verbleibend} other {{charactersRemaining} Zeichen verbleibend}}"
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "{expiresInMinutes, plural, one {This code will expire in # minute.} other {This code will expire in # minutes.}}"
msgstr "{expiresInMinutes, plural, one {Dieser Code wird in # Minute ablaufen.} other {Dieser Code wird in # Minuten ablaufen.}}"
#: packages/email/templates/document-invite.tsx
msgid "{inviterName} <0>({inviterEmail})</0>"
msgstr "{inviterName} <0>({inviterEmail})</0>"
@ -254,6 +260,10 @@ msgstr "{inviterName} im Namen von \"{teamName}\" hat dich eingeladen, {0}<0/>\"
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
msgstr "{inviterName} im Namen von \"{teamName}\" hat Sie eingeladen, das Dokument {documentName} {action}"
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "{MAXIMUM_PASSKEYS, plural, one {You cannot have more than # passkey.} other {You cannot have more than # passkeys.}}"
msgstr "{MAXIMUM_PASSKEYS, plural, one {Sie können nicht mehr als # Zugangsschlüssel haben.} other {Sie können nicht mehr als # Zugangsschlüssel haben.}}"
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} added a field"
msgstr "{prefix} hat ein Feld hinzugefügt"
@ -1231,6 +1241,7 @@ msgid "All templates"
msgstr "Alle Vorlagen"
#: apps/remix/app/components/general/period-selector.tsx
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "All Time"
msgstr "Alle Zeiten"
@ -1478,7 +1489,7 @@ msgid "An error occurred while updating your profile."
msgstr "Ein Fehler ist aufgetreten, während dein Profil aktualisiert wurde."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "An error occurred while uploading your document."
msgstr "Ein Fehler ist aufgetreten, während dein Dokument hochgeladen wurde."
@ -1857,6 +1868,10 @@ msgstr "Schwarz"
msgid "Blue"
msgstr "Blau"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Bottom"
msgstr "Unten"
#: apps/remix/app/components/forms/branding-preferences-form.tsx
msgid "Brand Details"
msgstr "Markendetails"
@ -2097,6 +2112,10 @@ msgstr "Ccers"
msgid "Center"
msgstr "Zentrum"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
msgid "Character limit"
msgstr "Zeichenbeschränkung"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
msgid "Character Limit"
@ -2263,6 +2282,7 @@ msgstr "Dokument abschließen"
msgid "Complete the fields for the following signers."
msgstr "Vervollständigen Sie die Felder für die folgenden Unterzeichner."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-document-jobs-table.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
@ -2543,11 +2563,6 @@ msgstr "Signierlinks kopieren"
msgid "Copy token"
msgstr "Token kopieren"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "couldn't be uploaded:"
msgstr "konnte nicht hochgeladen werden:"
#: apps/remix/app/routes/_profile+/_layout.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
@ -2739,9 +2754,11 @@ msgstr "Erstellen Sie Ihr Konto und beginnen Sie mit dem modernen Dokumentensign
#: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/documents-table.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/general/template/template-page-view-information.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
#: apps/remix/app/components/general/document/document-page-view-information.tsx
@ -3222,6 +3239,10 @@ msgstr "Dokument \"{0}\" - Ablehnung Bestätigt"
msgid "Document \"{0}\" Cancelled"
msgstr "Dokument „{0}“ abgebrochen"
#: packages/ui/primitives/document-upload-button.tsx
msgid "Document (Legacy)"
msgstr "Dokument (Legacy)"
#: apps/remix/app/components/general/envelope-editor/envelope-editor.tsx
msgid "Document & Recipients"
msgstr "Dokument & Empfänger"
@ -3354,6 +3375,10 @@ msgstr "Dokument in Ihrem Konto gefunden"
msgid "Document ID"
msgstr "Dokument-ID"
#: apps/remix/app/components/general/document/document-page-view-information.tsx
msgid "Document ID (Legacy)"
msgstr "Dokumenten-ID (Legacy)"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document inbox"
msgstr "Dokumenten-Posteingang"
@ -3479,12 +3504,12 @@ msgstr "Dokument erfolgreich aktualisiert"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Document upload disabled due to unpaid invoices"
msgstr "Dokumenten-Upload deaktiviert aufgrund unbezahlter Rechnungen"
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "Document uploaded"
msgstr "Dokument hochgeladen"
@ -3507,6 +3532,10 @@ msgctxt "Audit log format"
msgid "Document visibility updated"
msgstr "Sichtbarkeit des Dokuments aktualisiert"
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
msgid "Document Volume"
msgstr "Dokumentenmenge"
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "Document will be permanently deleted"
msgstr "Dokument wird dauerhaft gelöscht"
@ -3520,6 +3549,8 @@ msgstr "Dokumentation"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.legacy_editor.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/user-profile-timur.tsx
#: apps/remix/app/components/general/app-nav-mobile.tsx
@ -3534,6 +3565,15 @@ msgstr "Dokumente"
msgid "Documents and resources related to this envelope."
msgstr "Dokumente und Ressourcen im Zusammenhang mit diesem Umschlag."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Documents Completed"
msgstr "Dokumente abgeschlossen"
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Documents Created"
msgstr "Dokumente erstellt"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
msgid "Documents created from template"
msgstr "Dokumente erstellt aus Vorlage"
@ -3736,6 +3776,7 @@ msgstr "Offenlegung der elektronischen Unterschrift"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._layout.tsx
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx
@ -3925,6 +3966,10 @@ msgstr "Aktivieren Sie das benutzerdefinierte Branding für alle Dokumente in di
msgid "Enable custom branding for all documents in this team"
msgstr "Aktivieren Sie das benutzerdefinierte Branding für alle Dokumente in diesem Team"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid "Enable direct link signing"
msgstr "Direktlink-Signierung aktivieren"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/lib/constants/template.ts
msgid "Enable Direct Link Signing"
@ -4097,7 +4142,7 @@ msgstr "Umschlag aktualisiert"
#: apps/remix/app/components/general/document-signing/document-signing-checkbox-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-auto-sign.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
@ -4254,7 +4299,6 @@ msgstr "Fehlgeschlagen: {failedCount}"
msgid "Feature Flags"
msgstr "Funktionsflaggen"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
msgid "Field character limit"
msgstr "Zeichenbeschränkung des Feldes"
@ -4312,7 +4356,7 @@ msgstr "Felder aktualisiert"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid "File cannot be larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
msgstr "Die Datei darf nicht größer als {APP_DOCUMENT_UPLOAD_SIZE_LIMIT} MB sein"
@ -4402,6 +4446,7 @@ msgstr "Passwort vergessen?"
msgid "Forgot your password?"
msgstr "Hast du dein Passwort vergessen?"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
msgid "Free"
msgstr "Kostenlos"
@ -4934,6 +4979,10 @@ msgstr "Tritt {organisationName} auf Documenso bei"
msgid "Join our community on <0>Discord</0> for community support and discussion."
msgstr "Treten Sie unserer Community auf <0>Discord</0> bei, um Unterstützung zu erhalten und sich auszutauschen."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Joined"
msgstr "Beigetreten"
#. placeholder {0}: DateTime.fromJSDate(team.createdAt).toRelative({ style: 'short' })
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
msgid "Joined {0}"
@ -4967,10 +5016,18 @@ msgstr "Die letzten 14 Tage"
msgid "Last 30 days"
msgstr "Die letzten 30 Tage"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last 30 Days"
msgstr "Letzte 30 Tage"
#: apps/remix/app/components/general/period-selector.tsx
msgid "Last 7 days"
msgstr "Die letzten 7 Tage"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last 90 Days"
msgstr "Letzte 90 Tage"
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
msgid "Last Active"
msgstr "Zuletzt aktiv"
@ -5000,9 +5057,9 @@ msgstr "Zuletzt aktualisiert am"
msgid "Last used"
msgstr "Zuletzt verwendet"
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
msgid "Leaderboard"
msgstr "Bestenliste"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last Year"
msgstr "Letztes Jahr"
#: apps/remix/app/components/tables/user-organisations-table.tsx
#: apps/remix/app/components/dialogs/organisation-leave-dialog.tsx
@ -5028,6 +5085,14 @@ msgstr "Links"
msgid "Legality of Electronic Signatures"
msgstr "Rechtlichkeit elektronischer Unterschriften"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Letter spacing"
msgstr "Buchstabenspacing"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Letter Spacing"
msgstr "Buchstabenspacing"
#: apps/remix/app/components/general/app-command-menu.tsx
msgid "Light Mode"
msgstr "Lichtmodus"
@ -5036,6 +5101,14 @@ msgstr "Lichtmodus"
msgid "Like to have your own public profile with agreements?"
msgstr "Möchten Sie Ihr eigenes öffentliches Profil mit Vereinbarungen haben?"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Line height"
msgstr "Zeilenhöhe"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Line Height"
msgstr "Zeilenhöhe"
#: packages/email/templates/confirm-team-email.tsx
msgid "Link expires in 1 hour."
msgstr "Link läuft in 1 Stunde ab."
@ -5315,7 +5388,10 @@ msgstr "Mitglied seit"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._layout.tsx
#: apps/remix/app/components/tables/team-groups-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
msgid "Members"
@ -5333,6 +5409,10 @@ msgstr "Nachricht"
msgid "Message <0>(Optional)</0>"
msgstr "Nachricht <0>(Optional)</0>"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Middle"
msgstr "Mitte"
#: apps/remix/app/components/forms/editor/editor-field-number-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
msgid "Min"
@ -5408,7 +5488,8 @@ msgstr "N/A"
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/tables/admin-document-jobs-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
@ -5475,7 +5556,6 @@ msgstr "Nie ablaufen"
msgid "New Password"
msgstr "Neues Passwort"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "New Template"
msgstr "Neue Vorlage"
@ -5813,6 +5893,11 @@ msgstr "Einstellungen für Organisationsgruppen"
msgid "Organisation has been updated successfully"
msgstr "Organisation wurde erfolgreich aktualisiert"
#: apps/remix/app/routes/_authenticated+/admin+/organisation-insights._index.tsx
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
msgid "Organisation Insights"
msgstr "Organisationseinblicke"
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
msgid "Organisation invitation"
msgstr "Organisationseinladung"
@ -5909,6 +5994,7 @@ msgid "Organize your documents and templates"
msgstr "Organisieren Sie Ihre Dokumente und Vorlagen"
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
msgctxt "Original document (adjective)"
msgid "Original"
msgstr "Original"
@ -5949,6 +6035,10 @@ msgstr "Seite {0} von {1}"
msgid "Page {0} of {numPages}"
msgstr "Seite {0} von {numPages}"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
msgid "Paid"
msgstr "Bezahlt"
#: apps/remix/app/components/general/document-signing/document-signing-auth-dialog.tsx
#: apps/remix/app/components/forms/signin.tsx
msgid "Passkey"
@ -6074,6 +6164,7 @@ msgid "per year"
msgstr "pro Jahr"
#: apps/remix/app/components/tables/user-organisations-table.tsx
msgctxt "Personal organisation (adjective)"
msgid "Personal"
msgstr "Persönlich"
@ -6439,6 +6530,7 @@ msgid "Read only"
msgstr "Nur lesen"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
#: packages/ui/components/document/envelope-recipient-field-tooltip.tsx
msgid "Read Only"
msgstr "Nur lesen"
@ -6874,6 +6966,7 @@ msgstr "Rechts"
#: apps/remix/app/components/tables/organisation-members-table.tsx
#: apps/remix/app/components/tables/organisation-member-invites-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/embed/authoring/configure-document-recipients.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
@ -6936,7 +7029,6 @@ msgstr "Suche nach Anspruchs-ID oder Name"
msgid "Search by document title"
msgstr "Nach Dokumenttitel suchen"
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
msgid "Search by name or email"
msgstr "Nach Name oder E-Mail suchen"
@ -6945,6 +7037,10 @@ msgstr "Nach Name oder E-Mail suchen"
msgid "Search by organisation ID, name, customer ID or owner email"
msgstr "Suche nach Organisations-ID, Name, Kunden-ID oder E-Mail des Inhabers"
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
msgid "Search by organisation name"
msgstr "Suche nach Organisationsname"
#: apps/remix/app/components/general/document/document-search.tsx
msgid "Search documents..."
msgstr "Dokumente suchen..."
@ -7115,6 +7211,10 @@ msgstr "Wählen Sie die Mitglieder, die in diese Gruppe aufgenommen werden solle
msgid "Select triggers"
msgstr "Auslöser auswählen"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Select vertical align"
msgstr "Vertikale Ausrichtung auswählen"
#: apps/remix/app/components/dialogs/folder-update-dialog.tsx
msgid "Select visibility"
msgstr "Sichtbarkeit auswählen"
@ -7485,12 +7585,16 @@ msgstr "Gesammelte Unterschriften"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
#: packages/ui/components/document/envelope-recipient-field-tooltip.tsx
#: packages/ui/components/document/document-read-only-fields.tsx
msgid "Signed"
msgstr "Unterzeichnet"
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
msgctxt "Signed document (adjective)"
msgid "Signed"
msgstr "Signiert"
#: packages/lib/constants/recipient-roles.ts
msgctxt "Recipient role actioned"
msgid "Signed"
@ -7556,11 +7660,6 @@ msgstr "Unterzeichnungslinks wurden für dieses Dokument erstellt."
msgid "Signing order is enabled."
msgstr "Unterzeichnungsreihenfolge ist aktiviert."
#: apps/remix/app/routes/_authenticated+/admin+/leaderboard.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
msgid "Signing Volume"
msgstr "Unterzeichnungsvolumen"
#: apps/remix/app/components/forms/signup.tsx
msgid "Signups are disabled."
msgstr "Registrierungen sind deaktiviert."
@ -7712,6 +7811,7 @@ msgstr "Statistiken"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/audit-log.tsx
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-email-domains-table.tsx
#: apps/remix/app/components/tables/organisation-billing-invoices-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
@ -7905,6 +8005,7 @@ msgstr "Systemthema"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Team"
msgstr "Team"
@ -7990,6 +8091,7 @@ msgstr "Teammitglieder"
msgid "Team members have been added."
msgstr "Teammitglieder wurden hinzugefügt."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/forms/team-update-form.tsx
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
msgid "Team Name"
@ -8034,6 +8136,9 @@ msgstr "Team-URL"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.teams.tsx
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings._layout.tsx
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/general/org-menu-switcher.tsx
msgid "Teams"
msgstr "Teams"
@ -8056,6 +8161,11 @@ msgstr "Teams, denen diese Organisationsgruppe derzeit zugewiesen ist"
msgid "Template"
msgstr "Vorlage"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Template (Legacy)"
msgstr "Vorlage (Legacy)"
#: apps/remix/app/routes/embed+/v1+/authoring_.completed.create.tsx
msgid "Template Created"
msgstr "Vorlage erstellt"
@ -8084,6 +8194,10 @@ msgstr "Vorlage ist von Deinem öffentlichen Profil entfernt worden."
msgid "Template has been updated."
msgstr "Vorlage wurde aktualisiert."
#: apps/remix/app/components/general/template/template-page-view-information.tsx
msgid "Template ID (Legacy)"
msgstr "Vorlagen-ID (Legacy)"
#: apps/remix/app/components/general/legacy-field-warning-popover.tsx
msgid "Template is using legacy field insertion"
msgstr "Vorlage verwendet Altfeld-Integration"
@ -8266,9 +8380,8 @@ msgstr "Das gesuchte Dokument konnte nicht gefunden werden."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.edit.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "The document you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Das gesuchte Dokument wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
msgid "The document you are looking for may have been removed, renamed or may have never existed."
msgstr "Das gesuchte Dokument wurde möglicherweise entfernt, umbenannt oder existierte nie."
#: packages/ui/components/document/document-send-email-message-helper.tsx
msgid "The document's name"
@ -8279,9 +8392,8 @@ msgid "The email address which will show up in the \"Reply To\" field in emails"
msgstr "Die E-Mail-Adresse, die im \"Antwort an\"-Feld in E-Mails angezeigt wird"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.email-domains.$id.tsx
msgid "The email domain you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Die gesuchte E-Mail-Domain wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
msgid "The email domain you are looking for may have been removed, renamed or may have never existed."
msgstr "Die gesuchte E-Mail-Domäne wurde möglicherweise entfernt, umbenannt oder existierte nie."
#: apps/remix/app/components/forms/signin.tsx
msgid "The email or password provided is incorrect"
@ -8337,23 +8449,17 @@ msgid "The organisation email has been created successfully."
msgstr "Die E-Mail der Organisation wurde erfolgreich erstellt."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Die Organisationsgruppe, nach der Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
msgid "The organisation group you are looking for may have been removed, renamed or may have never existed."
msgstr "Die gesuchte Organisationsgruppe wurde möglicherweise entfernt, umbenannt oder existierte nie."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "The organisation role that will be applied to all members in this group."
msgstr "Die Organisationsrolle, die auf alle Mitglieder in dieser Gruppe angewendet wird."
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Die Organisation, nach der Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
#: apps/remix/app/routes/_authenticated+/_layout.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Die Organisation, nach der Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never existed."
msgstr "Die gesuchte Organisation wurde möglicherweise entfernt, umbenannt oder existierte nie."
#: apps/remix/app/components/general/generic-error-layout.tsx
msgid "The page you are looking for was moved, removed, renamed or might never have existed."
@ -8444,14 +8550,9 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
msgstr "Die Team-E-Mail <0>{teamEmail}</0> wurde aus dem folgenden Team entfernt"
#: apps/remix/app/routes/_authenticated+/_layout.tsx
msgid "The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Das Team, das Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
msgid "The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Das Team, das Sie suchen, könnte entfernt, umbenannt oder nie existiert haben."
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/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
msgid "The template has been moved successfully."
@ -8466,9 +8567,8 @@ msgid "The template you are looking for could not be found."
msgstr "Die gesuchte Vorlage konnte nicht gefunden werden."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
msgid "The template you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Die gesuchte Vorlage wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
msgid "The template you are looking for may have been removed, renamed or may have never existed."
msgstr "Die gesuchte Vorlage wurde möglicherweise entfernt, umbenannt oder existierte nie."
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
msgid "The test webhook has been successfully sent to your endpoint."
@ -8508,9 +8608,8 @@ msgid "The URL for Documenso to send webhook events to."
msgstr "Die URL für Documenso, um Webhook-Ereignisse zu senden."
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
msgid "The user you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Der Benutzer, nach dem Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
msgid "The user you are looking for may have been removed, renamed or may have never existed."
msgstr "Der gesuchte Benutzer wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
#: apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx
msgid "The user's two factor authentication has been reset successfully."
@ -8529,9 +8628,8 @@ msgid "The webhook was successfully created."
msgstr "Der Webhook wurde erfolgreich erstellt."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
msgid "The webhook you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Das gesuchte Webhook wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
msgid "The webhook you are looking for may have been removed, renamed or may have never existed."
msgstr "Der gesuchte Webhook wurde möglicherweise entfernt, umbenannt oder existierte nie."
#: apps/remix/app/components/tables/documents-table-empty-state.tsx
msgid "There are no active drafts at the current moment. You can upload a document to start drafting."
@ -8587,10 +8685,6 @@ msgstr "Diese Aktion ist umkehrbar, jedoch bitte seien Sie vorsichtig, da das Ko
msgid "This claim is locked and cannot be deleted."
msgstr "Dieser Anspruch ist gesperrt und kann nicht gelöscht werden."
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "This code will expire in {expiresInMinutes} minutes."
msgstr "Dieser Code läuft in {expiresInMinutes} Minuten ab."
#: packages/email/template-components/template-document-super-delete.tsx
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."
@ -8836,6 +8930,7 @@ msgstr "Zeitzone"
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
#: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/documents-table.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
@ -8848,6 +8943,19 @@ msgstr "Titel"
msgid "Title cannot be empty"
msgstr "Titel darf nicht leer sein"
#. placeholder {0}: actionVerb.toLowerCase()
#. placeholder {1}: actionTarget.toLowerCase()
#. placeholder {2}: recipient.email
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To {0} this {1}, you need to be logged in as <0>{2}</0>"
msgstr "Um {0} diese {1}zu {2}müssen Sie als <0>{2}</0> angemeldet sein."
#. placeholder {0}: actionVerb.toLowerCase()
#. placeholder {1}: actionTarget.toLowerCase()
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To {0} this {1}, you need to be logged in."
msgstr "Um {0} diese {1}, müssen Sie angemeldet sein."
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
msgid "To accept this invitation you must create an account."
msgstr "Um diese Einladung anzunehmen, müssen Sie ein Konto erstellen."
@ -8888,6 +8996,10 @@ msgstr "Um Zugang zu Ihrem Konto zu erhalten, bestätigen Sie bitte Ihre E-Mail-
msgid "To mark this document as viewed, you need to be logged in as <0>{0}</0>"
msgstr "Um dieses Dokument als angesehen zu markieren, müssen Sie als <0>{0}</0> angemeldet sein"
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To mark this document as viewed, you need to be logged in."
msgstr "Um dieses Dokument als angesehen zu markieren, müssen Sie angemeldet sein."
#. placeholder {0}: emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'
#. placeholder {0}: emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'
#: packages/ui/primitives/template-flow/add-template-fields.tsx
@ -8943,6 +9055,10 @@ msgstr "Das Token ist abgelaufen. Bitte versuchen Sie es erneut."
msgid "Token name"
msgstr "Token-Name"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Top"
msgstr "Oben"
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Total Documents"
msgstr "Gesamtdokumente"
@ -9281,7 +9397,7 @@ msgstr "Passwort wird aktualisiert..."
msgid "Updating Your Information"
msgstr "Aktualisierung Ihrer Informationen"
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "Upgrade"
msgstr "Upgrade"
@ -9335,7 +9451,7 @@ msgstr "Hochladen deaktiviert"
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Upload Document"
msgstr "Dokument hochladen"
@ -9343,10 +9459,6 @@ msgstr "Dokument hochladen"
msgid "Upload documents and add recipients"
msgstr "Dokumente hochladen und Empfänger hinzufügen"
#: packages/ui/primitives/document-upload.tsx
msgid "Upload Envelope"
msgstr "Umschlag hochladen"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
@ -9359,10 +9471,10 @@ msgid "Upload Signature"
msgstr "Signatur hochladen"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Upload Template"
msgstr "Vorlage hochladen"
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "Upload Template Document"
msgstr "Vorlagendokument hochladen"
@ -9472,6 +9584,7 @@ msgid "User with this email already exists. Please use a different email address
msgstr "Ein Benutzer mit dieser E-Mail existiert bereits. Bitte verwenden Sie eine andere E-Mail-Adresse."
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Users"
msgstr "Benutzer"
@ -9494,6 +9607,10 @@ msgstr "Validierung fehlgeschlagen"
msgid "Value"
msgstr "Wert"
#: packages/lib/types/field-meta.ts
msgid "Value must be a number"
msgstr "Wert muss eine Zahl sein"
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "Verification Code Required"
msgstr "Verifizierungscode erforderlich"
@ -9527,7 +9644,7 @@ msgid "Verify your email address to unlock all features."
msgstr "Überprüfen Sie Ihre E-Mail-Adresse, um alle Funktionen freizuschalten."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Verify your email to upload documents."
msgstr "Überprüfen Sie Ihre E-Mail, um Dokumente hochzuladen."
@ -9541,6 +9658,10 @@ msgstr "Überprüfen Sie Ihre Team-E-Mail-Adresse"
msgid "Vertical"
msgstr "Vertikal"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Vertical Align"
msgstr "Vertikale Ausrichtung"
#: apps/remix/app/components/tables/organisation-billing-invoices-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
@ -10393,10 +10514,6 @@ msgstr "Sie können keine Gruppe löschen, die eine höhere Rolle hat als Sie."
msgid "You cannot delete this item because the document has been sent to recipients"
msgstr "Sie können dieses Element nicht löschen, da das Dokument an Empfänger gesendet wurde"
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "You cannot have more than {MAXIMUM_PASSKEYS} passkeys."
msgstr "Sie dürfen nicht mehr als {MAXIMUM_PASSKEYS} Passkeys haben."
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
msgid "You cannot modify a group which has a higher role than you."
msgstr "Sie können keine Gruppe ändern, die eine höhere Rolle hat als Sie."
@ -10413,13 +10530,13 @@ msgstr "Sie können ein Teammitglied, das eine höhere Rolle als Sie hat, nicht
msgid "You cannot remove members from this team if the inherit member feature is enabled."
msgstr "Sie können keine Mitglieder aus diesem Team entfernen, wenn die Funktion Mitglied übernehmen aktiviert ist."
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "You cannot upload documents at this time."
msgstr "Sie können derzeit keine Dokumente hochladen."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You cannot upload encrypted PDFs"
msgstr "Sie können keine verschlüsselten PDFs hochladen"
@ -10503,7 +10620,7 @@ msgid "You have not yet created or received any documents. To create a document
msgstr "Sie haben noch keine Dokumente erstellt oder erhalten. Bitte laden Sie ein Dokument hoch, um eines zu erstellen."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You have reached the limit of the number of files per envelope"
msgstr "Sie haben das Limit für die Anzahl der Dateien pro Umschlag erreicht"
@ -10518,13 +10635,13 @@ msgid "You have reached the maximum number of teams for your plan. Please contac
msgstr "Sie haben die maximale Anzahl an Teams für Ihren Plan erreicht. Bitte kontaktieren Sie den Vertrieb unter <0>{SUPPORT_EMAIL}</0>, wenn Sie Ihren Plan anpassen möchten."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You have reached your document limit for this month. Please upgrade your plan."
msgstr "Sie haben Ihr Dokumentenlimit für diesen Monat erreicht. Bitte aktualisieren Sie Ihren Plan."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "You have reached your document limit."
msgstr "Sie haben Ihr Dokumentenlimit erreicht."
@ -10714,7 +10831,7 @@ msgstr "Ihr aktueller Plan ist überfällig."
msgid "Your direct signing templates"
msgstr "Ihre direkten Unterzeichnungsvorlagen"
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid "Your document failed to upload."
msgstr "Ihr Dokument konnte nicht hochgeladen werden."
@ -10744,7 +10861,7 @@ msgid "Your document has been successfully duplicated."
msgstr "Ihr Dokument wurde erfolgreich dupliziert."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "Your document has been uploaded successfully."
msgstr "Ihr Dokument wurde erfolgreich hochgeladen."

View File

@ -13,10 +13,6 @@ msgstr ""
"Language-Team: \n"
"Plural-Forms: \n"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid " Enable direct link signing"
msgstr " Enable direct link signing"
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid ".PDF documents accepted (max {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB)"
msgstr ".PDF documents accepted (max {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB)"
@ -93,6 +89,11 @@ msgstr "{0, plural, one {# folder} other {# folders}}"
msgid "{0, plural, one {# recipient} other {# recipients}}"
msgstr "{0, plural, one {# recipient} other {# recipients}}"
#. placeholder {0}: envelope.recipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0, plural, one {# Recipient} other {# Recipients}}"
msgstr "{0, plural, one {# Recipient} other {# Recipients}}"
#. placeholder {0}: org.teams.length
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
msgid "{0, plural, one {# team} other {# teams}}"
@ -146,6 +147,12 @@ msgstr "{0, plural, one {Waiting on 1 recipient} other {Waiting on # recipients}
msgid "{0}"
msgstr "{0}"
#. placeholder {0}: file.name
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "{0} couldn't be uploaded:"
msgstr "{0} couldn't be uploaded:"
#. placeholder {0}: team.name
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
msgid "{0} direct signing templates"
@ -165,7 +172,7 @@ msgstr "{0} invited you to {recipientActionVerb} a document"
#. placeholder {0}: remaining.documents
#. placeholder {1}: quota.documents
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "{0} of {1} documents remaining this month."
msgstr "{0} of {1} documents remaining this month."
@ -183,11 +190,6 @@ msgstr "{0} of {1} row(s) selected."
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}\"."
#. placeholder {0}: envelope.recipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0} Recipient(s)"
msgstr "{0} Recipient(s)"
#. placeholder {0}: organisation.name
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "{0} Teams"
@ -201,6 +203,10 @@ msgstr "{browserInfo} on {os}"
msgid "{charactersRemaining, plural, one {1 character remaining} other {{charactersRemaining} characters remaining}}"
msgstr "{charactersRemaining, plural, one {1 character remaining} other {{charactersRemaining} characters remaining}}"
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "{expiresInMinutes, plural, one {This code will expire in # minute.} other {This code will expire in # minutes.}}"
msgstr "{expiresInMinutes, plural, one {This code will expire in # minute.} other {This code will expire in # minutes.}}"
#: packages/email/templates/document-invite.tsx
msgid "{inviterName} <0>({inviterEmail})</0>"
msgstr "{inviterName} <0>({inviterEmail})</0>"
@ -249,6 +255,10 @@ msgstr "{inviterName} on behalf of \"{teamName}\" has invited you to {0}<0/>\"{d
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
msgstr "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "{MAXIMUM_PASSKEYS, plural, one {You cannot have more than # passkey.} other {You cannot have more than # passkeys.}}"
msgstr "{MAXIMUM_PASSKEYS, plural, one {You cannot have more than # passkey.} other {You cannot have more than # passkeys.}}"
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} added a field"
msgstr "{prefix} added a field"
@ -1226,6 +1236,7 @@ msgid "All templates"
msgstr "All templates"
#: apps/remix/app/components/general/period-selector.tsx
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "All Time"
msgstr "All Time"
@ -1473,7 +1484,7 @@ msgid "An error occurred while updating your profile."
msgstr "An error occurred while updating your profile."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "An error occurred while uploading your document."
msgstr "An error occurred while uploading your document."
@ -1852,6 +1863,10 @@ msgstr "Black"
msgid "Blue"
msgstr "Blue"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Bottom"
msgstr "Bottom"
#: apps/remix/app/components/forms/branding-preferences-form.tsx
msgid "Brand Details"
msgstr "Brand Details"
@ -2092,6 +2107,10 @@ msgstr "Ccers"
msgid "Center"
msgstr "Center"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
msgid "Character limit"
msgstr "Character limit"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
msgid "Character Limit"
@ -2258,6 +2277,7 @@ msgstr "Complete Document"
msgid "Complete the fields for the following signers."
msgstr "Complete the fields for the following signers."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-document-jobs-table.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
@ -2538,11 +2558,6 @@ msgstr "Copy Signing Links"
msgid "Copy token"
msgstr "Copy token"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "couldn't be uploaded:"
msgstr "couldn't be uploaded:"
#: apps/remix/app/routes/_profile+/_layout.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
@ -2734,9 +2749,11 @@ msgstr "Create your account and start using state-of-the-art document signing. O
#: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/documents-table.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/general/template/template-page-view-information.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
#: apps/remix/app/components/general/document/document-page-view-information.tsx
@ -3217,6 +3234,10 @@ msgstr "Document \"{0}\" - Rejection Confirmed"
msgid "Document \"{0}\" Cancelled"
msgstr "Document \"{0}\" Cancelled"
#: packages/ui/primitives/document-upload-button.tsx
msgid "Document (Legacy)"
msgstr "Document (Legacy)"
#: apps/remix/app/components/general/envelope-editor/envelope-editor.tsx
msgid "Document & Recipients"
msgstr "Document & Recipients"
@ -3349,6 +3370,10 @@ msgstr "Document found in your account"
msgid "Document ID"
msgstr "Document ID"
#: apps/remix/app/components/general/document/document-page-view-information.tsx
msgid "Document ID (Legacy)"
msgstr "Document ID (Legacy)"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document inbox"
msgstr "Document inbox"
@ -3474,12 +3499,12 @@ msgstr "Document updated successfully"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Document upload disabled due to unpaid invoices"
msgstr "Document upload disabled due to unpaid invoices"
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "Document uploaded"
msgstr "Document uploaded"
@ -3502,6 +3527,10 @@ msgctxt "Audit log format"
msgid "Document visibility updated"
msgstr "Document visibility updated"
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
msgid "Document Volume"
msgstr "Document Volume"
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "Document will be permanently deleted"
msgstr "Document will be permanently deleted"
@ -3515,6 +3544,8 @@ msgstr "Documentation"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.legacy_editor.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/user-profile-timur.tsx
#: apps/remix/app/components/general/app-nav-mobile.tsx
@ -3529,6 +3560,15 @@ msgstr "Documents"
msgid "Documents and resources related to this envelope."
msgstr "Documents and resources related to this envelope."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Documents Completed"
msgstr "Documents Completed"
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Documents Created"
msgstr "Documents Created"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
msgid "Documents created from template"
msgstr "Documents created from template"
@ -3731,6 +3771,7 @@ msgstr "Electronic Signature Disclosure"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._layout.tsx
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx
@ -3920,6 +3961,10 @@ msgstr "Enable custom branding for all documents in this organisation"
msgid "Enable custom branding for all documents in this team"
msgstr "Enable custom branding for all documents in this team"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid "Enable direct link signing"
msgstr "Enable direct link signing"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/lib/constants/template.ts
msgid "Enable Direct Link Signing"
@ -4092,7 +4137,7 @@ msgstr "Envelope updated"
#: apps/remix/app/components/general/document-signing/document-signing-checkbox-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-auto-sign.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
@ -4249,7 +4294,6 @@ msgstr "Failed: {failedCount}"
msgid "Feature Flags"
msgstr "Feature Flags"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
msgid "Field character limit"
msgstr "Field character limit"
@ -4307,7 +4351,7 @@ msgstr "Fields updated"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid "File cannot be larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
msgstr "File cannot be larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
@ -4397,6 +4441,7 @@ msgstr "Forgot Password?"
msgid "Forgot your password?"
msgstr "Forgot your password?"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
msgid "Free"
msgstr "Free"
@ -4929,6 +4974,10 @@ msgstr "Join {organisationName} on Documenso"
msgid "Join our community on <0>Discord</0> for community support and discussion."
msgstr "Join our community on <0>Discord</0> for community support and discussion."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Joined"
msgstr "Joined"
#. placeholder {0}: DateTime.fromJSDate(team.createdAt).toRelative({ style: 'short' })
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
msgid "Joined {0}"
@ -4962,10 +5011,18 @@ msgstr "Last 14 days"
msgid "Last 30 days"
msgstr "Last 30 days"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last 30 Days"
msgstr "Last 30 Days"
#: apps/remix/app/components/general/period-selector.tsx
msgid "Last 7 days"
msgstr "Last 7 days"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last 90 Days"
msgstr "Last 90 Days"
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
msgid "Last Active"
msgstr "Last Active"
@ -4995,9 +5052,9 @@ msgstr "Last updated at"
msgid "Last used"
msgstr "Last used"
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
msgid "Leaderboard"
msgstr "Leaderboard"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last Year"
msgstr "Last Year"
#: apps/remix/app/components/tables/user-organisations-table.tsx
#: apps/remix/app/components/dialogs/organisation-leave-dialog.tsx
@ -5023,6 +5080,14 @@ msgstr "Left"
msgid "Legality of Electronic Signatures"
msgstr "Legality of Electronic Signatures"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Letter spacing"
msgstr "Letter spacing"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Letter Spacing"
msgstr "Letter Spacing"
#: apps/remix/app/components/general/app-command-menu.tsx
msgid "Light Mode"
msgstr "Light Mode"
@ -5031,6 +5096,14 @@ msgstr "Light Mode"
msgid "Like to have your own public profile with agreements?"
msgstr "Like to have your own public profile with agreements?"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Line height"
msgstr "Line height"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Line Height"
msgstr "Line Height"
#: packages/email/templates/confirm-team-email.tsx
msgid "Link expires in 1 hour."
msgstr "Link expires in 1 hour."
@ -5310,7 +5383,10 @@ msgstr "Member Since"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._layout.tsx
#: apps/remix/app/components/tables/team-groups-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
msgid "Members"
@ -5328,6 +5404,10 @@ msgstr "Message"
msgid "Message <0>(Optional)</0>"
msgstr "Message <0>(Optional)</0>"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Middle"
msgstr "Middle"
#: apps/remix/app/components/forms/editor/editor-field-number-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
msgid "Min"
@ -5403,7 +5483,8 @@ msgstr "N/A"
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/tables/admin-document-jobs-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
@ -5470,7 +5551,6 @@ msgstr "Never expire"
msgid "New Password"
msgstr "New Password"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "New Template"
msgstr "New Template"
@ -5808,6 +5888,11 @@ msgstr "Organisation Group Settings"
msgid "Organisation has been updated successfully"
msgstr "Organisation has been updated successfully"
#: apps/remix/app/routes/_authenticated+/admin+/organisation-insights._index.tsx
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
msgid "Organisation Insights"
msgstr "Organisation Insights"
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
msgid "Organisation invitation"
msgstr "Organisation invitation"
@ -5904,6 +5989,7 @@ msgid "Organize your documents and templates"
msgstr "Organize your documents and templates"
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
msgctxt "Original document (adjective)"
msgid "Original"
msgstr "Original"
@ -5944,6 +6030,10 @@ msgstr "Page {0} of {1}"
msgid "Page {0} of {numPages}"
msgstr "Page {0} of {numPages}"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
msgid "Paid"
msgstr "Paid"
#: apps/remix/app/components/general/document-signing/document-signing-auth-dialog.tsx
#: apps/remix/app/components/forms/signin.tsx
msgid "Passkey"
@ -6069,6 +6159,7 @@ msgid "per year"
msgstr "per year"
#: apps/remix/app/components/tables/user-organisations-table.tsx
msgctxt "Personal organisation (adjective)"
msgid "Personal"
msgstr "Personal"
@ -6434,6 +6525,7 @@ msgid "Read only"
msgstr "Read only"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
#: packages/ui/components/document/envelope-recipient-field-tooltip.tsx
msgid "Read Only"
msgstr "Read Only"
@ -6869,6 +6961,7 @@ msgstr "Right"
#: apps/remix/app/components/tables/organisation-members-table.tsx
#: apps/remix/app/components/tables/organisation-member-invites-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/embed/authoring/configure-document-recipients.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
@ -6931,7 +7024,6 @@ msgstr "Search by claim ID or name"
msgid "Search by document title"
msgstr "Search by document title"
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
msgid "Search by name or email"
msgstr "Search by name or email"
@ -6940,6 +7032,10 @@ msgstr "Search by name or email"
msgid "Search by organisation ID, name, customer ID or owner email"
msgstr "Search by organisation ID, name, customer ID or owner email"
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
msgid "Search by organisation name"
msgstr "Search by organisation name"
#: apps/remix/app/components/general/document/document-search.tsx
msgid "Search documents..."
msgstr "Search documents..."
@ -7110,6 +7206,10 @@ msgstr "Select the members to include in this group"
msgid "Select triggers"
msgstr "Select triggers"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Select vertical align"
msgstr "Select vertical align"
#: apps/remix/app/components/dialogs/folder-update-dialog.tsx
msgid "Select visibility"
msgstr "Select visibility"
@ -7480,12 +7580,16 @@ msgstr "Signatures Collected"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
#: packages/ui/components/document/envelope-recipient-field-tooltip.tsx
#: packages/ui/components/document/document-read-only-fields.tsx
msgid "Signed"
msgstr "Signed"
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
msgctxt "Signed document (adjective)"
msgid "Signed"
msgstr "Signed"
#: packages/lib/constants/recipient-roles.ts
msgctxt "Recipient role actioned"
msgid "Signed"
@ -7551,11 +7655,6 @@ msgstr "Signing links have been generated for this document."
msgid "Signing order is enabled."
msgstr "Signing order is enabled."
#: apps/remix/app/routes/_authenticated+/admin+/leaderboard.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
msgid "Signing Volume"
msgstr "Signing Volume"
#: apps/remix/app/components/forms/signup.tsx
msgid "Signups are disabled."
msgstr "Signups are disabled."
@ -7707,6 +7806,7 @@ msgstr "Stats"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/audit-log.tsx
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-email-domains-table.tsx
#: apps/remix/app/components/tables/organisation-billing-invoices-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
@ -7900,6 +8000,7 @@ msgstr "System Theme"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Team"
msgstr "Team"
@ -7985,6 +8086,7 @@ msgstr "Team Members"
msgid "Team members have been added."
msgstr "Team members have been added."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/forms/team-update-form.tsx
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
msgid "Team Name"
@ -8029,6 +8131,9 @@ msgstr "Team URL"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.teams.tsx
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings._layout.tsx
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/general/org-menu-switcher.tsx
msgid "Teams"
msgstr "Teams"
@ -8051,6 +8156,11 @@ msgstr "Teams that this organisation group is currently assigned to"
msgid "Template"
msgstr "Template"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Template (Legacy)"
msgstr "Template (Legacy)"
#: apps/remix/app/routes/embed+/v1+/authoring_.completed.create.tsx
msgid "Template Created"
msgstr "Template Created"
@ -8079,6 +8189,10 @@ msgstr "Template has been removed from your public profile."
msgid "Template has been updated."
msgstr "Template has been updated."
#: apps/remix/app/components/general/template/template-page-view-information.tsx
msgid "Template ID (Legacy)"
msgstr "Template ID (Legacy)"
#: apps/remix/app/components/general/legacy-field-warning-popover.tsx
msgid "Template is using legacy field insertion"
msgstr "Template is using legacy field insertion"
@ -8261,12 +8375,8 @@ msgstr "The document you are looking for could not be found."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.edit.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid ""
"The document you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr ""
"The document you are looking for may have been removed, renamed or may have never\n"
" existed."
msgid "The document you are looking for may have been removed, renamed or may have never existed."
msgstr "The document you are looking for may have been removed, renamed or may have never existed."
#: packages/ui/components/document/document-send-email-message-helper.tsx
msgid "The document's name"
@ -8277,12 +8387,8 @@ msgid "The email address which will show up in the \"Reply To\" field in emails"
msgstr "The email address which will show up in the \"Reply To\" field in emails"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.email-domains.$id.tsx
msgid ""
"The email domain you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr ""
"The email domain you are looking for may have been removed, renamed or may have never\n"
" existed."
msgid "The email domain you are looking for may have been removed, renamed or may have never existed."
msgstr "The email domain you are looking for may have been removed, renamed or may have never existed."
#: apps/remix/app/components/forms/signin.tsx
msgid "The email or password provided is incorrect"
@ -8338,32 +8444,17 @@ msgid "The organisation email has been created successfully."
msgstr "The organisation email has been created successfully."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid ""
"The organisation group you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr ""
"The organisation group you are looking for may have been removed, renamed or may have never\n"
" existed."
msgid "The organisation group you are looking for may have been removed, renamed or may have never existed."
msgstr "The organisation group you are looking for may have been removed, renamed or may have never existed."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "The organisation role that will be applied to all members in this group."
msgstr "The organisation role that will be applied to all members in this group."
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
msgid ""
"The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr ""
"The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
#: apps/remix/app/routes/_authenticated+/_layout.tsx
msgid ""
"The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr ""
"The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never existed."
msgstr "The organisation you are looking for may have been removed, renamed or may have never existed."
#: apps/remix/app/components/general/generic-error-layout.tsx
msgid "The page you are looking for was moved, removed, renamed or might never have existed."
@ -8454,20 +8545,9 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
msgstr "The team email <0>{teamEmail}</0> has been removed from the following team"
#: apps/remix/app/routes/_authenticated+/_layout.tsx
msgid ""
"The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr ""
"The team you are looking for may have been removed, renamed or may have never\n"
" existed."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
msgid ""
"The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr ""
"The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgid "The team you are looking for may have been removed, renamed or may have never existed."
msgstr "The team you are looking for may have been removed, renamed or may have never existed."
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
msgid "The template has been moved successfully."
@ -8482,12 +8562,8 @@ msgid "The template you are looking for could not be found."
msgstr "The template you are looking for could not be found."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
msgid ""
"The template you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr ""
"The template you are looking for may have been removed, renamed or may have never\n"
" existed."
msgid "The template you are looking for may have been removed, renamed or may have never existed."
msgstr "The template you are looking for may have been removed, renamed or may have never existed."
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
msgid "The test webhook has been successfully sent to your endpoint."
@ -8527,12 +8603,8 @@ msgid "The URL for Documenso to send webhook events to."
msgstr "The URL for Documenso to send webhook events to."
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
msgid ""
"The user you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr ""
"The user you are looking for may have been removed, renamed or may have never\n"
" existed."
msgid "The user you are looking for may have been removed, renamed or may have never existed."
msgstr "The user you are looking for may have been removed, renamed or may have never existed."
#: apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx
msgid "The user's two factor authentication has been reset successfully."
@ -8551,12 +8623,8 @@ msgid "The webhook was successfully created."
msgstr "The webhook was successfully created."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
msgid ""
"The webhook you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr ""
"The webhook you are looking for may have been removed, renamed or may have never\n"
" existed."
msgid "The webhook you are looking for may have been removed, renamed or may have never existed."
msgstr "The webhook you are looking for may have been removed, renamed or may have never existed."
#: apps/remix/app/components/tables/documents-table-empty-state.tsx
msgid "There are no active drafts at the current moment. You can upload a document to start drafting."
@ -8612,10 +8680,6 @@ msgstr "This action is reversible, but please be careful as the account may be a
msgid "This claim is locked and cannot be deleted."
msgstr "This claim is locked and cannot be deleted."
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "This code will expire in {expiresInMinutes} minutes."
msgstr "This code will expire in {expiresInMinutes} minutes."
#: packages/email/template-components/template-document-super-delete.tsx
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."
@ -8861,6 +8925,7 @@ msgstr "Time Zone"
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
#: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/documents-table.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
@ -8873,6 +8938,19 @@ msgstr "Title"
msgid "Title cannot be empty"
msgstr "Title cannot be empty"
#. placeholder {0}: actionVerb.toLowerCase()
#. placeholder {1}: actionTarget.toLowerCase()
#. placeholder {2}: recipient.email
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To {0} this {1}, you need to be logged in as <0>{2}</0>"
msgstr "To {0} this {1}, you need to be logged in as <0>{2}</0>"
#. placeholder {0}: actionVerb.toLowerCase()
#. placeholder {1}: actionTarget.toLowerCase()
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To {0} this {1}, you need to be logged in."
msgstr "To {0} this {1}, you need to be logged in."
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
msgid "To accept this invitation you must create an account."
msgstr "To accept this invitation you must create an account."
@ -8913,6 +8991,10 @@ msgstr "To gain access to your account, please confirm your email address by cli
msgid "To mark this document as viewed, you need to be logged in as <0>{0}</0>"
msgstr "To mark this document as viewed, you need to be logged in as <0>{0}</0>"
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To mark this document as viewed, you need to be logged in."
msgstr "To mark this document as viewed, you need to be logged in."
#. placeholder {0}: emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'
#. placeholder {0}: emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'
#: packages/ui/primitives/template-flow/add-template-fields.tsx
@ -8968,6 +9050,10 @@ msgstr "Token has expired. Please try again."
msgid "Token name"
msgstr "Token name"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Top"
msgstr "Top"
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Total Documents"
msgstr "Total Documents"
@ -9306,7 +9392,7 @@ msgstr "Updating password..."
msgid "Updating Your Information"
msgstr "Updating Your Information"
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "Upgrade"
msgstr "Upgrade"
@ -9360,7 +9446,7 @@ msgstr "Upload disabled"
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Upload Document"
msgstr "Upload Document"
@ -9368,10 +9454,6 @@ msgstr "Upload Document"
msgid "Upload documents and add recipients"
msgstr "Upload documents and add recipients"
#: packages/ui/primitives/document-upload.tsx
msgid "Upload Envelope"
msgstr "Upload Envelope"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
@ -9384,10 +9466,10 @@ msgid "Upload Signature"
msgstr "Upload Signature"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Upload Template"
msgstr "Upload Template"
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "Upload Template Document"
msgstr "Upload Template Document"
@ -9497,6 +9579,7 @@ msgid "User with this email already exists. Please use a different email address
msgstr "User with this email already exists. Please use a different email address."
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Users"
msgstr "Users"
@ -9519,6 +9602,10 @@ msgstr "Validation failed"
msgid "Value"
msgstr "Value"
#: packages/lib/types/field-meta.ts
msgid "Value must be a number"
msgstr "Value must be a number"
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "Verification Code Required"
msgstr "Verification Code Required"
@ -9552,7 +9639,7 @@ msgid "Verify your email address to unlock all features."
msgstr "Verify your email address to unlock all features."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Verify your email to upload documents."
msgstr "Verify your email to upload documents."
@ -9566,6 +9653,10 @@ msgstr "Verify your team email address"
msgid "Vertical"
msgstr "Vertical"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Vertical Align"
msgstr "Vertical Align"
#: apps/remix/app/components/tables/organisation-billing-invoices-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
@ -10418,10 +10509,6 @@ msgstr "You cannot delete a group which has a higher role than you."
msgid "You cannot delete this item because the document has been sent to recipients"
msgstr "You cannot delete this item because the document has been sent to recipients"
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "You cannot have more than {MAXIMUM_PASSKEYS} passkeys."
msgstr "You cannot have more than {MAXIMUM_PASSKEYS} passkeys."
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
msgid "You cannot modify a group which has a higher role than you."
msgstr "You cannot modify a group which has a higher role than you."
@ -10438,13 +10525,13 @@ msgstr "You cannot modify a team member who has a higher role than you."
msgid "You cannot remove members from this team if the inherit member feature is enabled."
msgstr "You cannot remove members from this team if the inherit member feature is enabled."
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "You cannot upload documents at this time."
msgstr "You cannot upload documents at this time."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You cannot upload encrypted PDFs"
msgstr "You cannot upload encrypted PDFs"
@ -10528,7 +10615,7 @@ msgid "You have not yet created or received any documents. To create a document
msgstr "You have not yet created or received any documents. To create a document please upload one."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You have reached the limit of the number of files per envelope"
msgstr "You have reached the limit of the number of files per envelope"
@ -10543,13 +10630,13 @@ msgid "You have reached the maximum number of teams for your plan. Please contac
msgstr "You have reached the maximum number of teams for your plan. Please contact sales at <0>{SUPPORT_EMAIL}</0> if you would like to adjust your plan."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You have reached your document limit for this month. Please upgrade your plan."
msgstr "You have reached your document limit for this month. Please upgrade your plan."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "You have reached your document limit."
msgstr "You have reached your document limit."
@ -10739,7 +10826,7 @@ msgstr "Your current plan is past due."
msgid "Your direct signing templates"
msgstr "Your direct signing templates"
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid "Your document failed to upload."
msgstr "Your document failed to upload."
@ -10769,7 +10856,7 @@ msgid "Your document has been successfully duplicated."
msgstr "Your document has been successfully duplicated."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "Your document has been uploaded successfully."
msgstr "Your document has been uploaded successfully."

View File

@ -8,7 +8,7 @@ msgstr ""
"Language: es\n"
"Project-Id-Version: documenso-app\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-11-07 03:40\n"
"PO-Revision-Date: 2025-11-11 02:33\n"
"Last-Translator: \n"
"Language-Team: Spanish\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@ -18,10 +18,6 @@ msgstr ""
"X-Crowdin-File: web.po\n"
"X-Crowdin-File-ID: 8\n"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid " Enable direct link signing"
msgstr " Habilitar la firma mediante enlace directo"
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid ".PDF documents accepted (max {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB)"
msgstr ".Documentos PDF aceptados (máx {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB)"
@ -98,6 +94,11 @@ msgstr "{0, plural, one {# carpeta} other {# carpetas}}"
msgid "{0, plural, one {# recipient} other {# recipients}}"
msgstr "{0, plural, one {# destinatario} other {# destinatarios}}"
#. placeholder {0}: envelope.recipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0, plural, one {# Recipient} other {# Recipients}}"
msgstr "{0, plural, one {# Destinatario} other {# Destinatarios}}"
#. placeholder {0}: org.teams.length
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
msgid "{0, plural, one {# team} other {# teams}}"
@ -151,6 +152,12 @@ msgstr "{0, plural, one {Esperando 1 destinatario} other {Esperando # destinatar
msgid "{0}"
msgstr "{0}"
#. placeholder {0}: file.name
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "{0} couldn't be uploaded:"
msgstr "{0} no pudo ser subido:"
#. placeholder {0}: team.name
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
msgid "{0} direct signing templates"
@ -170,7 +177,7 @@ msgstr "{0} te invitó a {recipientActionVerb} un documento"
#. placeholder {0}: remaining.documents
#. placeholder {1}: quota.documents
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "{0} of {1} documents remaining this month."
msgstr "{0} de {1} documentos restantes este mes."
@ -188,11 +195,6 @@ msgstr "{0} de {1} fila(s) seleccionada."
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
msgstr "{0} en nombre de \"{1}\" te ha invitado a {recipientActionVerb} el documento \"{2}\"."
#. placeholder {0}: envelope.recipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0} Recipient(s)"
msgstr "{0} Destinatario(s)"
#. placeholder {0}: organisation.name
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "{0} Teams"
@ -206,6 +208,10 @@ msgstr "{browserInfo} en {os}"
msgid "{charactersRemaining, plural, one {1 character remaining} other {{charactersRemaining} characters remaining}}"
msgstr "{charactersRemaining, plural, one {1 carácter restante} other {{charactersRemaining} caracteres restantes}}"
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "{expiresInMinutes, plural, one {This code will expire in # minute.} other {This code will expire in # minutes.}}"
msgstr "{expiresInMinutes, plural, one {Este código expirará en # minuto.} other {Este código expirará en # minutos.}}"
#: packages/email/templates/document-invite.tsx
msgid "{inviterName} <0>({inviterEmail})</0>"
msgstr "{inviterName} <0>({inviterEmail})</0>"
@ -254,6 +260,10 @@ msgstr "{inviterName} en nombre de \"{teamName}\" te ha invitado a {0}<0/>\"{doc
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
msgstr "{inviterName} en nombre de \"{teamName}\" te ha invitado a {action} {documentName}"
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "{MAXIMUM_PASSKEYS, plural, one {You cannot have more than # passkey.} other {You cannot have more than # passkeys.}}"
msgstr "{MAXIMUM_PASSKEYS, plural, one {No puedes tener más de # clave de acceso.} other {No puedes tener más de # claves de acceso.}}"
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} added a field"
msgstr "{prefix} agregó un campo"
@ -1231,6 +1241,7 @@ msgid "All templates"
msgstr "Todas las plantillas"
#: apps/remix/app/components/general/period-selector.tsx
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "All Time"
msgstr "Todo el Tiempo"
@ -1478,7 +1489,7 @@ msgid "An error occurred while updating your profile."
msgstr "Ocurrió un error al actualizar tu perfil."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "An error occurred while uploading your document."
msgstr "Ocurrió un error al subir tu documento."
@ -1857,6 +1868,10 @@ msgstr "Negro"
msgid "Blue"
msgstr "Azul"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Bottom"
msgstr "Fondo"
#: apps/remix/app/components/forms/branding-preferences-form.tsx
msgid "Brand Details"
msgstr "Detalles de la Marca"
@ -2097,6 +2112,10 @@ msgstr "Ccers"
msgid "Center"
msgstr "Centro"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
msgid "Character limit"
msgstr "Límite de caracteres"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
msgid "Character Limit"
@ -2263,6 +2282,7 @@ msgstr "Documento Completo"
msgid "Complete the fields for the following signers."
msgstr "Completa los campos para los siguientes firmantes."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-document-jobs-table.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
@ -2543,11 +2563,6 @@ msgstr "Copiar enlaces de firma"
msgid "Copy token"
msgstr "Copiar token"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "couldn't be uploaded:"
msgstr "no se pudo cargar:"
#: apps/remix/app/routes/_profile+/_layout.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
@ -2739,9 +2754,11 @@ msgstr "Crea tu cuenta y comienza a utilizar la firma de documentos de última g
#: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/documents-table.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/general/template/template-page-view-information.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
#: apps/remix/app/components/general/document/document-page-view-information.tsx
@ -3222,6 +3239,10 @@ msgstr "Documento \"{0}\" - Rechazo confirmado"
msgid "Document \"{0}\" Cancelled"
msgstr "Documento \"{0}\" Cancelado"
#: packages/ui/primitives/document-upload-button.tsx
msgid "Document (Legacy)"
msgstr "Documento (Legado)"
#: apps/remix/app/components/general/envelope-editor/envelope-editor.tsx
msgid "Document & Recipients"
msgstr "Documento y Destinatarios"
@ -3354,6 +3375,10 @@ msgstr "Documento encontrado en tu cuenta"
msgid "Document ID"
msgstr "ID del documento"
#: apps/remix/app/components/general/document/document-page-view-information.tsx
msgid "Document ID (Legacy)"
msgstr "ID de documento (Legado)"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document inbox"
msgstr "Bandeja de documentos"
@ -3479,12 +3504,12 @@ msgstr "Documento actualizado con éxito"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Document upload disabled due to unpaid invoices"
msgstr "La carga de documentos está deshabilitada debido a facturas impagadas"
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "Document uploaded"
msgstr "Documento subido"
@ -3507,6 +3532,10 @@ msgctxt "Audit log format"
msgid "Document visibility updated"
msgstr "Visibilidad del documento actualizada"
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
msgid "Document Volume"
msgstr "Volumen del documento"
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "Document will be permanently deleted"
msgstr "El documento será eliminado permanentemente"
@ -3520,6 +3549,8 @@ msgstr "Documentación"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.legacy_editor.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/user-profile-timur.tsx
#: apps/remix/app/components/general/app-nav-mobile.tsx
@ -3534,6 +3565,15 @@ msgstr "Documentos"
msgid "Documents and resources related to this envelope."
msgstr "Documentos y recursos relacionados con este sobre."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Documents Completed"
msgstr "Documentos completados"
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Documents Created"
msgstr "Documentos creados"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
msgid "Documents created from template"
msgstr "Documentos creados a partir de la plantilla"
@ -3736,6 +3776,7 @@ msgstr "Divulgación de Firma Electrónica"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._layout.tsx
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx
@ -3925,6 +3966,10 @@ msgstr "Habilita la marca personalizada para todos los documentos en esta organi
msgid "Enable custom branding for all documents in this team"
msgstr "Habilita la marca personalizada para todos los documentos en este equipo"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid "Enable direct link signing"
msgstr "Habilitar firma por enlace directo"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/lib/constants/template.ts
msgid "Enable Direct Link Signing"
@ -4097,7 +4142,7 @@ msgstr "Sobre actualizado"
#: apps/remix/app/components/general/document-signing/document-signing-checkbox-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-auto-sign.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
@ -4254,7 +4299,6 @@ msgstr "Fallidos: {failedCount}"
msgid "Feature Flags"
msgstr "Flags de características"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
msgid "Field character limit"
msgstr "Límite de caracteres del campo"
@ -4312,7 +4356,7 @@ msgstr "Campos actualizados"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid "File cannot be larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
msgstr "El archivo no puede ser mayor a {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
@ -4402,6 +4446,7 @@ msgstr "¿Olvidaste tu contraseña?"
msgid "Forgot your password?"
msgstr "¿Olvidaste tu contraseña?"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
msgid "Free"
msgstr "Gratis"
@ -4934,6 +4979,10 @@ msgstr "Únete a {organisationName} en Documenso"
msgid "Join our community on <0>Discord</0> for community support and discussion."
msgstr "Únete a nuestra comunidad en <0>Discord</0> para soporte y debate."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Joined"
msgstr "Unido"
#. placeholder {0}: DateTime.fromJSDate(team.createdAt).toRelative({ style: 'short' })
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
msgid "Joined {0}"
@ -4967,10 +5016,18 @@ msgstr "Últimos 14 días"
msgid "Last 30 days"
msgstr "Últimos 30 días"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last 30 Days"
msgstr "Últimos 30 días"
#: apps/remix/app/components/general/period-selector.tsx
msgid "Last 7 days"
msgstr "Últimos 7 días"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last 90 Days"
msgstr "Últimos 90 días"
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
msgid "Last Active"
msgstr "Última actividad"
@ -5000,9 +5057,9 @@ msgstr "Última actualización el"
msgid "Last used"
msgstr "Último uso"
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
msgid "Leaderboard"
msgstr "Tabla de clasificación"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last Year"
msgstr "Último año"
#: apps/remix/app/components/tables/user-organisations-table.tsx
#: apps/remix/app/components/dialogs/organisation-leave-dialog.tsx
@ -5028,6 +5085,14 @@ msgstr "Izquierda"
msgid "Legality of Electronic Signatures"
msgstr "Legalidad de las Firmas Electrónicas"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Letter spacing"
msgstr "Espaciado de letras"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Letter Spacing"
msgstr "Espaciado de letras"
#: apps/remix/app/components/general/app-command-menu.tsx
msgid "Light Mode"
msgstr "Modo claro"
@ -5036,6 +5101,14 @@ msgstr "Modo claro"
msgid "Like to have your own public profile with agreements?"
msgstr "¿Te gustaría tener tu propio perfil público con acuerdos?"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Line height"
msgstr "Altura de línea"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Line Height"
msgstr "Altura de línea"
#: packages/email/templates/confirm-team-email.tsx
msgid "Link expires in 1 hour."
msgstr "El enlace expira en 1 hora."
@ -5315,7 +5388,10 @@ msgstr "Miembro desde"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._layout.tsx
#: apps/remix/app/components/tables/team-groups-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
msgid "Members"
@ -5333,6 +5409,10 @@ msgstr "Mensaje"
msgid "Message <0>(Optional)</0>"
msgstr "Mensaje <0>(Opcional)</0>"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Middle"
msgstr "Centro"
#: apps/remix/app/components/forms/editor/editor-field-number-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
msgid "Min"
@ -5408,7 +5488,8 @@ msgstr "N/A"
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/tables/admin-document-jobs-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
@ -5475,7 +5556,6 @@ msgstr "Nunca expira"
msgid "New Password"
msgstr "Nueva Contraseña"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "New Template"
msgstr "Nueva plantilla"
@ -5813,6 +5893,11 @@ msgstr "Configuración de Grupo de Organización"
msgid "Organisation has been updated successfully"
msgstr "La organización ha sido actualizada con éxito"
#: apps/remix/app/routes/_authenticated+/admin+/organisation-insights._index.tsx
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
msgid "Organisation Insights"
msgstr "Perspectivas de la organización"
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
msgid "Organisation invitation"
msgstr "Invitación de Organización"
@ -5909,6 +5994,7 @@ msgid "Organize your documents and templates"
msgstr "Organiza tus documentos y plantillas"
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
msgctxt "Original document (adjective)"
msgid "Original"
msgstr "Original"
@ -5949,6 +6035,10 @@ msgstr "Página {0} de {1}"
msgid "Page {0} of {numPages}"
msgstr "Página {0} de {numPages}"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
msgid "Paid"
msgstr "Pagado"
#: apps/remix/app/components/general/document-signing/document-signing-auth-dialog.tsx
#: apps/remix/app/components/forms/signin.tsx
msgid "Passkey"
@ -6074,6 +6164,7 @@ msgid "per year"
msgstr "al año"
#: apps/remix/app/components/tables/user-organisations-table.tsx
msgctxt "Personal organisation (adjective)"
msgid "Personal"
msgstr "Personal"
@ -6439,6 +6530,7 @@ msgid "Read only"
msgstr "Solo lectura"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
#: packages/ui/components/document/envelope-recipient-field-tooltip.tsx
msgid "Read Only"
msgstr "Solo lectura"
@ -6874,6 +6966,7 @@ msgstr "Derecha"
#: apps/remix/app/components/tables/organisation-members-table.tsx
#: apps/remix/app/components/tables/organisation-member-invites-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/embed/authoring/configure-document-recipients.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
@ -6936,7 +7029,6 @@ msgstr "Buscar por ID de reclamo o nombre"
msgid "Search by document title"
msgstr "Buscar por título del documento"
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
msgid "Search by name or email"
msgstr "Buscar por nombre o correo electrónico"
@ -6945,6 +7037,10 @@ msgstr "Buscar por nombre o correo electrónico"
msgid "Search by organisation ID, name, customer ID or owner email"
msgstr "Buscar por ID de organización, nombre, ID de cliente o correo electrónico del propietario"
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
msgid "Search by organisation name"
msgstr "Buscar por nombre de organización"
#: apps/remix/app/components/general/document/document-search.tsx
msgid "Search documents..."
msgstr "Buscar documentos..."
@ -7115,6 +7211,10 @@ msgstr "Seleccione a los miembros para incluir en este grupo"
msgid "Select triggers"
msgstr "Seleccionar activaciones"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Select vertical align"
msgstr "Seleccionar alineación vertical"
#: apps/remix/app/components/dialogs/folder-update-dialog.tsx
msgid "Select visibility"
msgstr "Seleccionar visibilidad"
@ -7485,12 +7585,16 @@ msgstr "Firmas recolectadas"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
#: packages/ui/components/document/envelope-recipient-field-tooltip.tsx
#: packages/ui/components/document/document-read-only-fields.tsx
msgid "Signed"
msgstr "Firmado"
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
msgctxt "Signed document (adjective)"
msgid "Signed"
msgstr "Firmado"
#: packages/lib/constants/recipient-roles.ts
msgctxt "Recipient role actioned"
msgid "Signed"
@ -7556,11 +7660,6 @@ msgstr "Se han generado enlaces de firma para este documento."
msgid "Signing order is enabled."
msgstr "El orden de firma está habilitado."
#: apps/remix/app/routes/_authenticated+/admin+/leaderboard.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
msgid "Signing Volume"
msgstr "Volumen de firmas"
#: apps/remix/app/components/forms/signup.tsx
msgid "Signups are disabled."
msgstr "Las inscripciones están deshabilitadas."
@ -7712,6 +7811,7 @@ msgstr "Estadísticas"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/audit-log.tsx
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-email-domains-table.tsx
#: apps/remix/app/components/tables/organisation-billing-invoices-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
@ -7905,6 +8005,7 @@ msgstr "Tema del sistema"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Team"
msgstr "Equipo"
@ -7990,6 +8091,7 @@ msgstr "Miembros del equipo"
msgid "Team members have been added."
msgstr "Se han añadido miembros del equipo."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/forms/team-update-form.tsx
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
msgid "Team Name"
@ -8034,6 +8136,9 @@ msgstr "URL del equipo"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.teams.tsx
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings._layout.tsx
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/general/org-menu-switcher.tsx
msgid "Teams"
msgstr "Equipos"
@ -8056,6 +8161,11 @@ msgstr "Equipos a los que actualmente está asignado este grupo de organización
msgid "Template"
msgstr "Plantilla"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Template (Legacy)"
msgstr "Plantilla (Legado)"
#: apps/remix/app/routes/embed+/v1+/authoring_.completed.create.tsx
msgid "Template Created"
msgstr "Plantilla Creada"
@ -8084,6 +8194,10 @@ msgstr "La plantilla ha sido eliminada de tu perfil público."
msgid "Template has been updated."
msgstr "La plantilla ha sido actualizada."
#: apps/remix/app/components/general/template/template-page-view-information.tsx
msgid "Template ID (Legacy)"
msgstr "ID de plantilla (Legado)"
#: apps/remix/app/components/general/legacy-field-warning-popover.tsx
msgid "Template is using legacy field insertion"
msgstr "La plantilla utiliza inserción de campos heredada"
@ -8266,10 +8380,8 @@ msgstr "No se pudo encontrar el documento que está buscando."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.edit.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "The document you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "El documento que está buscando puede haber sido eliminado, renombrado o puede que nunca\n"
" haya existido."
msgid "The document you are looking for may have been removed, renamed or may have never existed."
msgstr "El documento que estás buscando puede haber sido eliminado, renombrado o quizás nunca exist."
#: packages/ui/components/document/document-send-email-message-helper.tsx
msgid "The document's name"
@ -8280,9 +8392,8 @@ msgid "The email address which will show up in the \"Reply To\" field in emails"
msgstr "La dirección de correo que aparecerá en el campo \"Responder a\" en los correos electrónicos"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.email-domains.$id.tsx
msgid "The email domain you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "El dominio de correo electrónico que estás buscando puede haber sido eliminado, renombrado o puede que nunca haya existido."
msgid "The email domain you are looking for may have been removed, renamed or may have never existed."
msgstr "El dominio de correo electrónico que estás buscando puede haber sido eliminado, renombrado o quizás nunca exist."
#: apps/remix/app/components/forms/signin.tsx
msgid "The email or password provided is incorrect"
@ -8338,23 +8449,17 @@ msgid "The organisation email has been created successfully."
msgstr "El correo electrónico de la organización se ha creado con éxito."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "El grupo de organización que está buscando puede haber sido eliminado, renombrado o puede que nunca haya existido."
msgid "The organisation group you are looking for may have been removed, renamed or may have never existed."
msgstr "El grupo de organizaciones que estás buscando puede haber sido eliminado, renombrado o quizás nunca exist."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "The organisation role that will be applied to all members in this group."
msgstr "El rol de organización que se aplicará a todos los miembros de este grupo."
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "La organización que está buscando puede haber sido eliminada, renombrada o puede que nunca haya existido."
#: apps/remix/app/routes/_authenticated+/_layout.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "La organización que está buscando puede haber sido eliminada, renombrada o puede que nunca haya existido."
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never existed."
msgstr "La organización que estás buscando puede haber sido eliminada, renombrada o quizás nunca existió."
#: apps/remix/app/components/general/generic-error-layout.tsx
msgid "The page you are looking for was moved, removed, renamed or might never have existed."
@ -8445,15 +8550,9 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
msgstr "El correo electrónico del equipo <0>{teamEmail}</0> ha sido eliminado del siguiente equipo"
#: apps/remix/app/routes/_authenticated+/_layout.tsx
msgid "The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "El equipo que está buscando puede haber sido eliminado, renombrado o puede que nunca haya existido."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
msgid "The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "El equipo que buscas puede haber sido eliminado, renombrado o quizás nunca\n"
" existió."
msgid "The team you are looking for may have been removed, renamed or may have never existed."
msgstr "El equipo que estás buscando puede haber sido eliminado, renombrado o quizás nunca exist."
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
msgid "The template has been moved successfully."
@ -8468,10 +8567,8 @@ msgid "The template you are looking for could not be found."
msgstr "No se pudo encontrar la plantilla que está buscando."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
msgid "The template you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "La plantilla que está buscando puede haber sido eliminada, renombrada o puede que nunca\n"
" haya existido."
msgid "The template you are looking for may have been removed, renamed or may have never existed."
msgstr "La plantilla que estás buscando puede haber sido eliminada, renombrada o quizás nunca exist."
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
msgid "The test webhook has been successfully sent to your endpoint."
@ -8511,9 +8608,8 @@ msgid "The URL for Documenso to send webhook events to."
msgstr "La URL para Documenso para enviar eventos de webhook."
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
msgid "The user you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "El usuario que está buscando puede haber sido eliminado, renombrado o puede que nunca haya existido."
msgid "The user you are looking for may have been removed, renamed or may have never existed."
msgstr "El usuario que estás buscando puede haber sido eliminado, renombrado o quizás nunca exist."
#: apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx
msgid "The user's two factor authentication has been reset successfully."
@ -8532,9 +8628,8 @@ msgid "The webhook was successfully created."
msgstr "El webhook fue creado con éxito."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
msgid "The webhook you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "El webhook que buscas puede haber sido eliminado, renombrado o puede que nunca haya existido."
msgid "The webhook you are looking for may have been removed, renamed or may have never existed."
msgstr "El webhook que estás buscando puede haber sido eliminado, renombrado o quizás nunca exist."
#: apps/remix/app/components/tables/documents-table-empty-state.tsx
msgid "There are no active drafts at the current moment. You can upload a document to start drafting."
@ -8590,10 +8685,6 @@ msgstr "Esta acción es reversible, pero ten cuidado ya que la cuenta podría ve
msgid "This claim is locked and cannot be deleted."
msgstr "Esta reclamo está bloqueado y no puede ser eliminado."
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "This code will expire in {expiresInMinutes} minutes."
msgstr "Este código caducará en {expiresInMinutes} minutos."
#: packages/email/template-components/template-document-super-delete.tsx
msgid "This document can not be recovered, if you would like to dispute the reason for future documents please contact support."
msgstr "Este documento no se puede recuperar, si deseas impugnar la razón para documentos futuros, por favor contacta con el soporte."
@ -8839,6 +8930,7 @@ msgstr "Zona horaria"
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
#: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/documents-table.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
@ -8851,6 +8943,19 @@ msgstr "Título"
msgid "Title cannot be empty"
msgstr "El título no puede estar vacío"
#. placeholder {0}: actionVerb.toLowerCase()
#. placeholder {1}: actionTarget.toLowerCase()
#. placeholder {2}: recipient.email
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To {0} this {1}, you need to be logged in as <0>{2}</0>"
msgstr "Para {0} este {1}, necesitas estar conectado como <0>{2}</0>"
#. placeholder {0}: actionVerb.toLowerCase()
#. placeholder {1}: actionTarget.toLowerCase()
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To {0} this {1}, you need to be logged in."
msgstr "Para {0} este {1}, necesitas estar conectado."
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
msgid "To accept this invitation you must create an account."
msgstr "Para aceptar esta invitación debes crear una cuenta."
@ -8891,6 +8996,10 @@ msgstr "Para acceder a tu cuenta, por favor confirma tu dirección de correo ele
msgid "To mark this document as viewed, you need to be logged in as <0>{0}</0>"
msgstr "Para marcar este documento como visto, debes iniciar sesión como <0>{0}</0>"
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To mark this document as viewed, you need to be logged in."
msgstr "Para marcar este documento como visto, necesitas estar conectado."
#. placeholder {0}: emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'
#. placeholder {0}: emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'
#: packages/ui/primitives/template-flow/add-template-fields.tsx
@ -8946,6 +9055,10 @@ msgstr "El token ha expirado. Por favor, inténtelo de nuevo."
msgid "Token name"
msgstr "Nombre del token"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Top"
msgstr "Parte superior"
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Total Documents"
msgstr "Total de documentos"
@ -9284,7 +9397,7 @@ msgstr "Actualizando contraseña..."
msgid "Updating Your Information"
msgstr "Actualizando Su Información"
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "Upgrade"
msgstr "Actualizar"
@ -9338,7 +9451,7 @@ msgstr "Subida desactivada"
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Upload Document"
msgstr "Cargar Documento"
@ -9346,10 +9459,6 @@ msgstr "Cargar Documento"
msgid "Upload documents and add recipients"
msgstr "Suba documentos y añada destinatarios"
#: packages/ui/primitives/document-upload.tsx
msgid "Upload Envelope"
msgstr "Cargar sobre (subir)"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
@ -9362,10 +9471,10 @@ msgid "Upload Signature"
msgstr "Subir firma"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Upload Template"
msgstr "Subir plantilla"
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "Upload Template Document"
msgstr "Cargar Documento Plantilla"
@ -9475,6 +9584,7 @@ msgid "User with this email already exists. Please use a different email address
msgstr "Un usuario con este correo electrónico ya existe. Por favor, use una dirección de correo diferente."
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Users"
msgstr "Usuarios"
@ -9497,6 +9607,10 @@ msgstr "Validación fallida"
msgid "Value"
msgstr "Valor"
#: packages/lib/types/field-meta.ts
msgid "Value must be a number"
msgstr "El valor debe ser un número"
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "Verification Code Required"
msgstr "Código de verificación requerido"
@ -9530,7 +9644,7 @@ msgid "Verify your email address to unlock all features."
msgstr "Verifica tu dirección de correo electrónico para desbloquear todas las funciones."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Verify your email to upload documents."
msgstr "Verifica tu correo electrónico para subir documentos."
@ -9544,6 +9658,10 @@ msgstr "Verifica tu dirección de correo electrónico del equipo"
msgid "Vertical"
msgstr "Vertical"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Vertical Align"
msgstr "Alineación vertical"
#: apps/remix/app/components/tables/organisation-billing-invoices-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
@ -10396,10 +10514,6 @@ msgstr "No puedes eliminar un grupo que tiene un rol superior al tuyo."
msgid "You cannot delete this item because the document has been sent to recipients"
msgstr "No puede eliminar este elemento porque el documento ha sido enviado a los destinatarios"
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "You cannot have more than {MAXIMUM_PASSKEYS} passkeys."
msgstr "No puedes tener más de {MAXIMUM_PASSKEYS} claves de acceso."
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
msgid "You cannot modify a group which has a higher role than you."
msgstr "No puedes modificar un grupo que tiene un rol superior al tuyo."
@ -10416,13 +10530,13 @@ msgstr "No puedes modificar a un miembro del equipo que tenga un rol más alto q
msgid "You cannot remove members from this team if the inherit member feature is enabled."
msgstr "No puedes eliminar miembros de este equipo si la función de heredar miembros está habilitada."
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "You cannot upload documents at this time."
msgstr "No puede cargar documentos en este momento."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You cannot upload encrypted PDFs"
msgstr "No puedes subir PDFs encriptados"
@ -10506,7 +10620,7 @@ msgid "You have not yet created or received any documents. To create a document
msgstr "Aún no has creado ni recibido documentos. Para crear un documento, por favor carga uno."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You have reached the limit of the number of files per envelope"
msgstr "Has alcanzado el límite de archivos por sobre"
@ -10521,13 +10635,13 @@ msgid "You have reached the maximum number of teams for your plan. Please contac
msgstr "Has alcanzado el número máximo de equipos para tu plan. Por favor, contacta con ventas en <0>{SUPPORT_EMAIL}</0> si deseas ajustar tu plan."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You have reached your document limit for this month. Please upgrade your plan."
msgstr "Ha alcanzado su límite de documentos para este mes. Por favor, actualice su plan."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "You have reached your document limit."
msgstr "Ha alcanzado su límite de documentos."
@ -10717,7 +10831,7 @@ msgstr "Tu plan actual está vencido."
msgid "Your direct signing templates"
msgstr "Tus {0} plantillas de firma directa"
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid "Your document failed to upload."
msgstr "Tu documento no se pudo cargar."
@ -10747,7 +10861,7 @@ msgid "Your document has been successfully duplicated."
msgstr "Tu documento ha sido duplicado con éxito."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "Your document has been uploaded successfully."
msgstr "Tu documento ha sido subido con éxito."

View File

@ -8,7 +8,7 @@ msgstr ""
"Language: fr\n"
"Project-Id-Version: documenso-app\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-11-07 03:40\n"
"PO-Revision-Date: 2025-11-11 02:33\n"
"Last-Translator: \n"
"Language-Team: French\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
@ -18,10 +18,6 @@ msgstr ""
"X-Crowdin-File: web.po\n"
"X-Crowdin-File-ID: 8\n"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid " Enable direct link signing"
msgstr " Activer la signature par lien direct"
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid ".PDF documents accepted (max {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB)"
msgstr "Documents .PDF acceptés (max {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}Mo)"
@ -98,6 +94,11 @@ msgstr "{0, plural, one {# dossier} other {# dossiers}}"
msgid "{0, plural, one {# recipient} other {# recipients}}"
msgstr "{0, plural, one {# destinataire} other {# destinataires}}"
#. placeholder {0}: envelope.recipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0, plural, one {# Recipient} other {# Recipients}}"
msgstr "{0, plural, one {# Destinataire} other {# Destinataires}}"
#. placeholder {0}: org.teams.length
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
msgid "{0, plural, one {# team} other {# teams}}"
@ -151,6 +152,12 @@ msgstr "{0, plural, one {En attente d'1 destinataire} other {En attente de # des
msgid "{0}"
msgstr "{0}"
#. placeholder {0}: file.name
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "{0} couldn't be uploaded:"
msgstr "{0} n'a pas pu être téléchargé :"
#. placeholder {0}: team.name
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
msgid "{0} direct signing templates"
@ -170,7 +177,7 @@ msgstr "{0} vous a invité à {recipientActionVerb} un document"
#. placeholder {0}: remaining.documents
#. placeholder {1}: quota.documents
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "{0} of {1} documents remaining this month."
msgstr "{0} des {1} documents restants ce mois-ci."
@ -188,11 +195,6 @@ msgstr "{0} sur {1} ligne(s) sélectionnée(s)."
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
msgstr "{0} représentant \"{1}\" vous a invité à {recipientActionVerb} le document \"{2}\"."
#. placeholder {0}: envelope.recipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0} Recipient(s)"
msgstr "{0} Destinataire(s)"
#. placeholder {0}: organisation.name
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "{0} Teams"
@ -206,6 +208,10 @@ msgstr "{browserInfo} sur {os}"
msgid "{charactersRemaining, plural, one {1 character remaining} other {{charactersRemaining} characters remaining}}"
msgstr "{charactersRemaining, plural, one {1 caractère restant} other {{charactersRemaining} caractères restants}}"
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "{expiresInMinutes, plural, one {This code will expire in # minute.} other {This code will expire in # minutes.}}"
msgstr "{expiresInMinutes, plural, one {Ce code expirera dans # minute.} other {Ce code expirera dans # minutes.}}"
#: packages/email/templates/document-invite.tsx
msgid "{inviterName} <0>({inviterEmail})</0>"
msgstr "{inviterName} <0>({inviterEmail})</0>"
@ -254,6 +260,10 @@ msgstr "{inviterName} représentant \"{teamName}\" vous a invité à {0}<0/>\"{d
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
msgstr "{inviterName} représentant \"{teamName}\" vous a invité à {action} {documentName}"
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "{MAXIMUM_PASSKEYS, plural, one {You cannot have more than # passkey.} other {You cannot have more than # passkeys.}}"
msgstr "{MAXIMUM_PASSKEYS, plural, one {Vous ne pouvez pas avoir plus de # clé d'accès.} other {Vous ne pouvez pas avoir plus de # clés d'accès.}}"
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} added a field"
msgstr "{prefix} a ajouté un champ"
@ -1231,6 +1241,7 @@ msgid "All templates"
msgstr "Tous les modèles"
#: apps/remix/app/components/general/period-selector.tsx
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "All Time"
msgstr "Depuis toujours"
@ -1478,7 +1489,7 @@ msgid "An error occurred while updating your profile."
msgstr "Une erreur est survenue lors de la mise à jour de votre profil."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "An error occurred while uploading your document."
msgstr "Une erreur est survenue lors de l'importation de votre document."
@ -1857,6 +1868,10 @@ msgstr "Noir"
msgid "Blue"
msgstr "Bleu"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Bottom"
msgstr "Bas"
#: apps/remix/app/components/forms/branding-preferences-form.tsx
msgid "Brand Details"
msgstr "Détails de la marque"
@ -2097,6 +2112,10 @@ msgstr "CCers"
msgid "Center"
msgstr "Centre"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
msgid "Character limit"
msgstr "Limite de caractères"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
msgid "Character Limit"
@ -2263,6 +2282,7 @@ msgstr "Compléter le Document"
msgid "Complete the fields for the following signers."
msgstr "Complétez les champs pour les signataires suivants."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-document-jobs-table.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
@ -2543,11 +2563,6 @@ msgstr "Copier les liens de signature"
msgid "Copy token"
msgstr "Copier le token"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "couldn't be uploaded:"
msgstr "n'a pas pu être téléchargé :"
#: apps/remix/app/routes/_profile+/_layout.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
@ -2739,9 +2754,11 @@ msgstr "Créez votre compte et commencez à utiliser la signature de documents
#: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/documents-table.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/general/template/template-page-view-information.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
#: apps/remix/app/components/general/document/document-page-view-information.tsx
@ -3222,6 +3239,10 @@ msgstr "Document \"{0}\" - Rejet Confirmé"
msgid "Document \"{0}\" Cancelled"
msgstr "Document \"{0}\" Annulé"
#: packages/ui/primitives/document-upload-button.tsx
msgid "Document (Legacy)"
msgstr "Document (Legacy)"
#: apps/remix/app/components/general/envelope-editor/envelope-editor.tsx
msgid "Document & Recipients"
msgstr "Document & Destinataires"
@ -3354,6 +3375,10 @@ msgstr "Document trouvé dans votre compte"
msgid "Document ID"
msgstr "ID du document"
#: apps/remix/app/components/general/document/document-page-view-information.tsx
msgid "Document ID (Legacy)"
msgstr "ID de Document (Legacy)"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document inbox"
msgstr "Boîte de réception des documents"
@ -3479,12 +3504,12 @@ msgstr "Document mis à jour avec succès"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Document upload disabled due to unpaid invoices"
msgstr "Importation de documents désactivé en raison de factures impayées"
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "Document uploaded"
msgstr "Document importé"
@ -3507,6 +3532,10 @@ msgctxt "Audit log format"
msgid "Document visibility updated"
msgstr "Visibilité du document mise à jour"
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
msgid "Document Volume"
msgstr "Volume de documents"
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "Document will be permanently deleted"
msgstr "Le document sera supprimé de manière permanente"
@ -3520,6 +3549,8 @@ msgstr "Documentation"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.legacy_editor.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/user-profile-timur.tsx
#: apps/remix/app/components/general/app-nav-mobile.tsx
@ -3534,6 +3565,15 @@ msgstr "Documents"
msgid "Documents and resources related to this envelope."
msgstr "Documents et ressources liés à cette enveloppe."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Documents Completed"
msgstr "Documents Complétés"
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Documents Created"
msgstr "Documents Créés"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
msgid "Documents created from template"
msgstr "Documents créés à partir du modèle"
@ -3736,6 +3776,7 @@ msgstr "Divulgation de signature électronique"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._layout.tsx
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx
@ -3925,6 +3966,10 @@ msgstr "Activez la personnalisation de la marque pour tous les documents de cett
msgid "Enable custom branding for all documents in this team"
msgstr "Activez la personnalisation de la marque pour tous les documents de cette équipe"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid "Enable direct link signing"
msgstr "Activer la signature par lien direct"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/lib/constants/template.ts
msgid "Enable Direct Link Signing"
@ -4097,7 +4142,7 @@ msgstr "Enveloppe mise à jour"
#: apps/remix/app/components/general/document-signing/document-signing-checkbox-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-auto-sign.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
@ -4254,7 +4299,6 @@ msgstr "Échoués : {failedCount}"
msgid "Feature Flags"
msgstr "Drapeaux de fonctionnalités"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
msgid "Field character limit"
msgstr "Limite de caractères du champ"
@ -4312,7 +4356,7 @@ msgstr "Champs mis à jour"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid "File cannot be larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
msgstr "Le fichier ne peut pas dépasser {APP_DOCUMENT_UPLOAD_SIZE_LIMIT} Mo"
@ -4402,6 +4446,7 @@ msgstr "Mot de passe oublié ?"
msgid "Forgot your password?"
msgstr "Vous avez oublié votre mot de passe ?"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
msgid "Free"
msgstr "Gratuit"
@ -4934,6 +4979,10 @@ msgstr "Rejoindre {organisationName} sur Documenso"
msgid "Join our community on <0>Discord</0> for community support and discussion."
msgstr "Rejoignez notre communauté sur <0>Discord</0> pour obtenir de l'aide et discuter."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Joined"
msgstr "Joint"
#. placeholder {0}: DateTime.fromJSDate(team.createdAt).toRelative({ style: 'short' })
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
msgid "Joined {0}"
@ -4967,10 +5016,18 @@ msgstr "14 derniers jours"
msgid "Last 30 days"
msgstr "30 derniers jours"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last 30 Days"
msgstr "30 derniers jours"
#: apps/remix/app/components/general/period-selector.tsx
msgid "Last 7 days"
msgstr "7 derniers jours"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last 90 Days"
msgstr "90 derniers jours"
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
msgid "Last Active"
msgstr "Dernière activité"
@ -5000,9 +5057,9 @@ msgstr "Dernière mise à jour à"
msgid "Last used"
msgstr "Dernière utilisation"
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
msgid "Leaderboard"
msgstr "Classement"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last Year"
msgstr "Dernière Année"
#: apps/remix/app/components/tables/user-organisations-table.tsx
#: apps/remix/app/components/dialogs/organisation-leave-dialog.tsx
@ -5028,6 +5085,14 @@ msgstr "Gauche"
msgid "Legality of Electronic Signatures"
msgstr "Légalité des signatures électroniques"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Letter spacing"
msgstr "Espacement des lettres"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Letter Spacing"
msgstr "Espacement des Lettres"
#: apps/remix/app/components/general/app-command-menu.tsx
msgid "Light Mode"
msgstr "Mode clair"
@ -5036,6 +5101,14 @@ msgstr "Mode clair"
msgid "Like to have your own public profile with agreements?"
msgstr "Vous voulez avoir votre propre profil public avec des accords ?"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Line height"
msgstr "Hauteur de ligne"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Line Height"
msgstr "Hauteur de Ligne"
#: packages/email/templates/confirm-team-email.tsx
msgid "Link expires in 1 hour."
msgstr "Le lien expire dans 1 heure."
@ -5315,7 +5388,10 @@ msgstr "Membre depuis"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._layout.tsx
#: apps/remix/app/components/tables/team-groups-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
msgid "Members"
@ -5333,6 +5409,10 @@ msgstr "Message"
msgid "Message <0>(Optional)</0>"
msgstr "Message <0>(Optionnel)</0>"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Middle"
msgstr "Milieu"
#: apps/remix/app/components/forms/editor/editor-field-number-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
msgid "Min"
@ -5408,7 +5488,8 @@ msgstr "N/A"
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/tables/admin-document-jobs-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
@ -5475,7 +5556,6 @@ msgstr "Ne jamais expirer"
msgid "New Password"
msgstr "Nouveau Mot de Passe"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "New Template"
msgstr "Nouveau modèle"
@ -5813,6 +5893,11 @@ msgstr "Paramètres du groupe d'organisation"
msgid "Organisation has been updated successfully"
msgstr "L'organisation a été mise à jour avec succès"
#: apps/remix/app/routes/_authenticated+/admin+/organisation-insights._index.tsx
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
msgid "Organisation Insights"
msgstr "Aperçu de l'Organisation"
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
msgid "Organisation invitation"
msgstr "Invitation à l'organisation"
@ -5909,6 +5994,7 @@ msgid "Organize your documents and templates"
msgstr "Organisez vos documents et modèles"
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
msgctxt "Original document (adjective)"
msgid "Original"
msgstr "Original"
@ -5949,6 +6035,10 @@ msgstr "Page {0} sur {1}"
msgid "Page {0} of {numPages}"
msgstr "Page {0} sur {numPages}"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
msgid "Paid"
msgstr "Payé"
#: apps/remix/app/components/general/document-signing/document-signing-auth-dialog.tsx
#: apps/remix/app/components/forms/signin.tsx
msgid "Passkey"
@ -6074,6 +6164,7 @@ msgid "per year"
msgstr "par an"
#: apps/remix/app/components/tables/user-organisations-table.tsx
msgctxt "Personal organisation (adjective)"
msgid "Personal"
msgstr "Personnel"
@ -6439,6 +6530,7 @@ msgid "Read only"
msgstr "Lecture seule"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
#: packages/ui/components/document/envelope-recipient-field-tooltip.tsx
msgid "Read Only"
msgstr "Lecture Seule"
@ -6874,6 +6966,7 @@ msgstr "Droit"
#: apps/remix/app/components/tables/organisation-members-table.tsx
#: apps/remix/app/components/tables/organisation-member-invites-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/embed/authoring/configure-document-recipients.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
@ -6936,7 +7029,6 @@ msgstr "Recherche par ID de réclamation ou nom"
msgid "Search by document title"
msgstr "Recherche par titre de document"
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
msgid "Search by name or email"
msgstr "Recherche par nom ou e-mail"
@ -6945,6 +7037,10 @@ msgstr "Recherche par nom ou e-mail"
msgid "Search by organisation ID, name, customer ID or owner email"
msgstr "Rechercher par ID d'organisation, nom, ID client ou e-mail du propriétaire"
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
msgid "Search by organisation name"
msgstr "Rechercher par nom d'organisation"
#: apps/remix/app/components/general/document/document-search.tsx
msgid "Search documents..."
msgstr "Rechercher des documents..."
@ -7115,6 +7211,10 @@ msgstr "Sélectionnez les membres à inclure dans ce groupe"
msgid "Select triggers"
msgstr "Sélectionner les déclencheurs"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Select vertical align"
msgstr "Sélectionner l'alignement vertical"
#: apps/remix/app/components/dialogs/folder-update-dialog.tsx
msgid "Select visibility"
msgstr "Sélectionner la visibilité"
@ -7485,12 +7585,16 @@ msgstr "Signatures collectées"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
#: packages/ui/components/document/envelope-recipient-field-tooltip.tsx
#: packages/ui/components/document/document-read-only-fields.tsx
msgid "Signed"
msgstr "Signé"
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
msgctxt "Signed document (adjective)"
msgid "Signed"
msgstr "Signé"
#: packages/lib/constants/recipient-roles.ts
msgctxt "Recipient role actioned"
msgid "Signed"
@ -7556,11 +7660,6 @@ msgstr "Des liens de signature ont été générés pour ce document."
msgid "Signing order is enabled."
msgstr "L'ordre de signature est activé."
#: apps/remix/app/routes/_authenticated+/admin+/leaderboard.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
msgid "Signing Volume"
msgstr "Volume de signatures"
#: apps/remix/app/components/forms/signup.tsx
msgid "Signups are disabled."
msgstr "Les inscriptions sont désactivées."
@ -7712,6 +7811,7 @@ msgstr "Statistiques"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/audit-log.tsx
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-email-domains-table.tsx
#: apps/remix/app/components/tables/organisation-billing-invoices-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
@ -7905,6 +8005,7 @@ msgstr "Thème système"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Team"
msgstr "Équipe"
@ -7990,6 +8091,7 @@ msgstr "Membres de l'équipe"
msgid "Team members have been added."
msgstr "Les membres de l'équipe ont été ajoutés."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/forms/team-update-form.tsx
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
msgid "Team Name"
@ -8034,6 +8136,9 @@ msgstr "URL de l'équipe"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.teams.tsx
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings._layout.tsx
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/general/org-menu-switcher.tsx
msgid "Teams"
msgstr "Équipes"
@ -8056,6 +8161,11 @@ msgstr "Équipes auxquelles ce groupe d'organisation est actuellement attribué"
msgid "Template"
msgstr "Modèle"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Template (Legacy)"
msgstr "Modèle (Legacy)"
#: apps/remix/app/routes/embed+/v1+/authoring_.completed.create.tsx
msgid "Template Created"
msgstr "Modèle créé"
@ -8084,6 +8194,10 @@ msgstr "Le modèle a été retiré de votre profil public."
msgid "Template has been updated."
msgstr "Le modèle a été mis à jour."
#: apps/remix/app/components/general/template/template-page-view-information.tsx
msgid "Template ID (Legacy)"
msgstr "ID de Modèle (Legacy)"
#: apps/remix/app/components/general/legacy-field-warning-popover.tsx
msgid "Template is using legacy field insertion"
msgstr "Le modèle utilise l'insertion de champ héritée"
@ -8266,9 +8380,8 @@ msgstr "Le document que vous cherchez n'a pas pu être trouvé."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.edit.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "The document you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Le document que vous cherchez a peut-être été supprimé, renommé ou n'a peut-être jamais existé."
msgid "The document you are looking for may have been removed, renamed or may have never existed."
msgstr "Le document que vous recherchez a peut-être été supprimé, renommé ou n'a jamais existé."
#: packages/ui/components/document/document-send-email-message-helper.tsx
msgid "The document's name"
@ -8279,9 +8392,8 @@ msgid "The email address which will show up in the \"Reply To\" field in emails"
msgstr "L'adresse e-mail qui apparaîtra dans le champ \"Répondre à\" dans les courriels"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.email-domains.$id.tsx
msgid "The email domain you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Le domaine de messagerie que vous recherchez a peut-être été supprimé, renommé ou n'a peut-être jamais existé."
msgid "The email domain you are looking for may have been removed, renamed or may have never existed."
msgstr "Le domaine de messagerie que vous recherchez a peut-être été supprimé, renommé ou n'a jamais existé."
#: apps/remix/app/components/forms/signin.tsx
msgid "The email or password provided is incorrect"
@ -8337,23 +8449,17 @@ msgid "The organisation email has been created successfully."
msgstr "L'e-mail de l'organisation a été créé avec succès."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Le groupe d'organisation que vous cherchez peut avoir été supprimé, renommé ou n'a peut-être jamais existé."
msgid "The organisation group you are looking for may have been removed, renamed or may have never existed."
msgstr "Le groupe d'organisation que vous recherchez a peut-être été supprimé, renommé ou n'a jamais existé."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "The organisation role that will be applied to all members in this group."
msgstr "Le rôle d'organisation qui sera appliqué à tous les membres de ce groupe."
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "L'organisation que vous cherchez peut avoir été supprimée, renommée ou n'a peut-être jamais existé."
#: apps/remix/app/routes/_authenticated+/_layout.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "L'organisation que vous cherchez peut avoir été supprimée, renommée ou n'a peut-être jamais existé."
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never existed."
msgstr "L'organisation que vous recherchez a peut-être été supprimée, renommée ou n'a jamais existé."
#: apps/remix/app/components/general/generic-error-layout.tsx
msgid "The page you are looking for was moved, removed, renamed or might never have existed."
@ -8444,14 +8550,9 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
msgstr "L'email d'équipe <0>{teamEmail}</0> a été supprimé de l'équipe suivante"
#: apps/remix/app/routes/_authenticated+/_layout.tsx
msgid "The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "L'équipe que vous cherchez peut avoir été supprimée, renommée ou n'a peut-être jamais existé."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
msgid "The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "L'équipe que vous cherchez a peut-être été supprimée, renommée ou n'a peut-être jamais existé."
msgid "The team you are looking for may have been removed, renamed or may have never existed."
msgstr "L'équipe que vous recherchez a peut-être été supprimée, renommée ou n'a jamais existé."
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
msgid "The template has been moved successfully."
@ -8466,9 +8567,8 @@ msgid "The template you are looking for could not be found."
msgstr "Le modèle que vous cherchez n'a pas pu être trouvé."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
msgid "The template you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Le modèle que vous cherchez a peut-être été supprimé, renommé ou n'a peut-être jamais existé."
msgid "The template you are looking for may have been removed, renamed or may have never existed."
msgstr "Le modèle que vous recherchez a peut-être été supprimé, renommé ou n'a jamais existé."
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
msgid "The test webhook has been successfully sent to your endpoint."
@ -8508,9 +8608,8 @@ msgid "The URL for Documenso to send webhook events to."
msgstr "L'URL pour Documenso pour envoyer des événements webhook."
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
msgid "The user you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "L'utilisateur que vous cherchez peut avoir été supprimé, renommé ou n'a peut-être jamais existé."
msgid "The user you are looking for may have been removed, renamed or may have never existed."
msgstr "L'utilisateur que vous recherchez a peut-être été supprimé, renommé ou n'a jamais existé."
#: apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx
msgid "The user's two factor authentication has been reset successfully."
@ -8529,9 +8628,8 @@ msgid "The webhook was successfully created."
msgstr "Le webhook a été créé avec succès."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
msgid "The webhook you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Le webhook que vous recherchez a peut-être été supprimé, renommé ou n'a peut-être jamais existé."
msgid "The webhook you are looking for may have been removed, renamed or may have never existed."
msgstr "Le webhook que vous recherchez a peut-être été supprimé, renommé ou n'a jamais existé."
#: apps/remix/app/components/tables/documents-table-empty-state.tsx
msgid "There are no active drafts at the current moment. You can upload a document to start drafting."
@ -8587,10 +8685,6 @@ msgstr "Cette action est réversible, mais veuillez faire attention car le compt
msgid "This claim is locked and cannot be deleted."
msgstr "Cette réclamation est verrouillée et ne peut pas être supprimée."
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "This code will expire in {expiresInMinutes} minutes."
msgstr "Ce code expirera dans {expiresInMinutes} minutes."
#: packages/email/template-components/template-document-super-delete.tsx
msgid "This document can not be recovered, if you would like to dispute the reason for future documents please contact support."
msgstr "Ce document ne peut pas être récupéré, si vous souhaitez contester la raison des documents futurs, veuillez contacter le support."
@ -8836,6 +8930,7 @@ msgstr "Fuseau horaire"
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
#: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/documents-table.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
@ -8848,6 +8943,19 @@ msgstr "Titre"
msgid "Title cannot be empty"
msgstr "Le titre ne peut pas être vide"
#. placeholder {0}: actionVerb.toLowerCase()
#. placeholder {1}: actionTarget.toLowerCase()
#. placeholder {2}: recipient.email
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To {0} this {1}, you need to be logged in as <0>{2}</0>"
msgstr "Pour {0} ce {1}, vous devez être connecté en tant que <0>{2}</0>"
#. placeholder {0}: actionVerb.toLowerCase()
#. placeholder {1}: actionTarget.toLowerCase()
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To {0} this {1}, you need to be logged in."
msgstr "Pour {0} ce {1}, vous devez être connecté."
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
msgid "To accept this invitation you must create an account."
msgstr "Pour accepter cette invitation, vous devez créer un compte."
@ -8888,6 +8996,10 @@ msgstr "Pour accéder à votre compte, veuillez confirmer votre adresse e-mail e
msgid "To mark this document as viewed, you need to be logged in as <0>{0}</0>"
msgstr "Pour marquer ce document comme consulté, vous devez être connecté en tant que <0>{0}</0>"
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To mark this document as viewed, you need to be logged in."
msgstr "Pour marquer ce document comme vu, vous devez être connecté."
#. placeholder {0}: emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'
#. placeholder {0}: emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'
#: packages/ui/primitives/template-flow/add-template-fields.tsx
@ -8943,6 +9055,10 @@ msgstr "Le token a expiré. Veuillez réessayer."
msgid "Token name"
msgstr "Nom du token"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Top"
msgstr "Haut"
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Total Documents"
msgstr "Total des documents"
@ -9281,7 +9397,7 @@ msgstr "Mise à jour du mot de passe..."
msgid "Updating Your Information"
msgstr "Mise à jour de vos informations"
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "Upgrade"
msgstr "Améliorer"
@ -9335,7 +9451,7 @@ msgstr "Importation désactivée"
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Upload Document"
msgstr "Importer le document"
@ -9343,10 +9459,6 @@ msgstr "Importer le document"
msgid "Upload documents and add recipients"
msgstr "Importer des documents et ajouter des destinataires"
#: packages/ui/primitives/document-upload.tsx
msgid "Upload Envelope"
msgstr "Télécharger l'enveloppe"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
@ -9359,10 +9471,10 @@ msgid "Upload Signature"
msgstr "Importer une signature"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Upload Template"
msgstr "Télécharger le modèle"
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "Upload Template Document"
msgstr "Importer le document modèle"
@ -9472,6 +9584,7 @@ msgid "User with this email already exists. Please use a different email address
msgstr "Un utilisateur avec cet e-mail existe déjà. Veuillez utiliser une adresse e-mail différente."
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Users"
msgstr "Utilisateurs"
@ -9494,6 +9607,10 @@ msgstr "La validation a échoué"
msgid "Value"
msgstr "Valeur"
#: packages/lib/types/field-meta.ts
msgid "Value must be a number"
msgstr "La valeur doit être un nombre"
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "Verification Code Required"
msgstr "Code de vérification requis"
@ -9527,7 +9644,7 @@ msgid "Verify your email address to unlock all features."
msgstr "Vérifiez votre adresse e-mail pour débloquer toutes les fonctionnalités."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Verify your email to upload documents."
msgstr "Vérifiez votre e-mail pour importer des documents."
@ -9541,6 +9658,10 @@ msgstr "Vérifiez votre adresse e-mail d'équipe"
msgid "Vertical"
msgstr "Vertical"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Vertical Align"
msgstr "Alignement Vertical"
#: apps/remix/app/components/tables/organisation-billing-invoices-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
@ -10393,10 +10514,6 @@ msgstr "Vous ne pouvez pas supprimer un groupe qui a un rôle supérieur au vôt
msgid "You cannot delete this item because the document has been sent to recipients"
msgstr "Vous ne pouvez pas supprimer cet élément car le document a été envoyé aux destinataires"
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "You cannot have more than {MAXIMUM_PASSKEYS} passkeys."
msgstr "Vous ne pouvez pas avoir plus de {MAXIMUM_PASSKEYS} clés de passkey."
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
msgid "You cannot modify a group which has a higher role than you."
msgstr "Vous ne pouvez pas modifier un groupe qui a un rôle supérieur au vôtre."
@ -10413,13 +10530,13 @@ msgstr "Vous ne pouvez pas modifier un membre de l'équipe qui a un rôle plus
msgid "You cannot remove members from this team if the inherit member feature is enabled."
msgstr "Vous ne pouvez pas retirer des membres de cette équipe si la fonctionnalité d'héritage de membre est activée."
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "You cannot upload documents at this time."
msgstr "Vous ne pouvez pas importer de documents pour le moment."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You cannot upload encrypted PDFs"
msgstr "Vous ne pouvez pas importer de PDF cryptés"
@ -10503,7 +10620,7 @@ msgid "You have not yet created or received any documents. To create a document
msgstr "Vous n'avez pas encore créé ou reçu de documents. Pour créer un document, veuillez en importer un."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You have reached the limit of the number of files per envelope"
msgstr "Vous avez atteint la limite du nombre de fichiers par enveloppe"
@ -10518,13 +10635,13 @@ msgid "You have reached the maximum number of teams for your plan. Please contac
msgstr "Vous avez atteint le nombre maximum d'équipes pour votre abonnement. Veuillez contacter le service commercial à <0>{SUPPORT_EMAIL}</0> si vous souhaitez ajuster votre plan."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You have reached your document limit for this month. Please upgrade your plan."
msgstr "Vous avez atteint votre limite de documents pour ce mois. Veuillez passer à l'abonnement supérieur."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "You have reached your document limit."
msgstr "Vous avez atteint votre limite de documents."
@ -10714,7 +10831,7 @@ msgstr "Votre plan actuel est arrivé à échéance."
msgid "Your direct signing templates"
msgstr "Vos modèles de signature directe"
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid "Your document failed to upload."
msgstr "L'importation de votre document a échoué."
@ -10744,7 +10861,7 @@ msgid "Your document has been successfully duplicated."
msgstr "Votre document a été dupliqué avec succès."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "Your document has been uploaded successfully."
msgstr "Votre document a été importé avec succès."

View File

@ -8,7 +8,7 @@ msgstr ""
"Language: it\n"
"Project-Id-Version: documenso-app\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-11-07 03:40\n"
"PO-Revision-Date: 2025-11-11 02:33\n"
"Last-Translator: \n"
"Language-Team: Italian\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
@ -18,10 +18,6 @@ msgstr ""
"X-Crowdin-File: web.po\n"
"X-Crowdin-File-ID: 8\n"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid " Enable direct link signing"
msgstr " Abilita la firma tramite link diretto"
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid ".PDF documents accepted (max {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB)"
msgstr ".I documenti PDF accettati (max {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB)"
@ -98,6 +94,11 @@ msgstr "{0, plural, one {# cartella} other {# cartelle}}"
msgid "{0, plural, one {# recipient} other {# recipients}}"
msgstr "{0, plural, one {# destinatario} other {# destinatari}}"
#. placeholder {0}: envelope.recipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0, plural, one {# Recipient} other {# Recipients}}"
msgstr "{0, plural, one {# Destinatario} other {# Destinatari}}"
#. placeholder {0}: org.teams.length
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
msgid "{0, plural, one {# team} other {# teams}}"
@ -151,6 +152,12 @@ msgstr "{0, plural, one {In attesa di 1 destinatario} other {In attesa di # dest
msgid "{0}"
msgstr ""
#. placeholder {0}: file.name
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "{0} couldn't be uploaded:"
msgstr "{0} non può essere caricato:"
#. placeholder {0}: team.name
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
msgid "{0} direct signing templates"
@ -170,7 +177,7 @@ msgstr "{0} ti ha invitato a {recipientActionVerb} un documento"
#. placeholder {0}: remaining.documents
#. placeholder {1}: quota.documents
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "{0} of {1} documents remaining this month."
msgstr "{0} di {1} documenti rimanenti questo mese."
@ -188,11 +195,6 @@ msgstr "{0} di {1} riga selezionata."
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
msgstr "{0} per conto di \"{1}\" ti ha invitato a {recipientActionVerb} il documento \"{2}\"."
#. placeholder {0}: envelope.recipients.length
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "{0} Recipient(s)"
msgstr "{0} Destinatario(i)"
#. placeholder {0}: organisation.name
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
msgid "{0} Teams"
@ -206,6 +208,10 @@ msgstr "{browserInfo} su {os}"
msgid "{charactersRemaining, plural, one {1 character remaining} other {{charactersRemaining} characters remaining}}"
msgstr "{charactersRemaining, plural, one {1 carattere rimanente} other {{charactersRemaining} caratteri rimanenti}}"
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "{expiresInMinutes, plural, one {This code will expire in # minute.} other {This code will expire in # minutes.}}"
msgstr "{expiresInMinutes, plural, one {Questo codice scadrà in # minuto.} other {Questo codice scadrà in # minuti.}}"
#: packages/email/templates/document-invite.tsx
msgid "{inviterName} <0>({inviterEmail})</0>"
msgstr "{inviterName} <0>({inviterEmail})</0>"
@ -254,6 +260,10 @@ msgstr "{inviterName} per conto di \"{teamName}\" ti ha invitato a {0}<0/>\"{doc
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
msgstr "{inviterName} per conto di \"{teamName}\" ti ha invitato a {action} {documentName}"
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "{MAXIMUM_PASSKEYS, plural, one {You cannot have more than # passkey.} other {You cannot have more than # passkeys.}}"
msgstr "{MAXIMUM_PASSKEYS, plural, one {Non puoi avere più di # chiave di accesso.} other {Non puoi avere più di # chiavi di accesso.}}"
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} added a field"
msgstr "{prefix} ha aggiunto un campo"
@ -1231,6 +1241,7 @@ msgid "All templates"
msgstr "Tutti i modelli"
#: apps/remix/app/components/general/period-selector.tsx
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "All Time"
msgstr "Tutto il tempo"
@ -1478,7 +1489,7 @@ msgid "An error occurred while updating your profile."
msgstr "Si è verificato un errore durante l'aggiornamento del tuo profilo."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "An error occurred while uploading your document."
msgstr "Si è verificato un errore durante il caricamento del tuo documento."
@ -1857,6 +1868,10 @@ msgstr "Nero"
msgid "Blue"
msgstr "Blu"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Bottom"
msgstr "Inferiore"
#: apps/remix/app/components/forms/branding-preferences-form.tsx
msgid "Brand Details"
msgstr "Dettagli del Marchio"
@ -2097,6 +2112,10 @@ msgstr "Copiatori"
msgid "Center"
msgstr "Centro"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
msgid "Character limit"
msgstr "Limite di caratteri"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
msgid "Character Limit"
@ -2263,6 +2282,7 @@ msgstr "Completa Documento"
msgid "Complete the fields for the following signers."
msgstr "Compila i campi per i seguenti firmatari."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-document-jobs-table.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
@ -2543,11 +2563,6 @@ msgstr "Copia link di firma"
msgid "Copy token"
msgstr "Copia il token"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "couldn't be uploaded:"
msgstr "non è stato possibile caricare:"
#: apps/remix/app/routes/_profile+/_layout.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
@ -2739,9 +2754,11 @@ msgstr "Crea il tuo account e inizia a utilizzare firme digitali all'avanguardia
#: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/documents-table.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/general/template/template-page-view-information.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
#: apps/remix/app/components/general/document/document-page-view-information.tsx
@ -3222,6 +3239,10 @@ msgstr "Documento \"{0}\" - Rifiuto Confermato"
msgid "Document \"{0}\" Cancelled"
msgstr "Documento \"{0}\" Annullato"
#: packages/ui/primitives/document-upload-button.tsx
msgid "Document (Legacy)"
msgstr "Documento (Legacy)"
#: apps/remix/app/components/general/envelope-editor/envelope-editor.tsx
msgid "Document & Recipients"
msgstr "Documento & Destinatari"
@ -3354,6 +3375,10 @@ msgstr "Documento trovato nel tuo account"
msgid "Document ID"
msgstr "ID del documento"
#: apps/remix/app/components/general/document/document-page-view-information.tsx
msgid "Document ID (Legacy)"
msgstr "ID Documento (Legacy)"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document inbox"
msgstr "Posta in arrivo del documento"
@ -3479,12 +3504,12 @@ msgstr "Documento aggiornato con successo"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Document upload disabled due to unpaid invoices"
msgstr "Caricamento del documento disabilitato a causa di fatture non pagate"
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "Document uploaded"
msgstr "Documento caricato"
@ -3507,6 +3532,10 @@ msgctxt "Audit log format"
msgid "Document visibility updated"
msgstr "Visibilità del documento aggiornata"
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
msgid "Document Volume"
msgstr "Volume del documento"
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "Document will be permanently deleted"
msgstr "Il documento sarà eliminato definitivamente"
@ -3520,6 +3549,8 @@ msgstr "Documentazione"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.legacy_editor.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/user-profile-timur.tsx
#: apps/remix/app/components/general/app-nav-mobile.tsx
@ -3534,6 +3565,15 @@ msgstr "Documenti"
msgid "Documents and resources related to this envelope."
msgstr "Documenti e risorse relative a questa busta."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Documents Completed"
msgstr "Documenti completati"
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Documents Created"
msgstr "Documenti creati"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
msgid "Documents created from template"
msgstr "Documenti creati da modello"
@ -3736,6 +3776,7 @@ msgstr "Divulgazione della firma elettronica"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._layout.tsx
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx
@ -3925,6 +3966,10 @@ msgstr "Abilita il marchio personalizzato per tutti i documenti in questa organi
msgid "Enable custom branding for all documents in this team"
msgstr "Abilita il marchio personalizzato per tutti i documenti in questo team"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
msgid "Enable direct link signing"
msgstr "Abilita firma tramite link diretto"
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/lib/constants/template.ts
msgid "Enable Direct Link Signing"
@ -4097,7 +4142,7 @@ msgstr "Busta aggiornata"
#: apps/remix/app/components/general/document-signing/document-signing-checkbox-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-auto-sign.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
#: apps/remix/app/components/general/document/document-edit-form.tsx
@ -4254,7 +4299,6 @@ msgstr "Falliti: {failedCount}"
msgid "Feature Flags"
msgstr "Flags delle funzionalità"
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
msgid "Field character limit"
msgstr "Limite di caratteri del campo"
@ -4312,7 +4356,7 @@ msgstr "Campi aggiornati"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid "File cannot be larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
msgstr "Il file non può essere più grande di {APP_DOCUMENT_UPLOAD_SIZE_LIMIT} MB"
@ -4402,6 +4446,7 @@ msgstr "Password dimenticata?"
msgid "Forgot your password?"
msgstr "Hai dimenticato la tua password?"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
msgid "Free"
msgstr "Gratuito"
@ -4934,6 +4979,10 @@ msgstr "Unisciti a {organisationName} su Documenso"
msgid "Join our community on <0>Discord</0> for community support and discussion."
msgstr "Unisciti alla nostra comunità su <0>Discord</0> per supporto e discussioni."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Joined"
msgstr "Iscritto"
#. placeholder {0}: DateTime.fromJSDate(team.createdAt).toRelative({ style: 'short' })
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
msgid "Joined {0}"
@ -4967,10 +5016,18 @@ msgstr "Ultimi 14 giorni"
msgid "Last 30 days"
msgstr "Ultimi 30 giorni"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last 30 Days"
msgstr "Ultimi 30 giorni"
#: apps/remix/app/components/general/period-selector.tsx
msgid "Last 7 days"
msgstr "Ultimi 7 giorni"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last 90 Days"
msgstr "Ultimi 90 giorni"
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
msgid "Last Active"
msgstr "Ultimo accesso"
@ -5000,9 +5057,9 @@ msgstr "Ultimo aggiornamento il"
msgid "Last used"
msgstr "Ultimo utilizzo"
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
msgid "Leaderboard"
msgstr "Classifica"
#: apps/remix/app/components/filters/date-range-filter.tsx
msgid "Last Year"
msgstr "Ultimo anno"
#: apps/remix/app/components/tables/user-organisations-table.tsx
#: apps/remix/app/components/dialogs/organisation-leave-dialog.tsx
@ -5028,6 +5085,14 @@ msgstr "Sinistra"
msgid "Legality of Electronic Signatures"
msgstr "Legalità delle firme elettroniche"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Letter spacing"
msgstr "Spaziatura lettere"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Letter Spacing"
msgstr "Spaziatura Lettere"
#: apps/remix/app/components/general/app-command-menu.tsx
msgid "Light Mode"
msgstr "Modalità chiara"
@ -5036,6 +5101,14 @@ msgstr "Modalità chiara"
msgid "Like to have your own public profile with agreements?"
msgstr "Ti piacerebbe avere il tuo profilo pubblico con accordi?"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Line height"
msgstr "Interlinea"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Line Height"
msgstr "Interlinea"
#: packages/email/templates/confirm-team-email.tsx
msgid "Link expires in 1 hour."
msgstr "Il link scade tra 1 ora."
@ -5315,7 +5388,10 @@ msgstr "Membro dal"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings._layout.tsx
#: apps/remix/app/components/tables/team-groups-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
msgid "Members"
@ -5333,6 +5409,10 @@ msgstr "Messaggio"
msgid "Message <0>(Optional)</0>"
msgstr "Messaggio <0>(Opzionale)</0>"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Middle"
msgstr "Medio"
#: apps/remix/app/components/forms/editor/editor-field-number-form.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
msgid "Min"
@ -5408,7 +5488,8 @@ msgstr "N/A"
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/tables/admin-document-jobs-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
@ -5475,7 +5556,6 @@ msgstr "Mai scadere"
msgid "New Password"
msgstr "Nuova Password"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "New Template"
msgstr "Nuovo modello"
@ -5813,6 +5893,11 @@ msgstr "Impostazioni del gruppo organizzativo"
msgid "Organisation has been updated successfully"
msgstr "L'organizzazione è stata aggiornata con successo"
#: apps/remix/app/routes/_authenticated+/admin+/organisation-insights._index.tsx
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
msgid "Organisation Insights"
msgstr "Approfondimenti sull'organizzazione"
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
msgid "Organisation invitation"
msgstr "Invito all'organizzazione"
@ -5909,6 +5994,7 @@ msgid "Organize your documents and templates"
msgstr "Organizza i tuoi documenti e modelli."
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
msgctxt "Original document (adjective)"
msgid "Original"
msgstr "Originale"
@ -5949,6 +6035,10 @@ msgstr "Pagina {0} di {1}"
msgid "Page {0} of {numPages}"
msgstr "Pagina {0} di {numPages}"
#: apps/remix/app/components/tables/admin-organisations-table.tsx
msgid "Paid"
msgstr "Pagato"
#: apps/remix/app/components/general/document-signing/document-signing-auth-dialog.tsx
#: apps/remix/app/components/forms/signin.tsx
msgid "Passkey"
@ -6074,6 +6164,7 @@ msgid "per year"
msgstr "all'anno"
#: apps/remix/app/components/tables/user-organisations-table.tsx
msgctxt "Personal organisation (adjective)"
msgid "Personal"
msgstr "Personale"
@ -6439,6 +6530,7 @@ msgid "Read only"
msgstr "Sola lettura"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
#: packages/ui/components/document/envelope-recipient-field-tooltip.tsx
msgid "Read Only"
msgstr "Solo lettura"
@ -6874,6 +6966,7 @@ msgstr "Destra"
#: apps/remix/app/components/tables/organisation-members-table.tsx
#: apps/remix/app/components/tables/organisation-member-invites-table.tsx
#: apps/remix/app/components/tables/organisation-groups-table.tsx
#: apps/remix/app/components/tables/admin-organisations-table.tsx
#: apps/remix/app/components/embed/authoring/configure-document-recipients.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
@ -6936,7 +7029,6 @@ msgstr "Cerca per ID reclamo o nome"
msgid "Search by document title"
msgstr "Cerca per titolo del documento"
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
msgid "Search by name or email"
msgstr "Cerca per nome o email"
@ -6945,6 +7037,10 @@ msgstr "Cerca per nome o email"
msgid "Search by organisation ID, name, customer ID or owner email"
msgstr "Cerca per ID dell'organizzazione, nome, ID cliente o email del proprietario"
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
msgid "Search by organisation name"
msgstr "Cerca per nome organizzazione"
#: apps/remix/app/components/general/document/document-search.tsx
msgid "Search documents..."
msgstr "Cerca documenti..."
@ -7115,6 +7211,10 @@ msgstr "Seleziona i membri da includere in questo gruppo"
msgid "Select triggers"
msgstr "Seleziona trigger"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Select vertical align"
msgstr "Seleziona allineamento verticale"
#: apps/remix/app/components/dialogs/folder-update-dialog.tsx
msgid "Select visibility"
msgstr "Seleziona visibilità"
@ -7485,12 +7585,16 @@ msgstr "Firme raccolte"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
#: packages/ui/components/document/envelope-recipient-field-tooltip.tsx
#: packages/ui/components/document/document-read-only-fields.tsx
msgid "Signed"
msgstr "Firmato"
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
msgctxt "Signed document (adjective)"
msgid "Signed"
msgstr "Firmato"
#: packages/lib/constants/recipient-roles.ts
msgctxt "Recipient role actioned"
msgid "Signed"
@ -7556,11 +7660,6 @@ msgstr "I link di firma sono stati generati per questo documento."
msgid "Signing order is enabled."
msgstr "L'ordine di firma è abilitato."
#: apps/remix/app/routes/_authenticated+/admin+/leaderboard.tsx
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
msgid "Signing Volume"
msgstr "Volume di firma"
#: apps/remix/app/components/forms/signup.tsx
msgid "Signups are disabled."
msgstr "Le iscrizioni sono disabilitate."
@ -7712,6 +7811,7 @@ msgstr "Statistiche"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/audit-log.tsx
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-email-domains-table.tsx
#: apps/remix/app/components/tables/organisation-billing-invoices-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
@ -7905,6 +8005,7 @@ msgstr "Tema del sistema"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
#: apps/remix/app/components/tables/organisation-teams-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Team"
msgstr "Squadra"
@ -7990,6 +8091,7 @@ msgstr "Membri del team"
msgid "Team members have been added."
msgstr "I membri del team sono stati aggiunti."
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/forms/team-update-form.tsx
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
msgid "Team Name"
@ -8034,6 +8136,9 @@ msgstr "URL del team"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.teams.tsx
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings._layout.tsx
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
#: apps/remix/app/components/general/org-menu-switcher.tsx
msgid "Teams"
msgstr "Team"
@ -8056,6 +8161,11 @@ msgstr "Team a cui è attualmente assegnato questo gruppo di organizzazione"
msgid "Template"
msgstr "Modello"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Template (Legacy)"
msgstr "Modello (Legacy)"
#: apps/remix/app/routes/embed+/v1+/authoring_.completed.create.tsx
msgid "Template Created"
msgstr "Modello creato"
@ -8084,6 +8194,10 @@ msgstr "Il modello è stato rimosso dal tuo profilo pubblico."
msgid "Template has been updated."
msgstr "Il modello è stato aggiornato."
#: apps/remix/app/components/general/template/template-page-view-information.tsx
msgid "Template ID (Legacy)"
msgstr "ID Modello (Legacy)"
#: apps/remix/app/components/general/legacy-field-warning-popover.tsx
msgid "Template is using legacy field insertion"
msgstr "Il modello utilizza l'inserimento campo legacy"
@ -8266,10 +8380,8 @@ msgstr "Il documento che stai cercando non è stato trovato."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.edit.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
msgid "The document you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Il documento che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non\n"
"esistito mai."
msgid "The document you are looking for may have been removed, renamed or may have never existed."
msgstr "Il documento che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non essere mai esistito."
#: packages/ui/components/document/document-send-email-message-helper.tsx
msgid "The document's name"
@ -8280,10 +8392,8 @@ msgid "The email address which will show up in the \"Reply To\" field in emails"
msgstr "L'indirizzo email che verrà visualizzato nel campo \"Rispondi a\" nelle email"
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.email-domains.$id.tsx
msgid "The email domain you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Il dominio email che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
"esistito."
msgid "The email domain you are looking for may have been removed, renamed or may have never existed."
msgstr "Il dominio email che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non essere mai esistito."
#: apps/remix/app/components/forms/signin.tsx
msgid "The email or password provided is incorrect"
@ -8339,26 +8449,17 @@ msgid "The organisation email has been created successfully."
msgstr "L'email dell'organizzazione è stata creata con successo."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Il gruppo organizzativo che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
" esistito."
msgid "The organisation group you are looking for may have been removed, renamed or may have never existed."
msgstr "Il gruppo organizzativo che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non essere mai esistito."
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
msgid "The organisation role that will be applied to all members in this group."
msgstr "Il ruolo organizzativo che verrà applicato a tutti i membri in questo gruppo."
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "L'organizzazione che cerchi potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
" esistita."
#: apps/remix/app/routes/_authenticated+/_layout.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "L'organizzazione che cerchi potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
" esistita."
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
msgid "The organisation you are looking for may have been removed, renamed or may have never existed."
msgstr "L'organizzazione che stai cercando potrebbe essere stata rimossa, rinominata o potrebbe non essere mai esistita."
#: apps/remix/app/components/general/generic-error-layout.tsx
msgid "The page you are looking for was moved, removed, renamed or might never have existed."
@ -8449,16 +8550,9 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
msgstr "L'email del team <0>{teamEmail}</0> è stata rimossa dal seguente team"
#: apps/remix/app/routes/_authenticated+/_layout.tsx
msgid "The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Il team che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
" esistito."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
msgid "The team you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "La squadra che stai cercando potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
" esistita."
msgid "The team you are looking for may have been removed, renamed or may have never existed."
msgstr "Il team che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non essere mai esistito."
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
msgid "The template has been moved successfully."
@ -8473,10 +8567,8 @@ msgid "The template you are looking for could not be found."
msgstr "Il modello che stai cercando non è stato trovato."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
msgid "The template you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Il modello che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non\n"
"esistito mai."
msgid "The template you are looking for may have been removed, renamed or may have never existed."
msgstr "Il modello che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non essere mai esistito."
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
msgid "The test webhook has been successfully sent to your endpoint."
@ -8516,10 +8608,8 @@ msgid "The URL for Documenso to send webhook events to."
msgstr "L'URL per Documenso per inviare eventi webhook."
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
msgid "The user you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "L'utente che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
" esistito."
msgid "The user you are looking for may have been removed, renamed or may have never existed."
msgstr "L'utente che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non essere mai esistito."
#: apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx
msgid "The user's two factor authentication has been reset successfully."
@ -8538,10 +8628,8 @@ msgid "The webhook was successfully created."
msgstr "Il webhook è stato creato con successo."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
msgid "The webhook you are looking for may have been removed, renamed or may have never\n"
" existed."
msgstr "Il webhook che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non\n"
"esistito mai."
msgid "The webhook you are looking for may have been removed, renamed or may have never existed."
msgstr "Il webhook che stai cercando potrebbe essere stato rimosso, rinominato o potrebbe non esistito mai."
#: apps/remix/app/components/tables/documents-table-empty-state.tsx
msgid "There are no active drafts at the current moment. You can upload a document to start drafting."
@ -8597,10 +8685,6 @@ msgstr "Questa azione è reversibile, ma fai attenzione poiché l'account potreb
msgid "This claim is locked and cannot be deleted."
msgstr "Questo reclamo è bloccato e non può essere eliminato."
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "This code will expire in {expiresInMinutes} minutes."
msgstr "Questo codice scadrà in {expiresInMinutes} minuti."
#: packages/email/template-components/template-document-super-delete.tsx
msgid "This document can not be recovered, if you would like to dispute the reason for future documents please contact support."
msgstr "Questo documento non può essere recuperato, se vuoi contestare la ragione per i documenti futuri, contatta il supporto."
@ -8846,6 +8930,7 @@ msgstr "Fuso orario"
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
#: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/documents-table.tsx
#: apps/remix/app/components/general/template/template-page-view-documents-table.tsx
@ -8858,6 +8943,19 @@ msgstr "Titolo"
msgid "Title cannot be empty"
msgstr "Il titolo non può essere vuoto"
#. placeholder {0}: actionVerb.toLowerCase()
#. placeholder {1}: actionTarget.toLowerCase()
#. placeholder {2}: recipient.email
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To {0} this {1}, you need to be logged in as <0>{2}</0>"
msgstr "Per {0} questo {1}, devi essere loggato come <0>{2}</0>"
#. placeholder {0}: actionVerb.toLowerCase()
#. placeholder {1}: actionTarget.toLowerCase()
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To {0} this {1}, you need to be logged in."
msgstr "Per {0} questo {1}, devi essere loggato."
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
msgid "To accept this invitation you must create an account."
msgstr "Per accettare questo invito devi creare un account."
@ -8898,6 +8996,10 @@ msgstr "Per accedere al tuo account, conferma il tuo indirizzo email facendo cli
msgid "To mark this document as viewed, you need to be logged in as <0>{0}</0>"
msgstr "Per contrassegnare questo documento come visualizzato, è necessario essere connessi come <0>{0}</0>"
#: apps/remix/app/components/general/document-signing/document-signing-auth-account.tsx
msgid "To mark this document as viewed, you need to be logged in."
msgstr "Per segnare questo documento come visualizzato, devi essere loggato."
#. placeholder {0}: emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'
#. placeholder {0}: emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'
#: packages/ui/primitives/template-flow/add-template-fields.tsx
@ -8953,6 +9055,10 @@ msgstr "Il token è scaduto. Per favore riprova."
msgid "Token name"
msgstr "Nome del token"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Top"
msgstr "Superiore"
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Total Documents"
msgstr "Totale documenti"
@ -9291,7 +9397,7 @@ msgstr "Aggiornamento della password..."
msgid "Updating Your Information"
msgstr "Aggiornamento delle tue informazioni"
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "Upgrade"
msgstr "Aggiorna"
@ -9345,7 +9451,7 @@ msgstr "Caricamento disabilitato"
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Upload Document"
msgstr "Carica documento"
@ -9353,10 +9459,6 @@ msgstr "Carica documento"
msgid "Upload documents and add recipients"
msgstr "Carica documenti e aggiungi destinatari"
#: packages/ui/primitives/document-upload.tsx
msgid "Upload Envelope"
msgstr "Carica Busta"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
@ -9369,10 +9471,10 @@ msgid "Upload Signature"
msgstr "Carica Firma"
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
#: packages/ui/primitives/document-upload-button.tsx
msgid "Upload Template"
msgstr "Carica Modello"
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "Upload Template Document"
msgstr "Carica Documento Modello"
@ -9482,6 +9584,7 @@ msgid "User with this email already exists. Please use a different email address
msgstr "Un utente con questo email esiste già. Si prega di utilizzare un indirizzo email diverso."
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
#: apps/remix/app/components/tables/organisation-insights-table.tsx
msgid "Users"
msgstr "Utenti"
@ -9504,6 +9607,10 @@ msgstr "Validazione fallita"
msgid "Value"
msgstr "Valore"
#: packages/lib/types/field-meta.ts
msgid "Value must be a number"
msgstr "Il valore deve essere un numero"
#: packages/email/template-components/template-access-auth-2fa.tsx
msgid "Verification Code Required"
msgstr "Codice di verifica richiesto"
@ -9537,7 +9644,7 @@ msgid "Verify your email address to unlock all features."
msgstr "Verifica il tuo indirizzo email per sbloccare tutte le funzionalità."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
msgid "Verify your email to upload documents."
msgstr "Verifica il tuo email per caricare documenti."
@ -9551,6 +9658,10 @@ msgstr "Verifica il tuo indirizzo email del team"
msgid "Vertical"
msgstr "Verticale"
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
msgid "Vertical Align"
msgstr "Allineamento verticale"
#: apps/remix/app/components/tables/organisation-billing-invoices-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
#: apps/remix/app/components/tables/inbox-table.tsx
@ -10403,10 +10514,6 @@ msgstr "Non puoi eliminare un gruppo che ha un ruolo superiore al tuo."
msgid "You cannot delete this item because the document has been sent to recipients"
msgstr "Non puoi eliminare questo elemento perché il documento è stato inviato ai destinatari"
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "You cannot have more than {MAXIMUM_PASSKEYS} passkeys."
msgstr "Non puoi avere più di {MAXIMUM_PASSKEYS} passkey."
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
msgid "You cannot modify a group which has a higher role than you."
msgstr "Non puoi modificare un gruppo che ha un ruolo superiore al tuo."
@ -10423,13 +10530,13 @@ msgstr "Non puoi modificare un membro del team che ha un ruolo superiore al tuo.
msgid "You cannot remove members from this team if the inherit member feature is enabled."
msgstr "Non puoi rimuovere membri da questo team se la funzione di ereditarietà membri è abilitata."
#: packages/ui/primitives/document-upload.tsx
#: packages/ui/primitives/document-upload-button.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "You cannot upload documents at this time."
msgstr "Non puoi caricare documenti in questo momento."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You cannot upload encrypted PDFs"
msgstr "Non puoi caricare PDF crittografati"
@ -10513,7 +10620,7 @@ msgid "You have not yet created or received any documents. To create a document
msgstr "Non hai ancora creato o ricevuto documenti. Per creare un documento caricane uno."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You have reached the limit of the number of files per envelope"
msgstr "Hai raggiunto il limite del numero di file per busta"
@ -10528,13 +10635,13 @@ msgid "You have reached the maximum number of teams for your plan. Please contac
msgstr "Hai raggiunto il massimo numero di team per il tuo piano. Si prega di contattare le vendite a <0>{SUPPORT_EMAIL}</0> se si desidera modificare il proprio piano."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "You have reached your document limit for this month. Please upgrade your plan."
msgstr "Hai raggiunto il limite dei documenti per questo mese. Si prega di aggiornare il proprio piano."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: packages/ui/primitives/document-dropzone.tsx
msgid "You have reached your document limit."
msgstr "Hai raggiunto il tuo limite di documenti."
@ -10724,7 +10831,7 @@ msgstr "Il tuo attuale piano è scaduto."
msgid "Your direct signing templates"
msgstr "I tuoi modelli di firma diretta"
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
msgid "Your document failed to upload."
msgstr "Il tuo documento non è stato caricato."
@ -10754,7 +10861,7 @@ msgid "Your document has been successfully duplicated."
msgstr "Il tuo documento è stato duplicato correttamente."
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button.tsx
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
msgid "Your document has been uploaded successfully."
msgstr "Il tuo documento è stato caricato correttamente."

Some files were not shown because too many files have changed in this diff Show More