Compare commits
30 Commits
0977c16e33
...
v2.0.10
| Author | SHA1 | Date | |
|---|---|---|---|
| 1772c3ee36 | |||
| f55b902a01 | |||
| 50db4e39be | |||
| 2802813c76 | |||
| 29d40f1cca | |||
| d67f32eae2 | |||
| a33233443b | |||
| 68a3608aee | |||
| 378dd605b9 | |||
| 211ae6c9e9 | |||
| f931885a95 | |||
| 4ade408001 | |||
| 3d0e3c6e8e | |||
| 936d8d90b3 | |||
| c6b08d8594 | |||
| 575634e326 | |||
| c66eda4aae | |||
| ef52b35f79 | |||
| 95a647034a | |||
| 34dba0b6ff | |||
| fccd97e124 | |||
| 3dbbcefddf | |||
| 2aea3c4de0 | |||
| ff44ffbc03 | |||
| 441842d2bd | |||
| ca0b83579f | |||
| 6c0d1da91e | |||
| 805982f3e8 | |||
| e2f5e570cf | |||
| 9fd9613076 |
@ -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">
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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', () => (
|
||||
|
||||
@ -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),
|
||||
});
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
)}
|
||||
|
||||
@ -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)}
|
||||
/>
|
||||
|
||||
49
apps/remix/app/components/filters/date-range-filter.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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" />
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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();
|
||||
@ -75,8 +76,10 @@ export const DocumentUploadButton = ({ className }: DocumentUploadButtonProps) =
|
||||
|
||||
const payload = {
|
||||
title: file.name,
|
||||
timezone: userTimezone,
|
||||
folderId: folderId ?? undefined,
|
||||
meta: {
|
||||
timezone: userTimezone,
|
||||
},
|
||||
} satisfies TCreateDocumentPayloadSchema;
|
||||
|
||||
const formData = new FormData();
|
||||
@ -144,12 +147,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>
|
||||
@ -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>
|
||||
|
||||
@ -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',
|
||||
}}
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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} />
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
@ -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}
|
||||
@ -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,
|
||||
|
||||
287
apps/remix/app/components/tables/organisation-insights-table.tsx
Normal 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>
|
||||
);
|
||||
@ -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={
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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
|
||||
>
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
@ -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={
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -106,5 +106,5 @@
|
||||
"vite-plugin-babel-macros": "^1.0.6",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"version": "2.0.5"
|
||||
"version": "2.0.10"
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ export const downloadRoute = new Hono<HonoEnv>()
|
||||
* Requires API key authentication via Authorization header.
|
||||
*/
|
||||
.get(
|
||||
'/envelopeItem/:envelopeItemId/download',
|
||||
'/envelope/item/:envelopeItemId/download',
|
||||
sValidator('param', ZDownloadEnvelopeItemRequestParamsSchema),
|
||||
async (c) => {
|
||||
const logger = c.get('logger');
|
||||
|
||||
@ -2,12 +2,19 @@ import { lingui } from '@lingui/vite-plugin';
|
||||
import { reactRouter } from '@react-router/dev/vite';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
import serverAdapter from 'hono-react-router-adapter/vite';
|
||||
import { createRequire } from 'node:module';
|
||||
import path from 'node:path';
|
||||
import tailwindcss from 'tailwindcss';
|
||||
import { defineConfig } from 'vite';
|
||||
import { defineConfig, normalizePath } from 'vite';
|
||||
import macrosPlugin from 'vite-plugin-babel-macros';
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const pdfjsDistPath = path.dirname(require.resolve('pdfjs-dist/package.json'));
|
||||
const cMapsDir = normalizePath(path.join(pdfjsDistPath, 'cmaps'));
|
||||
|
||||
/**
|
||||
* Note: We load the env variables externally so we can have runtime enviroment variables
|
||||
* for docker.
|
||||
@ -25,6 +32,14 @@ export default defineConfig({
|
||||
strictPort: true,
|
||||
},
|
||||
plugins: [
|
||||
viteStaticCopy({
|
||||
targets: [
|
||||
{
|
||||
src: cMapsDir,
|
||||
dest: 'static',
|
||||
},
|
||||
],
|
||||
}),
|
||||
reactRouter(),
|
||||
macrosPlugin(),
|
||||
lingui(),
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
FROM node:22-alpine3.20 AS base
|
||||
|
||||
RUN apk add --no-cache openssl
|
||||
RUN apk add --no-cache font-freefont
|
||||
|
||||
|
||||
###########################
|
||||
@ -114,4 +115,4 @@ COPY --chown=nodejs:nodejs ./docker/start.sh /app/apps/remix/start.sh
|
||||
|
||||
WORKDIR /app/apps/remix
|
||||
|
||||
CMD ["sh", "start.sh"]
|
||||
CMD ["sh", "start.sh"]
|
||||
|
||||
102
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@documenso/root",
|
||||
"version": "2.0.5",
|
||||
"version": "2.0.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@documenso/root",
|
||||
"version": "2.0.5",
|
||||
"version": "2.0.10",
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
"packages/*"
|
||||
@ -52,6 +52,7 @@
|
||||
"trpc-to-openapi": "2.4.0",
|
||||
"turbo": "^1.9.3",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-static-copy": "^3.1.4",
|
||||
"zod-openapi": "^4.2.4",
|
||||
"zod-prisma-types": "3.3.5"
|
||||
},
|
||||
@ -100,7 +101,7 @@
|
||||
},
|
||||
"apps/remix": {
|
||||
"name": "@documenso/remix",
|
||||
"version": "2.0.5",
|
||||
"version": "2.0.10",
|
||||
"dependencies": {
|
||||
"@cantoo/pdf-lib": "^2.5.2",
|
||||
"@documenso/api": "*",
|
||||
@ -19112,10 +19113,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
@ -33479,13 +33483,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
||||
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
||||
"version": "0.2.15",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.4",
|
||||
"picomatch": "^4.0.2"
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
@ -33495,9 +33499,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@ -35944,6 +35948,76 @@
|
||||
"vite": ">=2"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-static-copy": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-3.1.4.tgz",
|
||||
"integrity": "sha512-iCmr4GSw4eSnaB+G8zc2f4dxSuDjbkjwpuBLLGvQYR9IW7rnDzftnUjOH5p4RYR+d4GsiBqXRvzuFhs5bnzVyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.6.0",
|
||||
"p-map": "^7.0.3",
|
||||
"picocolors": "^1.1.1",
|
||||
"tinyglobby": "^0.2.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-static-copy/node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-static-copy/node_modules/p-map": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
|
||||
"integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-plugin-static-copy/node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite-tsconfig-paths": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz",
|
||||
|
||||
21
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "2.0.5",
|
||||
"version": "2.0.10",
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"dev": "turbo run dev --filter=@documenso/remix",
|
||||
@ -45,6 +45,12 @@
|
||||
"@commitlint/config-conventional": "^17.7.0",
|
||||
"@lingui/cli": "^5.2.0",
|
||||
"@prisma/client": "^6.18.0",
|
||||
"@trpc/client": "11.7.0",
|
||||
"@trpc/react-query": "11.7.0",
|
||||
"@trpc/server": "11.7.0",
|
||||
"@ts-rest/core": "^3.52.1",
|
||||
"@ts-rest/open-api": "^3.52.1",
|
||||
"@ts-rest/serverless": "^3.52.1",
|
||||
"dotenv": "^16.5.0",
|
||||
"dotenv-cli": "^8.0.0",
|
||||
"eslint": "^8.40.0",
|
||||
@ -59,18 +65,13 @@
|
||||
"prisma-json-types-generator": "^3.6.2",
|
||||
"prisma-kysely": "^1.8.0",
|
||||
"rimraf": "^5.0.1",
|
||||
"turbo": "^1.9.3",
|
||||
"@trpc/client": "11.7.0",
|
||||
"@trpc/react-query": "11.7.0",
|
||||
"@trpc/server": "11.7.0",
|
||||
"superjson": "^2.2.5",
|
||||
"trpc-to-openapi": "2.4.0",
|
||||
"turbo": "^1.9.3",
|
||||
"vite": "^6.3.5",
|
||||
"vite-plugin-static-copy": "^3.1.4",
|
||||
"zod-openapi": "^4.2.4",
|
||||
"@ts-rest/core": "^3.52.1",
|
||||
"@ts-rest/open-api": "^3.52.1",
|
||||
"@ts-rest/serverless": "^3.52.1",
|
||||
"zod-prisma-types": "3.3.5",
|
||||
"vite": "^6.3.5"
|
||||
"zod-prisma-types": "3.3.5"
|
||||
},
|
||||
"name": "@documenso/root",
|
||||
"workspaces": [
|
||||
|
||||
@ -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,
|
||||
};
|
||||
});
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@ import { seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
import { expectTextToBeVisible } from '../fixtures/generic';
|
||||
|
||||
test.describe.configure({ mode: 'parallel' });
|
||||
|
||||
@ -81,20 +82,23 @@ 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);
|
||||
await fileInput.waitFor({ state: 'attached' });
|
||||
// Upload document.
|
||||
const [fileChooser] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.getByRole('button', { name: 'Document (Legacy)' }).click(),
|
||||
]);
|
||||
|
||||
await fileInput.setInputFiles(
|
||||
await fileChooser.setFiles(
|
||||
path.join(__dirname, '../../../assets/documenso-supporter-pledge.pdf'),
|
||||
);
|
||||
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
await expect(page.getByText('documenso-supporter-pledge.pdf')).toBeVisible();
|
||||
await expectTextToBeVisible(page, 'documenso-supporter-pledge.pdf');
|
||||
|
||||
await page.goto(`/t/${team.url}/documents/f/${teamFolder.id}`);
|
||||
|
||||
await expect(page.getByText('documenso-supporter-pledge.pdf')).toBeVisible();
|
||||
await expectTextToBeVisible(page, 'documenso-supporter-pledge.pdf');
|
||||
});
|
||||
|
||||
test('[TEAMS]: can pin a document folder', async ({ page }) => {
|
||||
@ -368,7 +372,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();
|
||||
|
||||
@ -382,11 +386,11 @@ test('[TEAMS]: can create a template inside a template folder', async ({ page })
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Expect redirect.
|
||||
await expect(page.getByText('documenso-supporter-pledge.pdf')).toBeVisible();
|
||||
await expectTextToBeVisible(page, 'documenso-supporter-pledge.pdf');
|
||||
|
||||
// Return to folder and verify file is visible.
|
||||
await page.goto(`/t/${team.url}/templates/f/${folder.id}`);
|
||||
await expect(page.getByText('documenso-supporter-pledge.pdf')).toBeVisible();
|
||||
await expectTextToBeVisible(page, 'documenso-supporter-pledge.pdf');
|
||||
});
|
||||
|
||||
test('[TEAMS]: can pin a template folder', async ({ page }) => {
|
||||
@ -842,7 +846,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(
|
||||
@ -851,7 +855,7 @@ test('[TEAMS]: documents inherit folder visibility', async ({ page }) => {
|
||||
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
await expect(page.getByText('documenso-supporter-pledge.pdf')).toBeVisible();
|
||||
await expectTextToBeVisible(page, 'documenso-supporter-pledge.pdf');
|
||||
|
||||
await expect(page.getByRole('combobox').filter({ hasText: 'Admins only' })).toBeVisible();
|
||||
});
|
||||
|
||||
BIN
packages/app-tests/visual-regression/alignment-pdf-0.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
packages/app-tests/visual-regression/alignment-pdf-1.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
packages/app-tests/visual-regression/alignment-pdf-2.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
packages/app-tests/visual-regression/alignment-pdf-3.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
packages/app-tests/visual-regression/blank-certificate.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
packages/app-tests/visual-regression/field-meta-pdf-0.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
packages/app-tests/visual-regression/field-meta-pdf-1.png
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
packages/app-tests/visual-regression/field-meta-pdf-2.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
packages/app-tests/visual-regression/field-meta-pdf-3.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
packages/app-tests/visual-regression/field-meta-pdf-4.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
packages/app-tests/visual-regression/field-meta-pdf-5.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
packages/app-tests/visual-regression/field-meta-pdf-6.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
packages/app-tests/visual-regression/field-meta-pdf-7.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
@ -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">
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -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 () => {
|
||||
|
||||
@ -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 ?? [],
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -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),
|
||||
};
|
||||
}
|
||||
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
@ -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?: {
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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 },
|
||||
);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import Konva from 'konva';
|
||||
// sort-imports-ignore
|
||||
import 'konva/skia-backend';
|
||||
|
||||
import Konva from 'konva';
|
||||
import path from 'node:path';
|
||||
import type { Canvas } from 'skia-canvas';
|
||||
import { FontLibrary } from 'skia-canvas';
|
||||
@ -21,21 +23,19 @@ export const insertFieldInPDFV2 = async ({
|
||||
}: InsertFieldInPDFV2Options) => {
|
||||
const fontPath = path.join(process.cwd(), 'public/fonts');
|
||||
|
||||
FontLibrary.use([
|
||||
path.join(fontPath, 'caveat.ttf'),
|
||||
path.join(fontPath, 'noto-sans.ttf'),
|
||||
path.join(fontPath, 'noto-sans-japanese.ttf'),
|
||||
path.join(fontPath, 'noto-sans-chinese.ttf'),
|
||||
path.join(fontPath, 'noto-sans-korean.ttf'),
|
||||
]);
|
||||
FontLibrary.use({
|
||||
['Caveat']: [path.join(fontPath, 'caveat.ttf')],
|
||||
['Noto Sans']: [path.join(fontPath, 'noto-sans.ttf')],
|
||||
['Noto Sans Japanese']: [path.join(fontPath, 'noto-sans-japanese.ttf')],
|
||||
['Noto Sans Chinese']: [path.join(fontPath, 'noto-sans-chinese.ttf')],
|
||||
['Noto Sans Korean']: [path.join(fontPath, 'noto-sans-korean.ttf')],
|
||||
});
|
||||
|
||||
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: {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -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-12 06:14\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,11 @@ 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/envelope/envelope-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"
|
||||
@ -169,9 +175,9 @@ 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "{0} of {1} documents remaining this month."
|
||||
msgstr "{0} von {1} Dokumenten verbleibend in diesem Monat."
|
||||
|
||||
@ -188,11 +194,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 +207,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 +259,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 +1240,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"
|
||||
|
||||
@ -1312,6 +1322,10 @@ msgstr "Eine E-Mail mit dieser Adresse existiert bereits."
|
||||
msgid "An error occurred"
|
||||
msgstr "Ein Fehler ist aufgetreten"
|
||||
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "An error occurred during upload."
|
||||
msgstr "Ein Fehler ist beim Hochladen aufgetreten"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
msgid "An error occurred while adding fields."
|
||||
msgstr "Ein Fehler ist aufgetreten beim Hinzufügen von Feldern."
|
||||
@ -1477,9 +1491,8 @@ msgstr "Ein Fehler ist aufgetreten, während die Unterschrift aktualisiert wurde
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "An error occurred while uploading your document."
|
||||
msgstr "Ein Fehler ist aufgetreten, während dein Dokument hochgeladen wurde."
|
||||
|
||||
@ -1857,6 +1870,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 +2114,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 +2284,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 +2565,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 +2756,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 +3241,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 +3377,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"
|
||||
@ -3478,14 +3505,14 @@ msgid "Document updated successfully"
|
||||
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/envelope/envelope-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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Document uploaded"
|
||||
msgstr "Dokument hochgeladen"
|
||||
|
||||
@ -3507,6 +3534,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 +3551,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 +3567,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"
|
||||
@ -3631,8 +3673,7 @@ msgstr "Ziehen Sie Ihr PDF hierher."
|
||||
msgid "Drag and drop or click to upload"
|
||||
msgstr "Ziehen Sie die Datei hierher oder klicken Sie, um hochzuladen"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Drag and drop your PDF file here"
|
||||
msgstr "Ziehen Sie Ihre PDF-Datei hierher"
|
||||
|
||||
@ -3736,6 +3777,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 +3967,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"
|
||||
@ -4073,6 +4119,8 @@ msgstr "Umschlag aktualisiert"
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
#: apps/remix/app/components/general/envelope-signing/envelope-signer-page-renderer.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-text-field.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-text-field.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
|
||||
@ -4096,8 +4144,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-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
|
||||
@ -4106,7 +4153,6 @@ msgstr "Umschlag aktualisiert"
|
||||
#: 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
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
#: apps/remix/app/components/embed/multisign/multi-sign-document-signing-view.tsx
|
||||
@ -4254,7 +4300,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"
|
||||
@ -4311,19 +4356,17 @@ msgid "Fields updated"
|
||||
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/envelope/envelope-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"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "File is larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
|
||||
msgstr "Datei ist größer als {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "File is too small"
|
||||
msgstr "Datei ist zu klein"
|
||||
|
||||
@ -4402,6 +4445,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 +4978,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 +5015,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 +5056,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 +5084,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 +5100,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 +5387,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 +5408,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 +5487,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 +5555,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"
|
||||
@ -5739,13 +5818,11 @@ msgstr "Nur Administratoren können auf das Dokument zugreifen und es anzeigen"
|
||||
msgid "Only managers and above can access and view the document"
|
||||
msgstr "Nur Manager und darüber können auf das Dokument zugreifen und es anzeigen"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Only one file can be uploaded at a time"
|
||||
msgstr "Es kann jeweils nur eine Datei hochgeladen werden."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Only PDF files are allowed"
|
||||
msgstr "Nur PDF-Dateien sind erlaubt"
|
||||
|
||||
@ -5813,6 +5890,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 +5991,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 +6032,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 +6161,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"
|
||||
|
||||
@ -6273,7 +6361,6 @@ msgstr "Bitte versuchen Sie eine andere Domain."
|
||||
msgid "Please try again and make sure you enter the correct email address."
|
||||
msgstr "Bitte versuchen Sie es erneut und stellen Sie sicher, dass Sie die korrekte E-Mail-Adresse eingeben."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "Please try again later."
|
||||
msgstr "Bitte versuchen Sie es später noch einmal."
|
||||
@ -6439,6 +6526,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 +6962,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 +7025,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 +7033,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 +7207,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 +7581,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 +7656,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."
|
||||
@ -7594,7 +7689,6 @@ msgstr "Einige Unterzeichner haben noch kein Unterschriftsfeld zugewiesen bekomm
|
||||
#: apps/remix/app/components/tables/organisation-member-invites-table.tsx
|
||||
#: apps/remix/app/components/general/billing-plans.tsx
|
||||
#: apps/remix/app/components/general/billing-plans.tsx
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-dropdown.tsx
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
@ -7712,6 +7806,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 +8000,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 +8086,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 +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"
|
||||
@ -8056,6 +8156,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 +8189,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"
|
||||
@ -8108,8 +8217,8 @@ msgstr "Vorlagentitel"
|
||||
msgid "Template updated successfully"
|
||||
msgstr "Vorlage erfolgreich aktualisiert"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Template uploaded"
|
||||
msgstr "Vorlage hochgeladen"
|
||||
|
||||
@ -8266,9 +8375,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 +8387,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 +8444,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 +8545,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 +8562,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 +8603,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 +8623,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 +8680,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 +8925,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 +8938,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 +8991,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 +9050,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"
|
||||
@ -9122,8 +9233,7 @@ msgstr "Unvollendet"
|
||||
msgid "Unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Unknown error"
|
||||
msgstr "Unbekannter Fehler"
|
||||
|
||||
@ -9281,7 +9391,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"
|
||||
@ -9291,7 +9401,7 @@ msgstr "Upgrade"
|
||||
msgid "Upgrade <0>{0}</0> to {planName}"
|
||||
msgstr "<0>{0}</0> auf {planName} aktualisieren"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Upgrade your plan to upload more documents"
|
||||
msgstr "Aktualisieren Sie Ihren Tarif, um mehr Dokumente hochzuladen"
|
||||
|
||||
@ -9333,9 +9443,9 @@ msgstr "Benutzerdefiniertes Dokument hochladen"
|
||||
msgid "Upload disabled"
|
||||
msgstr "Hochladen deaktiviert"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-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,14 +9453,9 @@ 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
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Upload failed"
|
||||
msgstr "Hochladen fehlgeschlagen"
|
||||
|
||||
@ -9358,11 +9463,11 @@ msgstr "Hochladen fehlgeschlagen"
|
||||
msgid "Upload Signature"
|
||||
msgstr "Signatur hochladen"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-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"
|
||||
@ -9389,17 +9494,10 @@ msgid "Uploaded file not an allowed file type"
|
||||
msgstr "Die hochgeladene Datei ist kein zulässiger Dateityp"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Uploading"
|
||||
msgstr "Hochladen"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
msgid "Uploading document..."
|
||||
msgstr "Dokument wird hochgeladen..."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
msgid "Uploading template..."
|
||||
msgstr "Vorlage wird hochgeladen..."
|
||||
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
msgid "URL"
|
||||
msgstr "URL"
|
||||
@ -9472,6 +9570,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 +9593,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"
|
||||
@ -9526,8 +9629,8 @@ msgstr "Überprüfen Sie Ihre E-Mail-Adresse"
|
||||
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/envelope/envelope-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 +9644,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 +10500,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,20 +10516,21 @@ 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "You cannot upload encrypted PDFs"
|
||||
msgstr "Sie können keine verschlüsselten PDFs hochladen"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
|
||||
#: 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/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "You cannot upload more than {maximumEnvelopeItemCount} items per envelope."
|
||||
msgstr "Sie können nicht mehr als {maximumEnvelopeItemCount} Elemente pro Umschlag hochladen."
|
||||
|
||||
@ -10502,9 +10606,9 @@ msgstr "Sie haben noch keine Vorlagen erstellt. Bitte laden Sie eine Datei hoch,
|
||||
msgid "You have not yet created or received any documents. To create a document please upload one."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.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"
|
||||
|
||||
@ -10517,14 +10621,14 @@ msgstr "Sie haben das maximale Limit von {0} direkten Vorlagen erreicht. <0>Upgr
|
||||
msgid "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."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.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/envelope/envelope-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 +10818,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."
|
||||
@ -10743,9 +10847,9 @@ msgstr "Ihr Dokument wurde erfolgreich gesendet."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Your document has been uploaded successfully."
|
||||
msgstr "Ihr Dokument wurde erfolgreich hochgeladen."
|
||||
|
||||
@ -10896,14 +11000,11 @@ msgstr "Ihre Vorlage wurde erfolgreich dupliziert."
|
||||
msgid "Your template has been successfully deleted."
|
||||
msgstr "Ihre Vorlage wurde erfolgreich gelöscht."
|
||||
|
||||
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Your template has been uploaded successfully."
|
||||
msgstr "Ihre Vorlage wurde erfolgreich hochgeladen."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
msgid "Your template has been uploaded successfully. You will be redirected to the template page."
|
||||
msgstr "Ihre Vorlage wurde erfolgreich hochgeladen. Sie werden zur Vorlagenseite weitergeleitet."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
msgid "Your template will be duplicated."
|
||||
msgstr "Ihre Vorlage wird dupliziert."
|
||||
|
||||
@ -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-12 06:14\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,11 @@ msgstr "{0, plural, one {Esperando 1 destinatario} other {Esperando # destinatar
|
||||
msgid "{0}"
|
||||
msgstr "{0}"
|
||||
|
||||
#. placeholder {0}: file.name
|
||||
#: apps/remix/app/components/general/envelope/envelope-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"
|
||||
@ -169,9 +175,9 @@ 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "{0} of {1} documents remaining this month."
|
||||
msgstr "{0} de {1} documentos restantes este mes."
|
||||
|
||||
@ -188,11 +194,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 +207,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 +259,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 +1240,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"
|
||||
|
||||
@ -1312,6 +1322,10 @@ msgstr "Ya existe un correo electrónico con esta dirección."
|
||||
msgid "An error occurred"
|
||||
msgstr "Ocurrió un error"
|
||||
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "An error occurred during upload."
|
||||
msgstr "Ocurrió un error durante la subida."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
msgid "An error occurred while adding fields."
|
||||
msgstr "Ocurrió un error al agregar campos."
|
||||
@ -1477,9 +1491,8 @@ msgstr "Ocurrió un error al actualizar la firma."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "An error occurred while uploading your document."
|
||||
msgstr "Ocurrió un error al subir tu documento."
|
||||
|
||||
@ -1857,6 +1870,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 +2114,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 +2284,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 +2565,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 +2756,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 +3241,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 +3377,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"
|
||||
@ -3478,14 +3505,14 @@ msgid "Document updated successfully"
|
||||
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/envelope/envelope-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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Document uploaded"
|
||||
msgstr "Documento subido"
|
||||
|
||||
@ -3507,6 +3534,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 +3551,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 +3567,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"
|
||||
@ -3631,8 +3673,7 @@ msgstr "Arrastre y suelte su PDF aquí."
|
||||
msgid "Drag and drop or click to upload"
|
||||
msgstr "Arrastra y suelta o haz clic para cargar"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Drag and drop your PDF file here"
|
||||
msgstr "Arrastra y suelta tu archivo PDF aquí"
|
||||
|
||||
@ -3736,6 +3777,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 +3967,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"
|
||||
@ -4073,6 +4119,8 @@ msgstr "Sobre actualizado"
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
#: apps/remix/app/components/general/envelope-signing/envelope-signer-page-renderer.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-text-field.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-text-field.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
|
||||
@ -4096,8 +4144,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-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
|
||||
@ -4106,7 +4153,6 @@ msgstr "Sobre actualizado"
|
||||
#: 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
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
#: apps/remix/app/components/embed/multisign/multi-sign-document-signing-view.tsx
|
||||
@ -4254,7 +4300,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"
|
||||
@ -4311,19 +4356,17 @@ msgid "Fields updated"
|
||||
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/envelope/envelope-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"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "File is larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
|
||||
msgstr "El archivo es mayor que {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "File is too small"
|
||||
msgstr "El archivo es demasiado pequeño"
|
||||
|
||||
@ -4402,6 +4445,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 +4978,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 +5015,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 +5056,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 +5084,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 +5100,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 +5387,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 +5408,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 +5487,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 +5555,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"
|
||||
@ -5739,13 +5818,11 @@ msgstr "Solo los administradores pueden acceder y ver el documento"
|
||||
msgid "Only managers and above can access and view the document"
|
||||
msgstr "Solo los gerentes y superiores pueden acceder y ver el documento"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Only one file can be uploaded at a time"
|
||||
msgstr "Solo se puede cargar un archivo a la vez"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Only PDF files are allowed"
|
||||
msgstr "Solo se permiten archivos PDF"
|
||||
|
||||
@ -5813,6 +5890,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 +5991,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 +6032,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 +6161,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"
|
||||
|
||||
@ -6273,7 +6361,6 @@ msgstr "Por favor, intenta con un dominio diferente."
|
||||
msgid "Please try again and make sure you enter the correct email address."
|
||||
msgstr "Por favor, intenta de nuevo y asegúrate de ingresar la dirección de correo electrónico correcta."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "Please try again later."
|
||||
msgstr "Por favor, intenta de nuevo más tarde."
|
||||
@ -6439,6 +6526,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 +6962,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 +7025,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 +7033,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 +7207,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 +7581,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 +7656,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."
|
||||
@ -7594,7 +7689,6 @@ msgstr "Algunos firmantes no han sido asignados a un campo de firma. Asigne al m
|
||||
#: apps/remix/app/components/tables/organisation-member-invites-table.tsx
|
||||
#: apps/remix/app/components/general/billing-plans.tsx
|
||||
#: apps/remix/app/components/general/billing-plans.tsx
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-dropdown.tsx
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
@ -7712,6 +7806,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 +8000,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 +8086,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 +8131,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 +8156,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 +8189,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"
|
||||
@ -8108,8 +8217,8 @@ msgstr "Título de plantilla"
|
||||
msgid "Template updated successfully"
|
||||
msgstr "Plantilla actualizada con éxito"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Template uploaded"
|
||||
msgstr "Plantilla subida"
|
||||
|
||||
@ -8266,10 +8375,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 existió."
|
||||
|
||||
#: packages/ui/components/document/document-send-email-message-helper.tsx
|
||||
msgid "The document's name"
|
||||
@ -8280,9 +8387,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 existió."
|
||||
|
||||
#: apps/remix/app/components/forms/signin.tsx
|
||||
msgid "The email or password provided is incorrect"
|
||||
@ -8338,23 +8444,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 existió."
|
||||
|
||||
#: 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 +8545,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 existió."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
msgid "The template has been moved successfully."
|
||||
@ -8468,10 +8562,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 existió."
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
|
||||
msgid "The test webhook has been successfully sent to your endpoint."
|
||||
@ -8511,9 +8603,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 existió."
|
||||
|
||||
#: 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 +8623,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 existió."
|
||||
|
||||
#: 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 +8680,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 +8925,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 +8938,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 +8991,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 +9050,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"
|
||||
@ -9125,8 +9233,7 @@ msgstr "Incompleto"
|
||||
msgid "Unknown"
|
||||
msgstr "Desconocido"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Unknown error"
|
||||
msgstr "Error desconocido"
|
||||
|
||||
@ -9284,7 +9391,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"
|
||||
@ -9294,7 +9401,7 @@ msgstr "Actualizar"
|
||||
msgid "Upgrade <0>{0}</0> to {planName}"
|
||||
msgstr "Actualizar <0>{0}</0> a {planName}"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Upgrade your plan to upload more documents"
|
||||
msgstr "Actualiza tu plan para cargar más documentos"
|
||||
|
||||
@ -9336,9 +9443,9 @@ msgstr "Subir documento personalizado"
|
||||
msgid "Upload disabled"
|
||||
msgstr "Subida desactivada"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-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,14 +9453,9 @@ 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
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Upload failed"
|
||||
msgstr "Subida fallida"
|
||||
|
||||
@ -9361,11 +9463,11 @@ msgstr "Subida fallida"
|
||||
msgid "Upload Signature"
|
||||
msgstr "Subir firma"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-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"
|
||||
@ -9392,17 +9494,10 @@ msgid "Uploaded file not an allowed file type"
|
||||
msgstr "El archivo subido no es un tipo de archivo permitido"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Uploading"
|
||||
msgstr "Subiendo"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
msgid "Uploading document..."
|
||||
msgstr "Cargando documento..."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
msgid "Uploading template..."
|
||||
msgstr "Subiendo plantilla..."
|
||||
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
msgid "URL"
|
||||
msgstr "URL"
|
||||
@ -9475,6 +9570,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 +9593,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"
|
||||
@ -9529,8 +9629,8 @@ msgstr "Verifica tu dirección de correo electrónico"
|
||||
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/envelope/envelope-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 +9644,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 +10500,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,20 +10516,21 @@ 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "You cannot upload encrypted PDFs"
|
||||
msgstr "No puedes subir PDFs encriptados"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
|
||||
#: 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/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "You cannot upload more than {maximumEnvelopeItemCount} items per envelope."
|
||||
msgstr "No puede subir más de {maximumEnvelopeItemCount} elementos por sobre."
|
||||
|
||||
@ -10505,9 +10606,9 @@ msgstr "Aún no has creado plantillas. Para crear una plantilla, por favor carga
|
||||
msgid "You have not yet created or received any documents. To create a document please upload one."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "You have reached the limit of the number of files per envelope"
|
||||
msgstr "Has alcanzado el límite de archivos por sobre"
|
||||
|
||||
@ -10520,14 +10621,14 @@ msgstr "Has alcanzado el límite máximo de {0} plantillas directas. <0>¡Actual
|
||||
msgid "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."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.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/envelope/envelope-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 +10818,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."
|
||||
@ -10746,9 +10847,9 @@ msgstr "Tu documento ha sido enviado con éxito."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Your document has been uploaded successfully."
|
||||
msgstr "Tu documento ha sido subido con éxito."
|
||||
|
||||
@ -10899,14 +11000,11 @@ msgstr "Tu plantilla ha sido duplicada con éxito."
|
||||
msgid "Your template has been successfully deleted."
|
||||
msgstr "Tu plantilla ha sido eliminada con éxito."
|
||||
|
||||
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Your template has been uploaded successfully."
|
||||
msgstr "Su plantilla ha sido subida exitosamente."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
msgid "Your template has been uploaded successfully. You will be redirected to the template page."
|
||||
msgstr "Tu plantilla ha sido cargada exitosamente. Serás redirigido a la página de plantillas."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
msgid "Your template will be duplicated."
|
||||
msgstr "Tu plantilla será duplicada."
|
||||
|
||||
@ -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-12 06:14\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,11 @@ 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/envelope/envelope-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"
|
||||
@ -169,9 +175,9 @@ 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "{0} of {1} documents remaining this month."
|
||||
msgstr "{0} des {1} documents restants ce mois-ci."
|
||||
|
||||
@ -188,11 +194,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 +207,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 +259,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 +1240,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"
|
||||
|
||||
@ -1312,6 +1322,10 @@ msgstr "Un email avec cette adresse existe déjà."
|
||||
msgid "An error occurred"
|
||||
msgstr "Une erreur est survenue"
|
||||
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "An error occurred during upload."
|
||||
msgstr "Une erreur est survenue lors du téléchargement."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
msgid "An error occurred while adding fields."
|
||||
msgstr "Une erreur est survenue lors de l'ajout des champs."
|
||||
@ -1477,9 +1491,8 @@ msgstr "Une erreur est survenue lors de la mise à jour de la signature."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "An error occurred while uploading your document."
|
||||
msgstr "Une erreur est survenue lors de l'importation de votre document."
|
||||
|
||||
@ -1857,6 +1870,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 +2114,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 +2284,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 +2565,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 +2756,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 +3241,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 +3377,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"
|
||||
@ -3478,14 +3505,14 @@ msgid "Document updated successfully"
|
||||
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/envelope/envelope-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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Document uploaded"
|
||||
msgstr "Document importé"
|
||||
|
||||
@ -3507,6 +3534,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 +3551,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 +3567,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"
|
||||
@ -3631,8 +3673,7 @@ msgstr "Faites glisser et déposez votre PDF ici."
|
||||
msgid "Drag and drop or click to upload"
|
||||
msgstr "Glissez-déposez ou cliquez pour importer"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Drag and drop your PDF file here"
|
||||
msgstr "Faites glisser et déposez votre fichier PDF ici"
|
||||
|
||||
@ -3736,6 +3777,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 +3967,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"
|
||||
@ -4073,6 +4119,8 @@ msgstr "Enveloppe mise à jour"
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
#: apps/remix/app/components/general/envelope-signing/envelope-signer-page-renderer.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-text-field.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-text-field.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
|
||||
@ -4096,8 +4144,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-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
|
||||
@ -4106,7 +4153,6 @@ msgstr "Enveloppe mise à jour"
|
||||
#: 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
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
#: apps/remix/app/components/embed/multisign/multi-sign-document-signing-view.tsx
|
||||
@ -4254,7 +4300,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"
|
||||
@ -4311,19 +4356,17 @@ msgid "Fields updated"
|
||||
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/envelope/envelope-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"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "File is larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
|
||||
msgstr "Le fichier est plus grand que {APP_DOCUMENT_UPLOAD_SIZE_LIMIT} Mo"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "File is too small"
|
||||
msgstr "Le fichier est trop petit"
|
||||
|
||||
@ -4402,6 +4445,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 +4978,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 +5015,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 +5056,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 +5084,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 +5100,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 +5387,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 +5408,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 +5487,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 +5555,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"
|
||||
@ -5739,13 +5818,11 @@ msgstr "Seules les administrateurs peuvent accéder et voir le document"
|
||||
msgid "Only managers and above can access and view the document"
|
||||
msgstr "Seuls les responsables et au-dessus peuvent accéder et voir le document"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Only one file can be uploaded at a time"
|
||||
msgstr "Un seul fichier peut être téléchargé à la fois"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Only PDF files are allowed"
|
||||
msgstr "Seuls les fichiers PDF sont autorisés"
|
||||
|
||||
@ -5813,6 +5890,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 +5991,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 +6032,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 +6161,7 @@ msgid "per year"
|
||||
msgstr "par an"
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
msgctxt "Personal organisation (adjective)"
|
||||
msgid "Personal"
|
||||
msgstr "Personnel"
|
||||
|
||||
@ -6273,7 +6361,6 @@ msgstr "Veuillez essayer un autre domaine."
|
||||
msgid "Please try again and make sure you enter the correct email address."
|
||||
msgstr "Veuillez réessayer et assurez-vous d'entrer la bonne adresse email."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "Please try again later."
|
||||
msgstr "Veuillez réessayer plus tard."
|
||||
@ -6439,6 +6526,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 +6962,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 +7025,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 +7033,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 +7207,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 +7581,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 +7656,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."
|
||||
@ -7594,7 +7689,6 @@ msgstr "Certains signataires n'ont pas été assignés à un champ de signature.
|
||||
#: apps/remix/app/components/tables/organisation-member-invites-table.tsx
|
||||
#: apps/remix/app/components/general/billing-plans.tsx
|
||||
#: apps/remix/app/components/general/billing-plans.tsx
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-dropdown.tsx
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
@ -7712,6 +7806,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 +8000,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 +8086,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 +8131,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 +8156,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 +8189,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"
|
||||
@ -8108,8 +8217,8 @@ msgstr "Titre du modèle"
|
||||
msgid "Template updated successfully"
|
||||
msgstr "Modèle mis à jour avec succès"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Template uploaded"
|
||||
msgstr "Modèle de document téléchargé"
|
||||
|
||||
@ -8266,9 +8375,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 +8387,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 +8444,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 +8545,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 +8562,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 +8603,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 +8623,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 +8680,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 +8925,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 +8938,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 +8991,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 +9050,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"
|
||||
@ -9122,8 +9233,7 @@ msgstr "Non complet"
|
||||
msgid "Unknown"
|
||||
msgstr "Inconnu"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Unknown error"
|
||||
msgstr "Erreur inconnue"
|
||||
|
||||
@ -9281,7 +9391,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"
|
||||
@ -9291,7 +9401,7 @@ msgstr "Améliorer"
|
||||
msgid "Upgrade <0>{0}</0> to {planName}"
|
||||
msgstr "Mettre à niveau <0>{0}</0> vers {planName}"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Upgrade your plan to upload more documents"
|
||||
msgstr "Mettez à niveau votre plan pour importer plus de documents"
|
||||
|
||||
@ -9333,9 +9443,9 @@ msgstr "Importer un document personnalisé"
|
||||
msgid "Upload disabled"
|
||||
msgstr "Importation désactivée"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-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,14 +9453,9 @@ 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
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Upload failed"
|
||||
msgstr "Échec de l'importation"
|
||||
|
||||
@ -9358,11 +9463,11 @@ msgstr "Échec de l'importation"
|
||||
msgid "Upload Signature"
|
||||
msgstr "Importer une signature"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-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"
|
||||
@ -9389,17 +9494,10 @@ msgid "Uploaded file not an allowed file type"
|
||||
msgstr "Le fichier importé n'est pas un type de fichier autorisé"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Uploading"
|
||||
msgstr "Importation en cours"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
msgid "Uploading document..."
|
||||
msgstr "Importation du document..."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
msgid "Uploading template..."
|
||||
msgstr "Téléchargement du modèle en cours..."
|
||||
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
msgid "URL"
|
||||
msgstr "URL"
|
||||
@ -9472,6 +9570,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 +9593,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"
|
||||
@ -9526,8 +9629,8 @@ msgstr "Vérifiez votre adresse e-mail"
|
||||
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/envelope/envelope-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 +9644,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 +10500,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,20 +10516,21 @@ 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "You cannot upload encrypted PDFs"
|
||||
msgstr "Vous ne pouvez pas importer de PDF cryptés"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
|
||||
#: 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/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "You cannot upload more than {maximumEnvelopeItemCount} items per envelope."
|
||||
msgstr "Vous ne pouvez pas télécharger plus de {maximumEnvelopeItemCount} articles par enveloppe."
|
||||
|
||||
@ -10502,9 +10606,9 @@ msgstr "Vous n'avez pas encore créé de modèles. Pour créer un modèle, veuil
|
||||
msgid "You have not yet created or received any documents. To create a document please upload one."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.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"
|
||||
|
||||
@ -10517,14 +10621,14 @@ msgstr "Vous avez atteint la limite maximale de {0} modèles directs. <0>Mettez
|
||||
msgid "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."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.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/envelope/envelope-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 +10818,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é."
|
||||
@ -10743,9 +10847,9 @@ msgstr "Votre document a été envoyé avec succès."
|
||||
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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Your document has been uploaded successfully."
|
||||
msgstr "Votre document a été importé avec succès."
|
||||
|
||||
@ -10896,14 +11000,11 @@ msgstr "Votre modèle a été dupliqué avec succès."
|
||||
msgid "Your template has been successfully deleted."
|
||||
msgstr "Votre modèle a été supprimé avec succès."
|
||||
|
||||
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Your template has been uploaded successfully."
|
||||
msgstr "Votre modèle a été importé avec succès."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
msgid "Your template has been uploaded successfully. You will be redirected to the template page."
|
||||
msgstr "Votre modèle a été téléchargé avec succès. Vous serez redirigé vers la page du modèle."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
msgid "Your template will be duplicated."
|
||||
msgstr "Votre modèle sera dupliqué."
|
||||
|
||||
@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: nl\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-12 06:14\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Dutch\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 " Directe koppelingsondertekening inschakelen"
|
||||
|
||||
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
|
||||
msgid ".PDF documents accepted (max {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB)"
|
||||
msgstr ".PDF documenten geaccepteerd (max {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB)"
|
||||
@ -98,6 +94,11 @@ msgstr "{0, plural, one {# map} other {# mappen}}"
|
||||
msgid "{0, plural, one {# recipient} other {# recipients}}"
|
||||
msgstr "{0, plural, one {# ontvanger} other {# ontvangers}}"
|
||||
|
||||
#. 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 {# Ontvanger} other {# Ontvangers}}"
|
||||
|
||||
#. placeholder {0}: org.teams.length
|
||||
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
|
||||
msgid "{0, plural, one {# team} other {# teams}}"
|
||||
@ -151,6 +152,11 @@ msgstr "{0, plural, one {Wachten op 1 ontvanger} other {Wachten op # ontvangers}
|
||||
msgid "{0}"
|
||||
msgstr "{0}"
|
||||
|
||||
#. placeholder {0}: file.name
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "{0} couldn't be uploaded:"
|
||||
msgstr "{0} kon niet worden geüpload:"
|
||||
|
||||
#. placeholder {0}: team.name
|
||||
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
|
||||
msgid "{0} direct signing templates"
|
||||
@ -169,9 +175,9 @@ msgstr "{0} heeft je uitgenodigd om {recipientActionVerb} een 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "{0} of {1} documents remaining this month."
|
||||
msgstr "{0} van de {1} documenten blijven deze maand over."
|
||||
|
||||
@ -188,11 +194,6 @@ msgstr "{0} van {1} rij(en) geselecteerd."
|
||||
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
|
||||
msgstr "{0} namens \"{1}\" heeft je uitgenodigd om {recipientActionVerb} het document \"{2}\"."
|
||||
|
||||
#. placeholder {0}: envelope.recipients.length
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
|
||||
msgid "{0} Recipient(s)"
|
||||
msgstr "{0} Ontvanger(s)"
|
||||
|
||||
#. placeholder {0}: organisation.name
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "{0} Teams"
|
||||
@ -206,6 +207,10 @@ msgstr "{browserInfo} op {os}"
|
||||
msgid "{charactersRemaining, plural, one {1 character remaining} other {{charactersRemaining} characters remaining}}"
|
||||
msgstr "{charactersRemaining, plural, one {1 resterend teken} other {{charactersRemaining} resterende tekens}}"
|
||||
|
||||
#: 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 {Deze code verloopt over # minuut.} other {Deze code verloopt over # minuten.}}"
|
||||
|
||||
#: packages/email/templates/document-invite.tsx
|
||||
msgid "{inviterName} <0>({inviterEmail})</0>"
|
||||
msgstr "{inviterName} <0>({inviterEmail})</0>"
|
||||
@ -254,6 +259,10 @@ msgstr "{inviterName} namens \"{teamName}\" heeft u uitgenodigd voor {0}<0/>\"{d
|
||||
msgid "{inviterName} on behalf of \"{teamName}\" has invited you to {action} {documentName}"
|
||||
msgstr "{inviterName} namens \"{teamName}\" heeft je uitgenodigd om {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 {U kunt niet meer dan # toegangssleutel hebben.} other {U kunt niet meer dan # toegangssleutels hebben.}}"
|
||||
|
||||
#: packages/lib/utils/document-audit-logs.ts
|
||||
msgid "{prefix} added a field"
|
||||
msgstr "{prefix} heeft een veld toegevoegd"
|
||||
@ -1231,6 +1240,7 @@ msgid "All templates"
|
||||
msgstr "Alle sjablonen"
|
||||
|
||||
#: apps/remix/app/components/general/period-selector.tsx
|
||||
#: apps/remix/app/components/filters/date-range-filter.tsx
|
||||
msgid "All Time"
|
||||
msgstr "Alle tijden"
|
||||
|
||||
@ -1312,6 +1322,10 @@ msgstr "Er bestaat al een e-mail met dit adres."
|
||||
msgid "An error occurred"
|
||||
msgstr "Er is een fout opgetreden"
|
||||
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "An error occurred during upload."
|
||||
msgstr "Er is een fout opgetreden tijdens het uploaden."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
msgid "An error occurred while adding fields."
|
||||
msgstr "Er is een fout opgetreden bij het toevoegen van velden."
|
||||
@ -1477,9 +1491,8 @@ msgstr "Er is een fout opgetreden bij het updaten van de handtekening."
|
||||
msgid "An error occurred while updating your profile."
|
||||
msgstr "Er is een fout opgetreden bij het updaten van uw profiel."
|
||||
|
||||
#: 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "An error occurred while uploading your document."
|
||||
msgstr "Er is een fout opgetreden bij het uploaden van uw document."
|
||||
|
||||
@ -1857,6 +1870,10 @@ msgstr "Zwart"
|
||||
msgid "Blue"
|
||||
msgstr "Blauw"
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
|
||||
msgid "Bottom"
|
||||
msgstr "Onderkant"
|
||||
|
||||
#: apps/remix/app/components/forms/branding-preferences-form.tsx
|
||||
msgid "Brand Details"
|
||||
msgstr "Merkgegevens"
|
||||
@ -2097,6 +2114,10 @@ msgstr "Cc-ers"
|
||||
msgid "Center"
|
||||
msgstr "Midden"
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-text-form.tsx
|
||||
msgid "Character limit"
|
||||
msgstr "Tekenlimiet"
|
||||
|
||||
#: 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 +2284,7 @@ msgstr "Document Voltooien"
|
||||
msgid "Complete the fields for the following signers."
|
||||
msgstr "Vul de velden in voor de volgende ondertekenaars."
|
||||
|
||||
#: 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 +2565,6 @@ msgstr "Ondertekeningslinks Kopiëren"
|
||||
msgid "Copy token"
|
||||
msgstr "Kopieer 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 "kon niet worden geüpload:"
|
||||
|
||||
#: 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 +2756,11 @@ msgstr "Maak uw account aan en begin met het gebruik van geavanceerde documenton
|
||||
#: 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 +3241,10 @@ msgstr "Document \"{0}\" - Afwijzing Bevestigd"
|
||||
msgid "Document \"{0}\" Cancelled"
|
||||
msgstr "Document \"{0}\" Geannuleerd"
|
||||
|
||||
#: 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 & Ontvangers"
|
||||
@ -3354,6 +3377,10 @@ msgstr "Document gevonden in uw 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 Inkommand"
|
||||
@ -3478,14 +3505,14 @@ msgid "Document updated successfully"
|
||||
msgstr "Document succesvol bijgewerkt"
|
||||
|
||||
#: 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/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Document upload disabled due to unpaid invoices"
|
||||
msgstr "Documentupload uitgeschakeld vanwege onbetaalde facturen"
|
||||
|
||||
#: 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Document uploaded"
|
||||
msgstr "Document Geüpload"
|
||||
|
||||
@ -3507,6 +3534,10 @@ msgctxt "Audit log format"
|
||||
msgid "Document visibility updated"
|
||||
msgstr "Zichtbaarheid van document bijgewerkt"
|
||||
|
||||
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
|
||||
msgid "Document Volume"
|
||||
msgstr "Documentvolume"
|
||||
|
||||
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx
|
||||
msgid "Document will be permanently deleted"
|
||||
msgstr "Document zal permanent worden verwijderd"
|
||||
@ -3520,6 +3551,8 @@ msgstr "Documentatie"
|
||||
#: 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 +3567,15 @@ msgstr "Documenten"
|
||||
msgid "Documents and resources related to this envelope."
|
||||
msgstr "Documenten en bronnen met betrekking tot deze envelop."
|
||||
|
||||
#: apps/remix/app/components/tables/organisation-insights-table.tsx
|
||||
#: apps/remix/app/components/tables/organisation-insights-table.tsx
|
||||
msgid "Documents Completed"
|
||||
msgstr "Voltooide documenten"
|
||||
|
||||
#: apps/remix/app/components/tables/organisation-insights-table.tsx
|
||||
msgid "Documents Created"
|
||||
msgstr "Aangemaakte documenten"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
|
||||
msgid "Documents created from template"
|
||||
msgstr "Documenten gemaakt op basis van sjabloon"
|
||||
@ -3631,8 +3673,7 @@ msgstr "Sleep & zet hier je PDF neer."
|
||||
msgid "Drag and drop or click to upload"
|
||||
msgstr "Sleep en zet neer of klik om te uploaden"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Drag and drop your PDF file here"
|
||||
msgstr "Sleep en zet uw PDF-bestand hier neer"
|
||||
|
||||
@ -3736,6 +3777,7 @@ msgstr "Elektronische Handtekening bekendmaking"
|
||||
#: 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 +3967,10 @@ msgstr "Aangepaste branding inschakelen voor alle documenten in deze organisatie
|
||||
msgid "Enable custom branding for all documents in this team"
|
||||
msgstr "Aangepaste branding inschakelen voor alle documenten in dit team"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
|
||||
msgid "Enable direct link signing"
|
||||
msgstr "Directe link ondertekenen inschakelen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
|
||||
#: packages/lib/constants/template.ts
|
||||
msgid "Enable Direct Link Signing"
|
||||
@ -4073,6 +4119,8 @@ msgstr "Envelop bijgewerkt"
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
#: apps/remix/app/components/general/template/template-edit-form.tsx
|
||||
#: apps/remix/app/components/general/envelope-signing/envelope-signer-page-renderer.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-text-field.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-text-field.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
|
||||
@ -4096,8 +4144,7 @@ msgstr "Envelop bijgewerkt"
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-checkbox-field.tsx
|
||||
#: 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
|
||||
@ -4106,7 +4153,6 @@ msgstr "Envelop bijgewerkt"
|
||||
#: 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
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
#: apps/remix/app/components/embed/multisign/multi-sign-document-signing-view.tsx
|
||||
@ -4254,7 +4300,6 @@ msgstr "Mislukt: {failedCount}"
|
||||
msgid "Feature Flags"
|
||||
msgstr "Functievlaggen"
|
||||
|
||||
#: 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 "Veld tekenlimiet"
|
||||
@ -4311,19 +4356,17 @@ msgid "Fields updated"
|
||||
msgstr "Velden bijgewerkt"
|
||||
|
||||
#: 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/envelope/envelope-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 "Bestand mag niet groter zijn dan {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "File is larger than {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
|
||||
msgstr "Bestand is groter dan {APP_DOCUMENT_UPLOAD_SIZE_LIMIT}MB"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "File is too small"
|
||||
msgstr "Bestand is te klein"
|
||||
|
||||
@ -4402,6 +4445,7 @@ msgstr "Wachtwoord Vergeten?"
|
||||
msgid "Forgot your password?"
|
||||
msgstr "Wachtwoord vergeten?"
|
||||
|
||||
#: apps/remix/app/components/tables/admin-organisations-table.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
|
||||
msgid "Free"
|
||||
msgstr "Gratis"
|
||||
@ -4934,6 +4978,10 @@ msgstr "Word lid van {organisationName} op Documenso"
|
||||
msgid "Join our community on <0>Discord</0> for community support and discussion."
|
||||
msgstr "Word lid van onze community op <0>Discord</0> voor community ondersteuning en discussie."
|
||||
|
||||
#: apps/remix/app/components/tables/organisation-insights-table.tsx
|
||||
msgid "Joined"
|
||||
msgstr "Toegetreden"
|
||||
|
||||
#. placeholder {0}: DateTime.fromJSDate(team.createdAt).toRelative({ style: 'short' })
|
||||
#: apps/remix/app/routes/_authenticated+/dashboard.tsx
|
||||
msgid "Joined {0}"
|
||||
@ -4967,10 +5015,18 @@ msgstr "Laatste 14 dagen"
|
||||
msgid "Last 30 days"
|
||||
msgstr "Laatste 30 dagen"
|
||||
|
||||
#: apps/remix/app/components/filters/date-range-filter.tsx
|
||||
msgid "Last 30 Days"
|
||||
msgstr "Laatste 30 dagen"
|
||||
|
||||
#: apps/remix/app/components/general/period-selector.tsx
|
||||
msgid "Last 7 days"
|
||||
msgstr "Laatste 7 dagen"
|
||||
|
||||
#: apps/remix/app/components/filters/date-range-filter.tsx
|
||||
msgid "Last 90 Days"
|
||||
msgstr "Laatste 90 dagen"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr "Laatst actief"
|
||||
@ -5000,9 +5056,9 @@ msgstr "Laatst geüpdatet om"
|
||||
msgid "Last used"
|
||||
msgstr "Laatst gebruikt"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
|
||||
msgid "Leaderboard"
|
||||
msgstr "Ranglijst"
|
||||
#: apps/remix/app/components/filters/date-range-filter.tsx
|
||||
msgid "Last Year"
|
||||
msgstr "Vorig jaar"
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-leave-dialog.tsx
|
||||
@ -5028,6 +5084,14 @@ msgstr "Links"
|
||||
msgid "Legality of Electronic Signatures"
|
||||
msgstr "Geldigheid van Elektronische Handtekeningen"
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
|
||||
msgid "Letter spacing"
|
||||
msgstr "Letterafstand"
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
|
||||
msgid "Letter Spacing"
|
||||
msgstr "Letterafstand"
|
||||
|
||||
#: apps/remix/app/components/general/app-command-menu.tsx
|
||||
msgid "Light Mode"
|
||||
msgstr "Lichte Modus"
|
||||
@ -5036,6 +5100,14 @@ msgstr "Lichte Modus"
|
||||
msgid "Like to have your own public profile with agreements?"
|
||||
msgstr "Wil je je eigen openbare profiel met overeenkomsten?"
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
|
||||
msgid "Line height"
|
||||
msgstr "Lijnhoogte"
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
|
||||
msgid "Line Height"
|
||||
msgstr "Lijnhoogte"
|
||||
|
||||
#: packages/email/templates/confirm-team-email.tsx
|
||||
msgid "Link expires in 1 hour."
|
||||
msgstr "Link verloopt in 1 uur."
|
||||
@ -5315,7 +5387,10 @@ msgstr "Lid sinds"
|
||||
#: 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 +5408,10 @@ msgstr "Bericht"
|
||||
msgid "Message <0>(Optional)</0>"
|
||||
msgstr "Bericht <0>(Optioneel)</0>"
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
|
||||
msgid "Middle"
|
||||
msgstr "Midden"
|
||||
|
||||
#: 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 +5487,8 @@ msgstr "N.v.t."
|
||||
#: 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 +5555,6 @@ msgstr "Nooit verlopen"
|
||||
msgid "New Password"
|
||||
msgstr "Nieuw wachtwoord"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "New Template"
|
||||
msgstr "Nieuwe sjabloon"
|
||||
@ -5739,13 +5818,11 @@ msgstr "Alleen beheerders hebben toegang tot en kunnen het document bekijken"
|
||||
msgid "Only managers and above can access and view the document"
|
||||
msgstr "Alleen managers en hoger kunnen toegang krijgen tot en het document bekijken"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Only one file can be uploaded at a time"
|
||||
msgstr "Er kan slechts één bestand tegelijk worden geüpload"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Only PDF files are allowed"
|
||||
msgstr "Alleen PDF-bestanden zijn toegestaan"
|
||||
|
||||
@ -5813,6 +5890,11 @@ msgstr "Instellingen organisatiegroep"
|
||||
msgid "Organisation has been updated successfully"
|
||||
msgstr "Organisatie succesvol bijgewerkt"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisation-insights._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
|
||||
msgid "Organisation Insights"
|
||||
msgstr "Organisatie-inzichten"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
|
||||
msgid "Organisation invitation"
|
||||
msgstr "Organisatie uitnodiging"
|
||||
@ -5909,6 +5991,7 @@ msgid "Organize your documents and templates"
|
||||
msgstr "Organiseer uw documenten en sjablonen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
|
||||
msgctxt "Original document (adjective)"
|
||||
msgid "Original"
|
||||
msgstr "Origineel"
|
||||
|
||||
@ -5949,6 +6032,10 @@ msgstr "Pagina {0} van {1}"
|
||||
msgid "Page {0} of {numPages}"
|
||||
msgstr "Pagina {0} van {numPages}"
|
||||
|
||||
#: apps/remix/app/components/tables/admin-organisations-table.tsx
|
||||
msgid "Paid"
|
||||
msgstr "Betaald"
|
||||
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-auth-dialog.tsx
|
||||
#: apps/remix/app/components/forms/signin.tsx
|
||||
msgid "Passkey"
|
||||
@ -6074,6 +6161,7 @@ msgid "per year"
|
||||
msgstr "per jaar"
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
msgctxt "Personal organisation (adjective)"
|
||||
msgid "Personal"
|
||||
msgstr "Persoonlijk"
|
||||
|
||||
@ -6273,7 +6361,6 @@ msgstr "Probeer een ander domein."
|
||||
msgid "Please try again and make sure you enter the correct email address."
|
||||
msgstr "Probeer opnieuw en zorg ervoor dat je het juiste e-mailadres invoert."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "Please try again later."
|
||||
msgstr "Probeer het later nog eens."
|
||||
@ -6439,6 +6526,7 @@ msgid "Read only"
|
||||
msgstr "Alleen lezen"
|
||||
|
||||
#: 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 "Alleen lezen"
|
||||
|
||||
@ -6874,6 +6962,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 +7025,6 @@ msgstr "Zoek op claim ID of naam"
|
||||
msgid "Search by document title"
|
||||
msgstr "Zoek op documenttitel"
|
||||
|
||||
#: 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 "Zoek op naam of e-mail"
|
||||
@ -6945,6 +7033,10 @@ msgstr "Zoek op naam of e-mail"
|
||||
msgid "Search by organisation ID, name, customer ID or owner email"
|
||||
msgstr "Zoek op organisatienummer, naam, klant-ID of e-mail van eigenaar"
|
||||
|
||||
#: apps/remix/app/components/tables/admin-organisation-overview-table.tsx
|
||||
msgid "Search by organisation name"
|
||||
msgstr "Zoek op organisatienaam"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-search.tsx
|
||||
msgid "Search documents..."
|
||||
msgstr "Documenten zoeken..."
|
||||
@ -7115,6 +7207,10 @@ msgstr "Selecteer de leden die in deze groep moeten worden opgenomen"
|
||||
msgid "Select triggers"
|
||||
msgstr "Selecteer triggers"
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
|
||||
msgid "Select vertical align"
|
||||
msgstr "Selecteer verticale uitlijning"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-update-dialog.tsx
|
||||
msgid "Select visibility"
|
||||
msgstr "Selecteer zichtbaarheid"
|
||||
@ -7485,12 +7581,16 @@ msgstr "Handtekeningen verzameld"
|
||||
|
||||
#: 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 "Ondertekend"
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-download-dialog.tsx
|
||||
msgctxt "Signed document (adjective)"
|
||||
msgid "Signed"
|
||||
msgstr "Ondertekend"
|
||||
|
||||
#: packages/lib/constants/recipient-roles.ts
|
||||
msgctxt "Recipient role actioned"
|
||||
msgid "Signed"
|
||||
@ -7556,11 +7656,6 @@ msgstr "Ondertekeningslinks zijn gegenereerd voor dit document."
|
||||
msgid "Signing order is enabled."
|
||||
msgstr "Ondertekenvolgorde is ingeschakeld."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/leaderboard.tsx
|
||||
#: apps/remix/app/components/tables/admin-leaderboard-table.tsx
|
||||
msgid "Signing Volume"
|
||||
msgstr "Onderteken volume"
|
||||
|
||||
#: apps/remix/app/components/forms/signup.tsx
|
||||
msgid "Signups are disabled."
|
||||
msgstr "Registraties zijn uitgeschakeld."
|
||||
@ -7594,7 +7689,6 @@ msgstr "Enkele ondertekenaars hebben geen handtekeningenveld toegewezen gekregen
|
||||
#: apps/remix/app/components/tables/organisation-member-invites-table.tsx
|
||||
#: apps/remix/app/components/general/billing-plans.tsx
|
||||
#: apps/remix/app/components/general/billing-plans.tsx
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-dropdown.tsx
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
@ -7712,6 +7806,7 @@ msgstr "Statistieken"
|
||||
|
||||
#: 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 +8000,7 @@ msgstr "Systeemthema"
|
||||
#: 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 +8086,7 @@ msgstr "Teamleden"
|
||||
msgid "Team members have been added."
|
||||
msgstr "Teamleden zijn toegevoegd."
|
||||
|
||||
#: 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 +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"
|
||||
@ -8056,6 +8156,11 @@ msgstr "Teams waaraan deze organisatiegroep momenteel is toegewezen"
|
||||
msgid "Template"
|
||||
msgstr "Sjabloon"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: packages/ui/primitives/document-upload-button.tsx
|
||||
msgid "Template (Legacy)"
|
||||
msgstr "Sjabloon (Legacy)"
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring_.completed.create.tsx
|
||||
msgid "Template Created"
|
||||
msgstr "Sjabloon Gemaakt"
|
||||
@ -8084,6 +8189,10 @@ msgstr "Sjabloon is verwijderd van je openbare profiel."
|
||||
msgid "Template has been updated."
|
||||
msgstr "Sjabloon is bijgewerkt."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
msgid "Template ID (Legacy)"
|
||||
msgstr "Sjabloon-ID (Legacy)"
|
||||
|
||||
#: apps/remix/app/components/general/legacy-field-warning-popover.tsx
|
||||
msgid "Template is using legacy field insertion"
|
||||
msgstr "Sjabloon maakt gebruik van verouderde veldinvoeging"
|
||||
@ -8108,8 +8217,8 @@ msgstr "Sjabloontitel"
|
||||
msgid "Template updated successfully"
|
||||
msgstr "Sjabloon succesvol bijgewerkt"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Template uploaded"
|
||||
msgstr "Sjabloon geüpload"
|
||||
|
||||
@ -8266,9 +8375,8 @@ msgstr "Het document dat u zoekt, kon niet worden gevonden."
|
||||
|
||||
#: 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 "Het document dat u zoekt, is mogelijk verwijderd, hernoemd of heeft nooit bestaan."
|
||||
msgid "The document you are looking for may have been removed, renamed or may have never existed."
|
||||
msgstr "Het document dat u zoekt, kan zijn verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
|
||||
#: packages/ui/components/document/document-send-email-message-helper.tsx
|
||||
msgid "The document's name"
|
||||
@ -8279,9 +8387,8 @@ msgid "The email address which will show up in the \"Reply To\" field in emails"
|
||||
msgstr "Het e-mailadres dat in het \"Antwoord aan\" veld in e-mails verschijnt"
|
||||
|
||||
#: 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 "Het e-maildomein dat u zoekt kan zijn verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
msgid "The email domain you are looking for may have been removed, renamed or may have never existed."
|
||||
msgstr "Het e-maildomein dat u zoekt, kan zijn verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
|
||||
#: apps/remix/app/components/forms/signin.tsx
|
||||
msgid "The email or password provided is incorrect"
|
||||
@ -8337,23 +8444,17 @@ msgid "The organisation email has been created successfully."
|
||||
msgstr "Het organisatie-e-mail is succesvol aangemaakt."
|
||||
|
||||
#: 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 "De organisatiegroep die u zoekt, is mogelijk verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
msgid "The organisation group you are looking for may have been removed, renamed or may have never existed."
|
||||
msgstr "De organisatiewerkgroep die u zoekt, kan zijn verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
|
||||
#: 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 "De organisatierol die op alle leden in deze groep zal worden toegepast."
|
||||
|
||||
#: 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 "De organisatie die u zoekt, is mogelijk verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
|
||||
#: 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 "De organisatie die u zoekt, is mogelijk verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
#: 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 "De organisatie die u zoekt, kan zijn verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
|
||||
#: 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,15 +8545,9 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
|
||||
msgstr "Het team e-mail <0>{teamEmail}</0> is verwijderd uit het volgende 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 "Het team dat u zoekt, is mogelijk verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
|
||||
#: 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 "Het team dat u zoekt, is mogelijk verwijderd of hernoemd, of heeft mogelijk nooit\n"
|
||||
" bestaan."
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never existed."
|
||||
msgstr "Het team dat u zoekt, kan zijn verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
msgid "The template has been moved successfully."
|
||||
@ -8467,9 +8562,8 @@ msgid "The template you are looking for could not be found."
|
||||
msgstr "De sjabloon die u zoekt, kon niet worden gevonden."
|
||||
|
||||
#: 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 "De sjabloon die u zoekt, is mogelijk verwijderd, hernoemd of heeft nooit bestaan."
|
||||
msgid "The template you are looking for may have been removed, renamed or may have never existed."
|
||||
msgstr "Het sjabloon dat u zoekt, kan zijn verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
|
||||
msgid "The test webhook has been successfully sent to your endpoint."
|
||||
@ -8509,9 +8603,8 @@ msgid "The URL for Documenso to send webhook events to."
|
||||
msgstr "De URL voor Documenso om webhook-gebeurtenissen naar toe te sturen."
|
||||
|
||||
#: 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 "De gebruiker die u zoekt, is mogelijk verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
msgid "The user you are looking for may have been removed, renamed or may have never existed."
|
||||
msgstr "De gebruiker die u zoekt, kan zijn verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
|
||||
#: apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx
|
||||
msgid "The user's two factor authentication has been reset successfully."
|
||||
@ -8530,9 +8623,8 @@ msgid "The webhook was successfully created."
|
||||
msgstr "De webhook is succesvol aangemaakt."
|
||||
|
||||
#: 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 "De webhook die u zoekt, kan zijn verwijderd, hernoemd of mogelijk nooit hebben bestaan."
|
||||
msgid "The webhook you are looking for may have been removed, renamed or may have never existed."
|
||||
msgstr "De webhook die u zoekt, kan zijn verwijderd, hernoemd of heeft mogelijk nooit bestaan."
|
||||
|
||||
#: 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."
|
||||
@ -8588,10 +8680,6 @@ msgstr "Deze actie is omkeerbaar, maar wees voorzichtig, omdat het account mogel
|
||||
msgid "This claim is locked and cannot be deleted."
|
||||
msgstr "Deze aanspraak is vergrendeld en kan niet worden verwijderd."
|
||||
|
||||
#: packages/email/template-components/template-access-auth-2fa.tsx
|
||||
msgid "This code will expire in {expiresInMinutes} minutes."
|
||||
msgstr "Deze code verloopt over {expiresInMinutes} minuten."
|
||||
|
||||
#: 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 "Dit document kan niet worden hersteld, als u de reden voor toekomstige documenten wilt betwisten, neem dan contact op met de ondersteuning."
|
||||
@ -8837,6 +8925,7 @@ msgstr "Tijdzone"
|
||||
|
||||
#: 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
|
||||
@ -8849,6 +8938,19 @@ msgstr "Titel"
|
||||
msgid "Title cannot be empty"
|
||||
msgstr "Titel mag niet leeg zijn"
|
||||
|
||||
#. 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 "Om {0} deze {1} te kunnen uitvoeren, moet u ingelogd zijn als <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 "Om {0} deze {1} te kunnen uitvoeren, moet u ingelogd zijn."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
|
||||
msgid "To accept this invitation you must create an account."
|
||||
msgstr "Om deze uitnodiging te accepteren moet u een account aanmaken."
|
||||
@ -8889,6 +8991,10 @@ msgstr "Om toegang te krijgen tot uw account, bevestig uw e-mailadres door op de
|
||||
msgid "To mark this document as viewed, you need to be logged in as <0>{0}</0>"
|
||||
msgstr "Om dit document als bekeken te markeren, moet u zijn ingelogd als <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 "Om dit document als bekeken te markeren, moet u ingelogd zijn."
|
||||
|
||||
#. 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
|
||||
@ -8944,6 +9050,10 @@ msgstr "Token is verlopen. Probeer het opnieuw."
|
||||
msgid "Token name"
|
||||
msgstr "Tokennaam"
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
|
||||
msgid "Top"
|
||||
msgstr "Bovenkant"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Total Documents"
|
||||
msgstr "Totaal aantal documenten"
|
||||
@ -9123,8 +9233,7 @@ msgstr "Onvoltooid"
|
||||
msgid "Unknown"
|
||||
msgstr "Onbekend"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Unknown error"
|
||||
msgstr "Onbekende fout"
|
||||
|
||||
@ -9282,7 +9391,7 @@ msgstr "Wachtwoord bijwerken..."
|
||||
msgid "Updating Your Information"
|
||||
msgstr "Uw Informatie Bijwerken"
|
||||
|
||||
#: packages/ui/primitives/document-upload.tsx
|
||||
#: packages/ui/primitives/document-upload-button.tsx
|
||||
#: packages/ui/primitives/document-dropzone.tsx
|
||||
msgid "Upgrade"
|
||||
msgstr "Upgrade"
|
||||
@ -9292,7 +9401,7 @@ msgstr "Upgrade"
|
||||
msgid "Upgrade <0>{0}</0> to {planName}"
|
||||
msgstr "Gebruik uw toegangssleutel voor authenticatie"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Upgrade your plan to upload more documents"
|
||||
msgstr "Upgrade je plan om meer documenten te uploaden"
|
||||
|
||||
@ -9334,9 +9443,9 @@ msgstr "Aangepast document uploaden"
|
||||
msgid "Upload disabled"
|
||||
msgstr "Uploaden uitgeschakeld"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-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 "Document uploaden"
|
||||
|
||||
@ -9344,14 +9453,9 @@ msgstr "Document uploaden"
|
||||
msgid "Upload documents and add recipients"
|
||||
msgstr "Documenten uploaden en ontvangers toevoegen"
|
||||
|
||||
#: packages/ui/primitives/document-upload.tsx
|
||||
msgid "Upload Envelope"
|
||||
msgstr "Upload envelop"
|
||||
|
||||
#: 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
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Upload failed"
|
||||
msgstr "Uploaden mislukt"
|
||||
|
||||
@ -9359,11 +9463,11 @@ msgstr "Uploaden mislukt"
|
||||
msgid "Upload Signature"
|
||||
msgstr "Handtekening uploaden"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: packages/ui/primitives/document-upload-button.tsx
|
||||
msgid "Upload Template"
|
||||
msgstr "Upload Sjabloon"
|
||||
|
||||
#: packages/ui/primitives/document-upload.tsx
|
||||
#: packages/ui/primitives/document-dropzone.tsx
|
||||
msgid "Upload Template Document"
|
||||
msgstr "Sjabloondocument uploaden"
|
||||
@ -9390,17 +9494,10 @@ msgid "Uploaded file not an allowed file type"
|
||||
msgstr "Geüpload bestand is geen toegestaan bestandsformaat"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Uploading"
|
||||
msgstr "Uploaden"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-drop-zone-wrapper.tsx
|
||||
msgid "Uploading document..."
|
||||
msgstr "Document uploaden..."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
msgid "Uploading template..."
|
||||
msgstr "Sjabloon uploaden..."
|
||||
|
||||
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
|
||||
msgid "URL"
|
||||
msgstr "URL"
|
||||
@ -9473,6 +9570,7 @@ msgid "User with this email already exists. Please use a different email address
|
||||
msgstr "Er bestaat al een gebruiker met dit e-mailadres. Gebruik een ander e-mailadres."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/_layout.tsx
|
||||
#: apps/remix/app/components/tables/organisation-insights-table.tsx
|
||||
msgid "Users"
|
||||
msgstr "Gebruikers"
|
||||
|
||||
@ -9495,6 +9593,10 @@ msgstr "Validatie mislukt"
|
||||
msgid "Value"
|
||||
msgstr "Waarde"
|
||||
|
||||
#: packages/lib/types/field-meta.ts
|
||||
msgid "Value must be a number"
|
||||
msgstr "Waarde moet een getal zijn"
|
||||
|
||||
#: packages/email/template-components/template-access-auth-2fa.tsx
|
||||
msgid "Verification Code Required"
|
||||
msgstr "Verificatiecode vereist"
|
||||
@ -9527,8 +9629,8 @@ msgstr "Verifieer uw e-mailadres"
|
||||
msgid "Verify your email address to unlock all features."
|
||||
msgstr "Verifieer uw e-mailadres om alle functies te ontgrendelen."
|
||||
|
||||
#: 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/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Verify your email to upload documents."
|
||||
msgstr "Verifieer uw e-mail om documenten te uploaden."
|
||||
|
||||
@ -9542,6 +9644,10 @@ msgstr "Verifieer uw e-mailadres van het team"
|
||||
msgid "Vertical"
|
||||
msgstr "Verticaal"
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-generic-field-forms.tsx
|
||||
msgid "Vertical Align"
|
||||
msgstr "Verticale uitlijning"
|
||||
|
||||
#: 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
|
||||
@ -10394,10 +10500,6 @@ msgstr "U kunt geen groep wijzigen die een hogere rol heeft dan u."
|
||||
msgid "You cannot delete this item because the document has been sent to recipients"
|
||||
msgstr "U kunt dit item niet verwijderen omdat het document naar ontvangers is verzonden"
|
||||
|
||||
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
|
||||
msgid "You cannot have more than {MAXIMUM_PASSKEYS} passkeys."
|
||||
msgstr "Je kunt niet meer dan {MAXIMUM_PASSKEYS} toegangssleutels hebben."
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
|
||||
msgid "You cannot modify a group which has a higher role than you."
|
||||
msgstr "U kunt geen organisatielid wijzigen die een hogere rol heeft dan u."
|
||||
@ -10414,20 +10516,21 @@ msgstr "Je kunt een teamlid met een hogere rol dan jezelf niet wijzigen."
|
||||
msgid "You cannot remove members from this team if the inherit member feature is enabled."
|
||||
msgstr "U heeft momenteel een inactief <0>{currentProductName}</0> abonnement."
|
||||
|
||||
#: 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 "Op dit moment kunt u geen documenten uploaden."
|
||||
|
||||
#: 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "You cannot upload encrypted PDFs"
|
||||
msgstr "Je kunt geen versleutelde PDF's uploaden"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-upload-page.tsx
|
||||
#: 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/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "You cannot upload more than {maximumEnvelopeItemCount} items per envelope."
|
||||
msgstr "U kunt niet meer dan {maximumEnvelopeItemCount} items per envelop uploaden."
|
||||
|
||||
@ -10503,9 +10606,9 @@ msgstr "Je hebt nog geen sjablonen gemaakt. Om een sjabloon te maken, upload er
|
||||
msgid "You have not yet created or received any documents. To create a document please upload one."
|
||||
msgstr "Je hebt nog geen documenten gemaakt of ontvangen. Om een document te maken, upload er een."
|
||||
|
||||
#: 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "You have reached the limit of the number of files per envelope"
|
||||
msgstr "U heeft de limiet van het aantal bestanden per envelop bereikt"
|
||||
|
||||
@ -10518,14 +10621,14 @@ msgstr "Je hebt de maximale limiet van {0} directe sjablonen bereikt. <0>Upgrade
|
||||
msgid "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."
|
||||
msgstr "U heeft het maximale aantal teams voor uw plan bereikt. Neem contact op met de verkoopafdeling via <0>{SUPPORT_EMAIL}</0> als u uw plan wilt aanpassen."
|
||||
|
||||
#: 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "You have reached your document limit for this month. Please upgrade your plan."
|
||||
msgstr "U heeft uw documentlimiet voor deze maand bereikt. Upgrade uw abonnement."
|
||||
|
||||
#: 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/envelope/envelope-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 "Je hebt jouw documentlimiet bereikt."
|
||||
@ -10715,7 +10818,7 @@ msgstr "Uw huidige abonnement is verlopen."
|
||||
msgid "Your direct signing templates"
|
||||
msgstr "Jouw directe ondertekeningssjablonen"
|
||||
|
||||
#: 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 "Het uploaden van je document is mislukt."
|
||||
@ -10744,9 +10847,9 @@ msgstr "Je document is succesvol verzonden."
|
||||
msgid "Your document has been successfully duplicated."
|
||||
msgstr "Jouw document is succesvol gedupliceerd."
|
||||
|
||||
#: 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-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Your document has been uploaded successfully."
|
||||
msgstr "Je document is succesvol geüpload."
|
||||
|
||||
@ -10897,14 +11000,11 @@ msgstr "Jouw sjabloon is succesvol gedupliceerd."
|
||||
msgid "Your template has been successfully deleted."
|
||||
msgstr "Jouw sjabloon is succesvol verwijderd."
|
||||
|
||||
#: apps/remix/app/components/general/document/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
msgid "Your template has been uploaded successfully."
|
||||
msgstr "Uw sjabloon is succesvol geüpload."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-drop-zone-wrapper.tsx
|
||||
msgid "Your template has been uploaded successfully. You will be redirected to the template page."
|
||||
msgstr "Uw sjabloon is succesvol geüpload. U wordt doorgestuurd naar de sjabloonpagina."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
msgid "Your template will be duplicated."
|
||||
msgstr "Jouw sjabloon zal worden gedupliceerd."
|
||||
|
||||
@ -1,9 +1,46 @@
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { FieldType } from '@prisma/client';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { DEFAULT_SIGNATURE_TEXT_FONT_SIZE } from '../constants/pdf';
|
||||
|
||||
export const DEFAULT_FIELD_FONT_SIZE = 14;
|
||||
export const FIELD_DEFAULT_GENERIC_VERTICAL_ALIGN = 'middle';
|
||||
export const FIELD_DEFAULT_GENERIC_ALIGN = 'left';
|
||||
export const FIELD_DEFAULT_LINE_HEIGHT = 1;
|
||||
export const FIELD_DEFAULT_LETTER_SPACING = 0;
|
||||
|
||||
export const FIELD_MIN_LINE_HEIGHT = 1;
|
||||
export const FIELD_MAX_LINE_HEIGHT = 10;
|
||||
|
||||
export const FIELD_MIN_LETTER_SPACING = 0;
|
||||
export const FIELD_MAX_LETTER_SPACING = 100;
|
||||
|
||||
export const DEFAULT_FIELD_FONT_SIZE = 12;
|
||||
|
||||
/**
|
||||
* Grouped field types that use the same generic text rendering function.
|
||||
*/
|
||||
export type GenericTextFieldTypeMetas =
|
||||
| TInitialsFieldMeta
|
||||
| TNameFieldMeta
|
||||
| TEmailFieldMeta
|
||||
| TDateFieldMeta
|
||||
| TTextFieldMeta
|
||||
| TNumberFieldMeta;
|
||||
|
||||
const ZFieldMetaLineHeight = z.coerce
|
||||
.number()
|
||||
.min(FIELD_MIN_LINE_HEIGHT)
|
||||
.max(FIELD_MAX_LINE_HEIGHT)
|
||||
.describe('The line height of the text');
|
||||
const ZFieldMetaLetterSpacing = z.coerce
|
||||
.number()
|
||||
.min(FIELD_MIN_LETTER_SPACING)
|
||||
.max(FIELD_MAX_LETTER_SPACING)
|
||||
.describe('The spacing between each character');
|
||||
const ZFieldMetaVerticalAlign = z
|
||||
.enum(['top', 'middle', 'bottom'])
|
||||
.describe('The vertical alignment of the text');
|
||||
|
||||
export const ZBaseFieldMeta = z.object({
|
||||
label: z.string().optional(),
|
||||
@ -50,8 +87,14 @@ export type TDateFieldMeta = z.infer<typeof ZDateFieldMeta>;
|
||||
export const ZTextFieldMeta = ZBaseFieldMeta.extend({
|
||||
type: z.literal('text'),
|
||||
text: z.string().optional(),
|
||||
characterLimit: z.number().optional(),
|
||||
characterLimit: z.coerce
|
||||
.number({ invalid_type_error: msg`Value must be a number`.id })
|
||||
.min(0)
|
||||
.optional(),
|
||||
textAlign: ZFieldTextAlignSchema.optional(),
|
||||
lineHeight: ZFieldMetaLineHeight.nullish(),
|
||||
letterSpacing: ZFieldMetaLetterSpacing.nullish(),
|
||||
verticalAlign: ZFieldMetaVerticalAlign.nullish(),
|
||||
});
|
||||
|
||||
export type TTextFieldMeta = z.infer<typeof ZTextFieldMeta>;
|
||||
@ -63,6 +106,9 @@ export const ZNumberFieldMeta = ZBaseFieldMeta.extend({
|
||||
minValue: z.coerce.number().nullish(),
|
||||
maxValue: z.coerce.number().nullish(),
|
||||
textAlign: ZFieldTextAlignSchema.optional(),
|
||||
lineHeight: ZFieldMetaLineHeight.nullish(),
|
||||
letterSpacing: ZFieldMetaLetterSpacing.nullish(),
|
||||
verticalAlign: ZFieldMetaVerticalAlign.nullish(),
|
||||
});
|
||||
|
||||
export type TNumberFieldMeta = z.infer<typeof ZNumberFieldMeta>;
|
||||
@ -326,3 +372,52 @@ export const FIELD_META_DEFAULT_VALUES: Record<FieldType, TFieldMetaSchema> = {
|
||||
[FieldType.CHECKBOX]: FIELD_CHECKBOX_META_DEFAULT_VALUES,
|
||||
[FieldType.DROPDOWN]: FIELD_DROPDOWN_META_DEFAULT_VALUES,
|
||||
} as const;
|
||||
|
||||
export const ZEnvelopeFieldAndMetaSchema = z.discriminatedUnion('type', [
|
||||
z.object({
|
||||
type: z.literal(FieldType.SIGNATURE),
|
||||
fieldMeta: ZSignatureFieldMeta.optional().default(FIELD_SIGNATURE_META_DEFAULT_VALUES),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.FREE_SIGNATURE),
|
||||
fieldMeta: z.undefined(),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.INITIALS),
|
||||
fieldMeta: ZInitialsFieldMeta.optional().default(FIELD_INITIALS_META_DEFAULT_VALUES),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.NAME),
|
||||
fieldMeta: ZNameFieldMeta.optional().default(FIELD_NAME_META_DEFAULT_VALUES),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.EMAIL),
|
||||
fieldMeta: ZEmailFieldMeta.optional().default(FIELD_EMAIL_META_DEFAULT_VALUES),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.DATE),
|
||||
fieldMeta: ZDateFieldMeta.optional().default(FIELD_DATE_META_DEFAULT_VALUES),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.TEXT),
|
||||
fieldMeta: ZTextFieldMeta.optional().default(FIELD_TEXT_META_DEFAULT_VALUES),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.NUMBER),
|
||||
fieldMeta: ZNumberFieldMeta.optional().default(FIELD_NUMBER_META_DEFAULT_VALUES),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.RADIO),
|
||||
fieldMeta: ZRadioFieldMeta.optional().default(FIELD_RADIO_META_DEFAULT_VALUES),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.CHECKBOX),
|
||||
fieldMeta: ZCheckboxFieldMeta.optional().default(FIELD_CHECKBOX_META_DEFAULT_VALUES),
|
||||
}),
|
||||
z.object({
|
||||
type: z.literal(FieldType.DROPDOWN),
|
||||
fieldMeta: ZDropdownFieldMeta.optional().default(FIELD_DROPDOWN_META_DEFAULT_VALUES),
|
||||
}),
|
||||
]);
|
||||
|
||||
type TEnvelopeFieldAndMeta = z.infer<typeof ZEnvelopeFieldAndMetaSchema>;
|
||||
|
||||