Merge branch 'main' into fix/field-rework

This commit is contained in:
David Nguyen
2025-03-24 19:51:22 +11:00
146 changed files with 5120 additions and 1706 deletions

View File

@@ -1,4 +1,9 @@
import { useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { import {
@@ -9,64 +14,208 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@documenso/ui/primitives/dialog'; } from '@documenso/ui/primitives/dialog';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { DocumentSigningDisclosure } from '../general/document-signing/document-signing-disclosure'; import { DocumentSigningDisclosure } from '../general/document-signing/document-signing-disclosure';
export type NextSigner = {
name: string;
email: string;
};
type ConfirmationDialogProps = { type ConfirmationDialogProps = {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
onConfirm: () => void; onConfirm: (nextSigner?: NextSigner) => void;
hasUninsertedFields: boolean; hasUninsertedFields: boolean;
isSubmitting: boolean; isSubmitting: boolean;
allowDictateNextSigner?: boolean;
defaultNextSigner?: NextSigner;
}; };
const ZNextSignerFormSchema = z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email address'),
});
type TNextSignerFormSchema = z.infer<typeof ZNextSignerFormSchema>;
export function AssistantConfirmationDialog({ export function AssistantConfirmationDialog({
isOpen, isOpen,
onClose, onClose,
onConfirm, onConfirm,
hasUninsertedFields, hasUninsertedFields,
isSubmitting, isSubmitting,
allowDictateNextSigner = false,
defaultNextSigner,
}: ConfirmationDialogProps) { }: ConfirmationDialogProps) {
const [isEditingNextSigner, setIsEditingNextSigner] = useState(false);
const form = useForm<TNextSignerFormSchema>({
resolver: zodResolver(ZNextSignerFormSchema),
defaultValues: {
name: defaultNextSigner?.name ?? '',
email: defaultNextSigner?.email ?? '',
},
});
const onOpenChange = () => { const onOpenChange = () => {
if (isSubmitting) { if (form.formState.isSubmitting) {
return; return;
} }
form.reset({
name: defaultNextSigner?.name ?? '',
email: defaultNextSigner?.email ?? '',
});
setIsEditingNextSigner(false);
onClose(); onClose();
}; };
const onFormSubmit = async (data: TNextSignerFormSchema) => {
if (allowDictateNextSigner && data.name && data.email) {
await onConfirm({
name: data.name,
email: data.email,
});
} else {
await onConfirm();
}
};
const isNextSignerValid = !allowDictateNextSigner || (form.watch('name') && form.watch('email'));
return ( return (
<Dialog open={isOpen} onOpenChange={onOpenChange}> <Dialog open={isOpen} onOpenChange={onOpenChange}>
<DialogContent> <DialogContent>
<DialogHeader> <Form {...form}>
<DialogTitle> <form onSubmit={form.handleSubmit(onFormSubmit)}>
<Trans>Complete Document</Trans> <fieldset
</DialogTitle> disabled={form.formState.isSubmitting || isSubmitting}
<DialogDescription> className="border-none p-0"
<Trans> >
Are you sure you want to complete the document? This action cannot be undone. Please <DialogHeader>
ensure that you have completed prefilling all relevant fields before proceeding. <DialogTitle>
</Trans> <Trans>Complete Document</Trans>
</DialogDescription> </DialogTitle>
</DialogHeader> <DialogDescription>
<Trans>
Are you sure you want to complete the document? This action cannot be undone.
Please ensure that you have completed prefilling all relevant fields before
proceeding.
</Trans>
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-4"> <div className="mt-4 flex flex-col gap-4">
<DocumentSigningDisclosure /> {allowDictateNextSigner && (
</div> <div className="space-y-4">
{!isEditingNextSigner && (
<div>
<p className="text-muted-foreground text-sm">
The next recipient to sign this document will be{' '}
<span className="font-semibold">{form.watch('name')}</span> (
<span className="font-semibold">{form.watch('email')}</span>).
</p>
<DialogFooter className="mt-4"> <Button
<Button variant="secondary" onClick={onClose} disabled={isSubmitting}> type="button"
Cancel className="mt-2"
</Button> variant="outline"
<Button size="sm"
variant={hasUninsertedFields ? 'destructive' : 'default'} onClick={() => setIsEditingNextSigner((prev) => !prev)}
onClick={onConfirm} >
disabled={isSubmitting} <Trans>Update Recipient</Trans>
loading={isSubmitting} </Button>
> </div>
{isSubmitting ? 'Submitting...' : hasUninsertedFields ? 'Proceed' : 'Continue'} )}
</Button>
</DialogFooter> {isEditingNextSigner && (
<div className="flex flex-col gap-4 md:flex-row">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>
<Trans>Name</Trans>
</FormLabel>
<FormControl>
<Input
{...field}
className="mt-2"
placeholder="Enter the next signer's name"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>
<Trans>Email</Trans>
</FormLabel>
<FormControl>
<Input
{...field}
type="email"
className="mt-2"
placeholder="Enter the next signer's email"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
</div>
)}
<DocumentSigningDisclosure className="mt-4" />
</div>
<DialogFooter className="mt-4">
<Button
type="button"
variant="secondary"
onClick={onClose}
disabled={form.formState.isSubmitting}
>
<Trans>Cancel</Trans>
</Button>
<Button
type="submit"
variant={hasUninsertedFields ? 'destructive' : 'default'}
disabled={form.formState.isSubmitting || !isNextSignerValid}
loading={form.formState.isSubmitting}
>
{form.formState.isSubmitting ? (
<Trans>Submitting...</Trans>
) : hasUninsertedFields ? (
<Trans>Proceed</Trans>
) : (
<Trans>Continue</Trans>
)}
</Button>
</DialogFooter>
</fieldset>
</form>
</Form>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
); );

View File

@@ -4,7 +4,7 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { DocumentStatus } from '@prisma/client'; import { DocumentStatus } from '@prisma/client';
import { match } from 'ts-pattern'; import { P, match } from 'ts-pattern';
import { useLimits } from '@documenso/ee/server-only/limits/provider/client'; import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
import { trpc as trpcReact } from '@documenso/trpc/react'; import { trpc as trpcReact } from '@documenso/trpc/react';
@@ -146,7 +146,7 @@ export const DocumentDeleteDialog = ({
</ul> </ul>
</AlertDescription> </AlertDescription>
)) ))
.with(DocumentStatus.COMPLETED, () => ( .with(P.union(DocumentStatus.COMPLETED, DocumentStatus.REJECTED), () => (
<AlertDescription> <AlertDescription>
<p> <p>
<Trans>By deleting this document, the following will occur:</Trans> <Trans>By deleting this document, the following will occur:</Trans>

View File

@@ -3,8 +3,8 @@ import { useEffect, useLayoutEffect, useState } from 'react';
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { type DocumentData, type Field, FieldType } from '@prisma/client';
import type { DocumentMeta, Recipient, Signature, TemplateMeta } from '@prisma/client'; import type { DocumentMeta, Recipient, Signature, TemplateMeta } from '@prisma/client';
import { type DocumentData, type Field, FieldType } from '@prisma/client';
import { LucideChevronDown, LucideChevronUp } from 'lucide-react'; import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { useSearchParams } from 'react-router'; import { useSearchParams } from 'react-router';
@@ -25,12 +25,11 @@ import type {
} from '@documenso/trpc/server/field-router/schema'; } from '@documenso/trpc/server/field-router/schema';
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { ElementVisible } from '@documenso/ui/primitives/element-visible'; import { ElementVisible } from '@documenso/ui/primitives/element-visible';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer'; import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
import { BrandingLogo } from '~/components/general/branding-logo'; import { BrandingLogo } from '~/components/general/branding-logo';
@@ -69,16 +68,8 @@ export const EmbedDirectTemplateClientPage = ({
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { const { fullName, email, signature, setFullName, setEmail, setSignature } =
fullName, useRequiredDocumentSigningContext();
email,
signature,
signatureValid,
setFullName,
setEmail,
setSignature,
setSignatureValid,
} = useRequiredDocumentSigningContext();
const [hasFinishedInit, setHasFinishedInit] = useState(false); const [hasFinishedInit, setHasFinishedInit] = useState(false);
const [hasDocumentLoaded, setHasDocumentLoaded] = useState(false); const [hasDocumentLoaded, setHasDocumentLoaded] = useState(false);
@@ -194,10 +185,6 @@ export const EmbedDirectTemplateClientPage = ({
const onCompleteClick = async () => { const onCompleteClick = async () => {
try { try {
if (hasSignatureField && !signatureValid) {
return;
}
const valid = validateFieldsInserted(pendingFields); const valid = validateFieldsInserted(pendingFields);
if (!valid) { if (!valid) {
@@ -419,34 +406,16 @@ export const EmbedDirectTemplateClientPage = ({
<Trans>Signature</Trans> <Trans>Signature</Trans>
</Label> </Label>
<Card className="mt-2" gradient degrees={-120}> <SignaturePadDialog
<CardContent className="p-0"> className="mt-2"
<SignaturePad disabled={isThrottled || isSubmitting}
className="h-44 w-full" disableAnimation
disabled={isThrottled || isSubmitting} value={signature ?? ''}
defaultValue={signature ?? undefined} onChange={(v) => setSignature(v ?? '')}
onChange={(value) => { typedSignatureEnabled={metadata?.typedSignatureEnabled}
setSignature(value); uploadSignatureEnabled={metadata?.uploadSignatureEnabled}
}} drawSignatureEnabled={metadata?.drawSignatureEnabled}
onValidityChange={(isValid) => { />
setSignatureValid(isValid);
}}
allowTypedSignature={Boolean(
metadata &&
'typedSignatureEnabled' in metadata &&
metadata.typedSignatureEnabled,
)}
/>
</CardContent>
</Card>
{hasSignatureField && !signatureValid && (
<div className="text-destructive mt-2 text-sm">
<Trans>
Signature is too small. Please provide a more complete signature.
</Trans>
</div>
)}
</div> </div>
)} )}
</div> </div>

View File

@@ -54,6 +54,8 @@ export const EmbedDocumentFields = ({
onSignField={onSignField} onSignField={onSignField}
onUnsignField={onUnsignField} onUnsignField={onUnsignField}
typedSignatureEnabled={metadata?.typedSignatureEnabled} typedSignatureEnabled={metadata?.typedSignatureEnabled}
uploadSignatureEnabled={metadata?.uploadSignatureEnabled}
drawSignatureEnabled={metadata?.drawSignatureEnabled}
/> />
)) ))
.with(FieldType.INITIALS, () => ( .with(FieldType.INITIALS, () => (

View File

@@ -21,13 +21,12 @@ import type { RecipientWithFields } from '@documenso/prisma/types/recipient-with
import { trpc } from '@documenso/trpc/react'; import { trpc } from '@documenso/trpc/react';
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { ElementVisible } from '@documenso/ui/primitives/element-visible'; import { ElementVisible } from '@documenso/ui/primitives/element-visible';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer'; import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer';
import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group'; import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
import { BrandingLogo } from '~/components/general/branding-logo'; import { BrandingLogo } from '~/components/general/branding-logo';
@@ -70,15 +69,8 @@ export const EmbedSignDocumentClientPage = ({
const { _ } = useLingui(); const { _ } = useLingui();
const { toast } = useToast(); const { toast } = useToast();
const { const { fullName, email, signature, setFullName, setSignature } =
fullName, useRequiredDocumentSigningContext();
email,
signature,
signatureValid,
setFullName,
setSignature,
setSignatureValid,
} = useRequiredDocumentSigningContext();
const [hasFinishedInit, setHasFinishedInit] = useState(false); const [hasFinishedInit, setHasFinishedInit] = useState(false);
const [hasDocumentLoaded, setHasDocumentLoaded] = useState(false); const [hasDocumentLoaded, setHasDocumentLoaded] = useState(false);
@@ -129,10 +121,6 @@ export const EmbedSignDocumentClientPage = ({
const onCompleteClick = async () => { const onCompleteClick = async () => {
try { try {
if (hasSignatureField && !signatureValid) {
return;
}
const valid = validateFieldsInserted(fieldsRequiringValidation); const valid = validateFieldsInserted(fieldsRequiringValidation);
if (!valid) { if (!valid) {
@@ -432,34 +420,16 @@ export const EmbedSignDocumentClientPage = ({
<Trans>Signature</Trans> <Trans>Signature</Trans>
</Label> </Label>
<Card className="mt-2" gradient degrees={-120}> <SignaturePadDialog
<CardContent className="p-0"> className="mt-2"
<SignaturePad disabled={isThrottled || isSubmitting}
className="h-44 w-full" disableAnimation
disabled={isThrottled || isSubmitting} value={signature ?? ''}
defaultValue={signature ?? undefined} onChange={(v) => setSignature(v ?? '')}
onChange={(value) => { typedSignatureEnabled={metadata?.typedSignatureEnabled}
setSignature(value); uploadSignatureEnabled={metadata?.uploadSignatureEnabled}
}} drawSignatureEnabled={metadata?.drawSignatureEnabled}
onValidityChange={(isValid) => { />
setSignatureValid(isValid);
}}
allowTypedSignature={Boolean(
metadata &&
'typedSignatureEnabled' in metadata &&
metadata.typedSignatureEnabled,
)}
/>
</CardContent>
</Card>
{hasSignatureField && !signatureValid && (
<div className="text-destructive mt-2 text-sm">
<Trans>
Signature is too small. Please provide a more complete signature.
</Trans>
</div>
)}
</div> </div>
)} )}
</> </>
@@ -477,9 +447,7 @@ export const EmbedSignDocumentClientPage = ({
) : ( ) : (
<Button <Button
className={allowDocumentRejection ? 'col-start-2' : 'col-span-2'} className={allowDocumentRejection ? 'col-start-2' : 'col-span-2'}
disabled={ disabled={isThrottled}
isThrottled || (!isAssistantMode && hasSignatureField && !signatureValid)
}
loading={isSubmitting} loading={isSubmitting}
onClick={() => throttledOnCompleteClick()} onClick={() => throttledOnCompleteClick()}
> >

View File

@@ -19,12 +19,15 @@ import {
} from '@documenso/ui/primitives/form/form'; } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
export const ZProfileFormSchema = z.object({ export const ZProfileFormSchema = z.object({
name: z.string().trim().min(1, { message: 'Please enter a valid name.' }), name: z
signature: z.string().min(1, 'Signature Pad cannot be empty'), .string()
.trim()
.min(1, { message: msg`Please enter a valid name.`.id }),
signature: z.string().min(1, { message: msg`Signature Pad cannot be empty.`.id }),
}); });
export const ZTwoFactorAuthTokenSchema = z.object({ export const ZTwoFactorAuthTokenSchema = z.object({
@@ -109,22 +112,20 @@ export const ProfileForm = ({ className }: ProfileFormProps) => {
</Label> </Label>
<Input id="email" type="email" className="bg-muted mt-2" value={user.email} disabled /> <Input id="email" type="email" className="bg-muted mt-2" value={user.email} disabled />
</div> </div>
<FormField <FormField
control={form.control} control={form.control}
name="signature" name="signature"
render={({ field: { onChange } }) => ( render={({ field: { onChange, value } }) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel>
<Trans>Signature</Trans> <Trans>Signature</Trans>
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<SignaturePad <SignaturePadDialog
className="h-44 w-full"
disabled={isSubmitting} disabled={isSubmitting}
containerClassName={cn('rounded-lg border bg-background')} value={value}
defaultValue={user.signature ?? undefined}
onChange={(v) => onChange(v ?? '')} onChange={(v) => onChange(v ?? '')}
allowTypedSignature={true}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -134,7 +135,7 @@ export const ProfileForm = ({ className }: ProfileFormProps) => {
</fieldset> </fieldset>
<Button type="submit" loading={isSubmitting} className="self-end"> <Button type="submit" loading={isSubmitting} className="self-end">
{isSubmitting ? <Trans>Updating profile...</Trans> : <Trans>Update profile</Trans>} <Trans>Update profile</Trans>
</Button> </Button>
</form> </form>
</Form> </Form>

View File

@@ -30,7 +30,7 @@ import {
} from '@documenso/ui/primitives/form/form'; } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { PasswordInput } from '@documenso/ui/primitives/password-input'; import { PasswordInput } from '@documenso/ui/primitives/password-input';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
import { UserProfileSkeleton } from '~/components/general/user-profile-skeleton'; import { UserProfileSkeleton } from '~/components/general/user-profile-skeleton';
@@ -353,16 +353,15 @@ export const SignUpForm = ({
<FormField <FormField
control={form.control} control={form.control}
name="signature" name="signature"
render={({ field: { onChange } }) => ( render={({ field: { onChange, value } }) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel>
<Trans>Sign Here</Trans> <Trans>Sign Here</Trans>
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<SignaturePad <SignaturePadDialog
className="h-36 w-full"
disabled={isSubmitting} disabled={isSubmitting}
containerClassName="mt-2 rounded-lg border bg-background" value={value}
onChange={(v) => onChange(v ?? '')} onChange={(v) => onChange(v ?? '')}
/> />
</FormControl> </FormControl>
@@ -531,6 +530,27 @@ export const SignUpForm = ({
</div> </div>
</form> </form>
</Form> </Form>
<p className="text-muted-foreground mt-6 text-xs">
<Trans>
By proceeding, you agree to our{' '}
<Link
to="https://documen.so/terms"
target="_blank"
className="text-documenso-700 duration-200 hover:opacity-70"
>
Terms of Service
</Link>{' '}
and{' '}
<Link
to="https://documen.so/privacy"
target="_blank"
className="text-documenso-700 duration-200 hover:opacity-70"
>
Privacy Policy
</Link>
.
</Trans>
</p>
</div> </div>
</div> </div>
); );

View File

@@ -308,7 +308,7 @@ export function TeamBrandingPreferencesForm({ team, settings }: TeamBrandingPref
<div className="flex flex-row justify-end space-x-4"> <div className="flex flex-row justify-end space-x-4">
<Button type="submit" loading={form.formState.isSubmitting}> <Button type="submit" loading={form.formState.isSubmitting}>
<Trans>Save</Trans> <Trans>Update</Trans>
</Button> </Button>
</div> </div>
</fieldset> </fieldset>

View File

@@ -8,12 +8,15 @@ import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { useSession } from '@documenso/lib/client-only/providers/session'; import { useSession } from '@documenso/lib/client-only/providers/session';
import { DOCUMENT_SIGNATURE_TYPES, DocumentSignatureType } from '@documenso/lib/constants/document';
import { import {
SUPPORTED_LANGUAGES, SUPPORTED_LANGUAGES,
SUPPORTED_LANGUAGE_CODES, SUPPORTED_LANGUAGE_CODES,
isValidLanguageCode, isValidLanguageCode,
} from '@documenso/lib/constants/i18n'; } from '@documenso/lib/constants/i18n';
import { extractTeamSignatureSettings } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react'; import { trpc } from '@documenso/trpc/react';
import { DocumentSignatureSettingsTooltip } from '@documenso/ui/components/document/document-signature-settings-tooltip';
import { Alert } from '@documenso/ui/primitives/alert'; import { Alert } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { import {
@@ -23,7 +26,9 @@ import {
FormField, FormField,
FormItem, FormItem,
FormLabel, FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form'; } from '@documenso/ui/primitives/form/form';
import { MultiSelectCombobox } from '@documenso/ui/primitives/multi-select-combobox';
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -38,8 +43,10 @@ const ZTeamDocumentPreferencesFormSchema = z.object({
documentVisibility: z.nativeEnum(DocumentVisibility), documentVisibility: z.nativeEnum(DocumentVisibility),
documentLanguage: z.enum(SUPPORTED_LANGUAGE_CODES), documentLanguage: z.enum(SUPPORTED_LANGUAGE_CODES),
includeSenderDetails: z.boolean(), includeSenderDetails: z.boolean(),
typedSignatureEnabled: z.boolean(),
includeSigningCertificate: z.boolean(), includeSigningCertificate: z.boolean(),
signatureTypes: z.array(z.nativeEnum(DocumentSignatureType)).min(1, {
message: msg`At least one signature type must be enabled`.id,
}),
}); });
type TTeamDocumentPreferencesFormSchema = z.infer<typeof ZTeamDocumentPreferencesFormSchema>; type TTeamDocumentPreferencesFormSchema = z.infer<typeof ZTeamDocumentPreferencesFormSchema>;
@@ -69,8 +76,8 @@ export const TeamDocumentPreferencesForm = ({
? settings?.documentLanguage ? settings?.documentLanguage
: 'en', : 'en',
includeSenderDetails: settings?.includeSenderDetails ?? false, includeSenderDetails: settings?.includeSenderDetails ?? false,
typedSignatureEnabled: settings?.typedSignatureEnabled ?? true,
includeSigningCertificate: settings?.includeSigningCertificate ?? true, includeSigningCertificate: settings?.includeSigningCertificate ?? true,
signatureTypes: extractTeamSignatureSettings(settings),
}, },
resolver: zodResolver(ZTeamDocumentPreferencesFormSchema), resolver: zodResolver(ZTeamDocumentPreferencesFormSchema),
}); });
@@ -84,7 +91,7 @@ export const TeamDocumentPreferencesForm = ({
documentLanguage, documentLanguage,
includeSenderDetails, includeSenderDetails,
includeSigningCertificate, includeSigningCertificate,
typedSignatureEnabled, signatureTypes,
} = data; } = data;
await updateTeamDocumentPreferences({ await updateTeamDocumentPreferences({
@@ -93,8 +100,10 @@ export const TeamDocumentPreferencesForm = ({
documentVisibility, documentVisibility,
documentLanguage, documentLanguage,
includeSenderDetails, includeSenderDetails,
typedSignatureEnabled,
includeSigningCertificate, includeSigningCertificate,
typedSignatureEnabled: signatureTypes.includes(DocumentSignatureType.TYPE),
uploadSignatureEnabled: signatureTypes.includes(DocumentSignatureType.UPLOAD),
drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW),
}, },
}); });
@@ -190,6 +199,44 @@ export const TeamDocumentPreferencesForm = ({
)} )}
/> />
<FormField
control={form.control}
name="signatureTypes"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel className="flex flex-row items-center">
<Trans>Default Signature Settings</Trans>
<DocumentSignatureSettingsTooltip />
</FormLabel>
<FormControl>
<MultiSelectCombobox
options={Object.values(DOCUMENT_SIGNATURE_TYPES).map((option) => ({
label: _(option.label),
value: option.value,
}))}
selectedValues={field.value}
onChange={field.onChange}
className="bg-background w-full"
enableSearch={false}
emptySelectionPlaceholder="Select signature types"
testId="signature-types-combobox"
/>
</FormControl>
{form.formState.errors.signatureTypes ? (
<FormMessage />
) : (
<FormDescription>
<Trans>
Controls which signatures are allowed to be used when signing a document.
</Trans>
</FormDescription>
)}
</FormItem>
)}
/>
<FormField <FormField
control={form.control} control={form.control}
name="includeSenderDetails" name="includeSenderDetails"
@@ -238,36 +285,6 @@ export const TeamDocumentPreferencesForm = ({
)} )}
/> />
<FormField
control={form.control}
name="typedSignatureEnabled"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>
<Trans>Enable Typed Signature</Trans>
</FormLabel>
<div>
<FormControl className="block">
<Switch
ref={field.ref}
name={field.name}
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</div>
<FormDescription>
<Trans>
Controls whether the recipients can sign the documents using a typed signature.
Enable or disable the typed signature globally.
</Trans>
</FormDescription>
</FormItem>
)}
/>
<FormField <FormField
control={form.control} control={form.control}
name="includeSigningCertificate" name="includeSigningCertificate"
@@ -301,7 +318,7 @@ export const TeamDocumentPreferencesForm = ({
<div className="flex flex-row justify-end space-x-4"> <div className="flex flex-row justify-end space-x-4">
<Button type="submit" loading={form.formState.isSubmitting}> <Button type="submit" loading={form.formState.isSubmitting}>
<Trans>Save</Trans> <Trans>Update</Trans>
</Button> </Button>
</div> </div>
</fieldset> </fieldset>

View File

@@ -76,7 +76,7 @@ export const AppNavDesktop = ({
<Button <Button
variant="outline" variant="outline"
className="text-muted-foreground flex w-96 items-center justify-between rounded-lg" className="text-muted-foreground flex w-full max-w-96 items-center justify-between rounded-lg"
onClick={() => setIsCommandMenuOpen(true)} onClick={() => setIsCommandMenuOpen(true)}
> >
<div className="flex items-center"> <div className="flex items-center">

View File

@@ -1,4 +1,4 @@
import { useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import type { Field, Recipient, Signature } from '@prisma/client'; import type { Field, Recipient, Signature } from '@prisma/client';
@@ -24,7 +24,6 @@ import type {
} from '@documenso/trpc/server/field-router/schema'; } from '@documenso/trpc/server/field-router/schema';
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { import {
DocumentFlowFormContainerContent, DocumentFlowFormContainerContent,
DocumentFlowFormContainerFooter, DocumentFlowFormContainerFooter,
@@ -35,7 +34,7 @@ import type { DocumentFlowStep } from '@documenso/ui/primitives/document-flow/ty
import { ElementVisible } from '@documenso/ui/primitives/element-visible'; import { ElementVisible } from '@documenso/ui/primitives/element-visible';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useStep } from '@documenso/ui/primitives/stepper'; import { useStep } from '@documenso/ui/primitives/stepper';
import { DocumentSigningCheckboxField } from '~/components/general/document-signing/document-signing-checkbox-field'; import { DocumentSigningCheckboxField } from '~/components/general/document-signing/document-signing-checkbox-field';
@@ -73,8 +72,7 @@ export const DirectTemplateSigningForm = ({
template, template,
onSubmit, onSubmit,
}: DirectTemplateSigningFormProps) => { }: DirectTemplateSigningFormProps) => {
const { fullName, signature, signatureValid, setFullName, setSignature } = const { fullName, signature, setFullName, setSignature } = useRequiredDocumentSigningContext();
useRequiredDocumentSigningContext();
const [localFields, setLocalFields] = useState<DirectTemplateLocalField[]>(directRecipientFields); const [localFields, setLocalFields] = useState<DirectTemplateLocalField[]>(directRecipientFields);
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false); const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
@@ -135,8 +133,6 @@ export const DirectTemplateSigningForm = ({
); );
}; };
const hasSignatureField = localFields.some((field) => field.type === FieldType.SIGNATURE);
const uninsertedFields = useMemo(() => { const uninsertedFields = useMemo(() => {
return sortFieldsByPosition(localFields.filter((field) => !field.inserted)); return sortFieldsByPosition(localFields.filter((field) => !field.inserted));
}, [localFields]); }, [localFields]);
@@ -149,10 +145,6 @@ export const DirectTemplateSigningForm = ({
const handleSubmit = async () => { const handleSubmit = async () => {
setValidateUninsertedFields(true); setValidateUninsertedFields(true);
if (hasSignatureField && !signatureValid) {
return;
}
const isFieldsValid = validateFieldsInserted(localFields); const isFieldsValid = validateFieldsInserted(localFields);
if (!isFieldsValid) { if (!isFieldsValid) {
@@ -170,6 +162,55 @@ export const DirectTemplateSigningForm = ({
// Do not reset to false since we do a redirect. // Do not reset to false since we do a redirect.
}; };
useEffect(() => {
const updatedFields = [...localFields];
localFields.forEach((field) => {
const index = updatedFields.findIndex((f) => f.id === field.id);
let value = '';
match(field.type)
.with(FieldType.TEXT, () => {
const meta = field.fieldMeta ? ZTextFieldMeta.safeParse(field.fieldMeta) : null;
if (meta?.success) {
value = meta.data.text ?? '';
}
})
.with(FieldType.NUMBER, () => {
const meta = field.fieldMeta ? ZNumberFieldMeta.safeParse(field.fieldMeta) : null;
if (meta?.success) {
value = meta.data.value ?? '';
}
})
.with(FieldType.DROPDOWN, () => {
const meta = field.fieldMeta ? ZDropdownFieldMeta.safeParse(field.fieldMeta) : null;
if (meta?.success) {
value = meta.data.defaultValue ?? '';
}
});
if (value) {
const signedValue = {
token: directRecipient.token,
fieldId: field.id,
value,
};
updatedFields[index] = {
...field,
customText: value,
inserted: true,
signedValue,
};
}
});
setLocalFields(updatedFields);
}, []);
return ( return (
<DocumentSigningRecipientProvider recipient={directRecipient}> <DocumentSigningRecipientProvider recipient={directRecipient}>
<DocumentFlowFormContainerHeader title={flowStep.title} description={flowStep.description} /> <DocumentFlowFormContainerHeader title={flowStep.title} description={flowStep.description} />
@@ -191,6 +232,8 @@ export const DirectTemplateSigningForm = ({
onSignField={onSignField} onSignField={onSignField}
onUnsignField={onUnsignField} onUnsignField={onUnsignField}
typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled} typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
uploadSignatureEnabled={template.templateMeta?.uploadSignatureEnabled}
drawSignatureEnabled={template.templateMeta?.drawSignatureEnabled}
/> />
)) ))
.with(FieldType.INITIALS, () => ( .with(FieldType.INITIALS, () => (
@@ -335,19 +378,15 @@ export const DirectTemplateSigningForm = ({
<Trans>Signature</Trans> <Trans>Signature</Trans>
</Label> </Label>
<Card className="mt-2" gradient degrees={-120}> <SignaturePadDialog
<CardContent className="p-0"> className="mt-2"
<SignaturePad disabled={isSubmitting}
className="h-44 w-full" value={signature ?? ''}
disabled={isSubmitting} onChange={(value) => setSignature(value)}
defaultValue={signature ?? undefined} typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
onChange={(value) => { uploadSignatureEnabled={template.templateMeta?.uploadSignatureEnabled}
setSignature(value); drawSignatureEnabled={template.templateMeta?.drawSignatureEnabled}
}} />
allowTypedSignature={template.templateMeta?.typedSignatureEnabled}
/>
</CardContent>
</Card>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -97,11 +97,16 @@ export const DocumentSigningCheckboxField = ({
const onSign = async (authOptions?: TRecipientActionAuth) => { const onSign = async (authOptions?: TRecipientActionAuth) => {
try { try {
// Do nothing, since the user clicked on a non checkbox area. // Do nothing, this should only happen when the user clicks the field, but
// misses the checkbox which triggers this callback.
if (checkedValues.length === 0) { if (checkedValues.length === 0) {
return; return;
} }
if (!isLengthConditionMet) {
return;
}
const payload: TSignFieldWithTokenMutationSchema = { const payload: TSignFieldWithTokenMutationSchema = {
token: recipient.token, token: recipient.token,
fieldId: field.id, fieldId: field.id,
@@ -199,18 +204,30 @@ export const DocumentSigningCheckboxField = ({
setCheckedValues(updatedValues); setCheckedValues(updatedValues);
await removeSignedFieldWithToken({ const removePayload: TRemovedSignedFieldWithTokenMutationSchema = {
token: recipient.token, token: recipient.token,
fieldId: field.id, fieldId: field.id,
}); };
if (updatedValues.length > 0) { if (onUnsignField) {
await signFieldWithToken({ await onUnsignField(removePayload);
} else {
await removeSignedFieldWithToken(removePayload);
}
if (updatedValues.length > 0 && shouldAutoSignField) {
const signPayload: TSignFieldWithTokenMutationSchema = {
token: recipient.token, token: recipient.token,
fieldId: field.id, fieldId: field.id,
value: toCheckboxValue(updatedValues), value: toCheckboxValue(updatedValues),
isBase64: true, isBase64: true,
}); };
if (onSignField) {
await onSignField(signPayload);
} else {
await signFieldWithToken(signPayload);
}
} }
} catch (err) { } catch (err) {
console.error(err); console.error(err);

View File

@@ -1,9 +1,12 @@
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import type { Field } from '@prisma/client'; import type { Field } from '@prisma/client';
import { RecipientRole } from '@prisma/client'; import { RecipientRole } from '@prisma/client';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
import { z } from 'zod';
import { fieldsContainUnsignedRequiredField } from '@documenso/lib/utils/advanced-fields-helpers'; import { fieldsContainUnsignedRequiredField } from '@documenso/lib/utils/advanced-fields-helpers';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
@@ -14,6 +17,15 @@ import {
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from '@documenso/ui/primitives/dialog'; } from '@documenso/ui/primitives/dialog';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input';
import { DocumentSigningDisclosure } from '~/components/general/document-signing/document-signing-disclosure'; import { DocumentSigningDisclosure } from '~/components/general/document-signing/document-signing-disclosure';
@@ -22,11 +34,23 @@ export type DocumentSigningCompleteDialogProps = {
documentTitle: string; documentTitle: string;
fields: Field[]; fields: Field[];
fieldsValidated: () => void | Promise<void>; fieldsValidated: () => void | Promise<void>;
onSignatureComplete: () => void | Promise<void>; onSignatureComplete: (nextSigner?: { name: string; email: string }) => void | Promise<void>;
role: RecipientRole; role: RecipientRole;
disabled?: boolean; disabled?: boolean;
allowDictateNextSigner?: boolean;
defaultNextSigner?: {
name: string;
email: string;
};
}; };
const ZNextSignerFormSchema = z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email address'),
});
type TNextSignerFormSchema = z.infer<typeof ZNextSignerFormSchema>;
export const DocumentSigningCompleteDialog = ({ export const DocumentSigningCompleteDialog = ({
isSubmitting, isSubmitting,
documentTitle, documentTitle,
@@ -35,19 +59,54 @@ export const DocumentSigningCompleteDialog = ({
onSignatureComplete, onSignatureComplete,
role, role,
disabled = false, disabled = false,
allowDictateNextSigner = false,
defaultNextSigner,
}: DocumentSigningCompleteDialogProps) => { }: DocumentSigningCompleteDialogProps) => {
const [showDialog, setShowDialog] = useState(false); const [showDialog, setShowDialog] = useState(false);
const [isEditingNextSigner, setIsEditingNextSigner] = useState(false);
const form = useForm<TNextSignerFormSchema>({
resolver: allowDictateNextSigner ? zodResolver(ZNextSignerFormSchema) : undefined,
defaultValues: {
name: defaultNextSigner?.name ?? '',
email: defaultNextSigner?.email ?? '',
},
});
const isComplete = useMemo(() => !fieldsContainUnsignedRequiredField(fields), [fields]); const isComplete = useMemo(() => !fieldsContainUnsignedRequiredField(fields), [fields]);
const handleOpenChange = (open: boolean) => { const handleOpenChange = (open: boolean) => {
if (isSubmitting || !isComplete) { if (form.formState.isSubmitting || !isComplete) {
return; return;
} }
if (open) {
form.reset({
name: defaultNextSigner?.name ?? '',
email: defaultNextSigner?.email ?? '',
});
}
setIsEditingNextSigner(false);
setShowDialog(open); setShowDialog(open);
}; };
const onFormSubmit = async (data: TNextSignerFormSchema) => {
console.log('data', data);
console.log('form.formState.errors', form.formState.errors);
try {
if (allowDictateNextSigner && data.name && data.email) {
await onSignatureComplete({ name: data.name, email: data.email });
} else {
await onSignatureComplete();
}
} catch (error) {
console.error('Error completing signature:', error);
}
};
const isNextSignerValid = !allowDictateNextSigner || (form.watch('name') && form.watch('email'));
return ( return (
<Dialog open={showDialog} onOpenChange={handleOpenChange}> <Dialog open={showDialog} onOpenChange={handleOpenChange}>
<DialogTrigger asChild> <DialogTrigger asChild>
@@ -71,110 +130,184 @@ export const DocumentSigningCompleteDialog = ({
</DialogTrigger> </DialogTrigger>
<DialogContent> <DialogContent>
<DialogTitle> <Form {...form}>
<div className="text-foreground text-xl font-semibold"> <form onSubmit={form.handleSubmit(onFormSubmit)}>
{match(role) <fieldset disabled={form.formState.isSubmitting} className="border-none p-0">
.with(RecipientRole.VIEWER, () => <Trans>Complete Viewing</Trans>) <DialogTitle>
.with(RecipientRole.SIGNER, () => <Trans>Complete Signing</Trans>) <div className="text-foreground text-xl font-semibold">
.with(RecipientRole.APPROVER, () => <Trans>Complete Approval</Trans>) {match(role)
.with(RecipientRole.CC, () => <Trans>Complete Viewing</Trans>) .with(RecipientRole.VIEWER, () => <Trans>Complete Viewing</Trans>)
.with(RecipientRole.ASSISTANT, () => <Trans>Complete Assisting</Trans>) .with(RecipientRole.SIGNER, () => <Trans>Complete Signing</Trans>)
.exhaustive()} .with(RecipientRole.APPROVER, () => <Trans>Complete Approval</Trans>)
</div> .with(RecipientRole.CC, () => <Trans>Complete Viewing</Trans>)
</DialogTitle> .with(RecipientRole.ASSISTANT, () => <Trans>Complete Assisting</Trans>)
.exhaustive()}
</div>
</DialogTitle>
<div className="text-muted-foreground max-w-[50ch]"> <div className="text-muted-foreground max-w-[50ch]">
{match(role) {match(role)
.with(RecipientRole.VIEWER, () => ( .with(RecipientRole.VIEWER, () => (
<span> <span>
<Trans> <Trans>
<span className="inline-flex flex-wrap"> <span className="inline-flex flex-wrap">
You are about to complete viewing " You are about to complete viewing "
<span className="inline-block max-w-[11rem] truncate align-baseline"> <span className="inline-block max-w-[11rem] truncate align-baseline">
{documentTitle} {documentTitle}
</span>
".
</span>
<br /> Are you sure?
</Trans>
</span> </span>
". ))
</span> .with(RecipientRole.SIGNER, () => (
<br /> Are you sure? <span>
</Trans> <Trans>
</span> <span className="inline-flex flex-wrap">
)) You are about to complete signing "
.with(RecipientRole.SIGNER, () => ( <span className="inline-block max-w-[11rem] truncate align-baseline">
<span> {documentTitle}
<Trans> </span>
<span className="inline-flex flex-wrap"> ".
You are about to complete signing " </span>
<span className="inline-block max-w-[11rem] truncate align-baseline"> <br /> Are you sure?
{documentTitle} </Trans>
</span> </span>
". ))
</span> .with(RecipientRole.APPROVER, () => (
<br /> Are you sure? <span>
</Trans> <Trans>
</span> <span className="inline-flex flex-wrap">
)) You are about to complete approving{' '}
.with(RecipientRole.APPROVER, () => ( <span className="inline-block max-w-[11rem] truncate align-baseline">
<span> "{documentTitle}"
<Trans> </span>
<span className="inline-flex flex-wrap"> .
You are about to complete approving{' '} </span>
<span className="inline-block max-w-[11rem] truncate align-baseline"> <br /> Are you sure?
"{documentTitle}" </Trans>
</span> </span>
. ))
</span> .otherwise(() => (
<br /> Are you sure? <span>
</Trans> <Trans>
</span> <span className="inline-flex flex-wrap">
)) You are about to complete viewing "
.otherwise(() => ( <span className="inline-block max-w-[11rem] truncate align-baseline">
<span> {documentTitle}
<Trans> </span>
<span className="inline-flex flex-wrap"> ".
You are about to complete viewing " </span>
<span className="inline-block max-w-[11rem] truncate align-baseline"> <br /> Are you sure?
{documentTitle} </Trans>
</span> </span>
". ))}
</span> </div>
<br /> Are you sure?
</Trans>
</span>
))}
</div>
<DocumentSigningDisclosure className="mt-4" /> {allowDictateNextSigner && (
<div className="mt-4 flex flex-col gap-4">
{!isEditingNextSigner && (
<div>
<p className="text-muted-foreground text-sm">
The next recipient to sign this document will be{' '}
<span className="font-semibold">{form.watch('name')}</span> (
<span className="font-semibold">{form.watch('email')}</span>).
</p>
<DialogFooter> <Button
<div className="flex w-full flex-1 flex-nowrap gap-4"> type="button"
<Button className="mt-2"
type="button" variant="outline"
className="flex-1" size="sm"
variant="secondary" onClick={() => setIsEditingNextSigner((prev) => !prev)}
onClick={() => { >
setShowDialog(false); <Trans>Update Recipient</Trans>
}} </Button>
> </div>
<Trans>Cancel</Trans> )}
</Button>
<Button {isEditingNextSigner && (
type="button" <div className="flex flex-col gap-4 md:flex-row">
className="flex-1" <FormField
disabled={!isComplete} control={form.control}
loading={isSubmitting} name="name"
onClick={onSignatureComplete} render={({ field }) => (
> <FormItem className="flex-1">
{match(role) <FormLabel>
.with(RecipientRole.VIEWER, () => <Trans>Mark as Viewed</Trans>) <Trans>Name</Trans>
.with(RecipientRole.SIGNER, () => <Trans>Sign</Trans>) </FormLabel>
.with(RecipientRole.APPROVER, () => <Trans>Approve</Trans>) <FormControl>
.with(RecipientRole.CC, () => <Trans>Mark as Viewed</Trans>) <Input
.with(RecipientRole.ASSISTANT, () => <Trans>Complete</Trans>) {...field}
.exhaustive()} className="mt-2"
</Button> placeholder="Enter the next signer's name"
</div> />
</DialogFooter> </FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>
<Trans>Email</Trans>
</FormLabel>
<FormControl>
<Input
{...field}
type="email"
className="mt-2"
placeholder="Enter the next signer's email"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
)}
</div>
)}
<DocumentSigningDisclosure className="mt-4" />
<DialogFooter className="mt-4">
<div className="flex w-full flex-1 flex-nowrap gap-4">
<Button
type="button"
className="flex-1"
variant="secondary"
onClick={() => setShowDialog(false)}
disabled={form.formState.isSubmitting}
>
<Trans>Cancel</Trans>
</Button>
<Button
type="submit"
className="flex-1"
disabled={!isComplete || !isNextSignerValid}
loading={form.formState.isSubmitting}
>
{match(role)
.with(RecipientRole.VIEWER, () => <Trans>Mark as Viewed</Trans>)
.with(RecipientRole.SIGNER, () => <Trans>Sign</Trans>)
.with(RecipientRole.APPROVER, () => <Trans>Approve</Trans>)
.with(RecipientRole.CC, () => <Trans>Mark as Viewed</Trans>)
.with(RecipientRole.ASSISTANT, () => <Trans>Complete</Trans>)
.exhaustive()}
</Button>
</div>
</DialogFooter>
</fieldset>
</form>
</Form>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
); );

View File

@@ -1,11 +1,13 @@
import { useId, useMemo, useState } from 'react'; import { useId, useMemo, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { type Field, FieldType, type Recipient, RecipientRole } from '@prisma/client'; import { type Field, FieldType, type Recipient, RecipientRole } from '@prisma/client';
import { Controller, useForm } from 'react-hook-form'; import { Controller, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { z } from 'zod';
import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics'; import { useAnalytics } from '@documenso/lib/client-only/hooks/use-analytics';
import { useOptionalSession } from '@documenso/lib/client-only/providers/session'; import { useOptionalSession } from '@documenso/lib/client-only/providers/session';
@@ -18,17 +20,26 @@ import { trpc } from '@documenso/trpc/react';
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
import { cn } from '@documenso/ui/lib/utils'; import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group'; import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
import { AssistantConfirmationDialog } from '../../dialogs/assistant-confirmation-dialog'; import {
AssistantConfirmationDialog,
type NextSigner,
} from '../../dialogs/assistant-confirmation-dialog';
import { DocumentSigningCompleteDialog } from './document-signing-complete-dialog'; import { DocumentSigningCompleteDialog } from './document-signing-complete-dialog';
import { useRequiredDocumentSigningContext } from './document-signing-provider'; import { useRequiredDocumentSigningContext } from './document-signing-provider';
export const ZSigningFormSchema = z.object({
name: z.string().min(1, 'Name is required').optional(),
email: z.string().email('Invalid email address').optional(),
});
export type TSigningFormSchema = z.infer<typeof ZSigningFormSchema>;
export type DocumentSigningFormProps = { export type DocumentSigningFormProps = {
document: DocumentAndSender; document: DocumentAndSender;
recipient: Recipient; recipient: Recipient;
@@ -59,8 +70,7 @@ export const DocumentSigningForm = ({
const assistantSignersId = useId(); const assistantSignersId = useId();
const { fullName, signature, setFullName, setSignature, signatureValid, setSignatureValid } = const { fullName, signature, setFullName, setSignature } = useRequiredDocumentSigningContext();
useRequiredDocumentSigningContext();
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false); const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
const [isConfirmationDialogOpen, setIsConfirmationDialogOpen] = useState(false); const [isConfirmationDialogOpen, setIsConfirmationDialogOpen] = useState(false);
@@ -75,7 +85,9 @@ export const DocumentSigningForm = ({
}, },
}); });
const { handleSubmit, formState } = useForm(); const { handleSubmit, formState } = useForm<TSigningFormSchema>({
resolver: zodResolver(ZSigningFormSchema),
});
// Keep the loading state going if successful since the redirect may take some time. // Keep the loading state going if successful since the redirect may take some time.
const isSubmitting = formState.isSubmitting || formState.isSubmitSuccessful; const isSubmitting = formState.isSubmitting || formState.isSubmitSuccessful;
@@ -100,20 +112,32 @@ export const DocumentSigningForm = ({
validateFieldsInserted(fieldsRequiringValidation); validateFieldsInserted(fieldsRequiringValidation);
}; };
const onFormSubmit = async () => { const onFormSubmit = async (data: TSigningFormSchema) => {
setValidateUninsertedFields(true); try {
setValidateUninsertedFields(true);
const isFieldsValid = validateFieldsInserted(fieldsRequiringValidation); const isFieldsValid = validateFieldsInserted(fieldsRequiringValidation);
if (hasSignatureField && !signatureValid) { if (!isFieldsValid) {
return; return;
}
const nextSigner =
data.email && data.name
? {
email: data.email,
name: data.name,
}
: undefined;
await completeDocument(undefined, nextSigner);
} catch (error) {
toast({
title: 'Error',
description: error instanceof Error ? error.message : 'An error occurred while signing',
variant: 'destructive',
});
} }
if (!isFieldsValid) {
return;
}
await completeDocument();
}; };
const onAssistantFormSubmit = () => { const onAssistantFormSubmit = () => {
@@ -124,11 +148,11 @@ export const DocumentSigningForm = ({
setIsConfirmationDialogOpen(true); setIsConfirmationDialogOpen(true);
}; };
const handleAssistantConfirmDialogSubmit = async () => { const handleAssistantConfirmDialogSubmit = async (nextSigner?: NextSigner) => {
setIsAssistantSubmitting(true); setIsAssistantSubmitting(true);
try { try {
await completeDocument(); await completeDocument(undefined, nextSigner);
} catch (err) { } catch (err) {
toast({ toast({
title: 'Error', title: 'Error',
@@ -141,12 +165,18 @@ export const DocumentSigningForm = ({
} }
}; };
const completeDocument = async (authOptions?: TRecipientActionAuth) => { const completeDocument = async (
await completeDocumentWithToken({ authOptions?: TRecipientActionAuth,
nextSigner?: { email: string; name: string },
) => {
const payload = {
token: recipient.token, token: recipient.token,
documentId: document.id, documentId: document.id,
authOptions, authOptions,
}); ...(nextSigner?.email && nextSigner?.name ? { nextSigner } : {}),
};
await completeDocumentWithToken(payload);
analytics.capture('App: Recipient has completed signing', { analytics.capture('App: Recipient has completed signing', {
signerId: recipient.id, signerId: recipient.id,
@@ -161,6 +191,31 @@ export const DocumentSigningForm = ({
} }
}; };
const nextRecipient = useMemo(() => {
if (
!document.documentMeta?.signingOrder ||
document.documentMeta.signingOrder !== 'SEQUENTIAL'
) {
return undefined;
}
const sortedRecipients = allRecipients.sort((a, b) => {
// Sort by signingOrder first (nulls last), then by id
if (a.signingOrder === null && b.signingOrder === null) return a.id - b.id;
if (a.signingOrder === null) return 1;
if (b.signingOrder === null) return -1;
if (a.signingOrder === b.signingOrder) return a.id - b.id;
return a.signingOrder - b.signingOrder;
});
const currentIndex = sortedRecipients.findIndex((r) => r.id === recipient.id);
return currentIndex !== -1 && currentIndex < sortedRecipients.length - 1
? sortedRecipients[currentIndex + 1]
: undefined;
}, [document.documentMeta?.signingOrder, allRecipients, recipient.id]);
console.log('nextRecipient', nextRecipient);
return ( return (
<div <div
className={cn( className={cn(
@@ -210,12 +265,19 @@ export const DocumentSigningForm = ({
<DocumentSigningCompleteDialog <DocumentSigningCompleteDialog
isSubmitting={isSubmitting} isSubmitting={isSubmitting}
onSignatureComplete={handleSubmit(onFormSubmit)}
documentTitle={document.title} documentTitle={document.title}
fields={fields} fields={fields}
fieldsValidated={fieldsValidated} fieldsValidated={fieldsValidated}
onSignatureComplete={async (nextSigner) => {
await completeDocument(undefined, nextSigner);
}}
role={recipient.role} role={recipient.role}
disabled={!isRecipientsTurn} allowDictateNextSigner={document.documentMeta?.allowDictateNextSigner}
defaultNextSigner={
nextRecipient
? { name: nextRecipient.name, email: nextRecipient.email }
: undefined
}
/> />
</div> </div>
</div> </div>
@@ -306,6 +368,14 @@ export const DocumentSigningForm = ({
onClose={() => !isAssistantSubmitting && setIsConfirmationDialogOpen(false)} onClose={() => !isAssistantSubmitting && setIsConfirmationDialogOpen(false)}
onConfirm={handleAssistantConfirmDialogSubmit} onConfirm={handleAssistantConfirmDialogSubmit}
isSubmitting={isAssistantSubmitting} isSubmitting={isAssistantSubmitting}
allowDictateNextSigner={
nextRecipient && document.documentMeta?.allowDictateNextSigner
}
defaultNextSigner={
nextRecipient
? { name: nextRecipient.name, email: nextRecipient.email }
: undefined
}
/> />
</form> </form>
</> </>
@@ -347,59 +417,52 @@ export const DocumentSigningForm = ({
<Trans>Signature</Trans> <Trans>Signature</Trans>
</Label> </Label>
<Card className="mt-2" gradient degrees={-120}> <SignaturePadDialog
<CardContent className="p-0"> className="mt-2"
<SignaturePad disabled={isSubmitting}
className="h-44 w-full" value={signature ?? ''}
disabled={isSubmitting} onChange={(v) => setSignature(v ?? '')}
defaultValue={signature ?? undefined} typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled}
onValidityChange={(isValid) => { uploadSignatureEnabled={document.documentMeta?.uploadSignatureEnabled}
setSignatureValid(isValid); drawSignatureEnabled={document.documentMeta?.drawSignatureEnabled}
}} />
onChange={(value) => {
if (signatureValid) {
setSignature(value);
}
}}
allowTypedSignature={document.documentMeta?.typedSignatureEnabled}
/>
</CardContent>
</Card>
{!signatureValid && (
<div className="text-destructive mt-2 text-sm">
<Trans>
Signature is too small. Please provide a more complete signature.
</Trans>
</div>
)}
</div> </div>
)} )}
</div> </div>
<div className="flex flex-col gap-4 md:flex-row">
<Button
type="button"
className="dark:bg-muted dark:hover:bg-muted/80 w-full bg-black/5 hover:bg-black/10"
variant="secondary"
size="lg"
disabled={typeof window !== 'undefined' && window.history.length <= 1}
onClick={async () => navigate(-1)}
>
<Trans>Cancel</Trans>
</Button>
<DocumentSigningCompleteDialog
isSubmitting={isSubmitting}
onSignatureComplete={handleSubmit(onFormSubmit)}
documentTitle={document.title}
fields={fields}
fieldsValidated={fieldsValidated}
role={recipient.role}
disabled={!isRecipientsTurn}
/>
</div>
</fieldset> </fieldset>
<div className="mt-6 flex flex-col gap-4 md:flex-row">
<Button
type="button"
className="dark:bg-muted dark:hover:bg-muted/80 w-full bg-black/5 hover:bg-black/10"
variant="secondary"
size="lg"
disabled={typeof window !== 'undefined' && window.history.length <= 1}
onClick={async () => navigate(-1)}
>
<Trans>Cancel</Trans>
</Button>
<DocumentSigningCompleteDialog
isSubmitting={isSubmitting || isAssistantSubmitting}
documentTitle={document.title}
fields={fields}
fieldsValidated={fieldsValidated}
disabled={!isRecipientsTurn}
onSignatureComplete={async (nextSigner) => {
await completeDocument(undefined, nextSigner);
}}
role={recipient.role}
allowDictateNextSigner={
nextRecipient && document.documentMeta?.allowDictateNextSigner
}
defaultNextSigner={
nextRecipient
? { name: nextRecipient.name, email: nextRecipient.email }
: undefined
}
/>
</div>
</form> </form>
</> </>
)} )}

View File

@@ -40,9 +40,9 @@ import { DocumentSigningTextField } from '~/components/general/document-signing/
import { DocumentSigningRecipientProvider } from './document-signing-recipient-provider'; import { DocumentSigningRecipientProvider } from './document-signing-recipient-provider';
export type SigningPageViewProps = { export type DocumentSigningPageViewProps = {
document: DocumentAndSender;
recipient: RecipientWithFields; recipient: RecipientWithFields;
document: DocumentAndSender;
fields: Field[]; fields: Field[];
completedFields: CompletedField[]; completedFields: CompletedField[];
isRecipientsTurn: boolean; isRecipientsTurn: boolean;
@@ -50,13 +50,13 @@ export type SigningPageViewProps = {
}; };
export const DocumentSigningPageView = ({ export const DocumentSigningPageView = ({
document,
recipient, recipient,
document,
fields, fields,
completedFields, completedFields,
isRecipientsTurn, isRecipientsTurn,
allRecipients = [], allRecipients = [],
}: SigningPageViewProps) => { }: DocumentSigningPageViewProps) => {
const { documentData, documentMeta } = document; const { documentData, documentMeta } = document;
const [selectedSignerId, setSelectedSignerId] = useState<number | null>(allRecipients?.[0]?.id); const [selectedSignerId, setSelectedSignerId] = useState<number | null>(allRecipients?.[0]?.id);
@@ -177,6 +177,8 @@ export const DocumentSigningPageView = ({
key={field.id} key={field.id}
field={field} field={field}
typedSignatureEnabled={documentMeta?.typedSignatureEnabled} typedSignatureEnabled={documentMeta?.typedSignatureEnabled}
uploadSignatureEnabled={documentMeta?.uploadSignatureEnabled}
drawSignatureEnabled={documentMeta?.drawSignatureEnabled}
/> />
)) ))
.with(FieldType.INITIALS, () => ( .with(FieldType.INITIALS, () => (

View File

@@ -1,4 +1,6 @@
import { createContext, useContext, useEffect, useState } from 'react'; import { createContext, useContext, useState } from 'react';
import { isBase64Image } from '@documenso/lib/constants/signatures';
export type DocumentSigningContextValue = { export type DocumentSigningContextValue = {
fullName: string; fullName: string;
@@ -7,8 +9,6 @@ export type DocumentSigningContextValue = {
setEmail: (_value: string) => void; setEmail: (_value: string) => void;
signature: string | null; signature: string | null;
setSignature: (_value: string | null) => void; setSignature: (_value: string | null) => void;
signatureValid: boolean;
setSignatureValid: (_valid: boolean) => void;
}; };
const DocumentSigningContext = createContext<DocumentSigningContextValue | null>(null); const DocumentSigningContext = createContext<DocumentSigningContextValue | null>(null);
@@ -31,6 +31,9 @@ export interface DocumentSigningProviderProps {
fullName?: string | null; fullName?: string | null;
email?: string | null; email?: string | null;
signature?: string | null; signature?: string | null;
typedSignatureEnabled?: boolean;
uploadSignatureEnabled?: boolean;
drawSignatureEnabled?: boolean;
children: React.ReactNode; children: React.ReactNode;
} }
@@ -38,18 +41,31 @@ export const DocumentSigningProvider = ({
fullName: initialFullName, fullName: initialFullName,
email: initialEmail, email: initialEmail,
signature: initialSignature, signature: initialSignature,
typedSignatureEnabled = true,
uploadSignatureEnabled = true,
drawSignatureEnabled = true,
children, children,
}: DocumentSigningProviderProps) => { }: DocumentSigningProviderProps) => {
const [fullName, setFullName] = useState(initialFullName || ''); const [fullName, setFullName] = useState(initialFullName || '');
const [email, setEmail] = useState(initialEmail || ''); const [email, setEmail] = useState(initialEmail || '');
const [signature, setSignature] = useState(initialSignature || null);
const [signatureValid, setSignatureValid] = useState(true);
useEffect(() => { // Ensure the user signature doesn't show up if it's not allowed.
if (initialSignature) { const [signature, setSignature] = useState(
setSignature(initialSignature); (() => {
} const sig = initialSignature || '';
}, [initialSignature]); const isBase64 = isBase64Image(sig);
if (isBase64 && (uploadSignatureEnabled || drawSignatureEnabled)) {
return sig;
}
if (!isBase64 && typedSignatureEnabled) {
return sig;
}
return null;
})(),
);
return ( return (
<DocumentSigningContext.Provider <DocumentSigningContext.Provider
@@ -60,8 +76,6 @@ export const DocumentSigningProvider = ({
setEmail, setEmail,
signature, signature,
setSignature, setSignature,
signatureValid,
setSignatureValid,
}} }}
> >
{children} {children}

View File

@@ -31,10 +31,7 @@ import { Textarea } from '@documenso/ui/primitives/textarea';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
const ZRejectDocumentFormSchema = z.object({ const ZRejectDocumentFormSchema = z.object({
reason: z reason: z.string().max(500, msg`Reason must be less than 500 characters`),
.string()
.min(5, msg`Please provide a reason`)
.max(500, msg`Reason must be less than 500 characters`),
}); });
type TRejectDocumentFormSchema = z.infer<typeof ZRejectDocumentFormSchema>; type TRejectDocumentFormSchema = z.infer<typeof ZRejectDocumentFormSchema>;

View File

@@ -17,7 +17,6 @@ import type {
} from '@documenso/trpc/server/field-router/schema'; } from '@documenso/trpc/server/field-router/schema';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { Dialog, DialogContent, DialogFooter, DialogTitle } from '@documenso/ui/primitives/dialog'; import { Dialog, DialogContent, DialogFooter, DialogTitle } from '@documenso/ui/primitives/dialog';
import { Label } from '@documenso/ui/primitives/label';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePad } from '@documenso/ui/primitives/signature-pad';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
@@ -29,11 +28,14 @@ import { useRequiredDocumentSigningContext } from './document-signing-provider';
import { useDocumentSigningRecipientContext } from './document-signing-recipient-provider'; import { useDocumentSigningRecipientContext } from './document-signing-recipient-provider';
type SignatureFieldState = 'empty' | 'signed-image' | 'signed-text'; type SignatureFieldState = 'empty' | 'signed-image' | 'signed-text';
export type DocumentSigningSignatureFieldProps = { export type DocumentSigningSignatureFieldProps = {
field: FieldWithSignature; field: FieldWithSignature;
onSignField?: (value: TSignFieldWithTokenMutationSchema) => Promise<void> | void; onSignField?: (value: TSignFieldWithTokenMutationSchema) => Promise<void> | void;
onUnsignField?: (value: TRemovedSignedFieldWithTokenMutationSchema) => Promise<void> | void; onUnsignField?: (value: TRemovedSignedFieldWithTokenMutationSchema) => Promise<void> | void;
typedSignatureEnabled?: boolean; typedSignatureEnabled?: boolean;
uploadSignatureEnabled?: boolean;
drawSignatureEnabled?: boolean;
}; };
export const DocumentSigningSignatureField = ({ export const DocumentSigningSignatureField = ({
@@ -41,6 +43,8 @@ export const DocumentSigningSignatureField = ({
onSignField, onSignField,
onUnsignField, onUnsignField,
typedSignatureEnabled, typedSignatureEnabled,
uploadSignatureEnabled,
drawSignatureEnabled,
}: DocumentSigningSignatureFieldProps) => { }: DocumentSigningSignatureFieldProps) => {
const { _ } = useLingui(); const { _ } = useLingui();
const { toast } = useToast(); const { toast } = useToast();
@@ -52,12 +56,8 @@ export const DocumentSigningSignatureField = ({
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [fontSize, setFontSize] = useState(2); const [fontSize, setFontSize] = useState(2);
const { const { signature: providedSignature, setSignature: setProvidedSignature } =
signature: providedSignature, useRequiredDocumentSigningContext();
setSignature: setProvidedSignature,
signatureValid,
setSignatureValid,
} = useRequiredDocumentSigningContext();
const { executeActionAuthProcedure } = useRequiredDocumentSigningAuthContext(); const { executeActionAuthProcedure } = useRequiredDocumentSigningAuthContext();
@@ -89,7 +89,7 @@ export const DocumentSigningSignatureField = ({
}, [field.inserted, signature?.signatureImageAsBase64]); }, [field.inserted, signature?.signatureImageAsBase64]);
const onPreSign = () => { const onPreSign = () => {
if (!providedSignature || !signatureValid) { if (!providedSignature) {
setShowSignatureModal(true); setShowSignatureModal(true);
return false; return false;
} }
@@ -102,6 +102,7 @@ export const DocumentSigningSignatureField = ({
const onDialogSignClick = () => { const onDialogSignClick = () => {
setShowSignatureModal(false); setShowSignatureModal(false);
setProvidedSignature(localSignature); setProvidedSignature(localSignature);
if (!localSignature) { if (!localSignature) {
return; return;
} }
@@ -116,14 +117,14 @@ export const DocumentSigningSignatureField = ({
try { try {
const value = signature || providedSignature; const value = signature || providedSignature;
if (!value || (signature && !signatureValid)) { if (!value) {
setShowSignatureModal(true); setShowSignatureModal(true);
return; return;
} }
const isTypedSignature = !value.startsWith('data:image'); const isTypedSignature = !value.startsWith('data:image');
if (isTypedSignature && !typedSignatureEnabled) { if (isTypedSignature && typedSignatureEnabled === false) {
toast({ toast({
title: _(msg`Error`), title: _(msg`Error`),
description: _(msg`Typed signatures are not allowed. Please draw your signature.`), description: _(msg`Typed signatures are not allowed. Please draw your signature.`),
@@ -275,29 +276,14 @@ export const DocumentSigningSignatureField = ({
</Trans> </Trans>
</DialogTitle> </DialogTitle>
<div className=""> <SignaturePad
<Label htmlFor="signature"> className="mt-2"
<Trans>Signature</Trans> value={localSignature ?? ''}
</Label> onChange={({ value }) => setLocalSignature(value)}
typedSignatureEnabled={typedSignatureEnabled}
<div className="border-border mt-2 rounded-md border"> uploadSignatureEnabled={uploadSignatureEnabled}
<SignaturePad drawSignatureEnabled={drawSignatureEnabled}
id="signature" />
className="h-44 w-full"
onChange={(value) => setLocalSignature(value)}
allowTypedSignature={typedSignatureEnabled}
onValidityChange={(isValid) => {
setSignatureValid(isValid);
}}
/>
</div>
{!signatureValid && (
<div className="text-destructive mt-2 text-sm">
<Trans>Signature is too small. Please provide a more complete signature.</Trans>
</div>
)}
</div>
<DocumentSigningDisclosure /> <DocumentSigningDisclosure />
@@ -317,7 +303,7 @@ export const DocumentSigningSignatureField = ({
<Button <Button
type="button" type="button"
className="flex-1" className="flex-1"
disabled={!localSignature || !signatureValid} disabled={!localSignature}
onClick={() => onDialogSignClick()} onClick={() => onDialogSignClick()}
> >
<Trans>Sign</Trans> <Trans>Sign</Trans>

View File

@@ -1,9 +1,10 @@
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { DocumentStatus } from '@prisma/client'; import type { DocumentStatus } from '@prisma/client';
import { DownloadIcon } from 'lucide-react'; import { DownloadIcon } from 'lucide-react';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { trpc } from '@documenso/trpc/react'; import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils'; import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
@@ -76,7 +77,7 @@ export const DocumentCertificateDownloadButton = ({
className={cn('w-full sm:w-auto', className)} className={cn('w-full sm:w-auto', className)}
loading={isPending} loading={isPending}
variant="outline" variant="outline"
disabled={documentStatus !== DocumentStatus.COMPLETED} disabled={!isDocumentCompleted(documentStatus)}
onClick={() => void onDownloadCertificatesClick()} onClick={() => void onDownloadCertificatesClick()}
> >
{!isPending && <DownloadIcon className="mr-1.5 h-4 w-4" />} {!isPending && <DownloadIcon className="mr-1.5 h-4 w-4" />}

View File

@@ -5,6 +5,7 @@ import { useLingui } from '@lingui/react';
import { DocumentDistributionMethod, DocumentStatus } from '@prisma/client'; import { DocumentDistributionMethod, DocumentStatus } from '@prisma/client';
import { useNavigate, useSearchParams } from 'react-router'; import { useNavigate, useSearchParams } from 'react-router';
import { DocumentSignatureType } from '@documenso/lib/constants/document';
import { isValidLanguageCode } from '@documenso/lib/constants/i18n'; import { isValidLanguageCode } from '@documenso/lib/constants/i18n';
import { import {
DO_NOT_INVALIDATE_QUERY_ON_MUTATION, DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
@@ -71,7 +72,7 @@ export const DocumentEditForm = ({
const { recipients, fields } = document; const { recipients, fields } = document;
const { mutateAsync: updateDocument } = trpc.document.setSettingsForDocument.useMutation({ const { mutateAsync: updateDocument } = trpc.document.updateDocument.useMutation({
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION, ...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
onSuccess: (newData) => { onSuccess: (newData) => {
utils.document.getDocumentWithDetailsById.setData( utils.document.getDocumentWithDetailsById.setData(
@@ -174,7 +175,7 @@ export const DocumentEditForm = ({
const onAddSettingsFormSubmit = async (data: TAddSettingsFormSchema) => { const onAddSettingsFormSubmit = async (data: TAddSettingsFormSchema) => {
try { try {
const { timezone, dateFormat, redirectUrl, language } = data.meta; const { timezone, dateFormat, redirectUrl, language, signatureTypes } = data.meta;
await updateDocument({ await updateDocument({
documentId: document.id, documentId: document.id,
@@ -190,6 +191,9 @@ export const DocumentEditForm = ({
dateFormat, dateFormat,
redirectUrl, redirectUrl,
language: isValidLanguageCode(language) ? language : undefined, language: isValidLanguageCode(language) ? language : undefined,
typedSignatureEnabled: signatureTypes.includes(DocumentSignatureType.TYPE),
uploadSignatureEnabled: signatureTypes.includes(DocumentSignatureType.UPLOAD),
drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW),
}, },
}); });
@@ -213,6 +217,13 @@ export const DocumentEditForm = ({
signingOrder: data.signingOrder, signingOrder: data.signingOrder,
}), }),
updateDocument({
documentId: document.id,
meta: {
allowDictateNextSigner: data.allowDictateNextSigner,
},
}),
setRecipients({ setRecipients({
documentId: document.id, documentId: document.id,
recipients: data.signers.map((signer) => ({ recipients: data.signers.map((signer) => ({
@@ -242,14 +253,6 @@ export const DocumentEditForm = ({
fields: data.fields, fields: data.fields,
}); });
await updateDocument({
documentId: document.id,
meta: {
typedSignatureEnabled: data.typedSignatureEnabled,
},
});
// Clear all field data from localStorage // Clear all field data from localStorage
for (let i = 0; i < localStorage.length; i++) { for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i); const key = localStorage.key(i);
@@ -365,6 +368,7 @@ export const DocumentEditForm = ({
documentFlow={documentFlow.signers} documentFlow={documentFlow.signers}
recipients={recipients} recipients={recipients}
signingOrder={document.documentMeta?.signingOrder} signingOrder={document.documentMeta?.signingOrder}
allowDictateNextSigner={document.documentMeta?.allowDictateNextSigner}
fields={fields} fields={fields}
isDocumentEnterprise={isDocumentEnterprise} isDocumentEnterprise={isDocumentEnterprise}
onSubmit={onAddSignersFormSubmit} onSubmit={onAddSignersFormSubmit}
@@ -378,7 +382,6 @@ export const DocumentEditForm = ({
fields={fields} fields={fields}
onSubmit={onAddFieldsFormSubmit} onSubmit={onAddFieldsFormSubmit}
isDocumentPdfLoaded={isDocumentPdfLoaded} isDocumentPdfLoaded={isDocumentPdfLoaded}
typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled}
teamId={team?.id} teamId={team?.id}
/> />

View File

@@ -9,6 +9,7 @@ import { match } from 'ts-pattern';
import { downloadPDF } from '@documenso/lib/client-only/download-pdf'; import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
import { useSession } from '@documenso/lib/client-only/providers/session'; import { useSession } from '@documenso/lib/client-only/providers/session';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { formatDocumentsPath } from '@documenso/lib/utils/teams'; import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { trpc as trpcClient } from '@documenso/trpc/client'; import { trpc as trpcClient } from '@documenso/trpc/client';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
@@ -32,7 +33,7 @@ export const DocumentPageViewButton = ({ document }: DocumentPageViewButtonProps
const isRecipient = !!recipient; const isRecipient = !!recipient;
const isPending = document.status === DocumentStatus.PENDING; const isPending = document.status === DocumentStatus.PENDING;
const isComplete = document.status === DocumentStatus.COMPLETED; const isComplete = isDocumentCompleted(document);
const isSigned = recipient?.signingStatus === SigningStatus.SIGNED; const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
const role = recipient?.role; const role = recipient?.role;

View File

@@ -20,6 +20,7 @@ import { useNavigate } from 'react-router';
import { downloadPDF } from '@documenso/lib/client-only/download-pdf'; import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
import { useSession } from '@documenso/lib/client-only/providers/session'; import { useSession } from '@documenso/lib/client-only/providers/session';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { formatDocumentsPath } from '@documenso/lib/utils/teams'; import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { trpc as trpcClient } from '@documenso/trpc/client'; import { trpc as trpcClient } from '@documenso/trpc/client';
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button'; import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
@@ -63,7 +64,7 @@ export const DocumentPageViewDropdown = ({ document }: DocumentPageViewDropdownP
const isDraft = document.status === DocumentStatus.DRAFT; const isDraft = document.status === DocumentStatus.DRAFT;
const isPending = document.status === DocumentStatus.PENDING; const isPending = document.status === DocumentStatus.PENDING;
const isDeleted = document.deletedAt !== null; const isDeleted = document.deletedAt !== null;
const isComplete = document.status === DocumentStatus.COMPLETED; const isComplete = isDocumentCompleted(document);
const isCurrentTeamDocument = team && document.team?.url === team.url; const isCurrentTeamDocument = team && document.team?.url === team.url;
const canManageDocument = Boolean(isOwner || isCurrentTeamDocument); const canManageDocument = Boolean(isOwner || isCurrentTeamDocument);

View File

@@ -17,6 +17,7 @@ import { Link } from 'react-router';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles'; import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { formatSigningLink } from '@documenso/lib/utils/recipients'; import { formatSigningLink } from '@documenso/lib/utils/recipients';
import { CopyTextButton } from '@documenso/ui/components/common/copy-text-button'; import { CopyTextButton } from '@documenso/ui/components/common/copy-text-button';
import { SignatureIcon } from '@documenso/ui/icons/signature'; import { SignatureIcon } from '@documenso/ui/icons/signature';
@@ -48,7 +49,7 @@ export const DocumentPageViewRecipients = ({
<Trans>Recipients</Trans> <Trans>Recipients</Trans>
</h1> </h1>
{document.status !== DocumentStatus.COMPLETED && ( {!isDocumentCompleted(document.status) && (
<Link <Link
to={`${documentRootPath}/${document.id}/edit?step=signers`} to={`${documentRootPath}/${document.id}/edit?step=signers`}
title={_(msg`Modify recipients`)} title={_(msg`Modify recipients`)}

View File

@@ -3,7 +3,7 @@ import type { HTMLAttributes } from 'react';
import type { MessageDescriptor } from '@lingui/core'; import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { CheckCircle2, Clock, File } from 'lucide-react'; import { CheckCircle2, Clock, File, XCircle } from 'lucide-react';
import type { LucideIcon } from 'lucide-react/dist/lucide-react'; import type { LucideIcon } from 'lucide-react/dist/lucide-react';
import type { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status'; import type { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
@@ -36,6 +36,12 @@ export const FRIENDLY_STATUS_MAP: Record<ExtendedDocumentStatus, FriendlyStatus>
icon: File, icon: File,
color: 'text-yellow-500 dark:text-yellow-200', color: 'text-yellow-500 dark:text-yellow-200',
}, },
REJECTED: {
label: msg`Rejected`,
labelExtended: msg`Document rejected`,
icon: XCircle,
color: 'text-red-500 dark:text-red-300',
},
INBOX: { INBOX: {
label: msg`Inbox`, label: msg`Inbox`,
labelExtended: msg`Document inbox`, labelExtended: msg`Document inbox`,

View File

@@ -4,6 +4,7 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { DocumentSignatureType } from '@documenso/lib/constants/document';
import { isValidLanguageCode } from '@documenso/lib/constants/i18n'; import { isValidLanguageCode } from '@documenso/lib/constants/i18n';
import { import {
DO_NOT_INVALIDATE_QUERY_ON_MUTATION, DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
@@ -124,6 +125,8 @@ export const TemplateEditForm = ({
}); });
const onAddSettingsFormSubmit = async (data: TAddTemplateSettingsFormSchema) => { const onAddSettingsFormSubmit = async (data: TAddTemplateSettingsFormSchema) => {
const { signatureTypes } = data.meta;
try { try {
await updateTemplateSettings({ await updateTemplateSettings({
templateId: template.id, templateId: template.id,
@@ -136,6 +139,9 @@ export const TemplateEditForm = ({
}, },
meta: { meta: {
...data.meta, ...data.meta,
typedSignatureEnabled: signatureTypes.includes(DocumentSignatureType.TYPE),
uploadSignatureEnabled: signatureTypes.includes(DocumentSignatureType.UPLOAD),
drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW),
language: isValidLanguageCode(data.meta.language) ? data.meta.language : undefined, language: isValidLanguageCode(data.meta.language) ? data.meta.language : undefined,
}, },
}); });
@@ -161,6 +167,7 @@ export const TemplateEditForm = ({
templateId: template.id, templateId: template.id,
meta: { meta: {
signingOrder: data.signingOrder, signingOrder: data.signingOrder,
allowDictateNextSigner: data.allowDictateNextSigner,
}, },
}), }),
@@ -187,13 +194,6 @@ export const TemplateEditForm = ({
fields: data.fields, fields: data.fields,
}); });
await updateTemplateSettings({
templateId: template.id,
meta: {
typedSignatureEnabled: data.typedSignatureEnabled,
},
});
// Clear all field data from localStorage // Clear all field data from localStorage
for (let i = 0; i < localStorage.length; i++) { for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i); const key = localStorage.key(i);
@@ -271,6 +271,7 @@ export const TemplateEditForm = ({
recipients={recipients} recipients={recipients}
fields={fields} fields={fields}
signingOrder={template.templateMeta?.signingOrder} signingOrder={template.templateMeta?.signingOrder}
allowDictateNextSigner={template.templateMeta?.allowDictateNextSigner}
templateDirectLink={template.directLink} templateDirectLink={template.directLink}
onSubmit={onAddTemplatePlaceholderFormSubmit} onSubmit={onAddTemplatePlaceholderFormSubmit}
isEnterprise={isEnterprise} isEnterprise={isEnterprise}
@@ -284,7 +285,6 @@ export const TemplateEditForm = ({
fields={fields} fields={fields}
onSubmit={onAddFieldsFormSubmit} onSubmit={onAddFieldsFormSubmit}
teamId={team?.id} teamId={team?.id}
typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
/> />
</Stepper> </Stepper>
</DocumentFlowFormContainer> </DocumentFlowFormContainer>

View File

@@ -9,6 +9,7 @@ import { match } from 'ts-pattern';
import { downloadPDF } from '@documenso/lib/client-only/download-pdf'; import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
import { useSession } from '@documenso/lib/client-only/providers/session'; import { useSession } from '@documenso/lib/client-only/providers/session';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { formatDocumentsPath } from '@documenso/lib/utils/teams'; import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { trpc as trpcClient } from '@documenso/trpc/client'; import { trpc as trpcClient } from '@documenso/trpc/client';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
@@ -37,7 +38,7 @@ export const DocumentsTableActionButton = ({ row }: DocumentsTableActionButtonPr
const isRecipient = !!recipient; const isRecipient = !!recipient;
const isDraft = row.status === DocumentStatus.DRAFT; const isDraft = row.status === DocumentStatus.DRAFT;
const isPending = row.status === DocumentStatus.PENDING; const isPending = row.status === DocumentStatus.PENDING;
const isComplete = row.status === DocumentStatus.COMPLETED; const isComplete = isDocumentCompleted(row.status);
const isSigned = recipient?.signingStatus === SigningStatus.SIGNED; const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
const role = recipient?.role; const role = recipient?.role;
const isCurrentTeamDocument = team && row.team?.url === team.url; const isCurrentTeamDocument = team && row.team?.url === team.url;

View File

@@ -22,6 +22,7 @@ import { Link } from 'react-router';
import { downloadPDF } from '@documenso/lib/client-only/download-pdf'; import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
import { useSession } from '@documenso/lib/client-only/providers/session'; import { useSession } from '@documenso/lib/client-only/providers/session';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { formatDocumentsPath } from '@documenso/lib/utils/teams'; import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { trpc as trpcClient } from '@documenso/trpc/client'; import { trpc as trpcClient } from '@documenso/trpc/client';
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button'; import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
@@ -66,7 +67,7 @@ export const DocumentsTableActionDropdown = ({ row }: DocumentsTableActionDropdo
// const isRecipient = !!recipient; // const isRecipient = !!recipient;
const isDraft = row.status === DocumentStatus.DRAFT; const isDraft = row.status === DocumentStatus.DRAFT;
const isPending = row.status === DocumentStatus.PENDING; const isPending = row.status === DocumentStatus.PENDING;
const isComplete = row.status === DocumentStatus.COMPLETED; const isComplete = isDocumentCompleted(row.status);
// const isSigned = recipient?.signingStatus === SigningStatus.SIGNED; // const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
const isCurrentTeamDocument = team && row.team?.url === team.url; const isCurrentTeamDocument = team && row.team?.url === team.url;
const canManageDocument = Boolean(isOwner || isCurrentTeamDocument); const canManageDocument = Boolean(isOwner || isCurrentTeamDocument);

View File

@@ -9,8 +9,8 @@ import { match } from 'ts-pattern';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params'; import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import { useSession } from '@documenso/lib/client-only/providers/session'; import { useSession } from '@documenso/lib/client-only/providers/session';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { formatDocumentsPath } from '@documenso/lib/utils/teams'; import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
import type { TFindDocumentsResponse } from '@documenso/trpc/server/document-router/schema'; import type { TFindDocumentsResponse } from '@documenso/trpc/server/document-router/schema';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table'; import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
import { DataTable } from '@documenso/ui/primitives/data-table'; import { DataTable } from '@documenso/ui/primitives/data-table';
@@ -77,7 +77,7 @@ export const DocumentsTable = ({ data, isLoading, isLoadingError }: DocumentsTab
{ {
header: _(msg`Actions`), header: _(msg`Actions`),
cell: ({ row }) => cell: ({ row }) =>
(!row.original.deletedAt || row.original.status === ExtendedDocumentStatus.COMPLETED) && ( (!row.original.deletedAt || isDocumentCompleted(row.original.status)) && (
<div className="flex items-center gap-x-4"> <div className="flex items-center gap-x-4">
<DocumentsTableActionButton row={row.original} /> <DocumentsTableActionButton row={row.original} />
<DocumentsTableActionDropdown row={row.original} /> <DocumentsTableActionDropdown row={row.original} />

View File

@@ -103,7 +103,9 @@ export default function AdminDocumentDetailsPage({ loaderData }: Route.Component
variant="outline" variant="outline"
loading={isResealDocumentLoading} loading={isResealDocumentLoading}
disabled={document.recipients.some( disabled={document.recipients.some(
(recipient) => recipient.signingStatus !== SigningStatus.SIGNED, (recipient) =>
recipient.signingStatus !== SigningStatus.SIGNED &&
recipient.signingStatus !== SigningStatus.REJECTED,
)} )}
onClick={() => resealDocument({ id: document.id })} onClick={() => resealDocument({ id: document.id })}
> >

View File

@@ -226,6 +226,9 @@ export default function DocumentPage() {
.with(DocumentStatus.COMPLETED, () => ( .with(DocumentStatus.COMPLETED, () => (
<Trans>This document has been signed by all recipients</Trans> <Trans>This document has been signed by all recipients</Trans>
)) ))
.with(DocumentStatus.REJECTED, () => (
<Trans>This document has been rejected by a recipient</Trans>
))
.with(DocumentStatus.DRAFT, () => ( .with(DocumentStatus.DRAFT, () => (
<Trans>This document is currently a draft and has not been sent</Trans> <Trans>This document is currently a draft and has not been sent</Trans>
)) ))

View File

@@ -1,5 +1,5 @@
import { Plural, Trans } from '@lingui/react/macro'; import { Plural, Trans } from '@lingui/react/macro';
import { DocumentStatus as InternalDocumentStatus, TeamMemberRole } from '@prisma/client'; import { TeamMemberRole } from '@prisma/client';
import { ChevronLeft, Users2 } from 'lucide-react'; import { ChevronLeft, Users2 } from 'lucide-react';
import { Link, redirect } from 'react-router'; import { Link, redirect } from 'react-router';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
@@ -9,6 +9,7 @@ import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-ent
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id'; import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import { type TGetTeamByUrlResponse, getTeamByUrl } from '@documenso/lib/server-only/team/get-team'; import { type TGetTeamByUrlResponse, getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { DocumentVisibility } from '@documenso/lib/types/document-visibility'; import { DocumentVisibility } from '@documenso/lib/types/document-visibility';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { formatDocumentsPath } from '@documenso/lib/utils/teams'; import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { DocumentEditForm } from '~/components/general/document/document-edit-form'; import { DocumentEditForm } from '~/components/general/document/document-edit-form';
@@ -71,7 +72,7 @@ export async function loader({ params, request }: Route.LoaderArgs) {
throw redirect(documentRootPath); throw redirect(documentRootPath);
} }
if (document.status === InternalDocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
throw redirect(`${documentRootPath}/${documentId}`); throw redirect(`${documentRootPath}/${documentId}`);
} }

View File

@@ -129,7 +129,7 @@ export default function DocumentsLogsPage({ loaderData }: Route.ComponentProps)
<Trans>Document</Trans> <Trans>Document</Trans>
</Link> </Link>
<div className="flex flex-col justify-between truncate sm:flex-row"> <div className="flex flex-col">
<div> <div>
<h1 <h1
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl" className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
@@ -137,7 +137,8 @@ export default function DocumentsLogsPage({ loaderData }: Route.ComponentProps)
> >
{document.title} {document.title}
</h1> </h1>
</div>
<div className="mt-1 flex flex-col justify-between sm:flex-row">
<div className="mt-2.5 flex items-center gap-x-6"> <div className="mt-2.5 flex items-center gap-x-6">
<DocumentStatusComponent <DocumentStatusComponent
inheritColor inheritColor
@@ -145,16 +146,15 @@ export default function DocumentsLogsPage({ loaderData }: Route.ComponentProps)
className="text-muted-foreground" className="text-muted-foreground"
/> />
</div> </div>
</div> <div className="mt-4 flex w-full flex-row sm:mt-0 sm:w-auto sm:self-end">
<DocumentCertificateDownloadButton
className="mr-2"
documentId={document.id}
documentStatus={document.status}
/>
<div className="mt-4 flex w-full flex-row sm:mt-0 sm:w-auto sm:self-end"> <DocumentAuditLogDownloadButton documentId={document.id} />
<DocumentCertificateDownloadButton </div>
className="mr-2"
documentId={document.id}
documentStatus={document.status}
/>
<DocumentAuditLogDownloadButton documentId={document.id} />
</div> </div>
</div> </div>
@@ -163,7 +163,7 @@ export default function DocumentsLogsPage({ loaderData }: Route.ComponentProps)
{documentInformation.map((info, i) => ( {documentInformation.map((info, i) => (
<div className="text-foreground text-sm" key={i}> <div className="text-foreground text-sm" key={i}>
<h3 className="font-semibold">{_(info.description)}</h3> <h3 className="font-semibold">{_(info.description)}</h3>
<p className="text-muted-foreground">{info.value}</p> <p className="text-muted-foreground truncate">{info.value}</p>
</div> </div>
))} ))}

View File

@@ -50,6 +50,7 @@ export default function DocumentsPage() {
[ExtendedDocumentStatus.DRAFT]: 0, [ExtendedDocumentStatus.DRAFT]: 0,
[ExtendedDocumentStatus.PENDING]: 0, [ExtendedDocumentStatus.PENDING]: 0,
[ExtendedDocumentStatus.COMPLETED]: 0, [ExtendedDocumentStatus.COMPLETED]: 0,
[ExtendedDocumentStatus.REJECTED]: 0,
[ExtendedDocumentStatus.INBOX]: 0, [ExtendedDocumentStatus.INBOX]: 0,
[ExtendedDocumentStatus.ALL]: 0, [ExtendedDocumentStatus.ALL]: 0,
}); });

View File

@@ -1,11 +1,12 @@
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { FieldType } from '@prisma/client'; import { FieldType, SigningStatus } from '@prisma/client';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { redirect } from 'react-router'; import { redirect } from 'react-router';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
import { UAParser } from 'ua-parser-js'; import { UAParser } from 'ua-parser-js';
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
import { APP_I18N_OPTIONS, ZSupportedLanguageCodeSchema } from '@documenso/lib/constants/i18n'; import { APP_I18N_OPTIONS, ZSupportedLanguageCodeSchema } from '@documenso/lib/constants/i18n';
import { import {
RECIPIENT_ROLES_DESCRIPTION, RECIPIENT_ROLES_DESCRIPTION,
@@ -59,6 +60,8 @@ export async function loader({ request }: Route.LoaderArgs) {
throw redirect('/'); throw redirect('/');
} }
const isPlatformDocument = await isDocumentPlatform(document);
const documentLanguage = ZSupportedLanguageCodeSchema.parse(document.documentMeta?.language); const documentLanguage = ZSupportedLanguageCodeSchema.parse(document.documentMeta?.language);
const auditLogs = await getDocumentCertificateAuditLogs({ const auditLogs = await getDocumentCertificateAuditLogs({
@@ -70,6 +73,7 @@ export async function loader({ request }: Route.LoaderArgs) {
return { return {
document, document,
documentLanguage, documentLanguage,
isPlatformDocument,
auditLogs, auditLogs,
messages, messages,
}; };
@@ -85,7 +89,7 @@ export async function loader({ request }: Route.LoaderArgs) {
* Update: Maybe <Trans> tags work now after RR7 migration. * Update: Maybe <Trans> tags work now after RR7 migration.
*/ */
export default function SigningCertificate({ loaderData }: Route.ComponentProps) { export default function SigningCertificate({ loaderData }: Route.ComponentProps) {
const { document, documentLanguage, auditLogs, messages } = loaderData; const { document, documentLanguage, isPlatformDocument, auditLogs, messages } = loaderData;
const { i18n, _ } = useLingui(); const { i18n, _ } = useLingui();
@@ -159,6 +163,13 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED && log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED &&
log.data.recipientId === recipientId, log.data.recipientId === recipientId,
), ),
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED]: auditLogs[
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED
].filter(
(log) =>
log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED &&
log.data.recipientId === recipientId,
),
}; };
}; };
@@ -282,25 +293,42 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
</span> </span>
</p> </p>
<p className="text-muted-foreground text-sm print:text-xs"> {logs.DOCUMENT_RECIPIENT_REJECTED[0] ? (
<span className="font-medium">{_(msg`Signed`)}:</span>{' '} <p className="text-muted-foreground text-sm print:text-xs">
<span className="inline-block"> <span className="font-medium">{_(msg`Rejected`)}:</span>{' '}
{logs.DOCUMENT_RECIPIENT_COMPLETED[0] <span className="inline-block">
? DateTime.fromJSDate(logs.DOCUMENT_RECIPIENT_COMPLETED[0].createdAt) {logs.DOCUMENT_RECIPIENT_REJECTED[0]
.setLocale(APP_I18N_OPTIONS.defaultLocale) ? DateTime.fromJSDate(logs.DOCUMENT_RECIPIENT_REJECTED[0].createdAt)
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)') .setLocale(APP_I18N_OPTIONS.defaultLocale)
: _(msg`Unknown`)} .toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')
</span> : _(msg`Unknown`)}
</p> </span>
</p>
) : (
<p className="text-muted-foreground text-sm print:text-xs">
<span className="font-medium">{_(msg`Signed`)}:</span>{' '}
<span className="inline-block">
{logs.DOCUMENT_RECIPIENT_COMPLETED[0]
? DateTime.fromJSDate(
logs.DOCUMENT_RECIPIENT_COMPLETED[0].createdAt,
)
.setLocale(APP_I18N_OPTIONS.defaultLocale)
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')
: _(msg`Unknown`)}
</span>
</p>
)}
<p className="text-muted-foreground text-sm print:text-xs"> <p className="text-muted-foreground text-sm print:text-xs">
<span className="font-medium">{_(msg`Reason`)}:</span>{' '} <span className="font-medium">{_(msg`Reason`)}:</span>{' '}
<span className="inline-block"> <span className="inline-block">
{_( {recipient.signingStatus === SigningStatus.REJECTED
isOwner(recipient.email) ? recipient.rejectionReason
? FRIENDLY_SIGNING_REASONS['__OWNER__'] : _(
: FRIENDLY_SIGNING_REASONS[recipient.role], isOwner(recipient.email)
)} ? FRIENDLY_SIGNING_REASONS['__OWNER__']
: FRIENDLY_SIGNING_REASONS[recipient.role],
)}
</span> </span>
</p> </p>
</div> </div>
@@ -313,15 +341,17 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
</CardContent> </CardContent>
</Card> </Card>
<div className="my-8 flex-row-reverse"> {isPlatformDocument && (
<div className="flex items-end justify-end gap-x-4"> <div className="my-8 flex-row-reverse">
<p className="flex-shrink-0 text-sm font-medium print:text-xs"> <div className="flex items-end justify-end gap-x-4">
{_(msg`Signing certificate provided by`)}: <p className="flex-shrink-0 text-sm font-medium print:text-xs">
</p> {_(msg`Signing certificate provided by`)}:
</p>
<BrandingLogo className="max-h-6 print:max-h-4" /> <BrandingLogo className="max-h-6 print:max-h-4" />
</div>
</div> </div>
</div> )}
</div> </div>
); );
} }

View File

@@ -79,7 +79,14 @@ export default function DirectTemplatePage() {
const { template, directTemplateRecipient } = data; const { template, directTemplateRecipient } = data;
return ( return (
<DocumentSigningProvider email={user?.email} fullName={user?.name} signature={user?.signature}> <DocumentSigningProvider
email={user?.email}
fullName={user?.name}
signature={user?.signature}
typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
uploadSignatureEnabled={template.templateMeta?.uploadSignatureEnabled}
drawSignatureEnabled={template.templateMeta?.drawSignatureEnabled}
>
<DocumentSigningAuthProvider <DocumentSigningAuthProvider
documentAuthOptions={template.authOptions} documentAuthOptions={template.authOptions}
recipient={directTemplateRecipient} recipient={directTemplateRecipient}

View File

@@ -1,5 +1,5 @@
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { DocumentStatus, RecipientRole, SigningStatus } from '@prisma/client'; import { DocumentSigningOrder, DocumentStatus, RecipientRole, SigningStatus } from '@prisma/client';
import { Clock8 } from 'lucide-react'; import { Clock8 } from 'lucide-react';
import { Link, redirect } from 'react-router'; import { Link, redirect } from 'react-router';
import { getOptionalLoaderContext } from 'server/utils/get-loader-session'; import { getOptionalLoaderContext } from 'server/utils/get-loader-session';
@@ -13,6 +13,7 @@ import { viewedDocument } from '@documenso/lib/server-only/document/viewed-docum
import { getCompletedFieldsForToken } from '@documenso/lib/server-only/field/get-completed-fields-for-token'; import { getCompletedFieldsForToken } from '@documenso/lib/server-only/field/get-completed-fields-for-token';
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token'; import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
import { getIsRecipientsTurnToSign } from '@documenso/lib/server-only/recipient/get-is-recipient-turn'; import { getIsRecipientsTurnToSign } from '@documenso/lib/server-only/recipient/get-is-recipient-turn';
import { getNextPendingRecipient } from '@documenso/lib/server-only/recipient/get-next-pending-recipient';
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token'; import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
import { getRecipientSignatures } from '@documenso/lib/server-only/recipient/get-recipient-signatures'; import { getRecipientSignatures } from '@documenso/lib/server-only/recipient/get-recipient-signatures';
import { getRecipientsForAssistant } from '@documenso/lib/server-only/recipient/get-recipients-for-assistant'; import { getRecipientsForAssistant } from '@documenso/lib/server-only/recipient/get-recipients-for-assistant';
@@ -72,7 +73,24 @@ export async function loader({ params, request }: Route.LoaderArgs) {
? await getRecipientsForAssistant({ ? await getRecipientsForAssistant({
token, token,
}) })
: []; : [recipient];
if (
document.documentMeta?.signingOrder === DocumentSigningOrder.SEQUENTIAL &&
recipient.role !== RecipientRole.ASSISTANT
) {
const nextPendingRecipient = await getNextPendingRecipient({
documentId: document.id,
currentRecipientId: recipient.id,
});
if (nextPendingRecipient) {
allRecipients.push({
...nextPendingRecipient,
fields: [],
});
}
}
const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({ const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({
documentAuth: document.authOptions, documentAuth: document.authOptions,
@@ -160,7 +178,7 @@ export default function SigningPage() {
recipientWithFields, recipientWithFields,
} = data; } = data;
if (document.deletedAt) { if (document.deletedAt || document.status === DocumentStatus.REJECTED) {
return ( return (
<div className="-mx-4 flex max-w-[100vw] flex-col items-center overflow-x-hidden px-4 pt-16 md:-mx-8 md:px-8 lg:pt-16 xl:pt-24"> <div className="-mx-4 flex max-w-[100vw] flex-col items-center overflow-x-hidden px-4 pt-16 md:-mx-8 md:px-8 lg:pt-16 xl:pt-24">
<SigningCard3D <SigningCard3D
@@ -215,6 +233,9 @@ export default function SigningPage() {
email={recipient.email} email={recipient.email}
fullName={user?.email === recipient.email ? user?.name : recipient.name} fullName={user?.email === recipient.email ? user?.name : recipient.name}
signature={user?.email === recipient.email ? user?.signature : undefined} signature={user?.email === recipient.email ? user?.signature : undefined}
typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled}
uploadSignatureEnabled={document.documentMeta?.uploadSignatureEnabled}
drawSignatureEnabled={document.documentMeta?.drawSignatureEnabled}
> >
<DocumentSigningAuthProvider <DocumentSigningAuthProvider
documentAuthOptions={document.authOptions} documentAuthOptions={document.authOptions}

View File

@@ -17,6 +17,7 @@ import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-f
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token'; import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
import { getRecipientSignatures } from '@documenso/lib/server-only/recipient/get-recipient-signatures'; import { getRecipientSignatures } from '@documenso/lib/server-only/recipient/get-recipient-signatures';
import { getUserByEmail } from '@documenso/lib/server-only/user/get-user-by-email'; import { getUserByEmail } from '@documenso/lib/server-only/user/get-user-by-email';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { env } from '@documenso/lib/utils/env'; import { env } from '@documenso/lib/utils/env';
import DocumentDialog from '@documenso/ui/components/document/document-dialog'; import DocumentDialog from '@documenso/ui/components/document/document-dialog';
import { DocumentDownloadButton } from '@documenso/ui/components/document/document-download-button'; import { DocumentDownloadButton } from '@documenso/ui/components/document/document-download-button';
@@ -205,12 +206,12 @@ export default function CompletedSigningPage({ loaderData }: Route.ComponentProp
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4"> <div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4">
<DocumentShareButton documentId={document.id} token={recipient.token} /> <DocumentShareButton documentId={document.id} token={recipient.token} />
{document.status === DocumentStatus.COMPLETED ? ( {isDocumentCompleted(document.status) ? (
<DocumentDownloadButton <DocumentDownloadButton
className="flex-1" className="flex-1"
fileName={document.title} fileName={document.title}
documentData={document.documentData} documentData={document.documentData}
disabled={document.status !== DocumentStatus.COMPLETED} disabled={!isDocumentCompleted(document.status)}
/> />
) : ( ) : (
<DocumentDialog <DocumentDialog
@@ -268,7 +269,7 @@ export const PollUntilDocumentCompleted = ({ document }: PollUntilDocumentComple
const { revalidate } = useRevalidator(); const { revalidate } = useRevalidator();
useEffect(() => { useEffect(() => {
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
return; return;
} }

View File

@@ -131,7 +131,14 @@ export default function EmbedDirectTemplatePage() {
} = useSuperLoaderData<typeof loader>(); } = useSuperLoaderData<typeof loader>();
return ( return (
<DocumentSigningProvider email={user?.email} fullName={user?.name} signature={user?.signature}> <DocumentSigningProvider
email={user?.email}
fullName={user?.name}
signature={user?.signature}
typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
uploadSignatureEnabled={template.templateMeta?.uploadSignatureEnabled}
drawSignatureEnabled={template.templateMeta?.drawSignatureEnabled}
>
<DocumentSigningAuthProvider <DocumentSigningAuthProvider
documentAuthOptions={template.authOptions} documentAuthOptions={template.authOptions}
recipient={recipient} recipient={recipient}

View File

@@ -1,4 +1,4 @@
import { DocumentStatus, RecipientRole } from '@prisma/client'; import { RecipientRole } from '@prisma/client';
import { data } from 'react-router'; import { data } from 'react-router';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
@@ -14,6 +14,7 @@ import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-re
import { getRecipientsForAssistant } from '@documenso/lib/server-only/recipient/get-recipients-for-assistant'; import { getRecipientsForAssistant } from '@documenso/lib/server-only/recipient/get-recipients-for-assistant';
import { getTeamById } from '@documenso/lib/server-only/team/get-team'; import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import { DocumentAccessAuth } from '@documenso/lib/types/document-auth'; import { DocumentAccessAuth } from '@documenso/lib/types/document-auth';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth'; import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
import { EmbedSignDocumentClientPage } from '~/components/embed/embed-document-signing-page'; import { EmbedSignDocumentClientPage } from '~/components/embed/embed-document-signing-page';
@@ -155,6 +156,9 @@ export default function EmbedSignDocumentPage() {
email={recipient.email} email={recipient.email}
fullName={user?.email === recipient.email ? user?.name : recipient.name} fullName={user?.email === recipient.email ? user?.name : recipient.name}
signature={user?.email === recipient.email ? user?.signature : undefined} signature={user?.email === recipient.email ? user?.signature : undefined}
typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled}
uploadSignatureEnabled={document.documentMeta?.uploadSignatureEnabled}
drawSignatureEnabled={document.documentMeta?.drawSignatureEnabled}
> >
<DocumentSigningAuthProvider <DocumentSigningAuthProvider
documentAuthOptions={document.authOptions} documentAuthOptions={document.authOptions}
@@ -168,7 +172,7 @@ export default function EmbedSignDocumentPage() {
recipient={recipient} recipient={recipient}
fields={fields} fields={fields}
metadata={document.documentMeta} metadata={document.documentMeta}
isCompleted={document.status === DocumentStatus.COMPLETED} isCompleted={isDocumentCompleted(document.status)}
hidePoweredBy={ hidePoweredBy={
isCommunityPlan || isPlatformDocument || isEnterpriseDocument || hidePoweredBy isCommunityPlan || isPlatformDocument || isEnterpriseDocument || hidePoweredBy
} }

View File

@@ -12,6 +12,7 @@ const themeSessionStorage = createCookieSessionStorage({
secrets: ['insecure-secret-do-not-care'], secrets: ['insecure-secret-do-not-care'],
secure: useSecureCookies, secure: useSecureCookies,
domain: getCookieDomain(), domain: getCookieDomain(),
maxAge: 60 * 60 * 24 * 365,
}, },
}); });

View File

@@ -99,5 +99,6 @@
"vite": "^6.1.0", "vite": "^6.1.0",
"vite-plugin-babel-macros": "^1.0.6", "vite-plugin-babel-macros": "^1.0.6",
"vite-tsconfig-paths": "^5.1.4" "vite-tsconfig-paths": "^5.1.4"
} },
"version": "1.10.0-rc.1"
} }

View File

@@ -8,6 +8,7 @@ command -v docker >/dev/null 2>&1 || {
SCRIPT_DIR="$(readlink -f "$(dirname "$0")")" SCRIPT_DIR="$(readlink -f "$(dirname "$0")")"
MONOREPO_ROOT="$(readlink -f "$SCRIPT_DIR/../")" MONOREPO_ROOT="$(readlink -f "$SCRIPT_DIR/../")"
# Get Git information
APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')" APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')"
GIT_SHA="$(git rev-parse HEAD)" GIT_SHA="$(git rev-parse HEAD)"
@@ -15,12 +16,39 @@ echo "Building docker image for monorepo at $MONOREPO_ROOT"
echo "App version: $APP_VERSION" echo "App version: $APP_VERSION"
echo "Git SHA: $GIT_SHA" echo "Git SHA: $GIT_SHA"
# Build with temporary base tag
docker build -f "$SCRIPT_DIR/Dockerfile" \ docker build -f "$SCRIPT_DIR/Dockerfile" \
--progress=plain \ --progress=plain \
-t "documenso/documenso:latest" \ -t "documenso-base" \
-t "documenso/documenso:$GIT_SHA" \
-t "documenso/documenso:$APP_VERSION" \
-t "ghcr.io/documenso/documenso:latest" \
-t "ghcr.io/documenso/documenso:$GIT_SHA" \
-t "ghcr.io/documenso/documenso:$APP_VERSION" \
"$MONOREPO_ROOT" "$MONOREPO_ROOT"
# Handle repository tagging
if [ ! -z "$DOCKER_REPOSITORY" ]; then
echo "Using custom repository: $DOCKER_REPOSITORY"
# Add tags for custom repository
docker tag "documenso-base" "$DOCKER_REPOSITORY:latest"
docker tag "documenso-base" "$DOCKER_REPOSITORY:$GIT_SHA"
# Add version tag if available
if [ ! -z "$APP_VERSION" ] && [ "$APP_VERSION" != "undefined" ]; then
docker tag "documenso-base" "$DOCKER_REPOSITORY:$APP_VERSION"
fi
else
echo "Using default repositories: dockerhub and ghcr.io"
# Add tags for both default repositories
docker tag "documenso-base" "documenso/documenso:latest"
docker tag "documenso-base" "documenso/documenso:$GIT_SHA"
docker tag "documenso-base" "ghcr.io/documenso/documenso:latest"
docker tag "documenso-base" "ghcr.io/documenso/documenso:$GIT_SHA"
# Add version tags if available
if [ ! -z "$APP_VERSION" ] && [ "$APP_VERSION" != "undefined" ]; then
docker tag "documenso-base" "documenso/documenso:$APP_VERSION"
docker tag "documenso-base" "ghcr.io/documenso/documenso:$APP_VERSION"
fi
fi
# Remove the temporary base tag
docker rmi "documenso-base"

View File

@@ -9,11 +9,11 @@ SCRIPT_DIR="$(readlink -f "$(dirname "$0")")"
MONOREPO_ROOT="$(readlink -f "$SCRIPT_DIR/../")" MONOREPO_ROOT="$(readlink -f "$SCRIPT_DIR/../")"
# Get the platform from environment variable or set to linux/amd64 if not set # Get the platform from environment variable or set to linux/amd64 if not set
# quote the string to prevent word splitting
if [ -z "$PLATFORM" ]; then if [ -z "$PLATFORM" ]; then
PLATFORM="linux/amd64" PLATFORM="linux/amd64"
fi fi
# Get Git information
APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')" APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')"
GIT_SHA="$(git rev-parse HEAD)" GIT_SHA="$(git rev-parse HEAD)"
@@ -21,14 +21,41 @@ echo "Building docker image for monorepo at $MONOREPO_ROOT"
echo "App version: $APP_VERSION" echo "App version: $APP_VERSION"
echo "Git SHA: $GIT_SHA" echo "Git SHA: $GIT_SHA"
# Build with temporary base tag
docker buildx build \ docker buildx build \
-f "$SCRIPT_DIR/Dockerfile" \ -f "$SCRIPT_DIR/Dockerfile" \
--platform=$PLATFORM \ --platform=$PLATFORM \
--progress=plain \ --progress=plain \
-t "documenso/documenso:latest" \ -t "documenso-base" \
-t "documenso/documenso:$GIT_SHA" \
-t "documenso/documenso:$APP_VERSION" \
-t "ghcr.io/documenso/documenso:latest" \
-t "ghcr.io/documenso/documenso:$GIT_SHA" \
-t "ghcr.io/documenso/documenso:$APP_VERSION" \
"$MONOREPO_ROOT" "$MONOREPO_ROOT"
# Handle repository tagging
if [ ! -z "$DOCKER_REPOSITORY" ]; then
echo "Using custom repository: $DOCKER_REPOSITORY"
# Add tags for custom repository
docker tag "documenso-base" "$DOCKER_REPOSITORY:latest"
docker tag "documenso-base" "$DOCKER_REPOSITORY:$GIT_SHA"
# Add version tag if available
if [ ! -z "$APP_VERSION" ] && [ "$APP_VERSION" != "undefined" ]; then
docker tag "documenso-base" "$DOCKER_REPOSITORY:$APP_VERSION"
fi
else
echo "Using default repositories: dockerhub and ghcr.io"
# Add tags for both default repositories
docker tag "documenso-base" "documenso/documenso:latest"
docker tag "documenso-base" "documenso/documenso:$GIT_SHA"
docker tag "documenso-base" "ghcr.io/documenso/documenso:latest"
docker tag "documenso-base" "ghcr.io/documenso/documenso:$GIT_SHA"
# Add version tags if available
if [ ! -z "$APP_VERSION" ] && [ "$APP_VERSION" != "undefined" ]; then
docker tag "documenso-base" "documenso/documenso:$APP_VERSION"
docker tag "documenso-base" "ghcr.io/documenso/documenso:$APP_VERSION"
fi
fi
# Remove the temporary base tag
docker rmi "documenso-base"

5
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@documenso/root", "name": "@documenso/root",
"version": "1.9.0-rc.11", "version": "1.10.0-rc.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@documenso/root", "name": "@documenso/root",
"version": "1.9.0-rc.11", "version": "1.10.0-rc.1",
"workspaces": [ "workspaces": [
"apps/*", "apps/*",
"packages/*" "packages/*"
@@ -95,6 +95,7 @@
}, },
"apps/remix": { "apps/remix": {
"name": "@documenso/remix", "name": "@documenso/remix",
"version": "1.10.0-rc.1",
"dependencies": { "dependencies": {
"@documenso/api": "*", "@documenso/api": "*",
"@documenso/assets": "*", "@documenso/assets": "*",

View File

@@ -1,6 +1,6 @@
{ {
"private": true, "private": true,
"version": "1.9.0-rc.11", "version": "1.10.0-rc.1",
"scripts": { "scripts": {
"build": "turbo run build", "build": "turbo run build",
"dev": "turbo run dev --filter=@documenso/remix", "dev": "turbo run dev --filter=@documenso/remix",
@@ -14,7 +14,7 @@
"prepare": "husky && husky install || true", "prepare": "husky && husky install || true",
"commitlint": "commitlint --edit", "commitlint": "commitlint --edit",
"clean": "turbo run clean && rimraf node_modules", "clean": "turbo run clean && rimraf node_modules",
"d": "npm run dx && npm run dev", "d": "npm run dx && npm run translate:compile && npm run dev",
"dx": "npm i && npm run dx:up && npm run prisma:migrate-dev && npm run prisma:seed", "dx": "npm i && npm run dx:up && npm run prisma:migrate-dev && npm run prisma:seed",
"dx:up": "docker compose -f docker/development/compose.yml up -d", "dx:up": "docker compose -f docker/development/compose.yml up -d",
"dx:down": "docker compose -f docker/development/compose.yml down", "dx:down": "docker compose -f docker/development/compose.yml down",

View File

@@ -1,5 +1,5 @@
import type { Prisma } from '@prisma/client'; import type { Prisma } from '@prisma/client';
import { DocumentDataType, DocumentStatus, SigningStatus, TeamMemberRole } from '@prisma/client'; import { DocumentDataType, SigningStatus, TeamMemberRole } from '@prisma/client';
import { tsr } from '@ts-rest/serverless/fetch'; import { tsr } from '@ts-rest/serverless/fetch';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
@@ -50,6 +50,7 @@ import {
getPresignGetUrl, getPresignGetUrl,
getPresignPostUrl, getPresignPostUrl,
} from '@documenso/lib/universal/upload/server-actions'; } from '@documenso/lib/universal/upload/server-actions';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
@@ -176,7 +177,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
}; };
} }
if (document.status !== DocumentStatus.COMPLETED) { if (!isDocumentCompleted(document.status)) {
return { return {
status: 400, status: 400,
body: { body: {
@@ -322,8 +323,11 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
dateFormat: dateFormat?.value, dateFormat: dateFormat?.value,
redirectUrl: body.meta.redirectUrl, redirectUrl: body.meta.redirectUrl,
signingOrder: body.meta.signingOrder, signingOrder: body.meta.signingOrder,
allowDictateNextSigner: body.meta.allowDictateNextSigner,
language: body.meta.language, language: body.meta.language,
typedSignatureEnabled: body.meta.typedSignatureEnabled, typedSignatureEnabled: body.meta.typedSignatureEnabled,
uploadSignatureEnabled: body.meta.uploadSignatureEnabled,
drawSignatureEnabled: body.meta.drawSignatureEnabled,
distributionMethod: body.meta.distributionMethod, distributionMethod: body.meta.distributionMethod,
emailSettings: body.meta.emailSettings, emailSettings: body.meta.emailSettings,
requestMetadata: metadata, requestMetadata: metadata,
@@ -669,7 +673,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
}; };
} }
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
return { return {
status: 400, status: 400,
body: { body: {
@@ -772,7 +776,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
}; };
} }
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
return { return {
status: 400, status: 400,
body: { body: {
@@ -863,7 +867,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
}; };
} }
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
return { return {
status: 400, status: 400,
body: { body: {
@@ -922,7 +926,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
}; };
} }
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
return { return {
status: 400, status: 400,
body: { body: {
@@ -987,7 +991,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
}; };
} }
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
return { return {
status: 400, status: 400,
body: { message: 'Document is already completed' }, body: { message: 'Document is already completed' },
@@ -1149,7 +1153,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
}; };
} }
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
return { return {
status: 400, status: 400,
body: { body: {
@@ -1237,7 +1241,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
}; };
} }
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
return { return {
status: 400, status: 400,
body: { body: {

View File

@@ -96,7 +96,7 @@ export const ZSendDocumentForSigningMutationSchema = z
'Whether to send completion emails when the document is fully signed. This will override the document email settings.', 'Whether to send completion emails when the document is fully signed. This will override the document email settings.',
}), }),
}) })
.or(z.literal('').transform(() => ({ sendEmail: true, sendCompletionEmails: undefined }))); .or(z.any().transform(() => ({ sendEmail: true, sendCompletionEmails: undefined })));
export type TSendDocumentForSigningMutationSchema = typeof ZSendDocumentForSigningMutationSchema; export type TSendDocumentForSigningMutationSchema = typeof ZSendDocumentForSigningMutationSchema;
@@ -155,8 +155,11 @@ export const ZCreateDocumentMutationSchema = z.object({
}), }),
redirectUrl: z.string(), redirectUrl: z.string(),
signingOrder: z.nativeEnum(DocumentSigningOrder).optional(), signingOrder: z.nativeEnum(DocumentSigningOrder).optional(),
allowDictateNextSigner: z.boolean().optional(),
language: z.enum(SUPPORTED_LANGUAGE_CODES).optional(), language: z.enum(SUPPORTED_LANGUAGE_CODES).optional(),
typedSignatureEnabled: z.boolean().optional().default(true), typedSignatureEnabled: z.boolean().optional().default(true),
uploadSignatureEnabled: z.boolean().optional().default(true),
drawSignatureEnabled: z.boolean().optional().default(true),
distributionMethod: z.nativeEnum(DocumentDistributionMethod).optional(), distributionMethod: z.nativeEnum(DocumentDistributionMethod).optional(),
emailSettings: ZDocumentEmailSettingsSchema.optional(), emailSettings: ZDocumentEmailSettingsSchema.optional(),
}) })
@@ -218,6 +221,7 @@ export const ZCreateDocumentFromTemplateMutationSchema = z.object({
dateFormat: z.string(), dateFormat: z.string(),
redirectUrl: z.string(), redirectUrl: z.string(),
signingOrder: z.nativeEnum(DocumentSigningOrder).optional(), signingOrder: z.nativeEnum(DocumentSigningOrder).optional(),
allowDictateNextSigner: z.boolean().optional(),
language: z.enum(SUPPORTED_LANGUAGE_CODES).optional(), language: z.enum(SUPPORTED_LANGUAGE_CODES).optional(),
}) })
.partial() .partial()
@@ -285,9 +289,12 @@ export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
dateFormat: z.string(), dateFormat: z.string(),
redirectUrl: ZUrlSchema, redirectUrl: ZUrlSchema,
signingOrder: z.nativeEnum(DocumentSigningOrder), signingOrder: z.nativeEnum(DocumentSigningOrder),
allowDictateNextSigner: z.boolean(),
language: z.enum(SUPPORTED_LANGUAGE_CODES), language: z.enum(SUPPORTED_LANGUAGE_CODES),
distributionMethod: z.nativeEnum(DocumentDistributionMethod), distributionMethod: z.nativeEnum(DocumentDistributionMethod),
typedSignatureEnabled: z.boolean(), typedSignatureEnabled: z.boolean(),
uploadSignatureEnabled: z.boolean(),
drawSignatureEnabled: z.boolean(),
emailSettings: ZDocumentEmailSettingsSchema, emailSettings: ZDocumentEmailSettingsSchema,
}) })
.partial() .partial()

View File

@@ -210,7 +210,7 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient au
}), }),
}, },
], ],
fields: [FieldType.DATE], fields: [FieldType.DATE, FieldType.SIGNATURE],
}); });
for (const recipient of recipients) { for (const recipient of recipients) {
@@ -246,7 +246,9 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient au
}); });
} }
await signSignaturePad(page); if (fields.some((field) => field.type === FieldType.SIGNATURE)) {
await signSignaturePad(page);
}
for (const field of fields) { for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click(); await page.locator(`#field-${field.id}`).getByRole('button').click();
@@ -307,7 +309,7 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient an
}), }),
}, },
], ],
fields: [FieldType.DATE], fields: [FieldType.DATE, FieldType.SIGNATURE],
updateDocumentOptions: { updateDocumentOptions: {
authOptions: createDocumentAuthOptions({ authOptions: createDocumentAuthOptions({
globalAccessAuth: null, globalAccessAuth: null,
@@ -349,7 +351,9 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient an
}); });
} }
await signSignaturePad(page); if (fields.some((field) => field.type === FieldType.SIGNATURE)) {
await signSignaturePad(page);
}
for (const field of fields) { for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click(); await page.locator(`#field-${field.id}`).getByRole('button').click();

View File

@@ -0,0 +1,390 @@
import { expect, test } from '@playwright/test';
import {
DocumentSigningOrder,
DocumentStatus,
FieldType,
RecipientRole,
SigningStatus,
} from '@prisma/client';
import { prisma } from '@documenso/prisma';
import { seedPendingDocumentWithFullFields } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import { signDirectSignaturePad, signSignaturePad } from '../fixtures/signature';
test('[NEXT_RECIPIENT_DICTATION]: should allow updating next recipient when dictation is enabled', async ({
page,
}) => {
const user = await seedUser();
const firstSigner = await seedUser();
const secondSigner = await seedUser();
const thirdSigner = await seedUser();
const { recipients, document } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: [firstSigner, secondSigner, thirdSigner],
recipientsCreateOptions: [{ signingOrder: 1 }, { signingOrder: 2 }, { signingOrder: 3 }],
updateDocumentOptions: {
documentMeta: {
upsert: {
create: {
allowDictateNextSigner: true,
signingOrder: DocumentSigningOrder.SEQUENTIAL,
},
update: {
allowDictateNextSigner: true,
signingOrder: DocumentSigningOrder.SEQUENTIAL,
},
},
},
},
});
const firstRecipient = recipients[0];
const { token, fields } = firstRecipient;
const signUrl = `/sign/${token}`;
await page.goto(signUrl);
await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible();
await signSignaturePad(page);
// Fill in all fields
for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
if (field.type === FieldType.TEXT) {
await page.locator('#custom-text').fill('TEXT');
await page.getByRole('button', { name: 'Save' }).click();
}
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
}
// Complete signing and update next recipient
await page.getByRole('button', { name: 'Complete' }).click();
// Verify next recipient info is shown
await expect(page.getByRole('dialog')).toBeVisible();
await expect(page.getByText('The next recipient to sign this document will be')).toBeVisible();
// Update next recipient
await page.locator('button').filter({ hasText: 'Update Recipient' }).click();
await page.waitForTimeout(1000);
// Use dialog context to ensure we're targeting the correct form fields
const dialog = page.getByRole('dialog');
await dialog.getByLabel('Name').fill('New Recipient');
await dialog.getByLabel('Email').fill('new.recipient@example.com');
// Submit and verify completion
await page.getByRole('button', { name: 'Sign' }).click();
await page.waitForURL(`${signUrl}/complete`);
// Verify document and recipient states
const updatedDocument = await prisma.document.findUniqueOrThrow({
where: { id: document.id },
include: {
recipients: {
orderBy: { signingOrder: 'asc' },
},
},
});
// Document should still be pending as there are more recipients
expect(updatedDocument.status).toBe(DocumentStatus.PENDING);
// First recipient should be completed
const updatedFirstRecipient = updatedDocument.recipients[0];
expect(updatedFirstRecipient.signingStatus).toBe(SigningStatus.SIGNED);
// Second recipient should be the new recipient
const updatedSecondRecipient = updatedDocument.recipients[1];
expect(updatedSecondRecipient.name).toBe('New Recipient');
expect(updatedSecondRecipient.email).toBe('new.recipient@example.com');
expect(updatedSecondRecipient.signingOrder).toBe(2);
expect(updatedSecondRecipient.signingStatus).toBe(SigningStatus.NOT_SIGNED);
});
test('[NEXT_RECIPIENT_DICTATION]: should not show dictation UI when disabled', async ({ page }) => {
const user = await seedUser();
const firstSigner = await seedUser();
const secondSigner = await seedUser();
const { recipients, document } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: [firstSigner, secondSigner],
recipientsCreateOptions: [{ signingOrder: 1 }, { signingOrder: 2 }],
updateDocumentOptions: {
documentMeta: {
upsert: {
create: {
allowDictateNextSigner: false,
signingOrder: DocumentSigningOrder.SEQUENTIAL,
},
update: {
allowDictateNextSigner: false,
signingOrder: DocumentSigningOrder.SEQUENTIAL,
},
},
},
},
});
const firstRecipient = recipients[0];
const { token, fields } = firstRecipient;
const signUrl = `/sign/${token}`;
await page.goto(signUrl);
await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible();
await signSignaturePad(page);
// Fill in all fields
for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
if (field.type === FieldType.TEXT) {
await page.locator('#custom-text').fill('TEXT');
await page.getByRole('button', { name: 'Save' }).click();
}
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
}
// Complete signing
await page.getByRole('button', { name: 'Complete' }).click();
// Verify next recipient UI is not shown
await expect(
page.getByText('The next recipient to sign this document will be'),
).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Update Recipient' })).not.toBeVisible();
// Submit and verify completion
await page.getByRole('button', { name: 'Sign' }).click();
await page.waitForURL(`${signUrl}/complete`);
// Verify document and recipient states
const updatedDocument = await prisma.document.findUniqueOrThrow({
where: { id: document.id },
include: {
recipients: {
orderBy: { signingOrder: 'asc' },
},
},
});
// Document should still be pending as there are more recipients
expect(updatedDocument.status).toBe(DocumentStatus.PENDING);
// First recipient should be completed
const updatedFirstRecipient = updatedDocument.recipients[0];
expect(updatedFirstRecipient.signingStatus).toBe(SigningStatus.SIGNED);
// Second recipient should remain unchanged
const updatedSecondRecipient = updatedDocument.recipients[1];
expect(updatedSecondRecipient.email).toBe(secondSigner.email);
expect(updatedSecondRecipient.signingOrder).toBe(2);
expect(updatedSecondRecipient.signingStatus).toBe(SigningStatus.NOT_SIGNED);
});
test('[NEXT_RECIPIENT_DICTATION]: should work with parallel signing flow', async ({ page }) => {
const user = await seedUser();
const firstSigner = await seedUser();
const secondSigner = await seedUser();
const { recipients, document } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: [firstSigner, secondSigner],
recipientsCreateOptions: [{ signingOrder: 1 }, { signingOrder: 2 }],
updateDocumentOptions: {
documentMeta: {
upsert: {
create: {
allowDictateNextSigner: false,
signingOrder: DocumentSigningOrder.PARALLEL,
},
update: {
allowDictateNextSigner: false,
signingOrder: DocumentSigningOrder.PARALLEL,
},
},
},
},
});
// Test both recipients can sign in parallel
for (const recipient of recipients) {
const { token, fields } = recipient;
const signUrl = `/sign/${token}`;
await page.goto(signUrl);
await expect(page.getByRole('heading', { name: 'Sign Document' })).toBeVisible();
await signSignaturePad(page);
// Fill in all fields
for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
if (field.type === FieldType.TEXT) {
await page.locator('#custom-text').fill('TEXT');
await page.getByRole('button', { name: 'Save' }).click();
}
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
}
// Complete signing
await page.getByRole('button', { name: 'Complete' }).click();
// Verify next recipient UI is not shown in parallel flow
await expect(
page.getByText('The next recipient to sign this document will be'),
).not.toBeVisible();
await expect(page.getByRole('button', { name: 'Update Recipient' })).not.toBeVisible();
// Submit and verify completion
await page.getByRole('button', { name: 'Sign' }).click();
await page.waitForURL(`${signUrl}/complete`);
}
// Verify final document and recipient states
await expect(async () => {
const updatedDocument = await prisma.document.findUniqueOrThrow({
where: { id: document.id },
include: {
recipients: {
orderBy: { signingOrder: 'asc' },
},
},
});
// Document should be completed since all recipients have signed
expect(updatedDocument.status).toBe(DocumentStatus.COMPLETED);
// All recipients should be completed
for (const recipient of updatedDocument.recipients) {
expect(recipient.signingStatus).toBe(SigningStatus.SIGNED);
}
}).toPass();
});
test('[NEXT_RECIPIENT_DICTATION]: should allow assistant to dictate next signer', async ({
page,
}) => {
const user = await seedUser();
const assistant = await seedUser();
const signer = await seedUser();
const thirdSigner = await seedUser();
const { recipients, document } = await seedPendingDocumentWithFullFields({
owner: user,
recipients: [assistant, signer, thirdSigner],
recipientsCreateOptions: [
{ signingOrder: 1, role: RecipientRole.ASSISTANT },
{ signingOrder: 2, role: RecipientRole.SIGNER },
{ signingOrder: 3, role: RecipientRole.SIGNER },
],
updateDocumentOptions: {
documentMeta: {
upsert: {
create: {
allowDictateNextSigner: true,
signingOrder: DocumentSigningOrder.SEQUENTIAL,
},
update: {
allowDictateNextSigner: true,
signingOrder: DocumentSigningOrder.SEQUENTIAL,
},
},
},
},
});
const assistantRecipient = recipients[0];
const { token, fields } = assistantRecipient;
const signUrl = `/sign/${token}`;
await page.goto(signUrl);
await expect(page.getByRole('heading', { name: 'Assist Document' })).toBeVisible();
await page.getByRole('radio', { name: assistantRecipient.name }).click();
// Fill in all fields
for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click();
if (field.type === FieldType.SIGNATURE) {
await signDirectSignaturePad(page);
await page.getByRole('button', { name: 'Sign', exact: true }).click();
}
if (field.type === FieldType.TEXT) {
await page.locator('#custom-text').fill('TEXT');
await page.getByRole('button', { name: 'Save' }).click();
}
await expect(page.locator(`#field-${field.id}`)).toHaveAttribute('data-inserted', 'true');
}
// Complete assisting and update next recipient
await page.getByRole('button', { name: 'Continue' }).click();
// Verify next recipient info is shown
await expect(page.getByRole('dialog')).toBeVisible();
await expect(page.getByText('The next recipient to sign this document will be')).toBeVisible();
// Update next recipient
await page.locator('button').filter({ hasText: 'Update Recipient' }).click();
// Use dialog context to ensure we're targeting the correct form fields
const dialog = page.getByRole('dialog');
await dialog.getByLabel('Name').fill('New Signer');
await dialog.getByLabel('Email').fill('new.signer@example.com');
// Submit and verify completion
await page.getByRole('button', { name: /Continue|Proceed/i }).click();
await page.waitForURL(`${signUrl}/complete`);
// Verify document and recipient states
await expect(async () => {
const updatedDocument = await prisma.document.findUniqueOrThrow({
where: { id: document.id },
include: {
recipients: {
orderBy: { signingOrder: 'asc' },
},
},
});
// Document should still be pending as there are more recipients
expect(updatedDocument.status).toBe(DocumentStatus.PENDING);
// Assistant should be completed
const updatedAssistant = updatedDocument.recipients[0];
expect(updatedAssistant.signingStatus).toBe(SigningStatus.SIGNED);
expect(updatedAssistant.role).toBe(RecipientRole.ASSISTANT);
// Second recipient should be the new signer
const updatedSigner = updatedDocument.recipients[1];
expect(updatedSigner.name).toBe('New Signer');
expect(updatedSigner.email).toBe('new.signer@example.com');
expect(updatedSigner.signingOrder).toBe(2);
expect(updatedSigner.signingStatus).toBe(SigningStatus.NOT_SIGNED);
expect(updatedSigner.role).toBe(RecipientRole.SIGNER);
// Third recipient should remain unchanged
const thirdRecipient = updatedDocument.recipients[2];
expect(thirdRecipient.email).toBe(thirdSigner.email);
expect(thirdRecipient.signingOrder).toBe(3);
expect(thirdRecipient.signingStatus).toBe(SigningStatus.NOT_SIGNED);
expect(thirdRecipient.role).toBe(RecipientRole.SIGNER);
}).toPass();
});

View File

@@ -222,7 +222,10 @@ test.describe('Signing Certificate Tests', () => {
// Toggle signing certificate setting // Toggle signing certificate setting
await page.getByLabel('Include the Signing Certificate in the Document').click(); await page.getByLabel('Include the Signing Certificate in the Document').click();
await page.getByRole('button', { name: /Save/ }).first().click(); await page
.getByRole('button', { name: /Update/ })
.first()
.click();
await page.waitForTimeout(1000); await page.waitForTimeout(1000);
@@ -236,7 +239,10 @@ test.describe('Signing Certificate Tests', () => {
// Toggle the setting back to true // Toggle the setting back to true
await page.getByLabel('Include the Signing Certificate in the Document').click(); await page.getByLabel('Include the Signing Certificate in the Document').click();
await page.getByRole('button', { name: /Save/ }).first().click(); await page
.getByRole('button', { name: /Update/ })
.first()
.click();
await page.waitForTimeout(1000); await page.waitForTimeout(1000);

View File

@@ -1,40 +1,28 @@
import type { Page } from '@playwright/test'; import type { Page } from '@playwright/test';
/**
* Will open the signature pad dialog and sign it.
*/
export const signSignaturePad = async (page: Page) => { export const signSignaturePad = async (page: Page) => {
await page.waitForTimeout(200); await page.waitForTimeout(200);
const canvas = page.getByTestId('signature-pad'); await page.getByTestId('signature-pad-dialog-button').click();
const box = await canvas.boundingBox(); // Click type tab
await page.getByRole('tab', { name: 'Type' }).click();
await page.getByTestId('signature-pad-type-input').fill('Signature');
if (!box) { // Click Next button
throw new Error('Signature pad not found'); await page.getByRole('button', { name: 'Next' }).click();
} };
// Calculate center point /**
const centerX = box.x + box.width / 2; * For when the signature pad is already open.
const centerY = box.y + box.height / 2; */
export const signDirectSignaturePad = async (page: Page) => {
// Calculate square size (making it slightly smaller than the canvas) await page.waitForTimeout(200);
const squareSize = Math.min(box.width, box.height) * 0.4; // 40% of the smallest dimension
// Click type tab
// Move to center await page.getByRole('tab', { name: 'Type' }).click();
await page.mouse.move(centerX, centerY); await page.getByTestId('signature-pad-type-input').fill('Signature');
await page.mouse.down();
// Draw square clockwise from center
// Move right
await page.mouse.move(centerX + squareSize, centerY, { steps: 10 });
// Move down
await page.mouse.move(centerX + squareSize, centerY + squareSize, { steps: 10 });
// Move left
await page.mouse.move(centerX - squareSize, centerY + squareSize, { steps: 10 });
// Move up
await page.mouse.move(centerX - squareSize, centerY - squareSize, { steps: 10 });
// Move right
await page.mouse.move(centerX + squareSize, centerY - squareSize, { steps: 10 });
// Move down to close the square
await page.mouse.move(centerX + squareSize, centerY, { steps: 10 });
await page.mouse.up();
}; };

View File

@@ -56,6 +56,7 @@ test('[PUBLIC_PROFILE]: create profile', async ({ page }) => {
// Go back to public profile page. // Go back to public profile page.
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/settings/public-profile`); await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/settings/public-profile`);
await page.getByRole('switch').click(); await page.getByRole('switch').click();
await page.waitForTimeout(1000);
// Assert values. // Assert values.
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/p/${publicProfileUrl}`); await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/p/${publicProfileUrl}`);
@@ -127,6 +128,7 @@ test('[PUBLIC_PROFILE]: create team profile', async ({ page }) => {
// Go back to public profile page. // Go back to public profile page.
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/settings/public-profile`); await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/settings/public-profile`);
await page.getByRole('switch').click(); await page.getByRole('switch').click();
await page.waitForTimeout(1000);
// Assert values. // Assert values.
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/p/${publicProfileUrl}`); await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/p/${publicProfileUrl}`);

View File

@@ -23,7 +23,7 @@ test('[TEAMS]: update the default document visibility in the team global setting
// !: Brittle selector // !: Brittle selector
await page.getByRole('combobox').first().click(); await page.getByRole('combobox').first().click();
await page.getByRole('option', { name: 'Admin' }).click(); await page.getByRole('option', { name: 'Admin' }).click();
await page.getByRole('button', { name: 'Save' }).first().click(); await page.getByRole('button', { name: 'Update' }).first().click();
const toast = page.locator('li[role="status"][data-state="open"]').first(); const toast = page.locator('li[role="status"][data-state="open"]').first();
await expect(toast).toBeVisible(); await expect(toast).toBeVisible();
@@ -47,7 +47,7 @@ test('[TEAMS]: update the sender details in the team global settings', async ({
await expect(checkbox).toBeChecked(); await expect(checkbox).toBeChecked();
await page.getByRole('button', { name: 'Save' }).first().click(); await page.getByRole('button', { name: 'Update' }).first().click();
const toast = page.locator('li[role="status"][data-state="open"]').first(); const toast = page.locator('li[role="status"][data-state="open"]').first();
await expect(toast).toBeVisible(); await expect(toast).toBeVisible();

View File

@@ -0,0 +1,182 @@
import { expect, test } from '@playwright/test';
import { prisma } from '@documenso/prisma';
import {
seedTeamDocumentWithMeta,
seedTeamDocuments,
seedTeamTemplateWithMeta,
} from '@documenso/prisma/seed/documents';
import { apiSignin } from '../fixtures/authentication';
test.describe.configure({ mode: 'parallel' });
test('[TEAMS]: check that default team signature settings are all enabled', async ({ page }) => {
const { team } = await seedTeamDocuments();
await apiSignin({
page,
email: team.owner.email,
password: 'password',
redirectPath: `/t/${team.url}/settings/preferences`,
});
// Verify that the default created team settings has all signatures enabled
await expect(page.getByRole('combobox').filter({ hasText: 'Type' })).toBeVisible();
await expect(page.getByRole('combobox').filter({ hasText: 'Upload' })).toBeVisible();
await expect(page.getByRole('combobox').filter({ hasText: 'Draw' })).toBeVisible();
const document = await seedTeamDocumentWithMeta(team);
// Create a document and check the settings
await page.goto(`/t/${team.url}/documents/${document.id}/edit`);
// Verify that the settings match
await page.getByRole('button', { name: 'Advanced Options' }).click();
await expect(page.getByRole('combobox').filter({ hasText: 'Type' })).toBeVisible();
await expect(page.getByRole('combobox').filter({ hasText: 'Upload' })).toBeVisible();
await expect(page.getByRole('combobox').filter({ hasText: 'Draw' })).toBeVisible();
// Go to document and check that the signatured tabs are correct.
await page.goto(`/sign/${document.recipients[0].token}`);
await page.getByTestId('signature-pad-dialog-button').click();
// Check the tab values
await expect(page.getByRole('tab', { name: 'Type' })).toBeVisible();
await expect(page.getByRole('tab', { name: 'Upload' })).toBeVisible();
await expect(page.getByRole('tab', { name: 'Draw' })).toBeVisible();
});
test('[TEAMS]: check signature modes can be disabled', async ({ page }) => {
const { team } = await seedTeamDocuments();
await apiSignin({
page,
email: team.owner.email,
password: 'password',
redirectPath: `/t/${team.url}/settings/preferences`,
});
const allTabs = ['Type', 'Upload', 'Draw'];
const tabTest = [['Type', 'Upload', 'Draw'], ['Type', 'Upload'], ['Type']];
for (const tabs of tabTest) {
await page.goto(`/t/${team.url}/settings/preferences`);
// Update combobox to have the correct tabs
await page.getByTestId('signature-types-combobox').click();
await expect(page.getByRole('option', { name: 'Type' })).toBeVisible();
await expect(page.getByRole('option', { name: 'Upload' })).toBeVisible();
await expect(page.getByRole('option', { name: 'Draw' })).toBeVisible();
// Clear all selected items.
for (const tab of allTabs) {
const item = page.getByRole('option', { name: tab });
const isSelected = (await item.innerHTML()).includes('opacity-100');
if (isSelected) {
await item.click();
}
}
// Selected wanted items.
for (const tab of tabs) {
const item = page.getByRole('option', { name: tab });
await item.click();
}
await page.getByRole('button', { name: 'Update' }).first().click();
const document = await seedTeamDocumentWithMeta(team);
// Go to document and check that the signatured tabs are correct.
await page.goto(`/sign/${document.recipients[0].token}`);
await page.getByTestId('signature-pad-dialog-button').click();
// Check the tab values
for (const tab of allTabs) {
if (tabs.includes(tab)) {
await expect(page.getByRole('tab', { name: tab })).toBeVisible();
} else {
await expect(page.getByRole('tab', { name: tab })).not.toBeVisible();
}
}
}
});
test('[TEAMS]: check signature modes work for templates', async ({ page }) => {
const { team } = await seedTeamDocuments();
await apiSignin({
page,
email: team.owner.email,
password: 'password',
redirectPath: `/t/${team.url}/settings/preferences`,
});
const allTabs = ['Type', 'Upload', 'Draw'];
const tabTest = [['Type', 'Upload', 'Draw'], ['Type', 'Upload'], ['Type']];
for (const tabs of tabTest) {
await page.goto(`/t/${team.url}/settings/preferences`);
// Update combobox to have the correct tabs
await page.getByTestId('signature-types-combobox').click();
await expect(page.getByRole('option', { name: 'Type' })).toBeVisible();
await expect(page.getByRole('option', { name: 'Upload' })).toBeVisible();
await expect(page.getByRole('option', { name: 'Draw' })).toBeVisible();
// Clear all selected items.
for (const tab of allTabs) {
const item = page.getByRole('option', { name: tab });
const isSelected = (await item.innerHTML()).includes('opacity-100');
if (isSelected) {
await item.click();
}
}
// Selected wanted items.
for (const tab of tabs) {
const item = page.getByRole('option', { name: tab });
await item.click();
}
await page.getByRole('button', { name: 'Update' }).first().click();
const template = await seedTeamTemplateWithMeta(team);
await page.goto(`/t/${team.url}/templates/${template.id}`);
await page.getByRole('button', { name: 'Use' }).click();
// Check the send document checkbox to true
await page.getByLabel('Send document').click();
await page.getByRole('button', { name: 'Create and send' }).click();
await page.waitForTimeout(1000);
const document = await prisma.document.findFirst({
where: {
templateId: template.id,
},
include: {
documentMeta: true,
},
});
// Test kinda flaky, debug here.
// console.log({
// tabs,
// typedSignatureEnabled: document?.documentMeta?.typedSignatureEnabled,
// uploadSignatureEnabled: document?.documentMeta?.uploadSignatureEnabled,
// drawSignatureEnabled: document?.documentMeta?.drawSignatureEnabled,
// });
expect(document?.documentMeta?.typedSignatureEnabled).toEqual(tabs.includes('Type'));
expect(document?.documentMeta?.uploadSignatureEnabled).toEqual(tabs.includes('Upload'));
expect(document?.documentMeta?.drawSignatureEnabled).toEqual(tabs.includes('Draw'));
}
});

View File

@@ -298,6 +298,7 @@ test('[DIRECT_TEMPLATES]: use direct template link with 2 recipients', async ({
await page.goto(formatDirectTemplatePath(template.directLink?.token || '')); await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
await page.waitForTimeout(1000);
await page.getByPlaceholder('recipient@documenso.com').fill(seedTestEmail()); await page.getByPlaceholder('recipient@documenso.com').fill(seedTestEmail());
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();

View File

@@ -4,6 +4,7 @@ import { getUserByEmail } from '@documenso/lib/server-only/user/get-user-by-emai
import { seedUser } from '@documenso/prisma/seed/users'; import { seedUser } from '@documenso/prisma/seed/users';
import { apiSignin } from '../fixtures/authentication'; import { apiSignin } from '../fixtures/authentication';
import { signSignaturePad } from '../fixtures/signature';
test('[USER] update full name', async ({ page }) => { test('[USER] update full name', async ({ page }) => {
const user = await seedUser(); const user = await seedUser();
@@ -12,7 +13,7 @@ test('[USER] update full name', async ({ page }) => {
await page.getByLabel('Full Name').fill('John Doe'); await page.getByLabel('Full Name').fill('John Doe');
await page.getByPlaceholder('Type your signature').fill('John Doe'); await signSignaturePad(page);
await page.getByRole('button', { name: 'Update profile' }).click(); await page.getByRole('button', { name: 'Update profile' }).click();

View File

@@ -8,12 +8,14 @@ export interface TemplateDocumentCancelProps {
inviterEmail: string; inviterEmail: string;
documentName: string; documentName: string;
assetBaseUrl: string; assetBaseUrl: string;
cancellationReason?: string;
} }
export const TemplateDocumentCancel = ({ export const TemplateDocumentCancel = ({
inviterName, inviterName,
documentName, documentName,
assetBaseUrl, assetBaseUrl,
cancellationReason,
}: TemplateDocumentCancelProps) => { }: TemplateDocumentCancelProps) => {
return ( return (
<> <>
@@ -34,6 +36,12 @@ export const TemplateDocumentCancel = ({
<Text className="my-1 text-center text-base text-slate-400"> <Text className="my-1 text-center text-base text-slate-400">
<Trans>You don't need to sign it anymore.</Trans> <Trans>You don't need to sign it anymore.</Trans>
</Text> </Text>
{cancellationReason && (
<Text className="mt-4 text-center text-base">
<Trans>Reason for cancellation: {cancellationReason}</Trans>
</Text>
)}
</Section> </Section>
</> </>
); );

View File

@@ -14,6 +14,7 @@ export const DocumentCancelTemplate = ({
inviterEmail = 'lucas@documenso.com', inviterEmail = 'lucas@documenso.com',
documentName = 'Open Source Pledge.pdf', documentName = 'Open Source Pledge.pdf',
assetBaseUrl = 'http://localhost:3002', assetBaseUrl = 'http://localhost:3002',
cancellationReason,
}: DocumentCancelEmailTemplateProps) => { }: DocumentCancelEmailTemplateProps) => {
const { _ } = useLingui(); const { _ } = useLingui();
const branding = useBranding(); const branding = useBranding();
@@ -48,6 +49,7 @@ export const DocumentCancelTemplate = ({
inviterEmail={inviterEmail} inviterEmail={inviterEmail}
documentName={documentName} documentName={documentName}
assetBaseUrl={assetBaseUrl} assetBaseUrl={assetBaseUrl}
cancellationReason={cancellationReason}
/> />
</Section> </Section>
</Container> </Container>

View File

@@ -8,6 +8,9 @@ export const DOCUMENT_STATUS: {
[DocumentStatus.COMPLETED]: { [DocumentStatus.COMPLETED]: {
description: msg`Completed`, description: msg`Completed`,
}, },
[DocumentStatus.REJECTED]: {
description: msg`Rejected`,
},
[DocumentStatus.DRAFT]: { [DocumentStatus.DRAFT]: {
description: msg`Draft`, description: msg`Draft`,
}, },
@@ -31,3 +34,29 @@ export const DOCUMENT_DISTRIBUTION_METHODS: Record<string, DocumentDistributionM
description: msg`None`, description: msg`None`,
}, },
} satisfies Record<DocumentDistributionMethod, DocumentDistributionMethodTypeData>; } satisfies Record<DocumentDistributionMethod, DocumentDistributionMethodTypeData>;
export enum DocumentSignatureType {
DRAW = 'draw',
TYPE = 'type',
UPLOAD = 'upload',
}
type DocumentSignatureTypeData = {
label: MessageDescriptor;
value: DocumentSignatureType;
};
export const DOCUMENT_SIGNATURE_TYPES = {
[DocumentSignatureType.DRAW]: {
label: msg`Draw`,
value: DocumentSignatureType.DRAW,
},
[DocumentSignatureType.TYPE]: {
label: msg`Type`,
value: DocumentSignatureType.TYPE,
},
[DocumentSignatureType.UPLOAD]: {
label: msg`Upload`,
value: DocumentSignatureType.UPLOAD,
},
} satisfies Record<DocumentSignatureType, DocumentSignatureTypeData>;

View File

@@ -0,0 +1,4 @@
export const SIGNATURE_CANVAS_DPI = 2;
export const SIGNATURE_MIN_COVERAGE_THRESHOLD = 0.01;
export const isBase64Image = (value: string) => value.startsWith('data:image/png;base64,');

View File

@@ -1,5 +1,6 @@
import { JobClient } from './client/client'; import { JobClient } from './client/client';
import { SEND_CONFIRMATION_EMAIL_JOB_DEFINITION } from './definitions/emails/send-confirmation-email'; import { SEND_CONFIRMATION_EMAIL_JOB_DEFINITION } from './definitions/emails/send-confirmation-email';
import { SEND_DOCUMENT_CANCELLED_EMAILS_JOB_DEFINITION } from './definitions/emails/send-document-cancelled-emails';
import { SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION } from './definitions/emails/send-password-reset-success-email'; import { SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION } from './definitions/emails/send-password-reset-success-email';
import { SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION } from './definitions/emails/send-recipient-signed-email'; import { SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION } from './definitions/emails/send-recipient-signed-email';
import { SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION } from './definitions/emails/send-rejection-emails'; import { SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION } from './definitions/emails/send-rejection-emails';
@@ -24,6 +25,7 @@ export const jobsClient = new JobClient([
SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION, SEND_PASSWORD_RESET_SUCCESS_EMAIL_JOB_DEFINITION,
SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION, SEND_SIGNING_REJECTION_EMAILS_JOB_DEFINITION,
SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION, SEND_RECIPIENT_SIGNED_EMAIL_JOB_DEFINITION,
SEND_DOCUMENT_CANCELLED_EMAILS_JOB_DEFINITION,
BULK_SEND_TEMPLATE_JOB_DEFINITION, BULK_SEND_TEMPLATE_JOB_DEFINITION,
] as const); ] as const);

View File

@@ -0,0 +1,105 @@
import { createElement } from 'react';
import { msg } from '@lingui/core/macro';
import { ReadStatus, SendStatus, SigningStatus } from '@prisma/client';
import { mailer } from '@documenso/email/mailer';
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
import { FROM_ADDRESS, FROM_NAME } from '../../../constants/email';
import { extractDerivedDocumentEmailSettings } from '../../../types/document-email';
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../../utils/team-global-settings-to-branding';
import type { JobRunIO } from '../../client/_internal/job';
import type { TSendDocumentCancelledEmailsJobDefinition } from './send-document-cancelled-emails';
export const run = async ({
payload,
io,
}: {
payload: TSendDocumentCancelledEmailsJobDefinition;
io: JobRunIO;
}) => {
const { documentId, cancellationReason } = payload;
const document = await prisma.document.findFirstOrThrow({
where: {
id: documentId,
},
include: {
user: true,
documentMeta: true,
recipients: true,
team: {
select: {
teamEmail: true,
name: true,
url: true,
teamGlobalSettings: true,
},
},
},
});
const { documentMeta, user: documentOwner } = document;
// Check if document cancellation emails are enabled
const isEmailEnabled = extractDerivedDocumentEmailSettings(documentMeta).documentDeleted;
if (!isEmailEnabled) {
return;
}
const i18n = await getI18nInstance(documentMeta?.language);
// Send cancellation emails to all recipients who have been sent the document or viewed it
const recipientsToNotify = document.recipients.filter(
(recipient) =>
(recipient.sendStatus === SendStatus.SENT || recipient.readStatus === ReadStatus.OPENED) &&
recipient.signingStatus !== SigningStatus.REJECTED,
);
await io.runTask('send-cancellation-emails', async () => {
await Promise.all(
recipientsToNotify.map(async (recipient) => {
const template = createElement(DocumentCancelTemplate, {
documentName: document.title,
inviterName: documentOwner.name || undefined,
inviterEmail: documentOwner.email,
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
cancellationReason: cancellationReason || 'The document has been cancelled.',
});
const branding = document.team?.teamGlobalSettings
? teamGlobalSettingsToBranding(document.team.teamGlobalSettings)
: undefined;
const [html, text] = await Promise.all([
renderEmailWithI18N(template, { lang: documentMeta?.language, branding }),
renderEmailWithI18N(template, {
lang: documentMeta?.language,
branding,
plainText: true,
}),
]);
await mailer.sendMail({
to: {
name: recipient.name,
address: recipient.email,
},
from: {
name: FROM_NAME,
address: FROM_ADDRESS,
},
subject: i18n._(msg`Document "${document.title}" Cancelled`),
html,
text,
});
}),
);
});
};

View File

@@ -0,0 +1,33 @@
import { z } from 'zod';
import { type JobDefinition } from '../../client/_internal/job';
const SEND_DOCUMENT_CANCELLED_EMAILS_JOB_DEFINITION_ID = 'send.document.cancelled.emails';
const SEND_DOCUMENT_CANCELLED_EMAILS_JOB_DEFINITION_SCHEMA = z.object({
documentId: z.number(),
cancellationReason: z.string().optional(),
requestMetadata: z.any().optional(),
});
export type TSendDocumentCancelledEmailsJobDefinition = z.infer<
typeof SEND_DOCUMENT_CANCELLED_EMAILS_JOB_DEFINITION_SCHEMA
>;
export const SEND_DOCUMENT_CANCELLED_EMAILS_JOB_DEFINITION = {
id: SEND_DOCUMENT_CANCELLED_EMAILS_JOB_DEFINITION_ID,
name: 'Send Document Cancelled Emails',
version: '1.0.0',
trigger: {
name: SEND_DOCUMENT_CANCELLED_EMAILS_JOB_DEFINITION_ID,
schema: SEND_DOCUMENT_CANCELLED_EMAILS_JOB_DEFINITION_SCHEMA,
},
handler: async ({ payload, io }) => {
const handler = await import('./send-document-cancelled-emails.handler');
await handler.run({ payload, io });
},
} as const satisfies JobDefinition<
typeof SEND_DOCUMENT_CANCELLED_EMAILS_JOB_DEFINITION_ID,
TSendDocumentCancelledEmailsJobDefinition
>;

View File

@@ -23,6 +23,8 @@ const SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
brandingHidePoweredBy: z.boolean(), brandingHidePoweredBy: z.boolean(),
teamId: z.number(), teamId: z.number(),
typedSignatureEnabled: z.boolean(), typedSignatureEnabled: z.boolean(),
uploadSignatureEnabled: z.boolean(),
drawSignatureEnabled: z.boolean(),
}) })
.nullish(), .nullish(),
}), }),

View File

@@ -6,9 +6,11 @@ import { PDFDocument } from 'pdf-lib';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { signPdf } from '@documenso/signing'; import { signPdf } from '@documenso/signing';
import { AppError, AppErrorCode } from '../../../errors/app-error';
import { sendCompletedEmail } from '../../../server-only/document/send-completed-email'; import { sendCompletedEmail } from '../../../server-only/document/send-completed-email';
import PostHogServerClient from '../../../server-only/feature-flags/get-post-hog-server-client'; import PostHogServerClient from '../../../server-only/feature-flags/get-post-hog-server-client';
import { getCertificatePdf } from '../../../server-only/htmltopdf/get-certificate-pdf'; import { getCertificatePdf } from '../../../server-only/htmltopdf/get-certificate-pdf';
import { addRejectionStampToPdf } from '../../../server-only/pdf/add-rejection-stamp-to-pdf';
import { flattenAnnotations } from '../../../server-only/pdf/flatten-annotations'; import { flattenAnnotations } from '../../../server-only/pdf/flatten-annotations';
import { flattenForm } from '../../../server-only/pdf/flatten-form'; import { flattenForm } from '../../../server-only/pdf/flatten-form';
import { insertFieldInPDF } from '../../../server-only/pdf/insert-field-in-pdf'; import { insertFieldInPDF } from '../../../server-only/pdf/insert-field-in-pdf';
@@ -22,6 +24,7 @@ import {
import { getFileServerSide } from '../../../universal/upload/get-file.server'; import { getFileServerSide } from '../../../universal/upload/get-file.server';
import { putPdfFileServerSide } from '../../../universal/upload/put-file.server'; import { putPdfFileServerSide } from '../../../universal/upload/put-file.server';
import { fieldsContainUnsignedRequiredField } from '../../../utils/advanced-fields-helpers'; import { fieldsContainUnsignedRequiredField } from '../../../utils/advanced-fields-helpers';
import { isDocumentCompleted } from '../../../utils/document';
import { createDocumentAuditLogData } from '../../../utils/document-audit-logs'; import { createDocumentAuditLogData } from '../../../utils/document-audit-logs';
import type { JobRunIO } from '../../client/_internal/job'; import type { JobRunIO } from '../../client/_internal/job';
import type { TSealDocumentJobDefinition } from './seal-document'; import type { TSealDocumentJobDefinition } from './seal-document';
@@ -38,11 +41,6 @@ export const run = async ({
const document = await prisma.document.findFirstOrThrow({ const document = await prisma.document.findFirstOrThrow({
where: { where: {
id: documentId, id: documentId,
recipients: {
every: {
signingStatus: SigningStatus.SIGNED,
},
},
}, },
include: { include: {
documentMeta: true, documentMeta: true,
@@ -59,6 +57,16 @@ export const run = async ({
}, },
}); });
const isComplete =
document.recipients.some((recipient) => recipient.signingStatus === SigningStatus.REJECTED) ||
document.recipients.every((recipient) => recipient.signingStatus === SigningStatus.SIGNED);
if (!isComplete) {
throw new AppError(AppErrorCode.UNKNOWN_ERROR, {
message: 'Document is not complete',
});
}
// Seems silly but we need to do this in case the job is re-ran // Seems silly but we need to do this in case the job is re-ran
// after it has already run through the update task further below. // after it has already run through the update task further below.
// eslint-disable-next-line @typescript-eslint/require-await // eslint-disable-next-line @typescript-eslint/require-await
@@ -91,9 +99,15 @@ export const run = async ({
}, },
}); });
if (recipients.some((recipient) => recipient.signingStatus !== SigningStatus.SIGNED)) { // Determine if the document has been rejected by checking if any recipient has rejected it
throw new Error(`Document ${document.id} has unsigned recipients`); const rejectedRecipient = recipients.find(
} (recipient) => recipient.signingStatus === SigningStatus.REJECTED,
);
const isRejected = Boolean(rejectedRecipient);
// Get the rejection reason from the rejected recipient
const rejectionReason = rejectedRecipient?.rejectionReason ?? '';
const fields = await prisma.field.findMany({ const fields = await prisma.field.findMany({
where: { where: {
@@ -104,7 +118,8 @@ export const run = async ({
}, },
}); });
if (fieldsContainUnsignedRequiredField(fields)) { // Skip the field check if the document is rejected
if (!isRejected && fieldsContainUnsignedRequiredField(fields)) {
throw new Error(`Document ${document.id} has unsigned required fields`); throw new Error(`Document ${document.id} has unsigned required fields`);
} }
@@ -132,6 +147,11 @@ export const run = async ({
flattenForm(pdfDoc); flattenForm(pdfDoc);
flattenAnnotations(pdfDoc); flattenAnnotations(pdfDoc);
// Add rejection stamp if the document is rejected
if (isRejected && rejectionReason) {
await addRejectionStampToPdf(pdfDoc, rejectionReason);
}
if (certificateData) { if (certificateData) {
const certificateDoc = await PDFDocument.load(certificateData); const certificateDoc = await PDFDocument.load(certificateData);
@@ -160,8 +180,11 @@ export const run = async ({
const { name } = path.parse(document.title); const { name } = path.parse(document.title);
// Add suffix based on document status
const suffix = isRejected ? '_rejected.pdf' : '_signed.pdf';
const documentData = await putPdfFileServerSide({ const documentData = await putPdfFileServerSide({
name: `${name}_signed.pdf`, name: `${name}${suffix}`,
type: 'application/pdf', type: 'application/pdf',
arrayBuffer: async () => Promise.resolve(pdfBuffer), arrayBuffer: async () => Promise.resolve(pdfBuffer),
}); });
@@ -177,6 +200,7 @@ export const run = async ({
event: 'App: Document Sealed', event: 'App: Document Sealed',
properties: { properties: {
documentId: document.id, documentId: document.id,
isRejected,
}, },
}); });
} }
@@ -194,7 +218,7 @@ export const run = async ({
id: document.id, id: document.id,
}, },
data: { data: {
status: DocumentStatus.COMPLETED, status: isRejected ? DocumentStatus.REJECTED : DocumentStatus.COMPLETED,
completedAt: new Date(), completedAt: new Date(),
}, },
}); });
@@ -216,6 +240,7 @@ export const run = async ({
user: null, user: null,
data: { data: {
transactionId: nanoid(), transactionId: nanoid(),
...(isRejected ? { isRejected: true, rejectionReason: rejectionReason } : {}),
}, },
}), }),
}); });
@@ -223,9 +248,9 @@ export const run = async ({
}); });
await io.runTask('send-completed-email', async () => { await io.runTask('send-completed-email', async () => {
let shouldSendCompletedEmail = sendEmail && !isResealing; let shouldSendCompletedEmail = sendEmail && !isResealing && !isRejected;
if (isResealing && documentStatus !== DocumentStatus.COMPLETED) { if (isResealing && !isDocumentCompleted(document.status)) {
shouldSendCompletedEmail = sendEmail; shouldSendCompletedEmail = sendEmail;
} }
@@ -246,7 +271,9 @@ export const run = async ({
}); });
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_COMPLETED, event: isRejected
? WebhookTriggerEvents.DOCUMENT_REJECTED
: WebhookTriggerEvents.DOCUMENT_COMPLETED,
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)), data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)),
userId: updatedDocument.userId, userId: updatedDocument.userId,
teamId: updatedDocument.teamId ?? undefined, teamId: updatedDocument.teamId ?? undefined,

View File

@@ -13,6 +13,7 @@ export const getDocumentStats = async () => {
[ExtendedDocumentStatus.DRAFT]: 0, [ExtendedDocumentStatus.DRAFT]: 0,
[ExtendedDocumentStatus.PENDING]: 0, [ExtendedDocumentStatus.PENDING]: 0,
[ExtendedDocumentStatus.COMPLETED]: 0, [ExtendedDocumentStatus.COMPLETED]: 0,
[ExtendedDocumentStatus.REJECTED]: 0,
[ExtendedDocumentStatus.ALL]: 0, [ExtendedDocumentStatus.ALL]: 0,
}; };

View File

@@ -1,4 +1,4 @@
import { DocumentStatus } from '@prisma/client'; import { DocumentStatus, SubscriptionStatus } from '@prisma/client';
import { kyselyPrisma, sql } from '@documenso/prisma'; import { kyselyPrisma, sql } from '@documenso/prisma';
@@ -44,7 +44,6 @@ export async function getSigningVolume({
.on('td.status', '=', sql.lit(DocumentStatus.COMPLETED)) .on('td.status', '=', sql.lit(DocumentStatus.COMPLETED))
.on('td.deletedAt', 'is', null), .on('td.deletedAt', 'is', null),
) )
// @ts-expect-error - Raw SQL enum casting not properly typed by Kysely
.where(sql`s.status = ${SubscriptionStatus.ACTIVE}::"SubscriptionStatus"`) .where(sql`s.status = ${SubscriptionStatus.ACTIVE}::"SubscriptionStatus"`)
.where((eb) => .where((eb) =>
eb.or([ eb.or([
@@ -82,7 +81,6 @@ export async function getSigningVolume({
.selectFrom('Subscription as s') .selectFrom('Subscription as s')
.leftJoin('User as u', 's.userId', 'u.id') .leftJoin('User as u', 's.userId', 'u.id')
.leftJoin('Team as t', 's.teamId', 't.id') .leftJoin('Team as t', 's.teamId', 't.id')
// @ts-expect-error - Raw SQL enum casting not properly typed by Kysely
.where(sql`s.status = ${SubscriptionStatus.ACTIVE}::"SubscriptionStatus"`) .where(sql`s.status = ${SubscriptionStatus.ACTIVE}::"SubscriptionStatus"`)
.where((eb) => .where((eb) =>
eb.or([ eb.or([

View File

@@ -24,8 +24,11 @@ export type CreateDocumentMetaOptions = {
redirectUrl?: string; redirectUrl?: string;
emailSettings?: TDocumentEmailSettings; emailSettings?: TDocumentEmailSettings;
signingOrder?: DocumentSigningOrder; signingOrder?: DocumentSigningOrder;
allowDictateNextSigner?: boolean;
distributionMethod?: DocumentDistributionMethod; distributionMethod?: DocumentDistributionMethod;
typedSignatureEnabled?: boolean; typedSignatureEnabled?: boolean;
uploadSignatureEnabled?: boolean;
drawSignatureEnabled?: boolean;
language?: SupportedLanguageCodes; language?: SupportedLanguageCodes;
requestMetadata: ApiRequestMetadata; requestMetadata: ApiRequestMetadata;
}; };
@@ -41,9 +44,12 @@ export const upsertDocumentMeta = async ({
password, password,
redirectUrl, redirectUrl,
signingOrder, signingOrder,
allowDictateNextSigner,
emailSettings, emailSettings,
distributionMethod, distributionMethod,
typedSignatureEnabled, typedSignatureEnabled,
uploadSignatureEnabled,
drawSignatureEnabled,
language, language,
requestMetadata, requestMetadata,
}: CreateDocumentMetaOptions) => { }: CreateDocumentMetaOptions) => {
@@ -93,9 +99,12 @@ export const upsertDocumentMeta = async ({
documentId, documentId,
redirectUrl, redirectUrl,
signingOrder, signingOrder,
allowDictateNextSigner,
emailSettings, emailSettings,
distributionMethod, distributionMethod,
typedSignatureEnabled, typedSignatureEnabled,
uploadSignatureEnabled,
drawSignatureEnabled,
language, language,
}, },
update: { update: {
@@ -106,9 +115,12 @@ export const upsertDocumentMeta = async ({
timezone, timezone,
redirectUrl, redirectUrl,
signingOrder, signingOrder,
allowDictateNextSigner,
emailSettings, emailSettings,
distributionMethod, distributionMethod,
typedSignatureEnabled, typedSignatureEnabled,
uploadSignatureEnabled,
drawSignatureEnabled,
language, language,
}, },
}); });

View File

@@ -7,7 +7,10 @@ import {
WebhookTriggerEvents, WebhookTriggerEvents,
} from '@prisma/client'; } from '@prisma/client';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs'; import {
DOCUMENT_AUDIT_LOG_TYPE,
RECIPIENT_DIFF_TYPE,
} from '@documenso/lib/types/document-audit-logs';
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { fieldsContainUnsignedRequiredField } from '@documenso/lib/utils/advanced-fields-helpers'; import { fieldsContainUnsignedRequiredField } from '@documenso/lib/utils/advanced-fields-helpers';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
@@ -30,6 +33,10 @@ export type CompleteDocumentWithTokenOptions = {
userId?: number; userId?: number;
authOptions?: TRecipientActionAuth; authOptions?: TRecipientActionAuth;
requestMetadata?: RequestMetadata; requestMetadata?: RequestMetadata;
nextSigner?: {
email: string;
name: string;
};
}; };
const getDocument = async ({ token, documentId }: CompleteDocumentWithTokenOptions) => { const getDocument = async ({ token, documentId }: CompleteDocumentWithTokenOptions) => {
@@ -57,6 +64,7 @@ export const completeDocumentWithToken = async ({
token, token,
documentId, documentId,
requestMetadata, requestMetadata,
nextSigner,
}: CompleteDocumentWithTokenOptions) => { }: CompleteDocumentWithTokenOptions) => {
const document = await getDocument({ token, documentId }); const document = await getDocument({ token, documentId });
@@ -146,7 +154,6 @@ export const completeDocumentWithToken = async ({
recipientName: recipient.name, recipientName: recipient.name,
recipientId: recipient.id, recipientId: recipient.id,
recipientRole: recipient.role, recipientRole: recipient.role,
// actionAuth: derivedRecipientActionAuth || undefined,
}, },
}), }),
}); });
@@ -164,6 +171,9 @@ export const completeDocumentWithToken = async ({
select: { select: {
id: true, id: true,
signingOrder: true, signingOrder: true,
name: true,
email: true,
role: true,
}, },
where: { where: {
documentId: document.id, documentId: document.id,
@@ -186,9 +196,49 @@ export const completeDocumentWithToken = async ({
const [nextRecipient] = pendingRecipients; const [nextRecipient] = pendingRecipients;
await prisma.$transaction(async (tx) => { await prisma.$transaction(async (tx) => {
if (nextSigner && document.documentMeta?.allowDictateNextSigner) {
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED,
documentId: document.id,
user: {
name: recipient.name,
email: recipient.email,
},
requestMetadata,
data: {
recipientEmail: nextRecipient.email,
recipientName: nextRecipient.name,
recipientId: nextRecipient.id,
recipientRole: nextRecipient.role,
changes: [
{
type: RECIPIENT_DIFF_TYPE.NAME,
from: nextRecipient.name,
to: nextSigner.name,
},
{
type: RECIPIENT_DIFF_TYPE.EMAIL,
from: nextRecipient.email,
to: nextSigner.email,
},
],
},
}),
});
}
await tx.recipient.update({ await tx.recipient.update({
where: { id: nextRecipient.id }, where: { id: nextRecipient.id },
data: { sendStatus: SendStatus.SENT }, data: {
sendStatus: SendStatus.SENT,
...(nextSigner && document.documentMeta?.allowDictateNextSigner
? {
name: nextSigner.name,
email: nextSigner.email,
}
: {}),
},
}); });
await jobs.triggerJob({ await jobs.triggerJob({

View File

@@ -158,6 +158,10 @@ export const createDocumentV2 = async ({
language: meta?.language || team?.teamGlobalSettings?.documentLanguage, language: meta?.language || team?.teamGlobalSettings?.documentLanguage,
typedSignatureEnabled: typedSignatureEnabled:
meta?.typedSignatureEnabled ?? team?.teamGlobalSettings?.typedSignatureEnabled, meta?.typedSignatureEnabled ?? team?.teamGlobalSettings?.typedSignatureEnabled,
uploadSignatureEnabled:
meta?.uploadSignatureEnabled ?? team?.teamGlobalSettings?.uploadSignatureEnabled,
drawSignatureEnabled:
meta?.drawSignatureEnabled ?? team?.teamGlobalSettings?.drawSignatureEnabled,
}, },
}, },
}, },

View File

@@ -128,8 +128,10 @@ export const createDocument = async ({
documentMeta: { documentMeta: {
create: { create: {
language: team?.teamGlobalSettings?.documentLanguage, language: team?.teamGlobalSettings?.documentLanguage,
typedSignatureEnabled: team?.teamGlobalSettings?.typedSignatureEnabled,
timezone: timezone, timezone: timezone,
typedSignatureEnabled: team?.teamGlobalSettings?.typedSignatureEnabled ?? true,
uploadSignatureEnabled: team?.teamGlobalSettings?.uploadSignatureEnabled ?? true,
drawSignatureEnabled: team?.teamGlobalSettings?.drawSignatureEnabled ?? true,
}, },
}, },
}, },

View File

@@ -26,6 +26,7 @@ import {
mapDocumentToWebhookDocumentPayload, mapDocumentToWebhookDocumentPayload,
} from '../../types/webhook-payload'; } from '../../types/webhook-payload';
import type { ApiRequestMetadata } from '../../universal/extract-request-metadata'; import type { ApiRequestMetadata } from '../../universal/extract-request-metadata';
import { isDocumentCompleted } from '../../utils/document';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n'; import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-to-branding'; import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-to-branding';
@@ -161,7 +162,7 @@ const handleDocumentOwnerDelete = async ({
} }
// Soft delete completed documents. // Soft delete completed documents.
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
return await prisma.$transaction(async (tx) => { return await prisma.$transaction(async (tx) => {
await tx.documentAuditLog.create({ await tx.documentAuditLog.create({
data: createDocumentAuditLogData({ data: createDocumentAuditLogData({

View File

@@ -356,6 +356,24 @@ const findDocumentsFilter = (status: ExtendedDocumentStatus, user: User) => {
}, },
], ],
})) }))
.with(ExtendedDocumentStatus.REJECTED, () => ({
OR: [
{
userId: user.id,
teamId: null,
status: ExtendedDocumentStatus.REJECTED,
},
{
status: ExtendedDocumentStatus.REJECTED,
recipients: {
some: {
email: user.email,
signingStatus: SigningStatus.REJECTED,
},
},
},
],
}))
.exhaustive(); .exhaustive();
}; };
@@ -548,5 +566,38 @@ const findTeamDocumentsFilter = (
return filter; return filter;
}) })
.with(ExtendedDocumentStatus.REJECTED, () => {
const filter: Prisma.DocumentWhereInput = {
status: ExtendedDocumentStatus.REJECTED,
OR: [
{
teamId: team.id,
OR: visibilityFilters,
},
],
};
if (teamEmail && filter.OR) {
filter.OR.push(
{
recipients: {
some: {
email: teamEmail,
signingStatus: SigningStatus.REJECTED,
},
},
OR: visibilityFilters,
},
{
user: {
email: teamEmail,
},
OR: visibilityFilters,
},
);
}
return filter;
})
.exhaustive(); .exhaustive();
}; };

View File

@@ -16,6 +16,7 @@ export const getDocumentCertificateAuditLogs = async ({
type: { type: {
in: [ in: [
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED, DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED,
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED,
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED, DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT, DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
], ],
@@ -29,6 +30,9 @@ export const getDocumentCertificateAuditLogs = async ({
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED]: auditLogs.filter( [DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED]: auditLogs.filter(
(log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED, (log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED,
), ),
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED]: auditLogs.filter(
(log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED,
),
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED]: auditLogs.filter( [DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED]: auditLogs.filter(
(log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED, (log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
), ),

View File

@@ -44,6 +44,7 @@ export const getStats = async ({ user, period, search = '', ...options }: GetSta
[ExtendedDocumentStatus.DRAFT]: 0, [ExtendedDocumentStatus.DRAFT]: 0,
[ExtendedDocumentStatus.PENDING]: 0, [ExtendedDocumentStatus.PENDING]: 0,
[ExtendedDocumentStatus.COMPLETED]: 0, [ExtendedDocumentStatus.COMPLETED]: 0,
[ExtendedDocumentStatus.REJECTED]: 0,
[ExtendedDocumentStatus.INBOX]: 0, [ExtendedDocumentStatus.INBOX]: 0,
[ExtendedDocumentStatus.ALL]: 0, [ExtendedDocumentStatus.ALL]: 0,
}; };
@@ -64,6 +65,10 @@ export const getStats = async ({ user, period, search = '', ...options }: GetSta
if (stat.status === ExtendedDocumentStatus.PENDING) { if (stat.status === ExtendedDocumentStatus.PENDING) {
stats[ExtendedDocumentStatus.PENDING] += stat._count._all; stats[ExtendedDocumentStatus.PENDING] += stat._count._all;
} }
if (stat.status === ExtendedDocumentStatus.REJECTED) {
stats[ExtendedDocumentStatus.REJECTED] += stat._count._all;
}
}); });
Object.keys(stats).forEach((key) => { Object.keys(stats).forEach((key) => {

View File

@@ -1,18 +1,12 @@
import { SigningStatus } from '@prisma/client'; import { SigningStatus } from '@prisma/client';
import { WebhookTriggerEvents } from '@prisma/client';
import { jobs } from '@documenso/lib/jobs/client'; import { jobs } from '@documenso/lib/jobs/client';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../errors/app-error'; import { AppError, AppErrorCode } from '../../errors/app-error';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs'; import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../types/webhook-payload';
import type { RequestMetadata } from '../../universal/extract-request-metadata'; import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs'; import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type RejectDocumentWithTokenOptions = { export type RejectDocumentWithTokenOptions = {
token: string; token: string;
@@ -84,7 +78,16 @@ export async function rejectDocumentWithToken({
}), }),
]); ]);
// Send email notifications // Trigger the seal document job to process the document asynchronously
await jobs.triggerJob({
name: 'internal.seal-document',
payload: {
documentId,
requestMetadata,
},
});
// Send email notifications to the rejecting recipient
await jobs.triggerJob({ await jobs.triggerJob({
name: 'send.signing.rejected.emails', name: 'send.signing.rejected.emails',
payload: { payload: {
@@ -93,27 +96,14 @@ export async function rejectDocumentWithToken({
}, },
}); });
// Get the updated document with all recipients // Send cancellation emails to other recipients
const updatedDocument = await prisma.document.findFirst({ await jobs.triggerJob({
where: { name: 'send.document.cancelled.emails',
id: document.id, payload: {
documentId,
cancellationReason: reason,
requestMetadata,
}, },
include: {
recipients: true,
documentMeta: true,
},
});
if (!updatedDocument) {
throw new Error('Document not found after update');
}
// Trigger webhook for document rejection
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_REJECTED,
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)),
userId: document.userId,
teamId: document.teamId ?? undefined,
}); });
return updatedRecipient; return updatedRecipient;

View File

@@ -20,6 +20,7 @@ import { prisma } from '@documenso/prisma';
import { getI18nInstance } from '../../client-only/providers/i18n-server'; import { getI18nInstance } from '../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app'; import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { extractDerivedDocumentEmailSettings } from '../../types/document-email'; import { extractDerivedDocumentEmailSettings } from '../../types/document-email';
import { isDocumentCompleted } from '../../utils/document';
import { renderEmailWithI18N } from '../../utils/render-email-with-i18n'; import { renderEmailWithI18N } from '../../utils/render-email-with-i18n';
import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-to-branding'; import { teamGlobalSettingsToBranding } from '../../utils/team-global-settings-to-branding';
import { getDocumentWhereInput } from './get-document-by-id'; import { getDocumentWhereInput } from './get-document-by-id';
@@ -88,7 +89,7 @@ export const resendDocument = async ({
throw new Error('Can not send draft document'); throw new Error('Can not send draft document');
} }
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
throw new Error('Can not send completed document'); throw new Error('Can not send completed document');
} }

View File

@@ -18,6 +18,7 @@ import { getFileServerSide } from '../../universal/upload/get-file.server';
import { putPdfFileServerSide } from '../../universal/upload/put-file.server'; import { putPdfFileServerSide } from '../../universal/upload/put-file.server';
import { fieldsContainUnsignedRequiredField } from '../../utils/advanced-fields-helpers'; import { fieldsContainUnsignedRequiredField } from '../../utils/advanced-fields-helpers';
import { getCertificatePdf } from '../htmltopdf/get-certificate-pdf'; import { getCertificatePdf } from '../htmltopdf/get-certificate-pdf';
import { addRejectionStampToPdf } from '../pdf/add-rejection-stamp-to-pdf';
import { flattenAnnotations } from '../pdf/flatten-annotations'; import { flattenAnnotations } from '../pdf/flatten-annotations';
import { flattenForm } from '../pdf/flatten-form'; import { flattenForm } from '../pdf/flatten-form';
import { insertFieldInPDF } from '../pdf/insert-field-in-pdf'; import { insertFieldInPDF } from '../pdf/insert-field-in-pdf';
@@ -41,11 +42,6 @@ export const sealDocument = async ({
const document = await prisma.document.findFirstOrThrow({ const document = await prisma.document.findFirstOrThrow({
where: { where: {
id: documentId, id: documentId,
recipients: {
every: {
signingStatus: SigningStatus.SIGNED,
},
},
}, },
include: { include: {
documentData: true, documentData: true,
@@ -78,7 +74,21 @@ export const sealDocument = async ({
}, },
}); });
if (recipients.some((recipient) => recipient.signingStatus !== SigningStatus.SIGNED)) { // Determine if the document has been rejected by checking if any recipient has rejected it
const rejectedRecipient = recipients.find(
(recipient) => recipient.signingStatus === SigningStatus.REJECTED,
);
const isRejected = Boolean(rejectedRecipient);
// Get the rejection reason from the rejected recipient
const rejectionReason = rejectedRecipient?.rejectionReason ?? '';
// If the document is not rejected, ensure all recipients have signed
if (
!isRejected &&
recipients.some((recipient) => recipient.signingStatus !== SigningStatus.SIGNED)
) {
throw new Error(`Document ${document.id} has unsigned recipients`); throw new Error(`Document ${document.id} has unsigned recipients`);
} }
@@ -91,7 +101,8 @@ export const sealDocument = async ({
}, },
}); });
if (fieldsContainUnsignedRequiredField(fields)) { // Skip the field check if the document is rejected
if (!isRejected && fieldsContainUnsignedRequiredField(fields)) {
throw new Error(`Document ${document.id} has unsigned required fields`); throw new Error(`Document ${document.id} has unsigned required fields`);
} }
@@ -119,6 +130,11 @@ export const sealDocument = async ({
flattenForm(doc); flattenForm(doc);
flattenAnnotations(doc); flattenAnnotations(doc);
// Add rejection stamp if the document is rejected
if (isRejected && rejectionReason) {
await addRejectionStampToPdf(doc, rejectionReason);
}
if (certificateData) { if (certificateData) {
const certificate = await PDFDocument.load(certificateData); const certificate = await PDFDocument.load(certificateData);
@@ -142,8 +158,11 @@ export const sealDocument = async ({
const { name } = path.parse(document.title); const { name } = path.parse(document.title);
// Add suffix based on document status
const suffix = isRejected ? '_rejected.pdf' : '_signed.pdf';
const { data: newData } = await putPdfFileServerSide({ const { data: newData } = await putPdfFileServerSide({
name: `${name}_signed.pdf`, name: `${name}${suffix}`,
type: 'application/pdf', type: 'application/pdf',
arrayBuffer: async () => Promise.resolve(pdfBuffer), arrayBuffer: async () => Promise.resolve(pdfBuffer),
}); });
@@ -156,6 +175,7 @@ export const sealDocument = async ({
event: 'App: Document Sealed', event: 'App: Document Sealed',
properties: { properties: {
documentId: document.id, documentId: document.id,
isRejected,
}, },
}); });
} }
@@ -166,7 +186,7 @@ export const sealDocument = async ({
id: document.id, id: document.id,
}, },
data: { data: {
status: DocumentStatus.COMPLETED, status: isRejected ? DocumentStatus.REJECTED : DocumentStatus.COMPLETED,
completedAt: new Date(), completedAt: new Date(),
}, },
}); });
@@ -188,6 +208,7 @@ export const sealDocument = async ({
user: null, user: null,
data: { data: {
transactionId: nanoid(), transactionId: nanoid(),
...(isRejected ? { isRejected: true, rejectionReason } : {}),
}, },
}), }),
}); });
@@ -209,7 +230,9 @@ export const sealDocument = async ({
}); });
await triggerWebhook({ await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_COMPLETED, event: isRejected
? WebhookTriggerEvents.DOCUMENT_REJECTED
: WebhookTriggerEvents.DOCUMENT_COMPLETED,
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)), data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(updatedDocument)),
userId: document.userId, userId: document.userId,
teamId: document.teamId ?? undefined, teamId: document.teamId ?? undefined,

View File

@@ -20,6 +20,7 @@ import {
} from '../../types/webhook-payload'; } from '../../types/webhook-payload';
import { getFileServerSide } from '../../universal/upload/get-file.server'; import { getFileServerSide } from '../../universal/upload/get-file.server';
import { putPdfFileServerSide } from '../../universal/upload/put-file.server'; import { putPdfFileServerSide } from '../../universal/upload/put-file.server';
import { isDocumentCompleted } from '../../utils/document';
import { insertFormValuesInPdf } from '../pdf/insert-form-values-in-pdf'; import { insertFormValuesInPdf } from '../pdf/insert-form-values-in-pdf';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook'; import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
@@ -74,7 +75,7 @@ export const sendDocument = async ({
throw new Error('Document has no recipients'); throw new Error('Document has no recipients');
} }
if (document.status === DocumentStatus.COMPLETED) { if (isDocumentCompleted(document.status)) {
throw new Error('Can not send completed document'); throw new Error('Can not send completed document');
} }

View File

@@ -201,7 +201,7 @@ export const signFieldWithToken = async ({
throw new Error('Signature field must have a signature'); throw new Error('Signature field must have a signature');
} }
if (isSignatureField && !documentMeta?.typedSignatureEnabled && typedSignature) { if (isSignatureField && documentMeta?.typedSignatureEnabled === false && typedSignature) {
throw new Error('Typed signatures are not allowed. Please draw your signature'); throw new Error('Typed signatures are not allowed. Please draw your signature');
} }

View File

@@ -0,0 +1,87 @@
import fontkit from '@pdf-lib/fontkit';
import type { PDFDocument } from 'pdf-lib';
import { TextAlignment, rgb, setFontAndSize } from 'pdf-lib';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
/**
* Adds a rejection stamp to each page of a PDF document.
* The stamp is placed in the center of the page.
*/
export async function addRejectionStampToPdf(
pdfDoc: PDFDocument,
reason: string,
): Promise<PDFDocument> {
const pages = pdfDoc.getPages();
pdfDoc.registerFontkit(fontkit);
const fontBytes = await fetch(`${NEXT_PUBLIC_WEBAPP_URL()}/fonts/noto-sans.ttf`).then(
async (res) => res.arrayBuffer(),
);
const font = await pdfDoc.embedFont(fontBytes, {
customName: 'Noto',
});
const form = pdfDoc.getForm();
for (let i = 0; i < pages.length; i++) {
const page = pages[i];
const { width, height } = page.getSize();
// Draw the "REJECTED" text
const rejectedTitleText = 'DOCUMENT REJECTED';
const rejectedTitleFontSize = 36;
const rejectedTitleTextField = form.createTextField(`internal-document-rejected-title-${i}`);
if (!rejectedTitleTextField.acroField.getDefaultAppearance()) {
rejectedTitleTextField.acroField.setDefaultAppearance(
setFontAndSize('Noto', rejectedTitleFontSize).toString(),
);
}
rejectedTitleTextField.updateAppearances(font);
rejectedTitleTextField.setFontSize(rejectedTitleFontSize);
rejectedTitleTextField.setText(rejectedTitleText);
rejectedTitleTextField.setAlignment(TextAlignment.Center);
const rejectedTitleTextWidth =
font.widthOfTextAtSize(rejectedTitleText, rejectedTitleFontSize) * 1.2;
const rejectedTitleTextHeight = font.heightAtSize(rejectedTitleFontSize);
// Calculate the center position of the page
const centerX = width / 2;
const centerY = height / 2;
// Position the title text at the center of the page
const rejectedTitleTextX = centerX - rejectedTitleTextWidth / 2;
const rejectedTitleTextY = centerY - rejectedTitleTextHeight / 2;
// Add padding for the rectangle
const padding = 20;
// Draw the stamp background
page.drawRectangle({
x: rejectedTitleTextX - padding / 2,
y: rejectedTitleTextY - padding / 2,
width: rejectedTitleTextWidth + padding,
height: rejectedTitleTextHeight + padding,
borderColor: rgb(220 / 255, 38 / 255, 38 / 255),
borderWidth: 4,
});
rejectedTitleTextField.addToPage(page, {
x: rejectedTitleTextX,
y: rejectedTitleTextY,
width: rejectedTitleTextWidth,
height: rejectedTitleTextHeight,
textColor: rgb(220 / 255, 38 / 255, 38 / 255),
backgroundColor: undefined,
borderWidth: 0,
borderColor: undefined,
});
}
return pdfDoc;
}

View File

@@ -0,0 +1,37 @@
import { prisma } from '@documenso/prisma';
export const getNextPendingRecipient = async ({
documentId,
currentRecipientId,
}: {
documentId: number;
currentRecipientId: number;
}) => {
const recipients = await prisma.recipient.findMany({
where: {
documentId,
},
orderBy: [
{
signingOrder: {
sort: 'asc',
nulls: 'last',
},
},
{
id: 'asc',
},
],
});
const currentIndex = recipients.findIndex((r) => r.id === currentRecipientId);
if (currentIndex === -1 || currentIndex === recipients.length - 1) {
return null;
}
return {
...recipients[currentIndex + 1],
token: '',
};
};

View File

@@ -1,73 +0,0 @@
import type { DocumentVisibility } from '@prisma/client';
import { TeamMemberRole } from '@prisma/client';
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
import { TeamGlobalSettingsSchema } from '@documenso/prisma/generated/zod/modelSchema/TeamGlobalSettingsSchema';
import type { SupportedLanguageCodes } from '../../constants/i18n';
export type UpdateTeamDocumentSettingsOptions = {
userId: number;
teamId: number;
settings: {
documentVisibility: DocumentVisibility;
documentLanguage: SupportedLanguageCodes;
includeSenderDetails: boolean;
typedSignatureEnabled: boolean;
includeSigningCertificate: boolean;
};
};
export const ZUpdateTeamDocumentSettingsResponseSchema = TeamGlobalSettingsSchema;
export type TUpdateTeamDocumentSettingsResponse = z.infer<
typeof ZUpdateTeamDocumentSettingsResponseSchema
>;
export const updateTeamDocumentSettings = async ({
userId,
teamId,
settings,
}: UpdateTeamDocumentSettingsOptions): Promise<TUpdateTeamDocumentSettingsResponse> => {
const {
documentVisibility,
documentLanguage,
includeSenderDetails,
includeSigningCertificate,
typedSignatureEnabled,
} = settings;
const member = await prisma.teamMember.findFirst({
where: {
userId,
teamId,
},
});
if (!member || member.role !== TeamMemberRole.ADMIN) {
throw new Error('You do not have permission to update this team.');
}
return await prisma.teamGlobalSettings.upsert({
where: {
teamId,
},
create: {
teamId,
documentVisibility,
documentLanguage,
includeSenderDetails,
typedSignatureEnabled,
includeSigningCertificate,
},
update: {
documentVisibility,
documentLanguage,
includeSenderDetails,
typedSignatureEnabled,
includeSigningCertificate,
},
});
};

View File

@@ -324,6 +324,9 @@ export const createDocumentFromDirectTemplate = async ({
language: metaLanguage, language: metaLanguage,
signingOrder: metaSigningOrder, signingOrder: metaSigningOrder,
distributionMethod: template.templateMeta?.distributionMethod, distributionMethod: template.templateMeta?.distributionMethod,
typedSignatureEnabled: template.templateMeta?.typedSignatureEnabled,
uploadSignatureEnabled: template.templateMeta?.uploadSignatureEnabled,
drawSignatureEnabled: template.templateMeta?.drawSignatureEnabled,
}, },
}, },
}, },

View File

@@ -96,6 +96,9 @@ export const createDocumentFromTemplateLegacy = async ({
signingOrder: template.templateMeta?.signingOrder ?? undefined, signingOrder: template.templateMeta?.signingOrder ?? undefined,
language: language:
template.templateMeta?.language || template.team?.teamGlobalSettings?.documentLanguage, template.templateMeta?.language || template.team?.teamGlobalSettings?.documentLanguage,
typedSignatureEnabled: template.templateMeta?.typedSignatureEnabled,
uploadSignatureEnabled: template.templateMeta?.uploadSignatureEnabled,
drawSignatureEnabled: template.templateMeta?.drawSignatureEnabled,
}, },
}, },
}, },

View File

@@ -82,8 +82,11 @@ export type CreateDocumentFromTemplateOptions = {
signingOrder?: DocumentSigningOrder; signingOrder?: DocumentSigningOrder;
language?: SupportedLanguageCodes; language?: SupportedLanguageCodes;
distributionMethod?: DocumentDistributionMethod; distributionMethod?: DocumentDistributionMethod;
typedSignatureEnabled?: boolean; allowDictateNextSigner?: boolean;
emailSettings?: TDocumentEmailSettings; emailSettings?: TDocumentEmailSettings;
typedSignatureEnabled?: boolean;
uploadSignatureEnabled?: boolean;
drawSignatureEnabled?: boolean;
}; };
requestMetadata: ApiRequestMetadata; requestMetadata: ApiRequestMetadata;
}; };
@@ -404,6 +407,14 @@ export const createDocumentFromTemplate = async ({
template.team?.teamGlobalSettings?.documentLanguage, template.team?.teamGlobalSettings?.documentLanguage,
typedSignatureEnabled: typedSignatureEnabled:
override?.typedSignatureEnabled ?? template.templateMeta?.typedSignatureEnabled, override?.typedSignatureEnabled ?? template.templateMeta?.typedSignatureEnabled,
uploadSignatureEnabled:
override?.uploadSignatureEnabled ?? template.templateMeta?.uploadSignatureEnabled,
drawSignatureEnabled:
override?.drawSignatureEnabled ?? template.templateMeta?.drawSignatureEnabled,
allowDictateNextSigner:
override?.allowDictateNextSigner ??
template.templateMeta?.allowDictateNextSigner ??
false,
}, },
}, },
recipients: { recipients: {

View File

@@ -4,6 +4,8 @@ import { prisma } from '@documenso/prisma';
import { TemplateSchema } from '@documenso/prisma/generated/zod/modelSchema//TemplateSchema'; import { TemplateSchema } from '@documenso/prisma/generated/zod/modelSchema//TemplateSchema';
import type { TCreateTemplateMutationSchema } from '@documenso/trpc/server/template-router/schema'; import type { TCreateTemplateMutationSchema } from '@documenso/trpc/server/template-router/schema';
import { AppError, AppErrorCode } from '../../errors/app-error';
export type CreateTemplateOptions = TCreateTemplateMutationSchema & { export type CreateTemplateOptions = TCreateTemplateMutationSchema & {
userId: number; userId: number;
teamId?: number; teamId?: number;
@@ -19,8 +21,10 @@ export const createTemplate = async ({
teamId, teamId,
templateDocumentDataId, templateDocumentDataId,
}: CreateTemplateOptions) => { }: CreateTemplateOptions) => {
let team = null;
if (teamId) { if (teamId) {
await prisma.team.findFirstOrThrow({ team = await prisma.team.findFirst({
where: { where: {
id: teamId, id: teamId,
members: { members: {
@@ -29,7 +33,14 @@ export const createTemplate = async ({
}, },
}, },
}, },
include: {
teamGlobalSettings: true,
},
}); });
if (!team) {
throw new AppError(AppErrorCode.NOT_FOUND);
}
} }
return await prisma.template.create({ return await prisma.template.create({
@@ -38,6 +49,14 @@ export const createTemplate = async ({
userId, userId,
templateDocumentDataId, templateDocumentDataId,
teamId, teamId,
templateMeta: {
create: {
language: team?.teamGlobalSettings?.documentLanguage,
typedSignatureEnabled: team?.teamGlobalSettings?.typedSignatureEnabled ?? true,
uploadSignatureEnabled: team?.teamGlobalSettings?.uploadSignatureEnabled ?? true,
drawSignatureEnabled: team?.teamGlobalSettings?.drawSignatureEnabled ?? true,
},
},
}, },
}); });
}; };

View File

@@ -29,7 +29,7 @@ export const deleteUser = async ({ id }: DeleteUserOptions) => {
where: { where: {
userId: user.id, userId: user.id,
status: { status: {
in: [DocumentStatus.PENDING, DocumentStatus.COMPLETED], in: [DocumentStatus.PENDING, DocumentStatus.REJECTED, DocumentStatus.COMPLETED],
}, },
}, },
data: { data: {

View File

@@ -419,6 +419,10 @@ msgstr "<0>{teamName}</0> hat angefragt, Ihre E-Mail-Adresse für ihr Team bei D
msgid "<0>Click to upload</0> or drag and drop" msgid "<0>Click to upload</0> or drag and drop"
msgstr "<0>Klicken Sie hier, um hochzuladen</0> oder ziehen Sie die Datei per Drag & Drop" msgstr "<0>Klicken Sie hier, um hochzuladen</0> oder ziehen Sie die Datei per Drag & Drop"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Drawn</0> - A signature that is drawn using a mouse or stylus."
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc." msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc."
msgstr "<0>E-Mail</0> - Der Empfänger erhält das Dokument zur Unterschrift, Genehmigung usw." msgstr "<0>E-Mail</0> - Der Empfänger erhält das Dokument zur Unterschrift, Genehmigung usw."
@@ -465,6 +469,14 @@ msgstr "<0>Passkey erforderlich</0> - Der Empfänger muss ein Konto haben und de
msgid "<0>Sender:</0> All" msgid "<0>Sender:</0> All"
msgstr "<0>Absender:</0> Alle" msgstr "<0>Absender:</0> Alle"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Typed</0> - A signature that is typed using a keyboard."
msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Uploaded</0> - A signature that is uploaded from a file."
msgstr ""
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx #: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?" msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?"
msgstr "<0>Sie sind dabei, die Genehmigung von <1>\"{documentTitle}\"</1> abzuschließen.</0><2/> Sind Sie sicher?" msgstr "<0>Sie sind dabei, die Genehmigung von <1>\"{documentTitle}\"</1> abzuschließen.</0><2/> Sind Sie sicher?"
@@ -898,6 +910,16 @@ msgstr "Alle Zeiten"
msgid "Allow document recipients to reply directly to this email address" msgid "Allow document recipients to reply directly to this email address"
msgstr "Erlauben Sie den Dokumentempfängern, direkt an diese E-Mail-Adresse zu antworten" msgstr "Erlauben Sie den Dokumentempfängern, direkt an diese E-Mail-Adresse zu antworten"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "Allow signers to dictate next signer"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Allowed Signature Types"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
msgid "Allows authenticating using biometrics, password managers, hardware keys, etc." msgid "Allows authenticating using biometrics, password managers, hardware keys, etc."
msgstr "Erlaubt die Authentifizierung mit biometrischen Daten, Passwort-Managern, Hardware-Schlüsseln usw." msgstr "Erlaubt die Authentifizierung mit biometrischen Daten, Passwort-Managern, Hardware-Schlüsseln usw."
@@ -1237,6 +1259,13 @@ msgstr ""
msgid "Assisting" msgid "Assisting"
msgstr "" msgstr ""
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: packages/ui/primitives/template-flow/add-template-settings.types.tsx
#: packages/ui/primitives/document-flow/add-settings.types.ts
#: packages/lib/types/document-meta.ts
msgid "At least one signature type must be enabled"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx #: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document." msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document."
msgstr "Versuche, das Dokument erneut zu versiegeln, nützlich nach einer Codeänderung, um ein fehlerhaftes Dokument zu beheben." msgstr "Versuche, das Dokument erneut zu versiegeln, nützlich nach einer Codeänderung, um ein fehlerhaftes Dokument zu beheben."
@@ -1309,11 +1338,11 @@ msgstr "Bitte bestätige vor dem Start deine E-Mail-Adresse, indem du auf den Bu
msgid "Billing" msgid "Billing"
msgstr "Abrechnung" msgstr "Abrechnung"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Black" msgid "Black"
msgstr "Schwarz" msgstr "Schwarz"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Blue" msgid "Blue"
msgstr "Blau" msgstr "Blau"
@@ -1384,6 +1413,10 @@ msgstr "Indem Sie den elektronischen Unterzeichnungsdienst von Documenso verwend
msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions." msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions."
msgstr "Indem Sie fortfahren, Ihre elektronische Unterschrift zu leisten, erkennen Sie an und stimmen zu, dass sie verwendet wird, um das gegebene Dokument zu unterzeichnen, und die gleiche rechtliche Gültigkeit wie eine handschriftliche Unterschrift hat. Durch den Abschluss des elektronischen Unterzeichnungsprozesses bestätigen Sie Ihr Verständnis und Ihre Akzeptanz dieser Bedingungen." msgstr "Indem Sie fortfahren, Ihre elektronische Unterschrift zu leisten, erkennen Sie an und stimmen zu, dass sie verwendet wird, um das gegebene Dokument zu unterzeichnen, und die gleiche rechtliche Gültigkeit wie eine handschriftliche Unterschrift hat. Durch den Abschluss des elektronischen Unterzeichnungsprozesses bestätigen Sie Ihr Verständnis und Ihre Akzeptanz dieser Bedingungen."
#: apps/remix/app/components/forms/signup.tsx
msgid "By proceeding, you agree to our <0>Terms of Service</0> and <1>Privacy Policy</1>."
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing." msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing."
msgstr "Durch die Verwendung der elektronischen Unterschriftsfunktion stimmen Sie zu, Transaktionen durchzuführen und Offenlegungen elektronisch zu erhalten. Sie erkennen an, dass Ihre elektronische Unterschrift auf Dokumenten bindend ist und dass Sie die Bedingungen akzeptieren, die in den Dokumenten dargelegt sind, die Sie unterzeichnen." msgstr "Durch die Verwendung der elektronischen Unterschriftsfunktion stimmen Sie zu, Transaktionen durchzuführen und Offenlegungen elektronisch zu erhalten. Sie erkennen an, dass Ihre elektronische Unterschrift auf Dokumenten bindend ist und dass Sie die Bedingungen akzeptieren, die in den Dokumenten dargelegt sind, die Sie unterzeichnen."
@@ -1436,6 +1469,8 @@ msgstr ""
#: apps/remix/app/components/dialogs/document-move-dialog.tsx #: apps/remix/app/components/dialogs/document-move-dialog.tsx
#: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx #: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx #: packages/ui/primitives/document-flow/send-document-action-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Cancel" msgid "Cancel"
@@ -1522,7 +1557,7 @@ msgstr "Datei löschen"
msgid "Clear filters" msgid "Clear filters"
msgstr "Filter löschen" msgstr "Filter löschen"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
msgid "Clear Signature" msgid "Clear Signature"
msgstr "Unterschrift löschen" msgstr "Unterschrift löschen"
@@ -1697,6 +1732,7 @@ msgstr "Inhalt"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/document-flow/document-flow-root.tsx #: packages/ui/primitives/document-flow/document-flow-root.tsx
msgid "Continue" msgid "Continue"
msgstr "Fortsetzen" msgstr "Fortsetzen"
@@ -1737,14 +1773,18 @@ msgstr "Steuert die Standard-sichtbarkeit eines hochgeladenen Dokuments."
msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead." msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead."
msgstr "Steuert das Format der Nachricht, die gesendet wird, wenn ein Empfänger eingeladen wird, ein Dokument zu unterschreiben. Wenn eine benutzerdefinierte Nachricht beim Konfigurieren des Dokuments bereitgestellt wurde, wird diese stattdessen verwendet." msgstr "Steuert das Format der Nachricht, die gesendet wird, wenn ein Empfänger eingeladen wird, ein Dokument zu unterschreiben. Wenn eine benutzerdefinierte Nachricht beim Konfigurieren des Dokuments bereitgestellt wurde, wird diese stattdessen verwendet."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Controls whether the recipients can sign the documents using a typed signature. Enable or disable the typed signature globally." msgid "Controls the language for the document, including the language to be used for email notifications, and the final certificate that is generated and attached to the document."
msgstr "Legt fest, ob die Empfänger die Dokumente mit einer getippten Unterschrift unterschreiben können. Aktivieren oder deaktivieren Sie die getippte Unterschrift global." msgstr ""
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately." msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately."
msgstr "Legt fest, ob das Signaturzertifikat in das Dokument aufgenommen wird, wenn es heruntergeladen wird. Das Signaturzertifikat kann weiterhin separat von der Protokollseite heruntergeladen werden." msgstr "Legt fest, ob das Signaturzertifikat in das Dokument aufgenommen wird, wenn es heruntergeladen wird. Das Signaturzertifikat kann weiterhin separat von der Protokollseite heruntergeladen werden."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls which signatures are allowed to be used when signing a document."
msgstr ""
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx #: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied" msgid "Copied"
@@ -1975,6 +2015,10 @@ msgstr "Standardsprache des Dokuments"
msgid "Default Document Visibility" msgid "Default Document Visibility"
msgstr "Standard Sichtbarkeit des Dokuments" msgstr "Standard Sichtbarkeit des Dokuments"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Default Signature Settings"
msgstr ""
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "delete" msgid "delete"
msgstr "löschen" msgstr "löschen"
@@ -2201,6 +2245,11 @@ msgstr "Dokument \"{0}\" - Abgelehnt von {1}"
msgid "Document \"{0}\" - Rejection Confirmed" msgid "Document \"{0}\" - Rejection Confirmed"
msgstr "Dokument \"{0}\" - Ablehnung Bestätigt" msgstr "Dokument \"{0}\" - Ablehnung Bestätigt"
#. placeholder {0}: document.title
#: packages/lib/jobs/definitions/emails/send-document-cancelled-emails.handler.ts
msgid "Document \"{0}\" Cancelled"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
#: packages/ui/components/document/document-global-auth-access-select.tsx #: packages/ui/components/document/document-global-auth-access-select.tsx
@@ -2349,6 +2398,10 @@ msgstr "Dokumentpräferenzen aktualisiert"
msgid "Document re-sent" msgid "Document re-sent"
msgstr "Dokument erneut gesendet" msgstr "Dokument erneut gesendet"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document rejected"
msgstr ""
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx #: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
#: apps/remix/app/components/embed/embed-document-rejected.tsx #: apps/remix/app/components/embed/embed-document-rejected.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
@@ -2486,6 +2539,10 @@ msgstr "Entwurfte Dokumente"
msgid "Drag & drop your PDF here." msgid "Drag & drop your PDF here."
msgstr "Ziehen Sie Ihr PDF hierher." msgstr "Ziehen Sie Ihr PDF hierher."
#: packages/lib/constants/document.ts
msgid "Draw"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx #: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Dropdown" msgid "Dropdown"
@@ -2543,6 +2600,7 @@ msgstr "Offenlegung der elektronischen Unterschrift"
#: apps/remix/app/components/tables/admin-document-recipient-item-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/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx
#: apps/remix/app/components/forms/signin.tsx #: apps/remix/app/components/forms/signin.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -2553,6 +2611,7 @@ msgstr "Offenlegung der elektronischen Unterschrift"
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -2646,15 +2705,6 @@ msgstr "Direktlink-Signierung aktivieren"
msgid "Enable signing order" msgid "Enable signing order"
msgstr "Aktiviere die Signaturreihenfolge" msgstr "Aktiviere die Signaturreihenfolge"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Enable Typed Signature"
msgstr "Getippte Unterschrift aktivieren"
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Enable Typed Signatures"
msgstr "Aktivieren Sie getippte Unterschriften"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx
@@ -2929,7 +2979,7 @@ msgstr "Zum Eigentümer gehen"
msgid "Go to your <0>public profile settings</0> to add documents." msgid "Go to your <0>public profile settings</0> to add documents."
msgstr "Gehen Sie zu Ihren <0>öffentlichen Profileinstellungen</0>, um Dokumente hinzuzufügen." msgstr "Gehen Sie zu Ihren <0>öffentlichen Profileinstellungen</0>, um Dokumente hinzuzufügen."
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Green" msgid "Green"
msgstr "Grün" msgstr "Grün"
@@ -3483,10 +3533,12 @@ msgstr "Meine Vorlagen"
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx #: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -3537,6 +3589,7 @@ msgstr "Neue Vorlage"
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/embed/embed-document-signing-page.tsx #: apps/remix/app/components/embed/embed-document-signing-page.tsx
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx #: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
msgid "Next" msgid "Next"
msgstr "Nächster" msgstr "Nächster"
@@ -3954,6 +4007,7 @@ msgstr "Bitte geben Sie einen aussagekräftigen Namen für Ihr Token ein. Dies w
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/forms/profile.tsx
msgid "Please enter a valid name." msgid "Please enter a valid name."
msgstr "Bitte geben Sie einen gültigen Namen ein." msgstr "Bitte geben Sie einen gültigen Namen ein."
@@ -3989,10 +4043,6 @@ msgstr "Bitte beachten Sie, dass Sie den Zugriff auf alle Dokumente, die mit die
msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified" msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified"
msgstr "Bitte beachten Sie, dass Sie den Zugriff auf alle mit diesem Team verbundenen Dokumente verlieren werden und alle Mitglieder entfernt und benachrichtigt werden." msgstr "Bitte beachten Sie, dass Sie den Zugriff auf alle mit diesem Team verbundenen Dokumente verlieren werden und alle Mitglieder entfernt und benachrichtigt werden."
#: apps/remix/app/components/general/document-signing/document-signing-reject-dialog.tsx
msgid "Please provide a reason"
msgstr "Bitte geben Sie einen Grund an"
#: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx #: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx
msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support." msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support."
msgstr "Bitte geben Sie ein Token von der Authentifizierungs-App oder einen Backup-Code an. Wenn Sie keinen Backup-Code haben, kontaktieren Sie bitte den Support." msgstr "Bitte geben Sie ein Token von der Authentifizierungs-App oder einen Backup-Code an. Wenn Sie keinen Backup-Code haben, kontaktieren Sie bitte den Support."
@@ -4063,6 +4113,10 @@ msgstr "Privat"
msgid "Private templates can only be modified and viewed by you." msgid "Private templates can only be modified and viewed by you."
msgstr "Private Vorlagen können nur von Ihnen bearbeitet und angezeigt werden." msgstr "Private Vorlagen können nur von Ihnen bearbeitet und angezeigt werden."
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Proceed"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/profile.tsx #: apps/remix/app/routes/_authenticated+/settings+/profile.tsx
#: apps/remix/app/components/general/settings-nav-mobile.tsx #: apps/remix/app/components/general/settings-nav-mobile.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx #: apps/remix/app/components/general/settings-nav-desktop.tsx
@@ -4140,6 +4194,10 @@ msgstr "Bereit"
msgid "Reason" msgid "Reason"
msgstr "Grund" msgstr "Grund"
#: packages/email/template-components/template-document-cancel.tsx
msgid "Reason for cancellation: {cancellationReason}"
msgstr ""
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Reason for rejection: " msgid "Reason for rejection: "
msgstr "" msgstr ""
@@ -4222,7 +4280,7 @@ msgstr "Wiederherstellungscode kopiert"
msgid "Recovery codes" msgid "Recovery codes"
msgstr "Wiederherstellungscodes" msgstr "Wiederherstellungscodes"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Red" msgid "Red"
msgstr "Rot" msgstr "Rot"
@@ -4243,8 +4301,11 @@ msgstr "Registrierung erfolgreich"
msgid "Reject Document" msgid "Reject Document"
msgstr "Dokument Ablehnen" msgstr "Dokument Ablehnen"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx #: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/document/document-status.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: packages/lib/constants/document.ts
msgid "Rejected" msgid "Rejected"
msgstr "Abgelehnt" msgstr "Abgelehnt"
@@ -4429,8 +4490,6 @@ msgstr "Zeilen pro Seite"
#: 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-number-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-number-field.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx #: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Save" msgid "Save"
@@ -4740,7 +4799,6 @@ msgstr "Registrieren mit OIDC"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -4757,12 +4815,17 @@ msgstr "Unterschrift"
msgid "Signature ID" msgid "Signature ID"
msgstr "Signatur-ID" msgstr "Signatur-ID"
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx msgid "Signature is too small"
#: apps/remix/app/components/embed/embed-document-signing-page.tsx msgstr ""
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
msgid "Signature is too small. Please provide a more complete signature." #: apps/remix/app/components/forms/profile.tsx
msgstr "Die Unterschrift ist zu klein. Bitte geben Sie eine vollständigere Unterschrift an." msgid "Signature Pad cannot be empty."
msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "Signature types"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx #: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Signatures Collected" msgid "Signatures Collected"
@@ -4987,6 +5050,7 @@ msgid "Subject <0>(Optional)</0>"
msgstr "Betreff <0>(Optional)</0>" msgstr "Betreff <0>(Optional)</0>"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Submitting..." msgid "Submitting..."
msgstr "" msgstr ""
@@ -5501,6 +5565,10 @@ msgstr "Der Token, den Sie zur Zurücksetzung Ihres Passworts verwendet haben, i
msgid "The two-factor authentication code provided is incorrect" msgid "The two-factor authentication code provided is incorrect"
msgstr "" msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "The types of signatures that recipients are allowed to use when signing the document."
msgstr ""
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx #: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
@@ -5582,6 +5650,10 @@ msgstr "Dieses Dokument wurde vom Eigentümer storniert und steht anderen nicht
msgid "This document has been cancelled by the owner." msgid "This document has been cancelled by the owner."
msgstr "Dieses Dokument wurde vom Eigentümer storniert." msgstr "Dieses Dokument wurde vom Eigentümer storniert."
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been rejected by a recipient"
msgstr ""
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx #: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been signed by all recipients" msgid "This document has been signed by all recipients"
msgstr "Dieses Dokument wurde von allen Empfängern unterschrieben" msgstr "Dieses Dokument wurde von allen Empfängern unterschrieben"
@@ -5730,6 +5802,10 @@ msgstr "Zeitzone"
msgid "Title" msgid "Title"
msgstr "Titel" msgstr "Titel"
#: packages/ui/primitives/document-flow/add-settings.types.ts
msgid "Title cannot be empty"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx #: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx
msgid "To accept this invitation you must create an account." msgid "To accept this invitation you must create an account."
msgstr "Um diese Einladung anzunehmen, müssen Sie ein Konto erstellen." msgstr "Um diese Einladung anzunehmen, müssen Sie ein Konto erstellen."
@@ -5897,6 +5973,7 @@ msgstr "Zwei-Faktor-Wiederauthentifizierung"
#: apps/remix/app/components/tables/templates-table.tsx #: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: packages/lib/constants/document.ts
msgid "Type" msgid "Type"
msgstr "Typ" msgstr "Typ"
@@ -6004,6 +6081,7 @@ msgstr "Unvollendet"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx
msgid "Unknown" msgid "Unknown"
msgstr "Unbekannt" msgstr "Unbekannt"
@@ -6014,11 +6092,14 @@ msgstr "Unbezahlt"
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx #: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx #: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/forms/public-profile-form.tsx #: apps/remix/app/components/forms/public-profile-form.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx #: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Update" msgid "Update"
msgstr "Aktualisieren" msgstr "Aktualisieren"
@@ -6039,6 +6120,8 @@ msgid "Update profile"
msgstr "Profil aktualisieren" msgstr "Profil aktualisieren"
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Update Recipient" msgid "Update Recipient"
msgstr "Empfänger aktualisieren" msgstr "Empfänger aktualisieren"
@@ -6077,10 +6160,6 @@ msgstr "Webhook aktualisieren"
msgid "Updating password..." msgid "Updating password..."
msgstr "Passwort wird aktualisiert..." msgstr "Passwort wird aktualisiert..."
#: apps/remix/app/components/forms/profile.tsx
msgid "Updating profile..."
msgstr "Profil wird aktualisiert..."
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "Updating Your Information" msgid "Updating Your Information"
msgstr "Aktualisierung Ihrer Informationen" msgstr "Aktualisierung Ihrer Informationen"
@@ -6089,6 +6168,10 @@ msgstr "Aktualisierung Ihrer Informationen"
msgid "Upgrade" msgid "Upgrade"
msgstr "Upgrade" msgstr "Upgrade"
#: packages/lib/constants/document.ts
msgid "Upload"
msgstr ""
#: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx #: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx
msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details." msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details."
msgstr "Laden Sie eine CSV-Datei hoch, um mehrere Dokumente aus dieser Vorlage zu erstellen. Jede Zeile repräsentiert ein Dokument mit den Empfängerdaten." msgstr "Laden Sie eine CSV-Datei hoch, um mehrere Dokumente aus dieser Vorlage zu erstellen. Jede Zeile repräsentiert ein Dokument mit den Empfängerdaten."
@@ -6113,7 +6196,7 @@ msgstr "CSV hochladen"
msgid "Upload custom document" msgid "Upload custom document"
msgstr "Benutzerdefiniertes Dokument hochladen" msgstr "Benutzerdefiniertes Dokument hochladen"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-upload.tsx
msgid "Upload Signature" msgid "Upload Signature"
msgstr "Signatur hochladen" msgstr "Signatur hochladen"
@@ -6287,6 +6370,7 @@ msgstr "Dokument anzeigen"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
#: packages/email/template-components/template-document-invite.tsx #: packages/email/template-components/template-document-invite.tsx
msgid "View Document" msgid "View Document"
@@ -6653,6 +6737,11 @@ msgstr "Willkommen bei Documenso!"
msgid "Were you trying to edit this document instead?" msgid "Were you trying to edit this document instead?"
msgstr "Hast du stattdessen versucht, dieses Dokument zu bearbeiten?" msgstr "Hast du stattdessen versucht, dieses Dokument zu bearbeiten?"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "When enabled, signers can choose who should sign next in the sequence instead of following the predefined order."
msgstr ""
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "When you click continue, you will be prompted to add the first available authenticator on your system." msgid "When you click continue, you will be prompted to add the first available authenticator on your system."
msgstr "Wenn Sie auf Fortfahren klicken, werden Sie aufgefordert, den ersten verfügbaren Authenticator auf Ihrem System hinzuzufügen." msgstr "Wenn Sie auf Fortfahren klicken, werden Sie aufgefordert, den ersten verfügbaren Authenticator auf Ihrem System hinzuzufügen."

View File

@@ -414,6 +414,10 @@ msgstr "<0>{teamName}</0> has requested to use your email address for their team
msgid "<0>Click to upload</0> or drag and drop" msgid "<0>Click to upload</0> or drag and drop"
msgstr "<0>Click to upload</0> or drag and drop" msgstr "<0>Click to upload</0> or drag and drop"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Drawn</0> - A signature that is drawn using a mouse or stylus."
msgstr "<0>Drawn</0> - A signature that is drawn using a mouse or stylus."
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc." msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc."
msgstr "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc." msgstr "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc."
@@ -460,6 +464,14 @@ msgstr "<0>Require passkey</0> - The recipient must have an account and passkey
msgid "<0>Sender:</0> All" msgid "<0>Sender:</0> All"
msgstr "<0>Sender:</0> All" msgstr "<0>Sender:</0> All"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Typed</0> - A signature that is typed using a keyboard."
msgstr "<0>Typed</0> - A signature that is typed using a keyboard."
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Uploaded</0> - A signature that is uploaded from a file."
msgstr "<0>Uploaded</0> - A signature that is uploaded from a file."
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx #: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?" msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?"
msgstr "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?" msgstr "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?"
@@ -893,6 +905,16 @@ msgstr "All Time"
msgid "Allow document recipients to reply directly to this email address" msgid "Allow document recipients to reply directly to this email address"
msgstr "Allow document recipients to reply directly to this email address" msgstr "Allow document recipients to reply directly to this email address"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "Allow signers to dictate next signer"
msgstr "Allow signers to dictate next signer"
#: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Allowed Signature Types"
msgstr "Allowed Signature Types"
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
msgid "Allows authenticating using biometrics, password managers, hardware keys, etc." msgid "Allows authenticating using biometrics, password managers, hardware keys, etc."
msgstr "Allows authenticating using biometrics, password managers, hardware keys, etc." msgstr "Allows authenticating using biometrics, password managers, hardware keys, etc."
@@ -1232,6 +1254,13 @@ msgstr "Assisted"
msgid "Assisting" msgid "Assisting"
msgstr "Assisting" msgstr "Assisting"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: packages/ui/primitives/template-flow/add-template-settings.types.tsx
#: packages/ui/primitives/document-flow/add-settings.types.ts
#: packages/lib/types/document-meta.ts
msgid "At least one signature type must be enabled"
msgstr "At least one signature type must be enabled"
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx #: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document." msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document."
msgstr "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document." msgstr "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document."
@@ -1304,11 +1333,11 @@ msgstr "Before you get started, please confirm your email address by clicking th
msgid "Billing" msgid "Billing"
msgstr "Billing" msgstr "Billing"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Black" msgid "Black"
msgstr "Black" msgstr "Black"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Blue" msgid "Blue"
msgstr "Blue" msgstr "Blue"
@@ -1379,6 +1408,10 @@ msgstr "By proceeding to use the electronic signature service provided by Docume
msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions." msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions."
msgstr "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions." msgstr "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions."
#: apps/remix/app/components/forms/signup.tsx
msgid "By proceeding, you agree to our <0>Terms of Service</0> and <1>Privacy Policy</1>."
msgstr "By proceeding, you agree to our <0>Terms of Service</0> and <1>Privacy Policy</1>."
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing." msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing."
msgstr "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing." msgstr "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing."
@@ -1431,6 +1464,8 @@ msgstr "Can prepare"
#: apps/remix/app/components/dialogs/document-move-dialog.tsx #: apps/remix/app/components/dialogs/document-move-dialog.tsx
#: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx #: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx #: packages/ui/primitives/document-flow/send-document-action-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Cancel" msgid "Cancel"
@@ -1517,7 +1552,7 @@ msgstr "Clear file"
msgid "Clear filters" msgid "Clear filters"
msgstr "Clear filters" msgstr "Clear filters"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
msgid "Clear Signature" msgid "Clear Signature"
msgstr "Clear Signature" msgstr "Clear Signature"
@@ -1692,6 +1727,7 @@ msgstr "Content"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/document-flow/document-flow-root.tsx #: packages/ui/primitives/document-flow/document-flow-root.tsx
msgid "Continue" msgid "Continue"
msgstr "Continue" msgstr "Continue"
@@ -1732,14 +1768,18 @@ msgstr "Controls the default visibility of an uploaded document."
msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead." msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead."
msgstr "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead." msgstr "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Controls whether the recipients can sign the documents using a typed signature. Enable or disable the typed signature globally." msgid "Controls the language for the document, including the language to be used for email notifications, and the final certificate that is generated and attached to the document."
msgstr "Controls whether the recipients can sign the documents using a typed signature. Enable or disable the typed signature globally." msgstr "Controls the language for the document, including the language to be used for email notifications, and the final certificate that is generated and attached to the document."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately." msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately."
msgstr "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately." msgstr "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls which signatures are allowed to be used when signing a document."
msgstr "Controls which signatures are allowed to be used when signing a document."
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx #: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied" msgid "Copied"
@@ -1970,6 +2010,10 @@ msgstr "Default Document Language"
msgid "Default Document Visibility" msgid "Default Document Visibility"
msgstr "Default Document Visibility" msgstr "Default Document Visibility"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Default Signature Settings"
msgstr "Default Signature Settings"
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "delete" msgid "delete"
msgstr "delete" msgstr "delete"
@@ -2196,6 +2240,11 @@ msgstr "Document \"{0}\" - Rejected by {1}"
msgid "Document \"{0}\" - Rejection Confirmed" msgid "Document \"{0}\" - Rejection Confirmed"
msgstr "Document \"{0}\" - Rejection Confirmed" msgstr "Document \"{0}\" - Rejection Confirmed"
#. placeholder {0}: document.title
#: packages/lib/jobs/definitions/emails/send-document-cancelled-emails.handler.ts
msgid "Document \"{0}\" Cancelled"
msgstr "Document \"{0}\" Cancelled"
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
#: packages/ui/components/document/document-global-auth-access-select.tsx #: packages/ui/components/document/document-global-auth-access-select.tsx
@@ -2344,6 +2393,10 @@ msgstr "Document preferences updated"
msgid "Document re-sent" msgid "Document re-sent"
msgstr "Document re-sent" msgstr "Document re-sent"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document rejected"
msgstr "Document rejected"
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx #: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
#: apps/remix/app/components/embed/embed-document-rejected.tsx #: apps/remix/app/components/embed/embed-document-rejected.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
@@ -2481,6 +2534,10 @@ msgstr "Drafted Documents"
msgid "Drag & drop your PDF here." msgid "Drag & drop your PDF here."
msgstr "Drag & drop your PDF here." msgstr "Drag & drop your PDF here."
#: packages/lib/constants/document.ts
msgid "Draw"
msgstr "Draw"
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx #: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Dropdown" msgid "Dropdown"
@@ -2538,6 +2595,7 @@ msgstr "Electronic Signature Disclosure"
#: apps/remix/app/components/tables/admin-document-recipient-item-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/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx
#: apps/remix/app/components/forms/signin.tsx #: apps/remix/app/components/forms/signin.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -2548,6 +2606,7 @@ msgstr "Electronic Signature Disclosure"
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -2641,15 +2700,6 @@ msgstr "Enable Direct Link Signing"
msgid "Enable signing order" msgid "Enable signing order"
msgstr "Enable signing order" msgstr "Enable signing order"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Enable Typed Signature"
msgstr "Enable Typed Signature"
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Enable Typed Signatures"
msgstr "Enable Typed Signatures"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx
@@ -2924,7 +2974,7 @@ msgstr "Go to owner"
msgid "Go to your <0>public profile settings</0> to add documents." msgid "Go to your <0>public profile settings</0> to add documents."
msgstr "Go to your <0>public profile settings</0> to add documents." msgstr "Go to your <0>public profile settings</0> to add documents."
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Green" msgid "Green"
msgstr "Green" msgstr "Green"
@@ -3478,10 +3528,12 @@ msgstr "My templates"
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx #: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -3532,6 +3584,7 @@ msgstr "New Template"
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/embed/embed-document-signing-page.tsx #: apps/remix/app/components/embed/embed-document-signing-page.tsx
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx #: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
msgid "Next" msgid "Next"
msgstr "Next" msgstr "Next"
@@ -3949,6 +4002,7 @@ msgstr "Please enter a meaningful name for your token. This will help you identi
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/forms/profile.tsx
msgid "Please enter a valid name." msgid "Please enter a valid name."
msgstr "Please enter a valid name." msgstr "Please enter a valid name."
@@ -3984,10 +4038,6 @@ msgstr "Please note that this action is irreversible. Once confirmed, your webho
msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified" msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified"
msgstr "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified" msgstr "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified"
#: apps/remix/app/components/general/document-signing/document-signing-reject-dialog.tsx
msgid "Please provide a reason"
msgstr "Please provide a reason"
#: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx #: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx
msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support." msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support."
msgstr "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support." msgstr "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support."
@@ -4058,6 +4108,10 @@ msgstr "Private"
msgid "Private templates can only be modified and viewed by you." msgid "Private templates can only be modified and viewed by you."
msgstr "Private templates can only be modified and viewed by you." msgstr "Private templates can only be modified and viewed by you."
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Proceed"
msgstr "Proceed"
#: apps/remix/app/routes/_authenticated+/settings+/profile.tsx #: apps/remix/app/routes/_authenticated+/settings+/profile.tsx
#: apps/remix/app/components/general/settings-nav-mobile.tsx #: apps/remix/app/components/general/settings-nav-mobile.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx #: apps/remix/app/components/general/settings-nav-desktop.tsx
@@ -4135,6 +4189,10 @@ msgstr "Ready"
msgid "Reason" msgid "Reason"
msgstr "Reason" msgstr "Reason"
#: packages/email/template-components/template-document-cancel.tsx
msgid "Reason for cancellation: {cancellationReason}"
msgstr "Reason for cancellation: {cancellationReason}"
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Reason for rejection: " msgid "Reason for rejection: "
msgstr "Reason for rejection: " msgstr "Reason for rejection: "
@@ -4217,7 +4275,7 @@ msgstr "Recovery code copied"
msgid "Recovery codes" msgid "Recovery codes"
msgstr "Recovery codes" msgstr "Recovery codes"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Red" msgid "Red"
msgstr "Red" msgstr "Red"
@@ -4238,8 +4296,11 @@ msgstr "Registration Successful"
msgid "Reject Document" msgid "Reject Document"
msgstr "Reject Document" msgstr "Reject Document"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx #: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/document/document-status.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: packages/lib/constants/document.ts
msgid "Rejected" msgid "Rejected"
msgstr "Rejected" msgstr "Rejected"
@@ -4424,8 +4485,6 @@ msgstr "Rows per page"
#: 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-number-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-number-field.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx #: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Save" msgid "Save"
@@ -4735,7 +4794,6 @@ msgstr "Sign Up with OIDC"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -4752,12 +4810,17 @@ msgstr "Signature"
msgid "Signature ID" msgid "Signature ID"
msgstr "Signature ID" msgstr "Signature ID"
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx msgid "Signature is too small"
#: apps/remix/app/components/embed/embed-document-signing-page.tsx msgstr "Signature is too small"
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
msgid "Signature is too small. Please provide a more complete signature." #: apps/remix/app/components/forms/profile.tsx
msgstr "Signature is too small. Please provide a more complete signature." msgid "Signature Pad cannot be empty."
msgstr "Signature Pad cannot be empty."
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "Signature types"
msgstr "Signature types"
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx #: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Signatures Collected" msgid "Signatures Collected"
@@ -4982,6 +5045,7 @@ msgid "Subject <0>(Optional)</0>"
msgstr "Subject <0>(Optional)</0>" msgstr "Subject <0>(Optional)</0>"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Submitting..." msgid "Submitting..."
msgstr "Submitting..." msgstr "Submitting..."
@@ -5498,6 +5562,10 @@ msgstr "The token you have used to reset your password is either expired or it n
msgid "The two-factor authentication code provided is incorrect" msgid "The two-factor authentication code provided is incorrect"
msgstr "The two-factor authentication code provided is incorrect" msgstr "The two-factor authentication code provided is incorrect"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "The types of signatures that recipients are allowed to use when signing the document."
msgstr "The types of signatures that recipients are allowed to use when signing the document."
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx #: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
@@ -5579,6 +5647,10 @@ msgstr "This document has been cancelled by the owner and is no longer available
msgid "This document has been cancelled by the owner." msgid "This document has been cancelled by the owner."
msgstr "This document has been cancelled by the owner." msgstr "This document has been cancelled by the owner."
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been rejected by a recipient"
msgstr "This document has been rejected by a recipient"
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx #: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been signed by all recipients" msgid "This document has been signed by all recipients"
msgstr "This document has been signed by all recipients" msgstr "This document has been signed by all recipients"
@@ -5727,6 +5799,10 @@ msgstr "Time Zone"
msgid "Title" msgid "Title"
msgstr "Title" msgstr "Title"
#: packages/ui/primitives/document-flow/add-settings.types.ts
msgid "Title cannot be empty"
msgstr "Title cannot be empty"
#: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx #: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx
msgid "To accept this invitation you must create an account." msgid "To accept this invitation you must create an account."
msgstr "To accept this invitation you must create an account." msgstr "To accept this invitation you must create an account."
@@ -5894,6 +5970,7 @@ msgstr "Two-Factor Re-Authentication"
#: apps/remix/app/components/tables/templates-table.tsx #: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: packages/lib/constants/document.ts
msgid "Type" msgid "Type"
msgstr "Type" msgstr "Type"
@@ -6001,6 +6078,7 @@ msgstr "Uncompleted"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx
msgid "Unknown" msgid "Unknown"
msgstr "Unknown" msgstr "Unknown"
@@ -6011,11 +6089,14 @@ msgstr "Unpaid"
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx #: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx #: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/forms/public-profile-form.tsx #: apps/remix/app/components/forms/public-profile-form.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx #: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Update" msgid "Update"
msgstr "Update" msgstr "Update"
@@ -6036,6 +6117,8 @@ msgid "Update profile"
msgstr "Update profile" msgstr "Update profile"
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Update Recipient" msgid "Update Recipient"
msgstr "Update Recipient" msgstr "Update Recipient"
@@ -6074,10 +6157,6 @@ msgstr "Update webhook"
msgid "Updating password..." msgid "Updating password..."
msgstr "Updating password..." msgstr "Updating password..."
#: apps/remix/app/components/forms/profile.tsx
msgid "Updating profile..."
msgstr "Updating profile..."
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "Updating Your Information" msgid "Updating Your Information"
msgstr "Updating Your Information" msgstr "Updating Your Information"
@@ -6086,6 +6165,10 @@ msgstr "Updating Your Information"
msgid "Upgrade" msgid "Upgrade"
msgstr "Upgrade" msgstr "Upgrade"
#: packages/lib/constants/document.ts
msgid "Upload"
msgstr "Upload"
#: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx #: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx
msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details." msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details."
msgstr "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details." msgstr "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details."
@@ -6110,7 +6193,7 @@ msgstr "Upload CSV"
msgid "Upload custom document" msgid "Upload custom document"
msgstr "Upload custom document" msgstr "Upload custom document"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-upload.tsx
msgid "Upload Signature" msgid "Upload Signature"
msgstr "Upload Signature" msgstr "Upload Signature"
@@ -6284,6 +6367,7 @@ msgstr "View document"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
#: packages/email/template-components/template-document-invite.tsx #: packages/email/template-components/template-document-invite.tsx
msgid "View Document" msgid "View Document"
@@ -6650,6 +6734,11 @@ msgstr "Welcome to Documenso!"
msgid "Were you trying to edit this document instead?" msgid "Were you trying to edit this document instead?"
msgstr "Were you trying to edit this document instead?" msgstr "Were you trying to edit this document instead?"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "When enabled, signers can choose who should sign next in the sequence instead of following the predefined order."
msgstr "When enabled, signers can choose who should sign next in the sequence instead of following the predefined order."
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "When you click continue, you will be prompted to add the first available authenticator on your system." msgid "When you click continue, you will be prompted to add the first available authenticator on your system."
msgstr "When you click continue, you will be prompted to add the first available authenticator on your system." msgstr "When you click continue, you will be prompted to add the first available authenticator on your system."

View File

@@ -419,6 +419,10 @@ msgstr "<0>{teamName}</0> ha solicitado usar tu dirección de correo electrónic
msgid "<0>Click to upload</0> or drag and drop" msgid "<0>Click to upload</0> or drag and drop"
msgstr "<0>Haga clic para subir</0> o arrastre y suelte" msgstr "<0>Haga clic para subir</0> o arrastre y suelte"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Drawn</0> - A signature that is drawn using a mouse or stylus."
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc." msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc."
msgstr "<0>Correo electrónico</0> - Al destinatario se le enviará el documento para firmar, aprobar, etc." msgstr "<0>Correo electrónico</0> - Al destinatario se le enviará el documento para firmar, aprobar, etc."
@@ -465,6 +469,14 @@ msgstr "<0>Requerir clave de acceso</0> - El destinatario debe tener una cuenta
msgid "<0>Sender:</0> All" msgid "<0>Sender:</0> All"
msgstr "<0>Remitente:</0> Todos" msgstr "<0>Remitente:</0> Todos"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Typed</0> - A signature that is typed using a keyboard."
msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Uploaded</0> - A signature that is uploaded from a file."
msgstr ""
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx #: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?" msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?"
msgstr "<0>Está a punto de completar la aprobación de <1>\"{documentTitle}\"</1>.</0><2/> ¿Está seguro?" msgstr "<0>Está a punto de completar la aprobación de <1>\"{documentTitle}\"</1>.</0><2/> ¿Está seguro?"
@@ -898,6 +910,16 @@ msgstr "Todo el Tiempo"
msgid "Allow document recipients to reply directly to this email address" msgid "Allow document recipients to reply directly to this email address"
msgstr "Permitir que los destinatarios del documento respondan directamente a esta dirección de correo electrónico" msgstr "Permitir que los destinatarios del documento respondan directamente a esta dirección de correo electrónico"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "Allow signers to dictate next signer"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Allowed Signature Types"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
msgid "Allows authenticating using biometrics, password managers, hardware keys, etc." msgid "Allows authenticating using biometrics, password managers, hardware keys, etc."
msgstr "Permite autenticarse usando biometría, administradores de contraseñas, claves de hardware, etc." msgstr "Permite autenticarse usando biometría, administradores de contraseñas, claves de hardware, etc."
@@ -1237,6 +1259,13 @@ msgstr ""
msgid "Assisting" msgid "Assisting"
msgstr "" msgstr ""
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: packages/ui/primitives/template-flow/add-template-settings.types.tsx
#: packages/ui/primitives/document-flow/add-settings.types.ts
#: packages/lib/types/document-meta.ts
msgid "At least one signature type must be enabled"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx #: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document." msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document."
msgstr "Intenta sellar el documento de nuevo, útil después de que se haya producido un cambio de código para resolver un documento erróneo." msgstr "Intenta sellar el documento de nuevo, útil después de que se haya producido un cambio de código para resolver un documento erróneo."
@@ -1309,11 +1338,11 @@ msgstr "Antes de comenzar, por favor confirma tu dirección de correo electróni
msgid "Billing" msgid "Billing"
msgstr "Facturación" msgstr "Facturación"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Black" msgid "Black"
msgstr "Negro" msgstr "Negro"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Blue" msgid "Blue"
msgstr "Azul" msgstr "Azul"
@@ -1384,6 +1413,10 @@ msgstr "Al continuar utilizando el servicio de firma electrónica proporcionado
msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions." msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions."
msgstr "Al continuar con su firma electrónica, usted reconoce y consiente que se utilizará para firmar el documento dado y tiene la misma validez legal que una firma manuscrita. Al completar el proceso de firma electrónica, usted afirma su comprensión y aceptación de estas condiciones." msgstr "Al continuar con su firma electrónica, usted reconoce y consiente que se utilizará para firmar el documento dado y tiene la misma validez legal que una firma manuscrita. Al completar el proceso de firma electrónica, usted afirma su comprensión y aceptación de estas condiciones."
#: apps/remix/app/components/forms/signup.tsx
msgid "By proceeding, you agree to our <0>Terms of Service</0> and <1>Privacy Policy</1>."
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing." msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing."
msgstr "Al utilizar la función de firma electrónica, usted está consintiendo realizar transacciones y recibir divulgaciones electrónicamente. Reconoce que su firma electrónica en los documentos es vinculante y que acepta los términos esbozados en los documentos que está firmando." msgstr "Al utilizar la función de firma electrónica, usted está consintiendo realizar transacciones y recibir divulgaciones electrónicamente. Reconoce que su firma electrónica en los documentos es vinculante y que acepta los términos esbozados en los documentos que está firmando."
@@ -1436,6 +1469,8 @@ msgstr ""
#: apps/remix/app/components/dialogs/document-move-dialog.tsx #: apps/remix/app/components/dialogs/document-move-dialog.tsx
#: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx #: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx #: packages/ui/primitives/document-flow/send-document-action-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Cancel" msgid "Cancel"
@@ -1522,7 +1557,7 @@ msgstr "Limpiar archivo"
msgid "Clear filters" msgid "Clear filters"
msgstr "Limpiar filtros" msgstr "Limpiar filtros"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
msgid "Clear Signature" msgid "Clear Signature"
msgstr "Limpiar firma" msgstr "Limpiar firma"
@@ -1697,6 +1732,7 @@ msgstr "Contenido"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/document-flow/document-flow-root.tsx #: packages/ui/primitives/document-flow/document-flow-root.tsx
msgid "Continue" msgid "Continue"
msgstr "Continuar" msgstr "Continuar"
@@ -1737,14 +1773,18 @@ msgstr "Controla la visibilidad predeterminada de un documento cargado."
msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead." msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead."
msgstr "Controla el formato del mensaje que se enviará al invitar a un destinatario a firmar un documento. Si se ha proporcionado un mensaje personalizado al configurar el documento, se usará en su lugar." msgstr "Controla el formato del mensaje que se enviará al invitar a un destinatario a firmar un documento. Si se ha proporcionado un mensaje personalizado al configurar el documento, se usará en su lugar."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Controls whether the recipients can sign the documents using a typed signature. Enable or disable the typed signature globally." msgid "Controls the language for the document, including the language to be used for email notifications, and the final certificate that is generated and attached to the document."
msgstr "Controla si los destinatarios pueden firmar los documentos utilizando una firma mecanografiada. Habilitar o deshabilitar la firma mecanografiada globalmente." msgstr ""
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately." msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately."
msgstr "Controla si el certificado de firma se incluirá en el documento cuando se descargue. El certificado de firma aún puede descargarse por separado desde la página de registros." msgstr "Controla si el certificado de firma se incluirá en el documento cuando se descargue. El certificado de firma aún puede descargarse por separado desde la página de registros."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls which signatures are allowed to be used when signing a document."
msgstr ""
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx #: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied" msgid "Copied"
@@ -1975,6 +2015,10 @@ msgstr "Idioma predeterminado del documento"
msgid "Default Document Visibility" msgid "Default Document Visibility"
msgstr "Visibilidad predeterminada del documento" msgstr "Visibilidad predeterminada del documento"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Default Signature Settings"
msgstr ""
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "delete" msgid "delete"
msgstr "eliminar" msgstr "eliminar"
@@ -2201,6 +2245,11 @@ msgstr "Documento \"{0}\" - Rechazado por {1}"
msgid "Document \"{0}\" - Rejection Confirmed" msgid "Document \"{0}\" - Rejection Confirmed"
msgstr "Documento \"{0}\" - Rechazo confirmado" msgstr "Documento \"{0}\" - Rechazo confirmado"
#. placeholder {0}: document.title
#: packages/lib/jobs/definitions/emails/send-document-cancelled-emails.handler.ts
msgid "Document \"{0}\" Cancelled"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
#: packages/ui/components/document/document-global-auth-access-select.tsx #: packages/ui/components/document/document-global-auth-access-select.tsx
@@ -2349,6 +2398,10 @@ msgstr "Preferencias del documento actualizadas"
msgid "Document re-sent" msgid "Document re-sent"
msgstr "Documento reenviado" msgstr "Documento reenviado"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document rejected"
msgstr ""
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx #: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
#: apps/remix/app/components/embed/embed-document-rejected.tsx #: apps/remix/app/components/embed/embed-document-rejected.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
@@ -2486,6 +2539,10 @@ msgstr "Documentos redactados"
msgid "Drag & drop your PDF here." msgid "Drag & drop your PDF here."
msgstr "Arrastre y suelte su PDF aquí." msgstr "Arrastre y suelte su PDF aquí."
#: packages/lib/constants/document.ts
msgid "Draw"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx #: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Dropdown" msgid "Dropdown"
@@ -2543,6 +2600,7 @@ msgstr "Divulgación de Firma Electrónica"
#: apps/remix/app/components/tables/admin-document-recipient-item-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/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx
#: apps/remix/app/components/forms/signin.tsx #: apps/remix/app/components/forms/signin.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -2553,6 +2611,7 @@ msgstr "Divulgación de Firma Electrónica"
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -2646,15 +2705,6 @@ msgstr "Habilitar firma de enlace directo"
msgid "Enable signing order" msgid "Enable signing order"
msgstr "Habilitar orden de firma" msgstr "Habilitar orden de firma"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Enable Typed Signature"
msgstr "Habilitar firma mecanografiada"
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Enable Typed Signatures"
msgstr "Habilitar firmas escritas"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx
@@ -2929,7 +2979,7 @@ msgstr "Ir al propietario"
msgid "Go to your <0>public profile settings</0> to add documents." msgid "Go to your <0>public profile settings</0> to add documents."
msgstr "Ve a tu <0>configuración de perfil público</0> para agregar documentos." msgstr "Ve a tu <0>configuración de perfil público</0> para agregar documentos."
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Green" msgid "Green"
msgstr "Verde" msgstr "Verde"
@@ -3483,10 +3533,12 @@ msgstr "Mis plantillas"
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx #: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -3537,6 +3589,7 @@ msgstr "Nueva plantilla"
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/embed/embed-document-signing-page.tsx #: apps/remix/app/components/embed/embed-document-signing-page.tsx
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx #: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
msgid "Next" msgid "Next"
msgstr "Siguiente" msgstr "Siguiente"
@@ -3954,6 +4007,7 @@ msgstr "Por favor, ingresa un nombre significativo para tu token. Esto te ayudar
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/forms/profile.tsx
msgid "Please enter a valid name." msgid "Please enter a valid name."
msgstr "Por favor, introduce un nombre válido." msgstr "Por favor, introduce un nombre válido."
@@ -3989,10 +4043,6 @@ msgstr "Por favor, ten en cuenta que esta acción es irreversible. Una vez confi
msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified" msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified"
msgstr "Por favor, ten en cuenta que perderás acceso a todos los documentos asociados con este equipo y todos los miembros serán eliminados y notificados" msgstr "Por favor, ten en cuenta que perderás acceso a todos los documentos asociados con este equipo y todos los miembros serán eliminados y notificados"
#: apps/remix/app/components/general/document-signing/document-signing-reject-dialog.tsx
msgid "Please provide a reason"
msgstr "Please provide a reason"
#: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx #: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx
msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support." msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support."
msgstr "Por favor, proporciona un token del autenticador o un código de respaldo. Si no tienes un código de respaldo disponible, contacta al soporte." msgstr "Por favor, proporciona un token del autenticador o un código de respaldo. Si no tienes un código de respaldo disponible, contacta al soporte."
@@ -4063,6 +4113,10 @@ msgstr "Privado"
msgid "Private templates can only be modified and viewed by you." msgid "Private templates can only be modified and viewed by you."
msgstr "Las plantillas privadas solo pueden ser modificadas y vistas por ti." msgstr "Las plantillas privadas solo pueden ser modificadas y vistas por ti."
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Proceed"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/profile.tsx #: apps/remix/app/routes/_authenticated+/settings+/profile.tsx
#: apps/remix/app/components/general/settings-nav-mobile.tsx #: apps/remix/app/components/general/settings-nav-mobile.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx #: apps/remix/app/components/general/settings-nav-desktop.tsx
@@ -4140,6 +4194,10 @@ msgstr "Listo"
msgid "Reason" msgid "Reason"
msgstr "Razón" msgstr "Razón"
#: packages/email/template-components/template-document-cancel.tsx
msgid "Reason for cancellation: {cancellationReason}"
msgstr ""
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Reason for rejection: " msgid "Reason for rejection: "
msgstr "" msgstr ""
@@ -4222,7 +4280,7 @@ msgstr "Código de recuperación copiado"
msgid "Recovery codes" msgid "Recovery codes"
msgstr "Códigos de recuperación" msgstr "Códigos de recuperación"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Red" msgid "Red"
msgstr "Rojo" msgstr "Rojo"
@@ -4243,8 +4301,11 @@ msgstr "Registro exitoso"
msgid "Reject Document" msgid "Reject Document"
msgstr "Rechazar Documento" msgstr "Rechazar Documento"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx #: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/document/document-status.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: packages/lib/constants/document.ts
msgid "Rejected" msgid "Rejected"
msgstr "Rejected" msgstr "Rejected"
@@ -4429,8 +4490,6 @@ msgstr "Filas por página"
#: 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-number-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-number-field.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx #: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Save" msgid "Save"
@@ -4740,7 +4799,6 @@ msgstr "Regístrate con OIDC"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -4757,12 +4815,17 @@ msgstr "Firma"
msgid "Signature ID" msgid "Signature ID"
msgstr "ID de Firma" msgstr "ID de Firma"
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx msgid "Signature is too small"
#: apps/remix/app/components/embed/embed-document-signing-page.tsx msgstr ""
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
msgid "Signature is too small. Please provide a more complete signature." #: apps/remix/app/components/forms/profile.tsx
msgstr "La firma es demasiado pequeña. Proporcione una firma más completa." msgid "Signature Pad cannot be empty."
msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "Signature types"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx #: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Signatures Collected" msgid "Signatures Collected"
@@ -4987,6 +5050,7 @@ msgid "Subject <0>(Optional)</0>"
msgstr "Asunto <0>(Opcional)</0>" msgstr "Asunto <0>(Opcional)</0>"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Submitting..." msgid "Submitting..."
msgstr "" msgstr ""
@@ -5501,6 +5565,10 @@ msgstr "El token que has utilizado para restablecer tu contraseña ha expirado o
msgid "The two-factor authentication code provided is incorrect" msgid "The two-factor authentication code provided is incorrect"
msgstr "" msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "The types of signatures that recipients are allowed to use when signing the document."
msgstr ""
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx #: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
@@ -5582,6 +5650,10 @@ msgstr "Este documento ha sido cancelado por el propietario y ya no está dispon
msgid "This document has been cancelled by the owner." msgid "This document has been cancelled by the owner."
msgstr "Este documento ha sido cancelado por el propietario." msgstr "Este documento ha sido cancelado por el propietario."
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been rejected by a recipient"
msgstr ""
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx #: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been signed by all recipients" msgid "This document has been signed by all recipients"
msgstr "Este documento ha sido firmado por todos los destinatarios" msgstr "Este documento ha sido firmado por todos los destinatarios"
@@ -5730,6 +5802,10 @@ msgstr "Zona horaria"
msgid "Title" msgid "Title"
msgstr "Título" msgstr "Título"
#: packages/ui/primitives/document-flow/add-settings.types.ts
msgid "Title cannot be empty"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx #: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx
msgid "To accept this invitation you must create an account." msgid "To accept this invitation you must create an account."
msgstr "Para aceptar esta invitación debes crear una cuenta." msgstr "Para aceptar esta invitación debes crear una cuenta."
@@ -5897,6 +5973,7 @@ msgstr "Re-autenticación de Doble Factor"
#: apps/remix/app/components/tables/templates-table.tsx #: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: packages/lib/constants/document.ts
msgid "Type" msgid "Type"
msgstr "Tipo" msgstr "Tipo"
@@ -6004,6 +6081,7 @@ msgstr "Incompleto"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx
msgid "Unknown" msgid "Unknown"
msgstr "Desconocido" msgstr "Desconocido"
@@ -6014,11 +6092,14 @@ msgstr "No pagado"
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx #: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx #: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/forms/public-profile-form.tsx #: apps/remix/app/components/forms/public-profile-form.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx #: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Update" msgid "Update"
msgstr "Actualizar" msgstr "Actualizar"
@@ -6039,6 +6120,8 @@ msgid "Update profile"
msgstr "Actualizar perfil" msgstr "Actualizar perfil"
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Update Recipient" msgid "Update Recipient"
msgstr "Actualizar destinatario" msgstr "Actualizar destinatario"
@@ -6077,10 +6160,6 @@ msgstr "Actualizar webhook"
msgid "Updating password..." msgid "Updating password..."
msgstr "Actualizando contraseña..." msgstr "Actualizando contraseña..."
#: apps/remix/app/components/forms/profile.tsx
msgid "Updating profile..."
msgstr "Actualizando perfil..."
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "Updating Your Information" msgid "Updating Your Information"
msgstr "Actualizando Su Información" msgstr "Actualizando Su Información"
@@ -6089,6 +6168,10 @@ msgstr "Actualizando Su Información"
msgid "Upgrade" msgid "Upgrade"
msgstr "Actualizar" msgstr "Actualizar"
#: packages/lib/constants/document.ts
msgid "Upload"
msgstr ""
#: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx #: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx
msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details." msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details."
msgstr "Sube un archivo CSV para crear múltiples documentos a partir de esta plantilla. Cada fila representa un documento con los detalles del destinatario." msgstr "Sube un archivo CSV para crear múltiples documentos a partir de esta plantilla. Cada fila representa un documento con los detalles del destinatario."
@@ -6113,7 +6196,7 @@ msgstr "Subir CSV"
msgid "Upload custom document" msgid "Upload custom document"
msgstr "Subir documento personalizado" msgstr "Subir documento personalizado"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-upload.tsx
msgid "Upload Signature" msgid "Upload Signature"
msgstr "Subir firma" msgstr "Subir firma"
@@ -6287,6 +6370,7 @@ msgstr "Ver documento"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
#: packages/email/template-components/template-document-invite.tsx #: packages/email/template-components/template-document-invite.tsx
msgid "View Document" msgid "View Document"
@@ -6653,6 +6737,11 @@ msgstr "¡Bienvenido a Documenso!"
msgid "Were you trying to edit this document instead?" msgid "Were you trying to edit this document instead?"
msgstr "¿Estabas intentando editar este documento en su lugar?" msgstr "¿Estabas intentando editar este documento en su lugar?"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "When enabled, signers can choose who should sign next in the sequence instead of following the predefined order."
msgstr ""
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "When you click continue, you will be prompted to add the first available authenticator on your system." msgid "When you click continue, you will be prompted to add the first available authenticator on your system."
msgstr "Cuando haces clic en continuar, se te pedirá que añadas el primer autenticador disponible en tu sistema." msgstr "Cuando haces clic en continuar, se te pedirá que añadas el primer autenticador disponible en tu sistema."

View File

@@ -417,7 +417,11 @@ msgstr "<0>{teamName}</0> a demandé à utiliser votre adresse e-mail pour leur
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
msgid "<0>Click to upload</0> or drag and drop" msgid "<0>Click to upload</0> or drag and drop"
msgstr "<0>Cliquez pour télécharger</0> ou faites glisser et déposez" msgstr "<0>Cliquez pour importer</0> ou faites glisser et déposez"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Drawn</0> - A signature that is drawn using a mouse or stylus."
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc." msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc."
@@ -465,6 +469,14 @@ msgstr "<0>Clé d'accès requise</0> - Le destinataire doit avoir un compte et u
msgid "<0>Sender:</0> All" msgid "<0>Sender:</0> All"
msgstr "<0>Expéditeur :</0> Tous" msgstr "<0>Expéditeur :</0> Tous"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Typed</0> - A signature that is typed using a keyboard."
msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Uploaded</0> - A signature that is uploaded from a file."
msgstr ""
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx #: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?" msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?"
msgstr "<0>Vous êtes sur le point de terminer l'approbation de <1>\"{documentTitle}\"</1>.</0><2/> Êtes-vous sûr ?" msgstr "<0>Vous êtes sur le point de terminer l'approbation de <1>\"{documentTitle}\"</1>.</0><2/> Êtes-vous sûr ?"
@@ -898,6 +910,16 @@ msgstr "Depuis toujours"
msgid "Allow document recipients to reply directly to this email address" msgid "Allow document recipients to reply directly to this email address"
msgstr "Autoriser les destinataires du document à répondre directement à cette adresse e-mail" msgstr "Autoriser les destinataires du document à répondre directement à cette adresse e-mail"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "Allow signers to dictate next signer"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Allowed Signature Types"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
msgid "Allows authenticating using biometrics, password managers, hardware keys, etc." msgid "Allows authenticating using biometrics, password managers, hardware keys, etc."
msgstr "Permet d'authentifier en utilisant des biométries, des gestionnaires de mots de passe, des clés matérielles, etc." msgstr "Permet d'authentifier en utilisant des biométries, des gestionnaires de mots de passe, des clés matérielles, etc."
@@ -1082,7 +1104,7 @@ msgstr "Une erreur est survenue lors de la mise à jour de votre profil."
#: apps/remix/app/components/general/document/document-upload.tsx #: apps/remix/app/components/general/document/document-upload.tsx
msgid "An error occurred while uploading your document." msgid "An error occurred while uploading your document."
msgstr "Une erreur est survenue lors du téléchargement de votre document." msgstr "Une erreur est survenue lors de l'importation de votre document."
#: apps/remix/app/components/general/generic-error-layout.tsx #: apps/remix/app/components/general/generic-error-layout.tsx
msgid "An unexpected error occurred." msgid "An unexpected error occurred."
@@ -1237,6 +1259,13 @@ msgstr ""
msgid "Assisting" msgid "Assisting"
msgstr "" msgstr ""
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: packages/ui/primitives/template-flow/add-template-settings.types.tsx
#: packages/ui/primitives/document-flow/add-settings.types.ts
#: packages/lib/types/document-meta.ts
msgid "At least one signature type must be enabled"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx #: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document." msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document."
msgstr "Essaye de sceller le document à nouveau, utile après qu'un changement de code ait eu lieu pour résoudre un document erroné." msgstr "Essaye de sceller le document à nouveau, utile après qu'un changement de code ait eu lieu pour résoudre un document erroné."
@@ -1309,11 +1338,11 @@ msgstr "Avant de commencer, veuillez confirmer votre adresse e-mail en cliquant
msgid "Billing" msgid "Billing"
msgstr "Facturation" msgstr "Facturation"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Black" msgid "Black"
msgstr "Noir" msgstr "Noir"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Blue" msgid "Blue"
msgstr "Bleu" msgstr "Bleu"
@@ -1384,6 +1413,10 @@ msgstr "En procédant à l'utilisation du service de signature électronique fou
msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions." msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions."
msgstr "En procédant avec votre signature électronique, vous reconnaissez et consentez à ce qu'elle soit utilisée pour signer le document donné et a la même validité légale qu'une signature manuscrite. En complétant le processus de signature électronique, vous affirmez votre compréhension et votre acceptation de ces conditions." msgstr "En procédant avec votre signature électronique, vous reconnaissez et consentez à ce qu'elle soit utilisée pour signer le document donné et a la même validité légale qu'une signature manuscrite. En complétant le processus de signature électronique, vous affirmez votre compréhension et votre acceptation de ces conditions."
#: apps/remix/app/components/forms/signup.tsx
msgid "By proceeding, you agree to our <0>Terms of Service</0> and <1>Privacy Policy</1>."
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing." msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing."
msgstr "En utilisant la fonctionnalité de signature électronique, vous consentez à effectuer des transactions et à recevoir des divulgations électroniquement. Vous reconnaissez que votre signature électronique sur les documents est contraignante et que vous acceptez les termes énoncés dans les documents que vous signez." msgstr "En utilisant la fonctionnalité de signature électronique, vous consentez à effectuer des transactions et à recevoir des divulgations électroniquement. Vous reconnaissez que votre signature électronique sur les documents est contraignante et que vous acceptez les termes énoncés dans les documents que vous signez."
@@ -1436,6 +1469,8 @@ msgstr ""
#: apps/remix/app/components/dialogs/document-move-dialog.tsx #: apps/remix/app/components/dialogs/document-move-dialog.tsx
#: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx #: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx #: packages/ui/primitives/document-flow/send-document-action-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Cancel" msgid "Cancel"
@@ -1522,7 +1557,7 @@ msgstr "Effacer le fichier"
msgid "Clear filters" msgid "Clear filters"
msgstr "Effacer les filtres" msgstr "Effacer les filtres"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
msgid "Clear Signature" msgid "Clear Signature"
msgstr "Effacer la signature" msgstr "Effacer la signature"
@@ -1539,7 +1574,7 @@ msgstr "Cliquez ici pour réessayer"
#: apps/remix/app/components/dialogs/team-member-invite-dialog.tsx #: apps/remix/app/components/dialogs/team-member-invite-dialog.tsx
msgid "Click here to upload" msgid "Click here to upload"
msgstr "Cliquez ici pour télécharger" msgstr "Cliquez ici pour importer"
#: apps/remix/app/components/general/avatar-with-recipient.tsx #: apps/remix/app/components/general/avatar-with-recipient.tsx
#: apps/remix/app/components/general/avatar-with-recipient.tsx #: apps/remix/app/components/general/avatar-with-recipient.tsx
@@ -1697,6 +1732,7 @@ msgstr "Contenu"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/document-flow/document-flow-root.tsx #: packages/ui/primitives/document-flow/document-flow-root.tsx
msgid "Continue" msgid "Continue"
msgstr "Continuer" msgstr "Continuer"
@@ -1727,24 +1763,28 @@ msgstr "Continuer vers la connexion"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls the default language of an uploaded document. This will be used as the language in email communications with the recipients." msgid "Controls the default language of an uploaded document. This will be used as the language in email communications with the recipients."
msgstr "Contrôle la langue par défaut d'un document téléchargé. Cela sera utilisé comme langue dans les communications par e-mail avec les destinataires." msgstr "Contrôle la langue par défaut d'un document importé. Cela sera utilisé comme langue dans les communications par e-mail avec les destinataires."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls the default visibility of an uploaded document." msgid "Controls the default visibility of an uploaded document."
msgstr "Contrôle la visibilité par défaut d'un document téléchargé." msgstr "Contrôle la visibilité par défaut d'un document importé."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead." msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead."
msgstr "Contrôle le formatage du message qui sera envoyé lors de l'invitation d'un destinataire à signer un document. Si un message personnalisé a été fourni lors de la configuration du document, il sera utilisé à la place." msgstr "Contrôle le formatage du message qui sera envoyé lors de l'invitation d'un destinataire à signer un document. Si un message personnalisé a été fourni lors de la configuration du document, il sera utilisé à la place."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Controls whether the recipients can sign the documents using a typed signature. Enable or disable the typed signature globally." msgid "Controls the language for the document, including the language to be used for email notifications, and the final certificate that is generated and attached to the document."
msgstr "Contrôle si les destinataires peuvent signer les documents à l'aide d'une signature dactylographiée. Active ou désactive globalement la signature dactylographiée." msgstr ""
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately." msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately."
msgstr "Contrôle si le certificat de signature sera inclus dans le document lorsqu'il sera téléchargé. Le certificat de signature peut toujours être téléchargé séparément à partir de la page d'historique'." msgstr "Contrôle si le certificat de signature sera inclus dans le document lorsqu'il sera téléchargé. Le certificat de signature peut toujours être téléchargé séparément à partir de la page d'historique'."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls which signatures are allowed to be used when signing a document."
msgstr ""
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx #: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied" msgid "Copied"
@@ -1975,6 +2015,10 @@ msgstr "Langue par défaut du document"
msgid "Default Document Visibility" msgid "Default Document Visibility"
msgstr "Visibilité par défaut du document" msgstr "Visibilité par défaut du document"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Default Signature Settings"
msgstr ""
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "delete" msgid "delete"
msgstr "supprimer" msgstr "supprimer"
@@ -2201,6 +2245,11 @@ msgstr "Document \"{0}\" - Rejeté par {1}"
msgid "Document \"{0}\" - Rejection Confirmed" msgid "Document \"{0}\" - Rejection Confirmed"
msgstr "Document \"{0}\" - Rejet Confirmé" msgstr "Document \"{0}\" - Rejet Confirmé"
#. placeholder {0}: document.title
#: packages/lib/jobs/definitions/emails/send-document-cancelled-emails.handler.ts
msgid "Document \"{0}\" Cancelled"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
#: packages/ui/components/document/document-global-auth-access-select.tsx #: packages/ui/components/document/document-global-auth-access-select.tsx
@@ -2349,6 +2398,10 @@ msgstr "Préférences de document mises à jour"
msgid "Document re-sent" msgid "Document re-sent"
msgstr "Document renvoyé" msgstr "Document renvoyé"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document rejected"
msgstr ""
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx #: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
#: apps/remix/app/components/embed/embed-document-rejected.tsx #: apps/remix/app/components/embed/embed-document-rejected.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
@@ -2394,11 +2447,11 @@ msgstr "Document mis à jour"
#: apps/remix/app/components/general/document/document-upload.tsx #: apps/remix/app/components/general/document/document-upload.tsx
msgid "Document upload disabled due to unpaid invoices" msgid "Document upload disabled due to unpaid invoices"
msgstr "Téléchargement du document désactivé en raison de factures impayées" msgstr "Importation de documents désactivé en raison de factures impayées"
#: apps/remix/app/components/general/document/document-upload.tsx #: apps/remix/app/components/general/document/document-upload.tsx
msgid "Document uploaded" msgid "Document uploaded"
msgstr "Document téléchargé" msgstr "Document importé"
#: apps/remix/app/routes/_recipient+/sign.$token+/complete.tsx #: apps/remix/app/routes/_recipient+/sign.$token+/complete.tsx
msgid "Document Viewed" msgid "Document Viewed"
@@ -2486,6 +2539,10 @@ msgstr "Documents brouillon"
msgid "Drag & drop your PDF here." msgid "Drag & drop your PDF here."
msgstr "Faites glisser et déposez votre PDF ici." msgstr "Faites glisser et déposez votre PDF ici."
#: packages/lib/constants/document.ts
msgid "Draw"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx #: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Dropdown" msgid "Dropdown"
@@ -2543,6 +2600,7 @@ msgstr "Divulgation de signature électronique"
#: apps/remix/app/components/tables/admin-document-recipient-item-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/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx
#: apps/remix/app/components/forms/signin.tsx #: apps/remix/app/components/forms/signin.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -2553,6 +2611,7 @@ msgstr "Divulgation de signature électronique"
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -2646,15 +2705,6 @@ msgstr "Activer la signature de lien direct"
msgid "Enable signing order" msgid "Enable signing order"
msgstr "Activer l'ordre de signature" msgstr "Activer l'ordre de signature"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Enable Typed Signature"
msgstr "Activer la signature dactylographiée"
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Enable Typed Signatures"
msgstr "Activer les signatures tapées"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx
@@ -2860,7 +2910,7 @@ msgstr "Pour toute question concernant cette divulgation, les signatures électr
#: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx #: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx
msgid "For each recipient, provide their email (required) and name (optional) in separate columns. Download the template CSV below for the correct format." msgid "For each recipient, provide their email (required) and name (optional) in separate columns. Download the template CSV below for the correct format."
msgstr "Pour chaque destinataire, fournissez leur email (obligatoire) et leur nom (facultatif) dans des colonnes séparées. Téléchargez le modèle CSV ci-dessous pour le format correct." msgstr "Pour chaque destinataire, fournissez son e-mail (obligatoire) et son nom (facultatif) dans des colonnes séparées. Téléchargez le modèle CSV ci-dessous pour obtenir le format requis."
#: packages/lib/server-only/auth/send-forgot-password.ts #: packages/lib/server-only/auth/send-forgot-password.ts
msgid "Forgot Password?" msgid "Forgot Password?"
@@ -2907,7 +2957,7 @@ msgstr "Authentification d'action de destinataire globale"
#: apps/remix/app/components/general/generic-error-layout.tsx #: apps/remix/app/components/general/generic-error-layout.tsx
#: packages/ui/primitives/document-flow/document-flow-root.tsx #: packages/ui/primitives/document-flow/document-flow-root.tsx
msgid "Go Back" msgid "Go Back"
msgstr "Retourner" msgstr "Retour"
#: apps/remix/app/routes/_unauthenticated+/verify-email._index.tsx #: apps/remix/app/routes/_unauthenticated+/verify-email._index.tsx
#: apps/remix/app/routes/_unauthenticated+/verify-email.$token.tsx #: apps/remix/app/routes/_unauthenticated+/verify-email.$token.tsx
@@ -2929,7 +2979,7 @@ msgstr "Aller au propriétaire"
msgid "Go to your <0>public profile settings</0> to add documents." msgid "Go to your <0>public profile settings</0> to add documents."
msgstr "Allez à vos <0>paramètres de profil public</0> pour ajouter des documents." msgstr "Allez à vos <0>paramètres de profil public</0> pour ajouter des documents."
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Green" msgid "Green"
msgstr "Vert" msgstr "Vert"
@@ -2988,7 +3038,7 @@ msgstr "Salut, je suis Timur"
#: packages/email/templates/bulk-send-complete.tsx #: packages/email/templates/bulk-send-complete.tsx
msgid "Hi {userName}," msgid "Hi {userName},"
msgstr "Bonjour, {userName}," msgstr "Bonjour {userName},"
#: packages/email/templates/reset-password.tsx #: packages/email/templates/reset-password.tsx
msgid "Hi, {userName} <0>({userEmail})</0>" msgid "Hi, {userName} <0>({userEmail})</0>"
@@ -3010,7 +3060,7 @@ msgstr "Je suis un signataire de ce document"
#: packages/lib/constants/recipient-roles.ts #: packages/lib/constants/recipient-roles.ts
msgid "I am a viewer of this document" msgid "I am a viewer of this document"
msgstr "Je suis un visualiseur de ce document" msgstr "Je suis un lecteur de ce document"
#: packages/lib/constants/recipient-roles.ts #: packages/lib/constants/recipient-roles.ts
msgid "I am an approver of this document" msgid "I am an approver of this document"
@@ -3400,11 +3450,11 @@ msgstr "MAU (document terminé)"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
msgid "Max" msgid "Max"
msgstr "" msgstr "Maximum"
#: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx #: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx
msgid "Maximum file size: 4MB. Maximum 100 rows per upload. Blank values will use template defaults." msgid "Maximum file size: 4MB. Maximum 100 rows per upload. Blank values will use template defaults."
msgstr "Taille maximale du fichier : 4 Mo. Maximum de 100 lignes par téléversement. Les valeurs vides utiliseront les valeurs par défaut du modèle." msgstr "Taille maximale du fichier : 4 Mo. Maximum de 100 lignes par importation. Les valeurs vides utiliseront les valeurs par défaut du modèle."
#: packages/lib/constants/teams.ts #: packages/lib/constants/teams.ts
msgid "Member" msgid "Member"
@@ -3483,10 +3533,12 @@ msgstr "Mes modèles"
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx #: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -3537,6 +3589,7 @@ msgstr "Nouveau modèle"
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/embed/embed-document-signing-page.tsx #: apps/remix/app/components/embed/embed-document-signing-page.tsx
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx #: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
msgid "Next" msgid "Next"
msgstr "Suivant" msgstr "Suivant"
@@ -3954,6 +4007,7 @@ msgstr "Veuillez entrer un nom significatif pour votre token. Cela vous aidera
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/forms/profile.tsx
msgid "Please enter a valid name." msgid "Please enter a valid name."
msgstr "Veuiillez entrer un nom valide." msgstr "Veuiillez entrer un nom valide."
@@ -3989,10 +4043,6 @@ msgstr "Veuillez noter que cette action est irréversible. Une fois confirmée,
msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified" msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified"
msgstr "Veuillez noter que vous perdrez l'accès à tous les documents associés à cette équipe et que tous les membres seront supprimés et notifiés" msgstr "Veuillez noter que vous perdrez l'accès à tous les documents associés à cette équipe et que tous les membres seront supprimés et notifiés"
#: apps/remix/app/components/general/document-signing/document-signing-reject-dialog.tsx
msgid "Please provide a reason"
msgstr "Veuillez fournir une raison"
#: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx #: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx
msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support." msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support."
msgstr "Veuillez fournir un token de l'authentificateur, ou un code de secours. Si vous n'avez pas de code de secours disponible, veuillez contacter le support." msgstr "Veuillez fournir un token de l'authentificateur, ou un code de secours. Si vous n'avez pas de code de secours disponible, veuillez contacter le support."
@@ -4063,6 +4113,10 @@ msgstr "Privé"
msgid "Private templates can only be modified and viewed by you." msgid "Private templates can only be modified and viewed by you."
msgstr "Les modèles privés ne peuvent être modifiés et consultés que par vous." msgstr "Les modèles privés ne peuvent être modifiés et consultés que par vous."
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Proceed"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/profile.tsx #: apps/remix/app/routes/_authenticated+/settings+/profile.tsx
#: apps/remix/app/components/general/settings-nav-mobile.tsx #: apps/remix/app/components/general/settings-nav-mobile.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx #: apps/remix/app/components/general/settings-nav-desktop.tsx
@@ -4140,6 +4194,10 @@ msgstr "Prêt"
msgid "Reason" msgid "Reason"
msgstr "Raison" msgstr "Raison"
#: packages/email/template-components/template-document-cancel.tsx
msgid "Reason for cancellation: {cancellationReason}"
msgstr ""
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Reason for rejection: " msgid "Reason for rejection: "
msgstr "" msgstr ""
@@ -4222,7 +4280,7 @@ msgstr "Code de récupération copié"
msgid "Recovery codes" msgid "Recovery codes"
msgstr "Codes de récupération" msgstr "Codes de récupération"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Red" msgid "Red"
msgstr "Rouge" msgstr "Rouge"
@@ -4243,8 +4301,11 @@ msgstr "Inscription réussie"
msgid "Reject Document" msgid "Reject Document"
msgstr "Rejeter le Document" msgstr "Rejeter le Document"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx #: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/document/document-status.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: packages/lib/constants/document.ts
msgid "Rejected" msgid "Rejected"
msgstr "Rejeté" msgstr "Rejeté"
@@ -4429,8 +4490,6 @@ msgstr "Lignes par page"
#: 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-number-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-number-field.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx #: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Save" msgid "Save"
@@ -4740,7 +4799,6 @@ msgstr "S'inscrire avec OIDC"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -4757,12 +4815,17 @@ msgstr "Signature"
msgid "Signature ID" msgid "Signature ID"
msgstr "ID de signature" msgstr "ID de signature"
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx msgid "Signature is too small"
#: apps/remix/app/components/embed/embed-document-signing-page.tsx msgstr ""
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
msgid "Signature is too small. Please provide a more complete signature." #: apps/remix/app/components/forms/profile.tsx
msgstr "La signature est trop petite. Veuillez fournir une signature plus grande." msgid "Signature Pad cannot be empty."
msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "Signature types"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx #: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Signatures Collected" msgid "Signatures Collected"
@@ -4987,6 +5050,7 @@ msgid "Subject <0>(Optional)</0>"
msgstr "Objet <0>(Optionnel)</0>" msgstr "Objet <0>(Optionnel)</0>"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Submitting..." msgid "Submitting..."
msgstr "" msgstr ""
@@ -5215,7 +5279,7 @@ msgstr "Modèle supprimé"
#: apps/remix/app/components/dialogs/template-create-dialog.tsx #: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "Template document uploaded" msgid "Template document uploaded"
msgstr "Document modèle téléchargé" msgstr "Document modèle importé"
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx #: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
msgid "Template duplicated" msgid "Template duplicated"
@@ -5280,11 +5344,11 @@ msgstr "Couleur du texte"
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "Thank you for using Documenso to perform your electronic document signing. The purpose of this disclosure is to inform you about the process, legality, and your rights regarding the use of electronic signatures on our platform. By opting to use an electronic signature, you are agreeing to the terms and conditions outlined below." msgid "Thank you for using Documenso to perform your electronic document signing. The purpose of this disclosure is to inform you about the process, legality, and your rights regarding the use of electronic signatures on our platform. By opting to use an electronic signature, you are agreeing to the terms and conditions outlined below."
msgstr "Merci d'utiliser Documenso pour signer vos documents électroniquement. L'objectif de cette divulgation est de vous informer sur le processus, la légalité et vos droits concernant l'utilisation des signatures électroniques sur notre plateforme. En choisissant d'utiliser une signature électronique, vous acceptez les termes et conditions énoncés ci-dessous." msgstr "Merci d'utiliser Documenso pour signer vos documents électroniquement. L'objectif de cette clause est de vous informer sur le processus, la légalité et vos droits concernant l'utilisation de la signature électronique sur notre plateforme. En choisissant d'utiliser un sytème de signature électronique, vous acceptez les termes et conditions exposés ci-dessous."
#: packages/email/template-components/template-forgot-password.tsx #: packages/email/template-components/template-forgot-password.tsx
msgid "That's okay, it happens! Click the button below to reset your password." msgid "That's okay, it happens! Click the button below to reset your password."
msgstr "C'est d'accord, cela arrive ! Cliquez sur le bouton ci-dessous pour réinitialiser votre mot de passe." msgstr "Ce n'est pas grave, cela arrive ! Cliquez sur le bouton ci-dessous pour réinitialiser votre mot de passe."
#: apps/remix/app/components/dialogs/admin-user-delete-dialog.tsx #: apps/remix/app/components/dialogs/admin-user-delete-dialog.tsx
msgid "The account has been deleted successfully." msgid "The account has been deleted successfully."
@@ -5501,6 +5565,10 @@ msgstr "Le token que vous avez utilisé pour réinitialiser votre mot de passe a
msgid "The two-factor authentication code provided is incorrect" msgid "The two-factor authentication code provided is incorrect"
msgstr "" msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "The types of signatures that recipients are allowed to use when signing the document."
msgstr ""
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx #: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
@@ -5522,7 +5590,7 @@ msgstr "Le webhook a été créé avec succès."
#: apps/remix/app/components/tables/documents-table-empty-state.tsx #: 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." msgid "There are no active drafts at the current moment. You can upload a document to start drafting."
msgstr "Il n'y a pas de brouillons actifs pour le moment. Vous pouvez télécharger un document pour commencer à rédiger." msgstr "Il n'y a pas de brouillons actifs pour le moment. Vous pouvez importer un document pour commencer un brouillon."
#: apps/remix/app/components/tables/documents-table-empty-state.tsx #: apps/remix/app/components/tables/documents-table-empty-state.tsx
msgid "There are no completed documents yet. Documents that you have created or received will appear here once completed." msgid "There are no completed documents yet. Documents that you have created or received will appear here once completed."
@@ -5582,6 +5650,10 @@ msgstr "Ce document a été annulé par le propriétaire et n'est plus disponibl
msgid "This document has been cancelled by the owner." msgid "This document has been cancelled by the owner."
msgstr "Ce document a été annulé par le propriétaire." msgstr "Ce document a été annulé par le propriétaire."
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been rejected by a recipient"
msgstr ""
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx #: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been signed by all recipients" msgid "This document has been signed by all recipients"
msgstr "Ce document a été signé par tous les destinataires" msgstr "Ce document a été signé par tous les destinataires"
@@ -5730,6 +5802,10 @@ msgstr "Fuseau horaire"
msgid "Title" msgid "Title"
msgstr "Titre" msgstr "Titre"
#: packages/ui/primitives/document-flow/add-settings.types.ts
msgid "Title cannot be empty"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx #: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx
msgid "To accept this invitation you must create an account." msgid "To accept this invitation you must create an account."
msgstr "Pour accepter cette invitation, vous devez créer un compte." msgstr "Pour accepter cette invitation, vous devez créer un compte."
@@ -5897,6 +5973,7 @@ msgstr "Ré-authentification à deux facteurs"
#: apps/remix/app/components/tables/templates-table.tsx #: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: packages/lib/constants/document.ts
msgid "Type" msgid "Type"
msgstr "Type" msgstr "Type"
@@ -6004,6 +6081,7 @@ msgstr "Non complet"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx
msgid "Unknown" msgid "Unknown"
msgstr "Inconnu" msgstr "Inconnu"
@@ -6014,11 +6092,14 @@ msgstr "Non payé"
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx #: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx #: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/forms/public-profile-form.tsx #: apps/remix/app/components/forms/public-profile-form.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx #: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Update" msgid "Update"
msgstr "Mettre à jour" msgstr "Mettre à jour"
@@ -6039,6 +6120,8 @@ msgid "Update profile"
msgstr "Mettre à jour le profil" msgstr "Mettre à jour le profil"
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Update Recipient" msgid "Update Recipient"
msgstr "Mettre à jour le destinataire" msgstr "Mettre à jour le destinataire"
@@ -6077,10 +6160,6 @@ msgstr "Mettre à jour le webhook"
msgid "Updating password..." msgid "Updating password..."
msgstr "Mise à jour du mot de passe..." msgstr "Mise à jour du mot de passe..."
#: apps/remix/app/components/forms/profile.tsx
msgid "Updating profile..."
msgstr "Mise à jour du profil..."
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "Updating Your Information" msgid "Updating Your Information"
msgstr "Mise à jour de vos informations" msgstr "Mise à jour de vos informations"
@@ -6089,37 +6168,41 @@ msgstr "Mise à jour de vos informations"
msgid "Upgrade" msgid "Upgrade"
msgstr "Améliorer" msgstr "Améliorer"
#: packages/lib/constants/document.ts
msgid "Upload"
msgstr ""
#: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx #: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx
msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details." msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details."
msgstr "Téléchargez un fichier CSV pour créer plusieurs documents à partir de ce modèle. Chaque ligne représente un document avec ses détails de destinataire." msgstr "Importer un fichier CSV pour créer plusieurs documents à partir de ce modèle. Chaque ligne représente un document avec les coordonnées de son destinataire."
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
msgid "Upload a custom document to use instead of the template's default document" msgid "Upload a custom document to use instead of the template's default document"
msgstr "Téléchargez un document personnalisé à utiliser à la place du document par défaut du modèle" msgstr "Importer un document personnalisé à utiliser à la place du modèle par défaut"
#: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx #: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx
msgid "Upload and Process" msgid "Upload and Process"
msgstr "Télécharger et traiter" msgstr "Importer et traiter"
#: apps/remix/app/components/forms/avatar-image.tsx #: apps/remix/app/components/forms/avatar-image.tsx
msgid "Upload Avatar" msgid "Upload Avatar"
msgstr "Télécharger un avatar" msgstr "Importer un avatar"
#: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx #: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx
msgid "Upload CSV" msgid "Upload CSV"
msgstr "Télécharger le CSV" msgstr "Importer le CSV"
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
msgid "Upload custom document" msgid "Upload custom document"
msgstr "Télécharger un document personnalisé" msgstr "Importer un document personnalisé"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-upload.tsx
msgid "Upload Signature" msgid "Upload Signature"
msgstr "Importer une signature" msgstr "Importer une signature"
#: packages/ui/primitives/document-dropzone.tsx #: packages/ui/primitives/document-dropzone.tsx
msgid "Upload Template Document" msgid "Upload Template Document"
msgstr "Télécharger le document modèle" msgstr "Importer le document modèle"
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx #: apps/remix/app/components/forms/team-branding-preferences-form.tsx
msgid "Upload your brand logo (max 5MB, JPG, PNG, or WebP)" msgid "Upload your brand logo (max 5MB, JPG, PNG, or WebP)"
@@ -6132,15 +6215,15 @@ msgstr "Téléversé par"
#: apps/remix/app/components/forms/avatar-image.tsx #: apps/remix/app/components/forms/avatar-image.tsx
msgid "Uploaded file is too large" msgid "Uploaded file is too large"
msgstr "Le fichier téléchargé est trop volumineux" msgstr "Le fichier importé est trop volumineux"
#: apps/remix/app/components/forms/avatar-image.tsx #: apps/remix/app/components/forms/avatar-image.tsx
msgid "Uploaded file is too small" msgid "Uploaded file is too small"
msgstr "Le fichier téléchargé est trop petit" msgstr "Le fichier importé est trop petit"
#: apps/remix/app/components/forms/avatar-image.tsx #: apps/remix/app/components/forms/avatar-image.tsx
msgid "Uploaded file not an allowed file type" msgid "Uploaded file not an allowed file type"
msgstr "Le fichier téléchargé n'est pas un type de fichier autorisé" msgstr "Le fichier importé n'est pas un type de fichier autorisé"
#: apps/remix/app/routes/_authenticated+/templates.$id._index.tsx #: apps/remix/app/routes/_authenticated+/templates.$id._index.tsx
msgid "Use" msgid "Use"
@@ -6232,7 +6315,7 @@ msgstr "Vérifiez votre adresse e-mail pour débloquer toutes les fonctionnalit
#: apps/remix/app/components/general/document/document-upload.tsx #: apps/remix/app/components/general/document/document-upload.tsx
msgid "Verify your email to upload documents." msgid "Verify your email to upload documents."
msgstr "Vérifiez votre e-mail pour télécharger des documents." msgstr "Vérifiez votre e-mail pour importer des documents."
#: packages/email/templates/confirm-team-email.tsx #: packages/email/templates/confirm-team-email.tsx
msgid "Verify your team email address" msgid "Verify your team email address"
@@ -6287,6 +6370,7 @@ msgstr "Voir le document"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
#: packages/email/template-components/template-document-invite.tsx #: packages/email/template-components/template-document-invite.tsx
msgid "View Document" msgid "View Document"
@@ -6329,11 +6413,11 @@ msgstr "Vu"
#: packages/lib/constants/recipient-roles.ts #: packages/lib/constants/recipient-roles.ts
msgid "Viewer" msgid "Viewer"
msgstr "Visiteur" msgstr "Lecteur"
#: packages/lib/constants/recipient-roles.ts #: packages/lib/constants/recipient-roles.ts
msgid "Viewers" msgid "Viewers"
msgstr "Spectateurs" msgstr "Lecteurs"
#: packages/lib/constants/recipient-roles.ts #: packages/lib/constants/recipient-roles.ts
msgid "Viewing" msgid "Viewing"
@@ -6653,6 +6737,11 @@ msgstr "Bienvenue sur Documenso !"
msgid "Were you trying to edit this document instead?" msgid "Were you trying to edit this document instead?"
msgstr "Essayiez-vous d'éditer ce document à la place ?" msgstr "Essayiez-vous d'éditer ce document à la place ?"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "When enabled, signers can choose who should sign next in the sequence instead of following the predefined order."
msgstr ""
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "When you click continue, you will be prompted to add the first available authenticator on your system." msgid "When you click continue, you will be prompted to add the first available authenticator on your system."
msgstr "Lorsque vous cliquez sur continuer, vous serez invité à ajouter le premier authentificateur disponible sur votre système." msgstr "Lorsque vous cliquez sur continuer, vous serez invité à ajouter le premier authentificateur disponible sur votre système."
@@ -6728,7 +6817,7 @@ msgstr "Vous êtes sur le point d'envoyer ce document aux destinataires. Êtes-v
#: apps/remix/app/routes/_authenticated+/settings+/billing.tsx #: apps/remix/app/routes/_authenticated+/settings+/billing.tsx
msgid "You are currently on the <0>Free Plan</0>." msgid "You are currently on the <0>Free Plan</0>."
msgstr "" msgstr "Vous êtes actuellement sur l'<0>Abonnement Gratuit</0>."
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx #: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
msgid "You are currently updating <0>{teamMemberName}.</0>" msgid "You are currently updating <0>{teamMemberName}.</0>"
@@ -6809,11 +6898,11 @@ msgstr "Vous ne pouvez pas modifier un membre de l'équipe qui a un rôle plus
#: packages/ui/primitives/document-dropzone.tsx #: packages/ui/primitives/document-dropzone.tsx
msgid "You cannot upload documents at this time." msgid "You cannot upload documents at this time."
msgstr "Vous ne pouvez pas télécharger de documents pour le moment." msgstr "Vous ne pouvez pas importer de documents pour le moment."
#: apps/remix/app/components/general/document/document-upload.tsx #: apps/remix/app/components/general/document/document-upload.tsx
msgid "You cannot upload encrypted PDFs" msgid "You cannot upload encrypted PDFs"
msgstr "Vous ne pouvez pas télécharger de PDF cryptés" msgstr "Vous ne pouvez pas importer de PDF cryptés"
#: apps/remix/app/components/general/billing-portal-button.tsx #: apps/remix/app/components/general/billing-portal-button.tsx
msgid "You do not currently have a customer record, this should not happen. Please contact support for assistance." msgid "You do not currently have a customer record, this should not happen. Please contact support for assistance."
@@ -6885,11 +6974,11 @@ msgstr "Vous n'avez pas encore de webhooks. Vos webhooks seront affichés ici un
#: apps/remix/app/routes/_authenticated+/templates._index.tsx #: apps/remix/app/routes/_authenticated+/templates._index.tsx
msgid "You have not yet created any templates. To create a template please upload one." msgid "You have not yet created any templates. To create a template please upload one."
msgstr "Vous n'avez pas encore créé de modèles. Pour créer un modèle, veuillez en télécharger un." msgstr "Vous n'avez pas encore créé de modèles. Pour créer un modèle, veuillez en importer un."
#: apps/remix/app/components/tables/documents-table-empty-state.tsx #: apps/remix/app/components/tables/documents-table-empty-state.tsx
msgid "You have not yet created or received any documents. To create a document please upload one." 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 télécharger un." msgstr "Vous n'avez pas encore créé ou reçu de documents. Pour créer un document, veuillez en importer un."
#. placeholder {0}: quota.directTemplates #. placeholder {0}: quota.directTemplates
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx #: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
@@ -6898,7 +6987,7 @@ msgstr "Vous avez atteint la limite maximale de {0} modèles directs. <0>Mettez
#: apps/remix/app/components/general/document/document-upload.tsx #: apps/remix/app/components/general/document/document-upload.tsx
msgid "You have reached your document limit for this month. Please upgrade your plan." 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 à un plan supérieur." msgstr "Vous avez atteint votre limite de documents pour ce mois. Veuillez passer à l'abonnement supérieur."
#: apps/remix/app/components/general/document/document-upload.tsx #: apps/remix/app/components/general/document/document-upload.tsx
#: packages/ui/primitives/document-dropzone.tsx #: packages/ui/primitives/document-dropzone.tsx
@@ -7024,11 +7113,11 @@ msgstr "Votre envoi groupé a été initié. Vous recevrez une notification par
#: packages/email/templates/bulk-send-complete.tsx #: packages/email/templates/bulk-send-complete.tsx
msgid "Your bulk send operation for template \"{templateName}\" has completed." msgid "Your bulk send operation for template \"{templateName}\" has completed."
msgstr "Votre opération d'envoi groupé pour le modèle \"{templateName}\" est terminée." msgstr "Votre envoi groupé pour le modèle \"{templateName}\" est terminé."
#: apps/remix/app/routes/_authenticated+/settings+/billing.tsx #: apps/remix/app/routes/_authenticated+/settings+/billing.tsx
msgid "Your current plan is past due. Please update your payment information." msgid "Your current plan is past due. Please update your payment information."
msgstr "" msgstr "Votre abonnement actuel est arrivé à échéance. Veuillez mettre à jour vos informations de paiement."
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
msgid "Your direct signing templates" msgid "Your direct signing templates"
@@ -7036,7 +7125,7 @@ msgstr "Vos modèles de signature directe"
#: apps/remix/app/components/general/document/document-upload.tsx #: apps/remix/app/components/general/document/document-upload.tsx
msgid "Your document failed to upload." msgid "Your document failed to upload."
msgstr "Votre document a échoué à se télécharger." msgstr "L'importation de votre document a échoué."
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
msgid "Your document has been created from the template successfully." msgid "Your document has been created from the template successfully."
@@ -7060,11 +7149,11 @@ msgstr "Votre document a été dupliqué avec succès."
#: apps/remix/app/components/general/document/document-upload.tsx #: apps/remix/app/components/general/document/document-upload.tsx
msgid "Your document has been uploaded successfully." msgid "Your document has been uploaded successfully."
msgstr "Votre document a été téléchargé avec succès." msgstr "Votre document a été importé avec succès."
#: apps/remix/app/components/dialogs/template-create-dialog.tsx #: apps/remix/app/components/dialogs/template-create-dialog.tsx
msgid "Your document has been uploaded successfully. You will be redirected to the template page." msgid "Your document has been uploaded successfully. You will be redirected to the template page."
msgstr "Votre document a été téléchargé avec succès. Vous serez redirigé vers la page de modèle." msgstr "Votre document a été importé avec succès. Vous serez redirigé vers la page de modèle."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Your document preferences have been updated" msgid "Your document preferences have been updated"

View File

@@ -419,6 +419,10 @@ msgstr "<0>{teamName}</0> ha richiesto di utilizzare il tuo indirizzo email per
msgid "<0>Click to upload</0> or drag and drop" msgid "<0>Click to upload</0> or drag and drop"
msgstr "<0>Fai clic per caricare</0> o trascina e rilascia" msgstr "<0>Fai clic per caricare</0> o trascina e rilascia"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Drawn</0> - A signature that is drawn using a mouse or stylus."
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc." msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc."
msgstr "<0>Email</0> - Al destinatario verrà inviato il documento tramite email per firmare, approvare, ecc." msgstr "<0>Email</0> - Al destinatario verrà inviato il documento tramite email per firmare, approvare, ecc."
@@ -465,6 +469,14 @@ msgstr "<0>Richiede passkey</0> - Il destinatario deve avere un account e una pa
msgid "<0>Sender:</0> All" msgid "<0>Sender:</0> All"
msgstr "<0>Mittente:</0> Tutti" msgstr "<0>Mittente:</0> Tutti"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Typed</0> - A signature that is typed using a keyboard."
msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Uploaded</0> - A signature that is uploaded from a file."
msgstr ""
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx #: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?" msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?"
msgstr "<0>Stai per completare l'approvazione di <1>\"{documentTitle}\"</1>.</0><2/> Sei sicuro?" msgstr "<0>Stai per completare l'approvazione di <1>\"{documentTitle}\"</1>.</0><2/> Sei sicuro?"
@@ -898,6 +910,16 @@ msgstr "Tutto il tempo"
msgid "Allow document recipients to reply directly to this email address" msgid "Allow document recipients to reply directly to this email address"
msgstr "Consenti ai destinatari del documento di rispondere direttamente a questo indirizzo email" msgstr "Consenti ai destinatari del documento di rispondere direttamente a questo indirizzo email"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "Allow signers to dictate next signer"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Allowed Signature Types"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
msgid "Allows authenticating using biometrics, password managers, hardware keys, etc." msgid "Allows authenticating using biometrics, password managers, hardware keys, etc."
msgstr "Consente di autenticare utilizzando biometria, gestori di password, chiavi hardware, ecc." msgstr "Consente di autenticare utilizzando biometria, gestori di password, chiavi hardware, ecc."
@@ -1237,6 +1259,13 @@ msgstr ""
msgid "Assisting" msgid "Assisting"
msgstr "" msgstr ""
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: packages/ui/primitives/template-flow/add-template-settings.types.tsx
#: packages/ui/primitives/document-flow/add-settings.types.ts
#: packages/lib/types/document-meta.ts
msgid "At least one signature type must be enabled"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx #: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document." msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document."
msgstr "Tenta nuovamente di sigillare il documento, utile dopo una modifica al codice per risolvere un documento errato." msgstr "Tenta nuovamente di sigillare il documento, utile dopo una modifica al codice per risolvere un documento errato."
@@ -1309,11 +1338,11 @@ msgstr "Prima di iniziare, conferma il tuo indirizzo email facendo clic sul puls
msgid "Billing" msgid "Billing"
msgstr "Fatturazione" msgstr "Fatturazione"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Black" msgid "Black"
msgstr "Nero" msgstr "Nero"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Blue" msgid "Blue"
msgstr "Blu" msgstr "Blu"
@@ -1384,6 +1413,10 @@ msgstr "Procedendo con l'utilizzo del servizio di firma elettronica fornito da D
msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions." msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions."
msgstr "Procedendo con la tua firma elettronica, riconosci e acconsenti che sarà utilizzata per firmare il documento dato e ha la stessa validità legale di una firma autografa. Completando il processo di firma elettronica, affermi la tua comprensione e accettazione di queste condizioni." msgstr "Procedendo con la tua firma elettronica, riconosci e acconsenti che sarà utilizzata per firmare il documento dato e ha la stessa validità legale di una firma autografa. Completando il processo di firma elettronica, affermi la tua comprensione e accettazione di queste condizioni."
#: apps/remix/app/components/forms/signup.tsx
msgid "By proceeding, you agree to our <0>Terms of Service</0> and <1>Privacy Policy</1>."
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing." msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing."
msgstr "Utilizzando la funzione di firma elettronica, acconsenti a effettuare transazioni e ricevere divulgazioni elettronicamente. Riconosci che la tua firma elettronica sui documenti è vincolante e accetti i termini delineati nei documenti che stai firmando." msgstr "Utilizzando la funzione di firma elettronica, acconsenti a effettuare transazioni e ricevere divulgazioni elettronicamente. Riconosci che la tua firma elettronica sui documenti è vincolante e accetti i termini delineati nei documenti che stai firmando."
@@ -1436,6 +1469,8 @@ msgstr ""
#: apps/remix/app/components/dialogs/document-move-dialog.tsx #: apps/remix/app/components/dialogs/document-move-dialog.tsx
#: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx #: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx #: packages/ui/primitives/document-flow/send-document-action-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Cancel" msgid "Cancel"
@@ -1522,7 +1557,7 @@ msgstr "Rimuovi file"
msgid "Clear filters" msgid "Clear filters"
msgstr "Cancella filtri" msgstr "Cancella filtri"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
msgid "Clear Signature" msgid "Clear Signature"
msgstr "Cancella firma" msgstr "Cancella firma"
@@ -1697,6 +1732,7 @@ msgstr "Contenuto"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/document-flow/document-flow-root.tsx #: packages/ui/primitives/document-flow/document-flow-root.tsx
msgid "Continue" msgid "Continue"
msgstr "Continua" msgstr "Continua"
@@ -1737,14 +1773,18 @@ msgstr "Controlla la visibilità predefinita di un documento caricato."
msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead." msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead."
msgstr "Controlla la formattazione del messaggio che verrà inviato quando si invita un destinatario a firmare un documento. Se è stato fornito un messaggio personalizzato durante la configurazione del documento, verrà utilizzato invece." msgstr "Controlla la formattazione del messaggio che verrà inviato quando si invita un destinatario a firmare un documento. Se è stato fornito un messaggio personalizzato durante la configurazione del documento, verrà utilizzato invece."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Controls whether the recipients can sign the documents using a typed signature. Enable or disable the typed signature globally." msgid "Controls the language for the document, including the language to be used for email notifications, and the final certificate that is generated and attached to the document."
msgstr "Controlla se i destinatari possono firmare i documenti utilizzando una firma digitata. Abilita o disabilita la firma digitata a livello globale." msgstr ""
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately." msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately."
msgstr "Controlla se il certificato di firma sarà incluso nel documento quando viene scaricato. Il certificato di firma può comunque essere scaricato separatamente dalla pagina dei log." msgstr "Controlla se il certificato di firma sarà incluso nel documento quando viene scaricato. Il certificato di firma può comunque essere scaricato separatamente dalla pagina dei log."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls which signatures are allowed to be used when signing a document."
msgstr ""
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx #: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied" msgid "Copied"
@@ -1975,6 +2015,10 @@ msgstr "Lingua predefinita del documento"
msgid "Default Document Visibility" msgid "Default Document Visibility"
msgstr "Visibilità predefinita del documento" msgstr "Visibilità predefinita del documento"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Default Signature Settings"
msgstr ""
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "delete" msgid "delete"
msgstr "elimina" msgstr "elimina"
@@ -2201,6 +2245,11 @@ msgstr "Documento \"{0}\" - Rifiutato da {1}"
msgid "Document \"{0}\" - Rejection Confirmed" msgid "Document \"{0}\" - Rejection Confirmed"
msgstr "Documento \"{0}\" - Rifiuto Confermato" msgstr "Documento \"{0}\" - Rifiuto Confermato"
#. placeholder {0}: document.title
#: packages/lib/jobs/definitions/emails/send-document-cancelled-emails.handler.ts
msgid "Document \"{0}\" Cancelled"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
#: packages/ui/components/document/document-global-auth-access-select.tsx #: packages/ui/components/document/document-global-auth-access-select.tsx
@@ -2349,6 +2398,10 @@ msgstr "Preferenze del documento aggiornate"
msgid "Document re-sent" msgid "Document re-sent"
msgstr "Documento rinviato" msgstr "Documento rinviato"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document rejected"
msgstr ""
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx #: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
#: apps/remix/app/components/embed/embed-document-rejected.tsx #: apps/remix/app/components/embed/embed-document-rejected.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
@@ -2486,6 +2539,10 @@ msgstr "Documenti redatti"
msgid "Drag & drop your PDF here." msgid "Drag & drop your PDF here."
msgstr "Trascina e rilascia il tuo PDF qui." msgstr "Trascina e rilascia il tuo PDF qui."
#: packages/lib/constants/document.ts
msgid "Draw"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx #: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Dropdown" msgid "Dropdown"
@@ -2543,6 +2600,7 @@ msgstr "Divulgazione della firma elettronica"
#: apps/remix/app/components/tables/admin-document-recipient-item-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/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx
#: apps/remix/app/components/forms/signin.tsx #: apps/remix/app/components/forms/signin.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -2553,6 +2611,7 @@ msgstr "Divulgazione della firma elettronica"
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -2646,15 +2705,6 @@ msgstr "Abilita la firma di link diretto"
msgid "Enable signing order" msgid "Enable signing order"
msgstr "Abilita ordine di firma" msgstr "Abilita ordine di firma"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Enable Typed Signature"
msgstr "Abilita firma digitata"
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Enable Typed Signatures"
msgstr "Abilita firme digitate"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx
@@ -2929,7 +2979,7 @@ msgstr "Vai al proprietario"
msgid "Go to your <0>public profile settings</0> to add documents." msgid "Go to your <0>public profile settings</0> to add documents."
msgstr "Vai alle tue <0>impostazioni del profilo pubblico</0> per aggiungere documenti." msgstr "Vai alle tue <0>impostazioni del profilo pubblico</0> per aggiungere documenti."
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Green" msgid "Green"
msgstr "Verde" msgstr "Verde"
@@ -3483,10 +3533,12 @@ msgstr "I miei modelli"
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx #: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -3537,6 +3589,7 @@ msgstr "Nuovo modello"
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/embed/embed-document-signing-page.tsx #: apps/remix/app/components/embed/embed-document-signing-page.tsx
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx #: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
msgid "Next" msgid "Next"
msgstr "Successivo" msgstr "Successivo"
@@ -3954,6 +4007,7 @@ msgstr "Si prega di inserire un nome significativo per il proprio token. Questo
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/forms/profile.tsx
msgid "Please enter a valid name." msgid "Please enter a valid name."
msgstr "Per favore inserisci un nome valido." msgstr "Per favore inserisci un nome valido."
@@ -3989,10 +4043,6 @@ msgstr "Si prega di notare che questa azione è irreversibile. Una volta conferm
msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified" msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified"
msgstr "Si prega di notare che perderai l'accesso a tutti i documenti associati a questo team e tutti i membri saranno rimossi e notificati" msgstr "Si prega di notare che perderai l'accesso a tutti i documenti associati a questo team e tutti i membri saranno rimossi e notificati"
#: apps/remix/app/components/general/document-signing/document-signing-reject-dialog.tsx
msgid "Please provide a reason"
msgstr "Per favore, fornire una ragione"
#: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx #: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx
msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support." msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support."
msgstr "Si prega di fornire un token dal tuo autenticatore, o un codice di backup. Se non hai un codice di backup disponibile, contatta il supporto." msgstr "Si prega di fornire un token dal tuo autenticatore, o un codice di backup. Se non hai un codice di backup disponibile, contatta il supporto."
@@ -4063,6 +4113,10 @@ msgstr "Privato"
msgid "Private templates can only be modified and viewed by you." msgid "Private templates can only be modified and viewed by you."
msgstr "I modelli privati possono essere modificati e visualizzati solo da te." msgstr "I modelli privati possono essere modificati e visualizzati solo da te."
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Proceed"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/profile.tsx #: apps/remix/app/routes/_authenticated+/settings+/profile.tsx
#: apps/remix/app/components/general/settings-nav-mobile.tsx #: apps/remix/app/components/general/settings-nav-mobile.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx #: apps/remix/app/components/general/settings-nav-desktop.tsx
@@ -4140,6 +4194,10 @@ msgstr "Pronto"
msgid "Reason" msgid "Reason"
msgstr "Motivo" msgstr "Motivo"
#: packages/email/template-components/template-document-cancel.tsx
msgid "Reason for cancellation: {cancellationReason}"
msgstr ""
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Reason for rejection: " msgid "Reason for rejection: "
msgstr "" msgstr ""
@@ -4222,7 +4280,7 @@ msgstr "Codice di recupero copiato"
msgid "Recovery codes" msgid "Recovery codes"
msgstr "Codici di recupero" msgstr "Codici di recupero"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Red" msgid "Red"
msgstr "Rosso" msgstr "Rosso"
@@ -4243,8 +4301,11 @@ msgstr "Registrazione avvenuta con successo"
msgid "Reject Document" msgid "Reject Document"
msgstr "Rifiuta Documento" msgstr "Rifiuta Documento"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx #: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/document/document-status.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: packages/lib/constants/document.ts
msgid "Rejected" msgid "Rejected"
msgstr "Rifiutato" msgstr "Rifiutato"
@@ -4429,8 +4490,6 @@ msgstr "Righe per pagina"
#: 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-number-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-number-field.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx #: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Save" msgid "Save"
@@ -4740,7 +4799,6 @@ msgstr "Iscriviti con OIDC"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -4757,12 +4815,17 @@ msgstr "Firma"
msgid "Signature ID" msgid "Signature ID"
msgstr "ID Firma" msgstr "ID Firma"
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx msgid "Signature is too small"
#: apps/remix/app/components/embed/embed-document-signing-page.tsx msgstr ""
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
msgid "Signature is too small. Please provide a more complete signature." #: apps/remix/app/components/forms/profile.tsx
msgstr "La firma è troppo piccola. Si prega di fornire una firma più completa." msgid "Signature Pad cannot be empty."
msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "Signature types"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx #: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Signatures Collected" msgid "Signatures Collected"
@@ -4987,6 +5050,7 @@ msgid "Subject <0>(Optional)</0>"
msgstr "Oggetto <0>(Opzionale)</0>" msgstr "Oggetto <0>(Opzionale)</0>"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Submitting..." msgid "Submitting..."
msgstr "" msgstr ""
@@ -5501,6 +5565,10 @@ msgstr "Il token che hai usato per reimpostare la tua password è scaduto o non
msgid "The two-factor authentication code provided is incorrect" msgid "The two-factor authentication code provided is incorrect"
msgstr "" msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "The types of signatures that recipients are allowed to use when signing the document."
msgstr ""
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx #: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
@@ -5582,6 +5650,10 @@ msgstr "Questo documento è stato annullato dal proprietario e non è più dispo
msgid "This document has been cancelled by the owner." msgid "This document has been cancelled by the owner."
msgstr "Questo documento è stato annullato dal proprietario." msgstr "Questo documento è stato annullato dal proprietario."
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been rejected by a recipient"
msgstr ""
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx #: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been signed by all recipients" msgid "This document has been signed by all recipients"
msgstr "Questo documento è stato firmato da tutti i destinatari" msgstr "Questo documento è stato firmato da tutti i destinatari"
@@ -5730,6 +5802,10 @@ msgstr "Fuso orario"
msgid "Title" msgid "Title"
msgstr "Titolo" msgstr "Titolo"
#: packages/ui/primitives/document-flow/add-settings.types.ts
msgid "Title cannot be empty"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx #: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx
msgid "To accept this invitation you must create an account." msgid "To accept this invitation you must create an account."
msgstr "Per accettare questo invito devi creare un account." msgstr "Per accettare questo invito devi creare un account."
@@ -5897,6 +5973,7 @@ msgstr "Ri-autenticazione a due fattori"
#: apps/remix/app/components/tables/templates-table.tsx #: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: packages/lib/constants/document.ts
msgid "Type" msgid "Type"
msgstr "Tipo" msgstr "Tipo"
@@ -6004,6 +6081,7 @@ msgstr "Incompleto"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx
msgid "Unknown" msgid "Unknown"
msgstr "Sconosciuto" msgstr "Sconosciuto"
@@ -6014,11 +6092,14 @@ msgstr "Non pagato"
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx #: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx #: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/forms/public-profile-form.tsx #: apps/remix/app/components/forms/public-profile-form.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx #: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Update" msgid "Update"
msgstr "Aggiorna" msgstr "Aggiorna"
@@ -6039,6 +6120,8 @@ msgid "Update profile"
msgstr "Aggiorna profilo" msgstr "Aggiorna profilo"
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Update Recipient" msgid "Update Recipient"
msgstr "Aggiorna destinatario" msgstr "Aggiorna destinatario"
@@ -6077,10 +6160,6 @@ msgstr "Aggiorna webhook"
msgid "Updating password..." msgid "Updating password..."
msgstr "Aggiornamento della password..." msgstr "Aggiornamento della password..."
#: apps/remix/app/components/forms/profile.tsx
msgid "Updating profile..."
msgstr "Aggiornamento del profilo..."
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "Updating Your Information" msgid "Updating Your Information"
msgstr "Aggiornamento delle tue informazioni" msgstr "Aggiornamento delle tue informazioni"
@@ -6089,6 +6168,10 @@ msgstr "Aggiornamento delle tue informazioni"
msgid "Upgrade" msgid "Upgrade"
msgstr "Aggiorna" msgstr "Aggiorna"
#: packages/lib/constants/document.ts
msgid "Upload"
msgstr ""
#: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx #: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx
msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details." msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details."
msgstr "Carica un file CSV per creare più documenti da questo modello. Ogni riga rappresenta un documento con i dettagli del destinatario." msgstr "Carica un file CSV per creare più documenti da questo modello. Ogni riga rappresenta un documento con i dettagli del destinatario."
@@ -6113,7 +6196,7 @@ msgstr "Carica CSV"
msgid "Upload custom document" msgid "Upload custom document"
msgstr "Carica documento personalizzato" msgstr "Carica documento personalizzato"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-upload.tsx
msgid "Upload Signature" msgid "Upload Signature"
msgstr "Carica Firma" msgstr "Carica Firma"
@@ -6287,6 +6370,7 @@ msgstr "Visualizza documento"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
#: packages/email/template-components/template-document-invite.tsx #: packages/email/template-components/template-document-invite.tsx
msgid "View Document" msgid "View Document"
@@ -6653,6 +6737,11 @@ msgstr "Benvenuto su Documenso!"
msgid "Were you trying to edit this document instead?" msgid "Were you trying to edit this document instead?"
msgstr "Stavi provando a modificare questo documento invece?" msgstr "Stavi provando a modificare questo documento invece?"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "When enabled, signers can choose who should sign next in the sequence instead of following the predefined order."
msgstr ""
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "When you click continue, you will be prompted to add the first available authenticator on your system." msgid "When you click continue, you will be prompted to add the first available authenticator on your system."
msgstr "Quando fai clic su continua, ti verrà chiesto di aggiungere il primo autenticatore disponibile sul tuo sistema." msgstr "Quando fai clic su continua, ti verrà chiesto di aggiungere il primo autenticatore disponibile sul tuo sistema."

View File

@@ -419,6 +419,10 @@ msgstr "<0>{teamName}</0> poprosił o używanie twojego adresu e-mail dla swojeg
msgid "<0>Click to upload</0> or drag and drop" msgid "<0>Click to upload</0> or drag and drop"
msgstr "<0>Kliknij, aby przesłać</0> lub przeciągnij i upuść" msgstr "<0>Kliknij, aby przesłać</0> lub przeciągnij i upuść"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Drawn</0> - A signature that is drawn using a mouse or stylus."
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc." msgid "<0>Email</0> - The recipient will be emailed the document to sign, approve, etc."
msgstr "<0>E-mail</0> - Odbiorca otrzyma e-mail z dokumentem do podpisania, zatwierdzenia itp." msgstr "<0>E-mail</0> - Odbiorca otrzyma e-mail z dokumentem do podpisania, zatwierdzenia itp."
@@ -465,6 +469,14 @@ msgstr "<0>Wymagana passkey</0> - Odbiorca musi mieć konto i skonfigurowaną pa
msgid "<0>Sender:</0> All" msgid "<0>Sender:</0> All"
msgstr "<0>Rządzący:</0> Wszyscy" msgstr "<0>Rządzący:</0> Wszyscy"
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Typed</0> - A signature that is typed using a keyboard."
msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "<0>Uploaded</0> - A signature that is uploaded from a file."
msgstr ""
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx #: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?" msgid "<0>You are about to complete approving <1>\"{documentTitle}\"</1>.</0><2/> Are you sure?"
msgstr "<0>Jesteś na drodze do zatwierdzenia <1>\"{documentTitle}\"</1>.</0><2/> Czy jesteś pewien?" msgstr "<0>Jesteś na drodze do zatwierdzenia <1>\"{documentTitle}\"</1>.</0><2/> Czy jesteś pewien?"
@@ -898,6 +910,16 @@ msgstr "Cały czas"
msgid "Allow document recipients to reply directly to this email address" msgid "Allow document recipients to reply directly to this email address"
msgstr "Zezwól odbiorcom dokumentów na bezpośrednią odpowiedź na ten adres e-mail" msgstr "Zezwól odbiorcom dokumentów na bezpośrednią odpowiedź na ten adres e-mail"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "Allow signers to dictate next signer"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Allowed Signature Types"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
msgid "Allows authenticating using biometrics, password managers, hardware keys, etc." msgid "Allows authenticating using biometrics, password managers, hardware keys, etc."
msgstr "Pozwala na uwierzytelnianie za pomocą biometrii, menedżerów haseł, kluczy sprzętowych itp." msgstr "Pozwala na uwierzytelnianie za pomocą biometrii, menedżerów haseł, kluczy sprzętowych itp."
@@ -1237,6 +1259,13 @@ msgstr ""
msgid "Assisting" msgid "Assisting"
msgstr "" msgstr ""
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: packages/ui/primitives/template-flow/add-template-settings.types.tsx
#: packages/ui/primitives/document-flow/add-settings.types.ts
#: packages/lib/types/document-meta.ts
msgid "At least one signature type must be enabled"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx #: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document." msgid "Attempts sealing the document again, useful for after a code change has occurred to resolve an erroneous document."
msgstr "Ponowne próby zapieczętowania dokumentu, przydatne po zmianie kodu w celu rozwiązania błędnego dokumentu." msgstr "Ponowne próby zapieczętowania dokumentu, przydatne po zmianie kodu w celu rozwiązania błędnego dokumentu."
@@ -1309,11 +1338,11 @@ msgstr "Zanim zaczniesz, proszę potwierdź swój adres e-mail, klikając przyci
msgid "Billing" msgid "Billing"
msgstr "Fakturowanie" msgstr "Fakturowanie"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Black" msgid "Black"
msgstr "Czarny" msgstr "Czarny"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Blue" msgid "Blue"
msgstr "Niebieski" msgstr "Niebieski"
@@ -1384,6 +1413,10 @@ msgstr "Kontynuując korzystanie z usługi podpisu elektronicznego oferowanej pr
msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions." msgid "By proceeding with your electronic signature, you acknowledge and consent that it will be used to sign the given document and holds the same legal validity as a handwritten signature. By completing the electronic signing process, you affirm your understanding and acceptance of these conditions."
msgstr "Kontynuując z Twoim podpisem elektronicznym, przyjmujesz i zgadzasz się, że będzie on użyty do podpisania danego dokumentu i ma tę samą ważność prawną jak odręczny podpis. Dokonując procesu podpisu elektronicznego, potwierdzasz swoje zrozumienie i akceptację tych warunków." msgstr "Kontynuując z Twoim podpisem elektronicznym, przyjmujesz i zgadzasz się, że będzie on użyty do podpisania danego dokumentu i ma tę samą ważność prawną jak odręczny podpis. Dokonując procesu podpisu elektronicznego, potwierdzasz swoje zrozumienie i akceptację tych warunków."
#: apps/remix/app/components/forms/signup.tsx
msgid "By proceeding, you agree to our <0>Terms of Service</0> and <1>Privacy Policy</1>."
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing." msgid "By using the electronic signature feature, you are consenting to conduct transactions and receive disclosures electronically. You acknowledge that your electronic signature on documents is binding and that you accept the terms outlined in the documents you are signing."
msgstr "Korzystając z funkcji podpisu elektronicznego, wyrażasz zgodę na przeprowadzanie transakcji i otrzymywanie ujawnień elektronicznie. Przyjmujesz do wiadomości, że Twój podpis elektroniczny na dokumentach jest wiążący i akceptujesz warunki przedstawione w dokumentach, które podpisujesz." msgstr "Korzystając z funkcji podpisu elektronicznego, wyrażasz zgodę na przeprowadzanie transakcji i otrzymywanie ujawnień elektronicznie. Przyjmujesz do wiadomości, że Twój podpis elektroniczny na dokumentach jest wiążący i akceptujesz warunki przedstawione w dokumentach, które podpisujesz."
@@ -1436,6 +1469,8 @@ msgstr ""
#: apps/remix/app/components/dialogs/document-move-dialog.tsx #: apps/remix/app/components/dialogs/document-move-dialog.tsx
#: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx #: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
#: packages/ui/primitives/document-flow/send-document-action-dialog.tsx #: packages/ui/primitives/document-flow/send-document-action-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Cancel" msgid "Cancel"
@@ -1522,7 +1557,7 @@ msgstr "Wyczyść plik"
msgid "Clear filters" msgid "Clear filters"
msgstr "Wyczyść filtry" msgstr "Wyczyść filtry"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
msgid "Clear Signature" msgid "Clear Signature"
msgstr "Wyczyść podpis" msgstr "Wyczyść podpis"
@@ -1697,6 +1732,7 @@ msgstr "Treść"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/document-flow/document-flow-root.tsx #: packages/ui/primitives/document-flow/document-flow-root.tsx
msgid "Continue" msgid "Continue"
msgstr "Kontynuuj" msgstr "Kontynuuj"
@@ -1737,14 +1773,18 @@ msgstr "Kontroluje domyślną widoczność przesłanego dokumentu."
msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead." msgid "Controls the formatting of the message that will be sent when inviting a recipient to sign a document. If a custom message has been provided while configuring the document, it will be used instead."
msgstr "Kontroluje formatowanie wiadomości, która zostanie wysłana podczas zapraszania odbiorcy do podpisania dokumentu. Jeśli w konfiguracji dokumentu podano niestandardową wiadomość, zostanie użyta zamiast tego." msgstr "Kontroluje formatowanie wiadomości, która zostanie wysłana podczas zapraszania odbiorcy do podpisania dokumentu. Jeśli w konfiguracji dokumentu podano niestandardową wiadomość, zostanie użyta zamiast tego."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
msgid "Controls whether the recipients can sign the documents using a typed signature. Enable or disable the typed signature globally." msgid "Controls the language for the document, including the language to be used for email notifications, and the final certificate that is generated and attached to the document."
msgstr "Kontroluje, czy odbiorcy mogą podpisywać dokumenty za pomocą pisanych podpisów. Włącz lub wyłącz podpis pisany globalnie." msgstr ""
#: apps/remix/app/components/forms/team-document-preferences-form.tsx #: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately." msgid "Controls whether the signing certificate will be included in the document when it is downloaded. The signing certificate can still be downloaded from the logs page separately."
msgstr "Kontroluje, czy certyfikat podpisu zostanie dołączony do dokumentu podczas jego pobierania. Certyfikat podpisu można również pobrać osobno ze strony logów." msgstr "Kontroluje, czy certyfikat podpisu zostanie dołączony do dokumentu podczas jego pobierania. Certyfikat podpisu można również pobrać osobno ze strony logów."
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Controls which signatures are allowed to be used when signing a document."
msgstr ""
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx #: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied" msgid "Copied"
@@ -1975,6 +2015,10 @@ msgstr "Domyślny język dokumentu"
msgid "Default Document Visibility" msgid "Default Document Visibility"
msgstr "Domyślna widoczność dokumentu" msgstr "Domyślna widoczność dokumentu"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Default Signature Settings"
msgstr ""
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx #: apps/remix/app/components/dialogs/document-delete-dialog.tsx
msgid "delete" msgid "delete"
msgstr "usuń" msgstr "usuń"
@@ -2201,6 +2245,11 @@ msgstr "Dokument \"{0}\" - Odrzucony przez {1}"
msgid "Document \"{0}\" - Rejection Confirmed" msgid "Document \"{0}\" - Rejection Confirmed"
msgstr "Dokument \"{0}\" - Odrzucenie potwierdzone" msgstr "Dokument \"{0}\" - Odrzucenie potwierdzone"
#. placeholder {0}: document.title
#: packages/lib/jobs/definitions/emails/send-document-cancelled-emails.handler.ts
msgid "Document \"{0}\" Cancelled"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-settings.tsx #: packages/ui/primitives/template-flow/add-template-settings.tsx
#: packages/ui/primitives/document-flow/add-settings.tsx #: packages/ui/primitives/document-flow/add-settings.tsx
#: packages/ui/components/document/document-global-auth-access-select.tsx #: packages/ui/components/document/document-global-auth-access-select.tsx
@@ -2349,6 +2398,10 @@ msgstr "Preferencje dokumentu zaktualizowane"
msgid "Document re-sent" msgid "Document re-sent"
msgstr "Dokument ponownie wysłany" msgstr "Dokument ponownie wysłany"
#: apps/remix/app/components/general/document/document-status.tsx
msgid "Document rejected"
msgstr ""
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx #: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
#: apps/remix/app/components/embed/embed-document-rejected.tsx #: apps/remix/app/components/embed/embed-document-rejected.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
@@ -2486,6 +2539,10 @@ msgstr "Szkice dokumentów"
msgid "Drag & drop your PDF here." msgid "Drag & drop your PDF here."
msgstr "Przeciągnij i upuść swój PDF tutaj." msgstr "Przeciągnij i upuść swój PDF tutaj."
#: packages/lib/constants/document.ts
msgid "Draw"
msgstr ""
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx #: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Dropdown" msgid "Dropdown"
@@ -2543,6 +2600,7 @@ msgstr "Ujawnienie podpisu elektronicznego"
#: apps/remix/app/components/tables/admin-document-recipient-item-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/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-email-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-configure-form.tsx
#: apps/remix/app/components/forms/signin.tsx #: apps/remix/app/components/forms/signin.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -2553,6 +2611,7 @@ msgstr "Ujawnienie podpisu elektronicznego"
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -2646,15 +2705,6 @@ msgstr "Włącz podpisywanie linku bezpośredniego"
msgid "Enable signing order" msgid "Enable signing order"
msgstr "Włącz kolejność podpisów" msgstr "Włącz kolejność podpisów"
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
msgid "Enable Typed Signature"
msgstr "Włącz podpis pisany"
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
msgid "Enable Typed Signatures"
msgstr "Włącz podpisy typu pisanego"
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks._index.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks._index.tsx
@@ -2929,7 +2979,7 @@ msgstr "Przejdź do właściciela"
msgid "Go to your <0>public profile settings</0> to add documents." msgid "Go to your <0>public profile settings</0> to add documents."
msgstr "Przejdź do swojego <0>ustawienia profilu publicznego</0>, aby dodać dokumenty." msgstr "Przejdź do swojego <0>ustawienia profilu publicznego</0>, aby dodać dokumenty."
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Green" msgid "Green"
msgstr "Zielony" msgstr "Zielony"
@@ -3483,10 +3533,12 @@ msgstr "Moje szablony"
#: apps/remix/app/components/tables/admin-dashboard-users-table.tsx #: apps/remix/app/components/tables/admin-dashboard-users-table.tsx
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-name-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/template-use-dialog.tsx #: apps/remix/app/components/dialogs/template-use-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx #: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx #: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx #: packages/ui/primitives/template-flow/add-template-fields.tsx
@@ -3537,6 +3589,7 @@ msgstr "Nowy szablon"
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/embed/embed-document-signing-page.tsx #: apps/remix/app/components/embed/embed-document-signing-page.tsx
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx #: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
#: packages/ui/primitives/signature-pad/signature-pad-dialog.tsx
msgid "Next" msgid "Next"
msgstr "Dalej" msgstr "Dalej"
@@ -3954,6 +4007,7 @@ msgstr "Wpisz nazwę tokena. Pomoże to później w jego identyfikacji."
#: apps/remix/app/components/general/claim-account.tsx #: apps/remix/app/components/general/claim-account.tsx
#: apps/remix/app/components/forms/signup.tsx #: apps/remix/app/components/forms/signup.tsx
#: apps/remix/app/components/forms/profile.tsx
msgid "Please enter a valid name." msgid "Please enter a valid name."
msgstr "Proszę wpisać poprawną nazwę." msgstr "Proszę wpisać poprawną nazwę."
@@ -3989,10 +4043,6 @@ msgstr "Proszę pamiętać, że ta czynność jest nieodwracalna. Po potwierdzen
msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified" msgid "Please note that you will lose access to all documents associated with this team & all the members will be removed and notified"
msgstr "Proszę pamiętać, że stracisz dostęp do wszystkich dokumentów powiązanych z tym zespołem i wszyscy członkowie zostaną usunięci oraz powiadomieni" msgstr "Proszę pamiętać, że stracisz dostęp do wszystkich dokumentów powiązanych z tym zespołem i wszyscy członkowie zostaną usunięci oraz powiadomieni"
#: apps/remix/app/components/general/document-signing/document-signing-reject-dialog.tsx
msgid "Please provide a reason"
msgstr "Proszę podać powód"
#: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx #: apps/remix/app/components/forms/2fa/disable-authenticator-app-dialog.tsx
msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support." msgid "Please provide a token from the authenticator, or a backup code. If you do not have a backup code available, please contact support."
msgstr "Proszę podać token z aplikacji uwierzytelniającej lub kod zapasowy. Jeśli nie masz dostępnego kodu zapasowego, skontaktuj się z pomocą techniczną." msgstr "Proszę podać token z aplikacji uwierzytelniającej lub kod zapasowy. Jeśli nie masz dostępnego kodu zapasowego, skontaktuj się z pomocą techniczną."
@@ -4063,6 +4113,10 @@ msgstr "Prywatne"
msgid "Private templates can only be modified and viewed by you." msgid "Private templates can only be modified and viewed by you."
msgstr "Prywatne szablony mogą być modyfikowane i przeglądane tylko przez Ciebie." msgstr "Prywatne szablony mogą być modyfikowane i przeglądane tylko przez Ciebie."
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Proceed"
msgstr ""
#: apps/remix/app/routes/_authenticated+/settings+/profile.tsx #: apps/remix/app/routes/_authenticated+/settings+/profile.tsx
#: apps/remix/app/components/general/settings-nav-mobile.tsx #: apps/remix/app/components/general/settings-nav-mobile.tsx
#: apps/remix/app/components/general/settings-nav-desktop.tsx #: apps/remix/app/components/general/settings-nav-desktop.tsx
@@ -4140,6 +4194,10 @@ msgstr "Gotowy"
msgid "Reason" msgid "Reason"
msgstr "Powód" msgstr "Powód"
#: packages/email/template-components/template-document-cancel.tsx
msgid "Reason for cancellation: {cancellationReason}"
msgstr ""
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Reason for rejection: " msgid "Reason for rejection: "
msgstr "" msgstr ""
@@ -4222,7 +4280,7 @@ msgstr "Kod odzyskiwania skopiowany"
msgid "Recovery codes" msgid "Recovery codes"
msgstr "Kody odzyskiwania" msgstr "Kody odzyskiwania"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-color-picker.tsx
msgid "Red" msgid "Red"
msgstr "Czerwony" msgstr "Czerwony"
@@ -4243,8 +4301,11 @@ msgstr "Rejestracja zakończona sukcesem"
msgid "Reject Document" msgid "Reject Document"
msgstr "Odrzuć dokument" msgstr "Odrzuć dokument"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx #: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
#: apps/remix/app/components/general/document/document-status.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx #: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: packages/lib/constants/document.ts
msgid "Rejected" msgid "Rejected"
msgstr "Odrzucony" msgstr "Odrzucony"
@@ -4429,8 +4490,6 @@ msgstr "Wiersze na stronę"
#: 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-number-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-number-field.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx #: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx #: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
msgid "Save" msgid "Save"
@@ -4740,7 +4799,6 @@ msgstr "Zarejestruj się za pomocą OIDC"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx #: apps/remix/app/components/general/direct-template/direct-template-signing-form.tsx
#: apps/remix/app/components/forms/profile.tsx #: apps/remix/app/components/forms/profile.tsx
@@ -4757,12 +4815,17 @@ msgstr "Podpis"
msgid "Signature ID" msgid "Signature ID"
msgstr "Identyfikator podpisu" msgstr "Identyfikator podpisu"
#: apps/remix/app/components/general/document-signing/document-signing-signature-field.tsx #: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx msgid "Signature is too small"
#: apps/remix/app/components/embed/embed-document-signing-page.tsx msgstr ""
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
msgid "Signature is too small. Please provide a more complete signature." #: apps/remix/app/components/forms/profile.tsx
msgstr "Podpis jest zbyt mały. Proszę podać bardziej kompletny podpis." msgid "Signature Pad cannot be empty."
msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "Signature types"
msgstr ""
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx #: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
msgid "Signatures Collected" msgid "Signatures Collected"
@@ -4987,6 +5050,7 @@ msgid "Subject <0>(Optional)</0>"
msgstr "Temat <0>(Opcjonalnie)</0>" msgstr "Temat <0>(Opcjonalnie)</0>"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Submitting..." msgid "Submitting..."
msgstr "" msgstr ""
@@ -5501,6 +5565,10 @@ msgstr "Token, którego użyłeś do zresetowania hasła, jest albo wygasły, al
msgid "The two-factor authentication code provided is incorrect" msgid "The two-factor authentication code provided is incorrect"
msgstr "" msgstr ""
#: packages/ui/components/document/document-signature-settings-tooltip.tsx
msgid "The types of signatures that recipients are allowed to use when signing the document."
msgstr ""
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id.tsx
#: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx #: apps/remix/app/routes/_authenticated+/settings+/webhooks.$id.tsx
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx #: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
@@ -5582,6 +5650,10 @@ msgstr "Ten dokument został anulowany przez właściciela i nie jest już dost
msgid "This document has been cancelled by the owner." msgid "This document has been cancelled by the owner."
msgstr "Ten dokument został anulowany przez właściciela." msgstr "Ten dokument został anulowany przez właściciela."
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been rejected by a recipient"
msgstr ""
#: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx #: apps/remix/app/routes/_authenticated+/documents.$id._index.tsx
msgid "This document has been signed by all recipients" msgid "This document has been signed by all recipients"
msgstr "Ten dokument został podpisany przez wszystkich odbiorców" msgstr "Ten dokument został podpisany przez wszystkich odbiorców"
@@ -5730,6 +5802,10 @@ msgstr "Strefa czasowa"
msgid "Title" msgid "Title"
msgstr "Tytuł" msgstr "Tytuł"
#: packages/ui/primitives/document-flow/add-settings.types.ts
msgid "Title cannot be empty"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx #: apps/remix/app/routes/_unauthenticated+/team.invite.$token.tsx
msgid "To accept this invitation you must create an account." msgid "To accept this invitation you must create an account."
msgstr "Aby zaakceptować to zaproszenie, musisz założyć konto." msgstr "Aby zaakceptować to zaproszenie, musisz założyć konto."
@@ -5897,6 +5973,7 @@ msgstr "Ponowna autoryzacja za pomocą dwuetapowej weryfikacji"
#: apps/remix/app/components/tables/templates-table.tsx #: apps/remix/app/components/tables/templates-table.tsx
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: packages/lib/constants/document.ts
msgid "Type" msgid "Type"
msgstr "Typ" msgstr "Typ"
@@ -6004,6 +6081,7 @@ msgstr "Niezakończony"
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx #: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx #: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.billing.tsx
msgid "Unknown" msgid "Unknown"
msgstr "Nieznany" msgstr "Nieznany"
@@ -6014,11 +6092,14 @@ msgstr "Nieopłacone"
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx #: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx #: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
#: apps/remix/app/components/forms/team-document-preferences-form.tsx
#: apps/remix/app/components/forms/team-branding-preferences-form.tsx
#: apps/remix/app/components/forms/public-profile-form.tsx #: apps/remix/app/components/forms/public-profile-form.tsx
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx #: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
#: apps/remix/app/components/dialogs/team-email-update-dialog.tsx #: apps/remix/app/components/dialogs/team-email-update-dialog.tsx
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx #: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Update" msgid "Update"
msgstr "Zaktualizuj" msgstr "Zaktualizuj"
@@ -6039,6 +6120,8 @@ msgid "Update profile"
msgstr "Zaktualizuj profil" msgstr "Zaktualizuj profil"
#: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx #: apps/remix/app/components/tables/admin-document-recipient-item-table.tsx
#: apps/remix/app/components/general/document-signing/document-signing-complete-dialog.tsx
#: apps/remix/app/components/dialogs/assistant-confirmation-dialog.tsx
msgid "Update Recipient" msgid "Update Recipient"
msgstr "Zaktualizuj odbiorcę" msgstr "Zaktualizuj odbiorcę"
@@ -6077,10 +6160,6 @@ msgstr "Zaktualizuj webhook"
msgid "Updating password..." msgid "Updating password..."
msgstr "Aktualizowanie hasła..." msgstr "Aktualizowanie hasła..."
#: apps/remix/app/components/forms/profile.tsx
msgid "Updating profile..."
msgstr "Aktualizacja profilu..."
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx #: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "Updating Your Information" msgid "Updating Your Information"
msgstr "Aktualizacja Twoich informacji" msgstr "Aktualizacja Twoich informacji"
@@ -6089,6 +6168,10 @@ msgstr "Aktualizacja Twoich informacji"
msgid "Upgrade" msgid "Upgrade"
msgstr "Ulepsz" msgstr "Ulepsz"
#: packages/lib/constants/document.ts
msgid "Upload"
msgstr ""
#: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx #: apps/remix/app/components/dialogs/template-bulk-send-dialog.tsx
msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details." msgid "Upload a CSV file to create multiple documents from this template. Each row represents one document with its recipient details."
msgstr "Prześlij plik CSV, aby utworzyć wiele dokumentów z tego szablonu. Każda linia reprezentuje jeden dokument z jego szczegółami odbiorcy." msgstr "Prześlij plik CSV, aby utworzyć wiele dokumentów z tego szablonu. Każda linia reprezentuje jeden dokument z jego szczegółami odbiorcy."
@@ -6113,7 +6196,7 @@ msgstr "Prześlij CSV"
msgid "Upload custom document" msgid "Upload custom document"
msgstr "Prześlij niestandardowy dokument" msgstr "Prześlij niestandardowy dokument"
#: packages/ui/primitives/signature-pad/signature-pad.tsx #: packages/ui/primitives/signature-pad/signature-pad-upload.tsx
msgid "Upload Signature" msgid "Upload Signature"
msgstr "Prześlij podpis" msgstr "Prześlij podpis"
@@ -6287,6 +6370,7 @@ msgstr "Zobacz dokument"
#: apps/remix/app/components/general/document-signing/document-signing-form.tsx #: apps/remix/app/components/general/document-signing/document-signing-form.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx #: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/email/template-components/template-document-rejected.tsx #: packages/email/template-components/template-document-rejected.tsx
#: packages/email/template-components/template-document-invite.tsx #: packages/email/template-components/template-document-invite.tsx
msgid "View Document" msgid "View Document"
@@ -6653,6 +6737,11 @@ msgstr "Witamy w Documenso!"
msgid "Were you trying to edit this document instead?" msgid "Were you trying to edit this document instead?"
msgstr "Czy próbowałeś raczej edytować ten dokument?" msgstr "Czy próbowałeś raczej edytować ten dokument?"
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
#: packages/ui/primitives/document-flow/add-signers.tsx
msgid "When enabled, signers can choose who should sign next in the sequence instead of following the predefined order."
msgstr ""
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx #: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
msgid "When you click continue, you will be prompted to add the first available authenticator on your system." msgid "When you click continue, you will be prompted to add the first available authenticator on your system."
msgstr "Kiedy klikniesz kontynuuj, zostaniesz poproszony o dodanie pierwszego dostępnego autoryzatora w swoim systemie." msgstr "Kiedy klikniesz kontynuuj, zostaniesz poproszony o dodanie pierwszego dostępnego autoryzatora w swoim systemie."

View File

@@ -0,0 +1,53 @@
import { msg } from '@lingui/core/macro';
import { z } from 'zod';
import { DocumentMetaSchema } from '@documenso/prisma/generated/zod/modelSchema/DocumentMetaSchema';
/**
* The full document response schema.
*
* Mainly used for returning a single document from the API.
*/
export const ZDocumentMetaSchema = DocumentMetaSchema.pick({
signingOrder: true,
distributionMethod: true,
id: true,
subject: true,
message: true,
timezone: true,
password: true,
dateFormat: true,
documentId: true,
redirectUrl: true,
typedSignatureEnabled: true,
uploadSignatureEnabled: true,
drawSignatureEnabled: true,
language: true,
emailSettings: true,
});
export type TDocumentMeta = z.infer<typeof ZDocumentMetaSchema>;
/**
* If you update this, you must also update the schema.prisma @default value for
* - Template meta
* - Document meta
*/
export const ZDocumentSignatureSettingsSchema = z
.object({
typedSignatureEnabled: z.boolean(),
uploadSignatureEnabled: z.boolean(),
drawnSignatureEnabled: z.boolean(),
})
.refine(
(data) => {
return (
data.typedSignatureEnabled || data.uploadSignatureEnabled || data.drawnSignatureEnabled
);
},
{
message: msg`At least one signature type must be enabled`.id,
},
);
export type TDocumentSignatureSettings = z.infer<typeof ZDocumentSignatureSettingsSchema>;

View File

@@ -51,6 +51,9 @@ export const ZDocumentSchema = DocumentSchema.pick({
documentId: true, documentId: true,
redirectUrl: true, redirectUrl: true,
typedSignatureEnabled: true, typedSignatureEnabled: true,
uploadSignatureEnabled: true,
drawSignatureEnabled: true,
allowDictateNextSigner: true,
language: true, language: true,
emailSettings: true, emailSettings: true,
}).nullable(), }).nullable(),

View File

@@ -45,6 +45,9 @@ export const ZTemplateSchema = TemplateSchema.pick({
dateFormat: true, dateFormat: true,
signingOrder: true, signingOrder: true,
typedSignatureEnabled: true, typedSignatureEnabled: true,
uploadSignatureEnabled: true,
drawSignatureEnabled: true,
allowDictateNextSigner: true,
distributionMethod: true, distributionMethod: true,
templateId: true, templateId: true,
redirectUrl: true, redirectUrl: true,

View File

@@ -46,7 +46,10 @@ export const ZWebhookDocumentMetaSchema = z.object({
dateFormat: z.string(), dateFormat: z.string(),
redirectUrl: z.string().nullable(), redirectUrl: z.string().nullable(),
signingOrder: z.nativeEnum(DocumentSigningOrder), signingOrder: z.nativeEnum(DocumentSigningOrder),
allowDictateNextSigner: z.boolean(),
typedSignatureEnabled: z.boolean(), typedSignatureEnabled: z.boolean(),
uploadSignatureEnabled: z.boolean(),
drawSignatureEnabled: z.boolean(),
language: z.string(), language: z.string(),
distributionMethod: z.nativeEnum(DocumentDistributionMethod), distributionMethod: z.nativeEnum(DocumentDistributionMethod),
emailSettings: z.any().nullable(), emailSettings: z.any().nullable(),

View File

@@ -40,7 +40,13 @@ export type ApiRequestMetadata = {
}; };
export const extractRequestMetadata = (req: Request): RequestMetadata => { export const extractRequestMetadata = (req: Request): RequestMetadata => {
const parsedIp = ZIpSchema.safeParse(req.headers.get('x-forwarded-for')); const forwardedFor = req.headers.get('x-forwarded-for');
const ip = forwardedFor
?.split(',')
.map((ip) => ip.trim())
.at(0);
const parsedIp = ZIpSchema.safeParse(ip);
const ipAddress = parsedIp.success ? parsedIp.data : undefined; const ipAddress = parsedIp.success ? parsedIp.data : undefined;
const userAgent = req.headers.get('user-agent'); const userAgent = req.headers.get('user-agent');

View File

@@ -0,0 +1,8 @@
import type { Document } from '@prisma/client';
import { DocumentStatus } from '@prisma/client';
export const isDocumentCompleted = (document: Pick<Document, 'status'> | DocumentStatus) => {
const status = typeof document === 'string' ? document : document.status;
return status === DocumentStatus.COMPLETED || status === DocumentStatus.REJECTED;
};

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