mirror of
https://github.com/documenso/documenso.git
synced 2026-06-22 12:22:14 +10:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c5c87e3fd1 | |||
| 24a74c7b57 | |||
| f0a5a7e816 | |||
| 8462cd13fd | |||
| 576846de32 | |||
| 06071ea035 | |||
| b45a2691ba | |||
| f31cc575d0 | |||
| 05d7015ef0 | |||
| 2ca5d6cfaa | |||
| 04814ca14e | |||
| dd1dccdb6a | |||
| df4316ac5c | |||
| 02f1264eea | |||
| 928edb8645 | |||
| 54b0e4964e | |||
| 68e6ccdd19 | |||
| 09ab7e9a09 | |||
| 3bb0777914 | |||
| 4d6389e901 | |||
| 51e3d5030d | |||
| 0cebdec637 |
@@ -15,7 +15,7 @@
|
||||
"@documenso/tailwind-config": "*",
|
||||
"@documenso/trpc": "*",
|
||||
"@documenso/ui": "*",
|
||||
"next": "^15.5.7",
|
||||
"next": "15.5.9",
|
||||
"next-plausible": "^3.12.5",
|
||||
"nextra": "^3",
|
||||
"nextra-theme-docs": "^3",
|
||||
@@ -29,4 +29,4 @@
|
||||
"pagefind": "^1.2.0",
|
||||
"typescript": "5.6.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
"dependencies": {
|
||||
"@documenso/prisma": "*",
|
||||
"luxon": "^3.7.2",
|
||||
"next": "^15.5.7"
|
||||
"next": "15.5.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "18.3.27",
|
||||
"typescript": "5.6.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,8 +156,8 @@ export const AdminOrganisationMemberUpdateDialog = ({
|
||||
|
||||
<DialogDescription className="mt-4">
|
||||
<Trans>
|
||||
You are currently updating{' '}
|
||||
<span className="font-bold">{organisationMemberName}.</span>
|
||||
You are currently updating <span className="font-bold">{organisationMemberName}</span>
|
||||
.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -353,8 +353,10 @@ export const EnvelopeDistributeDialog = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Reply To Email</Trans>{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
<Trans>
|
||||
Reply To Email{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
</Trans>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
@@ -372,8 +374,10 @@ export const EnvelopeDistributeDialog = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Subject</Trans>{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
<Trans>
|
||||
Subject{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
</Trans>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
@@ -390,8 +394,10 @@ export const EnvelopeDistributeDialog = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="flex flex-row items-center">
|
||||
<Trans>Message</Trans>{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
<Trans>
|
||||
Message{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
</Trans>
|
||||
<Tooltip>
|
||||
<TooltipTrigger type="button">
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
|
||||
@@ -117,7 +117,7 @@ export const EnvelopeItemDeleteDialog = ({
|
||||
|
||||
<DialogDescription className="mt-4">
|
||||
<Trans>
|
||||
You cannot delete this item because the document has been sent to recipients
|
||||
You cannot delete this item because the document has been sent to recipients.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -121,7 +121,7 @@ export const OrganisationEmailDomainRecordContent = ({ records }: { records: Dom
|
||||
<Trans>
|
||||
Once you update your DNS records, it may take up to 48 hours for it to be propogated.
|
||||
Once the DNS propagation is complete you will need to come back and press the "Sync"
|
||||
domains button
|
||||
domains button.
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
@@ -148,8 +148,8 @@ export const OrganisationMemberUpdateDialog = ({
|
||||
|
||||
<DialogDescription className="mt-4">
|
||||
<Trans>
|
||||
You are currently updating{' '}
|
||||
<span className="font-bold">{organisationMemberName}.</span>
|
||||
You are currently updating <span className="font-bold">{organisationMemberName}</span>
|
||||
.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -17,6 +17,7 @@ import { DocumentSigningDisclosure } from '../general/document-signing/document-
|
||||
|
||||
export type SignFieldSignatureDialogProps = {
|
||||
initialSignature?: string;
|
||||
fullName?: string;
|
||||
typedSignatureEnabled?: boolean;
|
||||
uploadSignatureEnabled?: boolean;
|
||||
drawSignatureEnabled?: boolean;
|
||||
@@ -28,6 +29,7 @@ export const SignFieldSignatureDialog = createCallable<
|
||||
>(
|
||||
({
|
||||
call,
|
||||
fullName,
|
||||
typedSignatureEnabled,
|
||||
uploadSignatureEnabled,
|
||||
drawSignatureEnabled,
|
||||
@@ -46,6 +48,7 @@ export const SignFieldSignatureDialog = createCallable<
|
||||
</DialogHeader>
|
||||
|
||||
<SignaturePad
|
||||
fullName={fullName}
|
||||
value={localSignature ?? ''}
|
||||
onChange={({ value }) => setLocalSignature(value)}
|
||||
typedSignatureEnabled={typedSignatureEnabled}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
@@ -30,6 +31,13 @@ import {
|
||||
FormMessage,
|
||||
} from '@documenso/ui/primitives/form/form';
|
||||
import { Input } from '@documenso/ui/primitives/input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@documenso/ui/primitives/select';
|
||||
import type { Toast } from '@documenso/ui/primitives/use-toast';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
@@ -53,26 +61,34 @@ export const TeamDeleteDialog = ({
|
||||
const { toast } = useToast();
|
||||
const { refreshSession } = useSession();
|
||||
|
||||
const currentOrganisation = useCurrentOrganisation();
|
||||
|
||||
const deleteMessage = _(msg`delete ${teamName}`);
|
||||
|
||||
const filteredTeams = currentOrganisation.teams.filter((team) => team.id !== teamId);
|
||||
|
||||
const ZDeleteTeamFormSchema = z.object({
|
||||
teamName: z.literal(deleteMessage, {
|
||||
errorMap: () => ({ message: _(msg`You must enter '${deleteMessage}' to proceed`) }),
|
||||
}),
|
||||
transferTeamId: z.string().optional(),
|
||||
});
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(ZDeleteTeamFormSchema),
|
||||
defaultValues: {
|
||||
teamName: '',
|
||||
transferTeamId: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const { mutateAsync: deleteTeam } = trpc.team.delete.useMutation();
|
||||
|
||||
const onFormSubmit = async () => {
|
||||
const onFormSubmit = async (data: z.infer<typeof ZDeleteTeamFormSchema>) => {
|
||||
try {
|
||||
await deleteTeam({ teamId });
|
||||
const transferTeamId = data.transferTeamId ? parseInt(data.transferTeamId, 10) : undefined;
|
||||
|
||||
await deleteTeam({ teamId, transferTeamId: transferTeamId || undefined });
|
||||
|
||||
await refreshSession();
|
||||
|
||||
@@ -168,6 +184,43 @@ export const TeamDeleteDialog = ({
|
||||
)}
|
||||
/>
|
||||
|
||||
{filteredTeams.length > 0 && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="transferTeamId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Transfer documents to a different team</Trans>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Select {...field} onValueChange={field.onChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder={_(msg`Don't transfer (Delete all documents)`)}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
|
||||
<SelectContent>
|
||||
<SelectItem value="-1">
|
||||
<Trans>Don't transfer (Delete all documents)</Trans>
|
||||
</SelectItem>
|
||||
|
||||
{filteredTeams.map((team) => (
|
||||
<SelectItem key={team.id} value={team.id.toString()}>
|
||||
{team.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="button" variant="secondary" onClick={() => setOpen(false)}>
|
||||
<Trans>Cancel</Trans>
|
||||
|
||||
@@ -146,7 +146,7 @@ export const TeamMemberUpdateDialog = ({
|
||||
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
You are currently updating <span className="font-bold">{memberName}.</span>
|
||||
You are currently updating <span className="font-bold">{memberName}</span>.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { FilePlus, Loader } from 'lucide-react';
|
||||
import { useNavigate } from 'react-router';
|
||||
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { formatTemplatesPath } from '@documenso/lib/utils/teams';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import type { TCreateTemplatePayloadSchema } from '@documenso/trpc/server/template-router/schema';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@documenso/ui/primitives/dialog';
|
||||
import { DocumentDropzone } from '@documenso/ui/primitives/document-dropzone';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { useCurrentTeam } from '~/providers/team';
|
||||
|
||||
type TemplateCreateDialogProps = {
|
||||
folderId?: string;
|
||||
};
|
||||
|
||||
export const TemplateCreateDialog = ({ folderId }: TemplateCreateDialogProps) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { user } = useSession();
|
||||
const { toast } = useToast();
|
||||
const { _ } = useLingui();
|
||||
|
||||
const team = useCurrentTeam();
|
||||
|
||||
const { mutateAsync: createTemplate } = trpc.template.createTemplate.useMutation();
|
||||
|
||||
const [showTemplateCreateDialog, setShowTemplateCreateDialog] = useState(false);
|
||||
const [isUploadingFile, setIsUploadingFile] = useState(false);
|
||||
|
||||
const onFileDrop = async (files: File[]) => {
|
||||
const file = files[0];
|
||||
|
||||
if (isUploadingFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsUploadingFile(true);
|
||||
|
||||
try {
|
||||
const payload = {
|
||||
title: file.name,
|
||||
folderId: folderId,
|
||||
} satisfies TCreateTemplatePayloadSchema;
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('payload', JSON.stringify(payload));
|
||||
formData.append('file', file);
|
||||
|
||||
const { envelopeId: id } = await createTemplate(formData);
|
||||
|
||||
toast({
|
||||
title: _(msg`Template document uploaded`),
|
||||
description: _(
|
||||
msg`Your document has been uploaded successfully. You will be redirected to the template page.`,
|
||||
),
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
setShowTemplateCreateDialog(false);
|
||||
|
||||
await navigate(`${formatTemplatesPath(team.url)}/${id}/edit`);
|
||||
} catch {
|
||||
toast({
|
||||
title: _(msg`Something went wrong`),
|
||||
description: _(msg`Please try again later.`),
|
||||
variant: 'destructive',
|
||||
});
|
||||
|
||||
setIsUploadingFile(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={showTemplateCreateDialog}
|
||||
onOpenChange={(value) => !isUploadingFile && setShowTemplateCreateDialog(value)}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button className="cursor-pointer" disabled={!user.emailVerified}>
|
||||
<FilePlus className="-ml-1 mr-2 h-4 w-4" />
|
||||
<Trans>Template (Legacy)</Trans>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent className="w-full max-w-xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans>New Template</Trans>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
Templates allow you to quickly generate documents with pre-filled recipients and
|
||||
fields.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="relative">
|
||||
<DocumentDropzone className="h-[40vh]" onDrop={onFileDrop} type="template" />
|
||||
|
||||
{isUploadingFile && (
|
||||
<div className="bg-background/50 absolute inset-0 flex items-center justify-center rounded-lg">
|
||||
<Loader className="text-muted-foreground h-12 w-12 animate-spin" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="secondary" disabled={isUploadingFile}>
|
||||
<Trans>Close</Trans>
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -219,9 +219,8 @@ export const WebhookCreateDialog = ({ trigger, ...props }: WebhookCreateDialogPr
|
||||
<FormDescription>
|
||||
<Trans>
|
||||
A secret that will be sent to your URL so you can verify that the request
|
||||
has been sent by Documenso
|
||||
has been sent by Documenso.
|
||||
</Trans>
|
||||
.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
||||
@@ -438,6 +438,7 @@ export const EmbedDirectTemplateClientPage = ({
|
||||
className="mt-2"
|
||||
disabled={isThrottled || isSubmitting}
|
||||
disableAnimation
|
||||
fullName={fullName}
|
||||
value={signature ?? ''}
|
||||
onChange={(v) => setSignature(v ?? '')}
|
||||
typedSignatureEnabled={metadata?.typedSignatureEnabled}
|
||||
|
||||
@@ -455,6 +455,7 @@ export const EmbedSignDocumentV1ClientPage = ({
|
||||
className="mt-2"
|
||||
disabled={isThrottled || isSubmitting}
|
||||
disableAnimation
|
||||
fullName={fullName}
|
||||
value={signature ?? ''}
|
||||
onChange={(v) => setSignature(v ?? '')}
|
||||
typedSignatureEnabled={metadata?.typedSignatureEnabled}
|
||||
|
||||
@@ -319,6 +319,7 @@ export const MultiSignDocumentSigningView = ({
|
||||
className="mt-2"
|
||||
disabled={isSubmitting}
|
||||
disableAnimation
|
||||
fullName={fullName}
|
||||
value={signature ?? ''}
|
||||
onChange={(v) => setSignature(v ?? '')}
|
||||
typedSignatureEnabled={
|
||||
|
||||
@@ -110,7 +110,7 @@ export const ProfileForm = ({ className }: ProfileFormProps) => {
|
||||
<Label htmlFor="email" className="text-muted-foreground">
|
||||
<Trans>Email</Trans>
|
||||
</Label>
|
||||
<Input id="email" type="email" className="bg-muted mt-2" value={user.email} disabled />
|
||||
<Input id="email" type="email" className="mt-2 bg-muted" value={user.email} disabled />
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
@@ -124,6 +124,7 @@ export const ProfileForm = ({ className }: ProfileFormProps) => {
|
||||
<FormControl>
|
||||
<SignaturePadDialog
|
||||
disabled={isSubmitting}
|
||||
fullName={user.name ?? ''}
|
||||
value={value}
|
||||
onChange={(v) => onChange(v ?? '')}
|
||||
/>
|
||||
|
||||
@@ -244,11 +244,11 @@ export const SignInForm = ({
|
||||
const errorMessage = match(error.code)
|
||||
.with(
|
||||
AuthenticationErrorCode.InvalidCredentials,
|
||||
() => msg`The email or password provided is incorrect`,
|
||||
() => msg`The email or password provided is incorrect.`,
|
||||
)
|
||||
.with(
|
||||
AuthenticationErrorCode.InvalidTwoFactorCode,
|
||||
() => msg`The two-factor authentication code provided is incorrect`,
|
||||
() => msg`The two-factor authentication code provided is incorrect.`,
|
||||
)
|
||||
.otherwise(() => handleFallbackErrorMessages(error.code));
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => {
|
||||
const errorMessage = match(error.code)
|
||||
.with(
|
||||
AppErrorCode.UNAUTHORIZED,
|
||||
() => msg`You do not have permission to create a token for this team`,
|
||||
() => msg`You do not have permission to create a token for this team.`,
|
||||
)
|
||||
.otherwise(() => msg`Something went wrong. Please try again later.`);
|
||||
|
||||
|
||||
@@ -417,6 +417,7 @@ export const DirectTemplateSigningForm = ({
|
||||
<SignaturePadDialog
|
||||
className="mt-2"
|
||||
disabled={isSubmitting}
|
||||
fullName={fullName}
|
||||
value={signature ?? ''}
|
||||
onChange={(value) => setSignature(value)}
|
||||
typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
|
||||
@@ -433,7 +434,7 @@ export const DirectTemplateSigningForm = ({
|
||||
|
||||
<div className="mt-4 flex gap-x-4">
|
||||
<Button
|
||||
className="dark:bg-muted dark:hover:bg-muted/80 w-full bg-black/5 hover:bg-black/10"
|
||||
className="w-full bg-black/5 hover:bg-black/10 dark:bg-muted dark:hover:bg-muted/80"
|
||||
size="lg"
|
||||
variant="secondary"
|
||||
disabled={isSubmitting}
|
||||
|
||||
@@ -280,6 +280,7 @@ export const DocumentSigningForm = ({
|
||||
<SignaturePadDialog
|
||||
className="mt-2"
|
||||
disabled={isSubmitting}
|
||||
fullName={fullName}
|
||||
value={signature ?? ''}
|
||||
onChange={(v) => setSignature(v ?? '')}
|
||||
typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled}
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ export const DocumentSigningMobileWidget = () => {
|
||||
<motion.div
|
||||
layout="size"
|
||||
layoutId="document-signing-mobile-widget-progress-bar"
|
||||
className="bg-documenso absolute inset-y-0 left-0"
|
||||
className="bg-primary absolute inset-y-0 left-0"
|
||||
style={{
|
||||
width: `${100 - (100 / requiredRecipientFields.length) * (recipientFieldsRemaining.length ?? 0)}%`,
|
||||
}}
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ export const DocumentSigningPageViewV2 = () => {
|
||||
<motion.div
|
||||
layout="size"
|
||||
layoutId="document-flow-container-step"
|
||||
className="absolute inset-y-0 left-0 bg-documenso"
|
||||
className="absolute inset-y-0 left-0 bg-primary"
|
||||
style={{
|
||||
width: `${100 - (100 / requiredRecipientFields.length) * (recipientFieldsRemaining.length ?? 0)}%`,
|
||||
}}
|
||||
|
||||
+11
-7
@@ -56,8 +56,11 @@ export const DocumentSigningSignatureField = ({
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [fontSize, setFontSize] = useState(2);
|
||||
|
||||
const { signature: providedSignature, setSignature: setProvidedSignature } =
|
||||
useRequiredDocumentSigningContext();
|
||||
const {
|
||||
fullName,
|
||||
signature: providedSignature,
|
||||
setSignature: setProvidedSignature,
|
||||
} = useRequiredDocumentSigningContext();
|
||||
|
||||
const { executeActionAuthProcedure } = useRequiredDocumentSigningAuthContext();
|
||||
|
||||
@@ -236,13 +239,13 @@ export const DocumentSigningSignatureField = ({
|
||||
type="Signature"
|
||||
>
|
||||
{isLoading && (
|
||||
<div className="bg-background absolute inset-0 flex items-center justify-center rounded-md">
|
||||
<Loader className="text-primary h-5 w-5 animate-spin md:h-8 md:w-8" />
|
||||
<div className="absolute inset-0 flex items-center justify-center rounded-md bg-background">
|
||||
<Loader className="h-5 w-5 animate-spin text-primary md:h-8 md:w-8" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{state === 'empty' && (
|
||||
<p className="group-hover:text-primary font-signature text-muted-foreground group-hover:text-recipient-green text-[clamp(0.575rem,25cqw,1.2rem)] text-xl duration-200">
|
||||
<p className="font-signature text-[clamp(0.575rem,25cqw,1.2rem)] text-xl text-muted-foreground duration-200 group-hover:text-primary group-hover:text-recipient-green">
|
||||
<Trans>Signature</Trans>
|
||||
</p>
|
||||
)}
|
||||
@@ -259,7 +262,7 @@ export const DocumentSigningSignatureField = ({
|
||||
<div ref={containerRef} className="flex h-full w-full items-center justify-center p-2">
|
||||
<p
|
||||
ref={signatureRef}
|
||||
className="font-signature text-muted-foreground w-full overflow-hidden break-all text-center leading-tight duration-200"
|
||||
className="w-full overflow-hidden break-all text-center font-signature leading-tight text-muted-foreground duration-200"
|
||||
style={{ fontSize: `${fontSize}rem` }}
|
||||
>
|
||||
{signature?.typedSignature}
|
||||
@@ -272,12 +275,13 @@ export const DocumentSigningSignatureField = ({
|
||||
<DialogTitle>
|
||||
<Trans>
|
||||
Sign as {recipient.name}{' '}
|
||||
<div className="text-muted-foreground h-5">({recipient.email})</div>
|
||||
<div className="h-5 text-muted-foreground">({recipient.email})</div>
|
||||
</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<SignaturePad
|
||||
className="mt-2"
|
||||
fullName={fullName}
|
||||
value={localSignature ?? ''}
|
||||
onChange={({ value }) => setLocalSignature(value)}
|
||||
typedSignatureEnabled={typedSignatureEnabled}
|
||||
|
||||
@@ -14,9 +14,10 @@ import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { APP_DOCUMENT_UPLOAD_SIZE_LIMIT } from '@documenso/lib/constants/app';
|
||||
import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||
import { formatDocumentsPath, formatTemplatesPath } from '@documenso/lib/utils/teams';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import type { TCreateDocumentPayloadSchema } from '@documenso/trpc/server/document-router/create-document.types';
|
||||
import type { TCreateTemplatePayloadSchema } from '@documenso/trpc/server/template-router/schema';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { DocumentUploadButton as DocumentUploadButtonPrimitive } from '@documenso/ui/primitives/document-upload-button';
|
||||
import {
|
||||
@@ -31,9 +32,13 @@ import { useCurrentTeam } from '~/providers/team';
|
||||
|
||||
export type DocumentUploadButtonLegacyProps = {
|
||||
className?: string;
|
||||
type: EnvelopeType;
|
||||
};
|
||||
|
||||
export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLegacyProps) => {
|
||||
export const DocumentUploadButtonLegacy = ({
|
||||
className,
|
||||
type,
|
||||
}: DocumentUploadButtonLegacyProps) => {
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
const { user } = useSession();
|
||||
@@ -54,8 +59,18 @@ export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLe
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const { mutateAsync: createDocument } = trpc.document.create.useMutation();
|
||||
const { mutateAsync: createTemplate } = trpc.template.createTemplate.useMutation();
|
||||
|
||||
const disabledMessage = useMemo(() => {
|
||||
if (!user.emailVerified) {
|
||||
return msg`Verify your email to upload documents.`;
|
||||
}
|
||||
|
||||
// No errors for templates.
|
||||
if (type === EnvelopeType.TEMPLATE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (organisation.subscription && remaining.documents === 0) {
|
||||
return msg`Document upload disabled due to unpaid invoices`;
|
||||
}
|
||||
@@ -64,11 +79,8 @@ export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLe
|
||||
return msg`You have reached your document limit.`;
|
||||
}
|
||||
|
||||
if (!user.emailVerified) {
|
||||
return msg`Verify your email to upload documents.`;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [remaining.documents, user.emailVerified, team]);
|
||||
}, [remaining.documents, user.emailVerified, team, type]);
|
||||
|
||||
const onFileDrop = async (file: File) => {
|
||||
try {
|
||||
@@ -80,44 +92,62 @@ export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLe
|
||||
meta: {
|
||||
timezone: userTimezone,
|
||||
},
|
||||
} satisfies TCreateDocumentPayloadSchema;
|
||||
} satisfies TCreateDocumentPayloadSchema | TCreateTemplatePayloadSchema;
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('payload', JSON.stringify(payload));
|
||||
formData.append('file', file);
|
||||
|
||||
const { envelopeId: id } = await createDocument(formData);
|
||||
// Handle legacy document creation.
|
||||
if (type === EnvelopeType.DOCUMENT) {
|
||||
const { envelopeId: id } = await createDocument(formData);
|
||||
|
||||
void refreshLimits();
|
||||
void refreshLimits();
|
||||
|
||||
await navigate(`${formatDocumentsPath(team.url)}/${id}/edit`);
|
||||
await navigate(`${formatDocumentsPath(team.url)}/${id}/edit`);
|
||||
|
||||
toast({
|
||||
title: _(msg`Document uploaded`),
|
||||
description: _(msg`Your document has been uploaded successfully.`),
|
||||
duration: 5000,
|
||||
});
|
||||
toast({
|
||||
title: _(msg`Document uploaded`),
|
||||
description: _(msg`Your document has been uploaded successfully.`),
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
analytics.capture('App: Document Uploaded', {
|
||||
userId: user.id,
|
||||
documentId: id,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
analytics.capture('App: Document Uploaded', {
|
||||
userId: user.id,
|
||||
documentId: id,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
// Handle legacy template creation.
|
||||
if (type === EnvelopeType.TEMPLATE) {
|
||||
const { envelopeId: id } = await createTemplate(formData);
|
||||
|
||||
await navigate(`${formatTemplatesPath(team.url)}/${id}/edit`);
|
||||
|
||||
toast({
|
||||
title: _(msg`Template document uploaded`),
|
||||
description: _(
|
||||
msg`Your document has been uploaded successfully. You will be redirected to the template page.`,
|
||||
),
|
||||
duration: 5000,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
const error = AppError.parseError(err);
|
||||
|
||||
console.error(err);
|
||||
|
||||
const errorMessage = match(error.code)
|
||||
.with('INVALID_DOCUMENT_FILE', () => msg`You cannot upload encrypted PDFs`)
|
||||
.with('INVALID_DOCUMENT_FILE', () => msg`You cannot upload encrypted PDFs.`)
|
||||
.with(
|
||||
AppErrorCode.LIMIT_EXCEEDED,
|
||||
() => msg`You have reached your document limit for this month. Please upgrade your plan.`,
|
||||
)
|
||||
.with(
|
||||
'ENVELOPE_ITEM_LIMIT_EXCEEDED',
|
||||
() => msg`You have reached the limit of the number of files per envelope`,
|
||||
() => msg`You have reached the limit of the number of files per envelope.`,
|
||||
)
|
||||
.otherwise(() => msg`An error occurred while uploading your document.`);
|
||||
|
||||
@@ -149,17 +179,18 @@ export const DocumentUploadButtonLegacy = ({ className }: DocumentUploadButtonLe
|
||||
<div>
|
||||
<DocumentUploadButtonPrimitive
|
||||
loading={isLoading}
|
||||
disabled={remaining.documents === 0 || !user.emailVerified}
|
||||
disabled={disabledMessage !== undefined}
|
||||
disabledMessage={disabledMessage}
|
||||
onDrop={async (files) => onFileDrop(files[0])}
|
||||
onDropRejected={onFileDropRejected}
|
||||
type={EnvelopeType.DOCUMENT}
|
||||
type={type}
|
||||
internalVersion="1"
|
||||
/>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
|
||||
{team?.id === undefined &&
|
||||
type === EnvelopeType.DOCUMENT &&
|
||||
remaining.documents > 0 &&
|
||||
Number.isFinite(remaining.documents) && (
|
||||
<TooltipContent>
|
||||
|
||||
+7
-4
@@ -687,8 +687,10 @@ export const EnvelopeEditorSettingsDialog = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Reply To Email</Trans>{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
<Trans>
|
||||
Reply To Email{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
</Trans>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
@@ -726,8 +728,9 @@ export const EnvelopeEditorSettingsDialog = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="flex flex-row items-center">
|
||||
<Trans>Message</Trans>{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
<Trans>
|
||||
Message <span className="text-muted-foreground">(Optional)</span>
|
||||
</Trans>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
|
||||
@@ -175,7 +175,7 @@ export default function EnvelopeEditor() {
|
||||
<motion.div
|
||||
layout="size"
|
||||
layoutId="document-flow-container-step"
|
||||
className="absolute inset-y-0 left-0 bg-documenso"
|
||||
className="absolute inset-y-0 left-0 bg-primary"
|
||||
style={{
|
||||
width: `${(100 / envelopeEditorSteps.length) * (currentStepData.order ?? 0)}%`,
|
||||
}}
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function EnvelopeSignerForm() {
|
||||
|
||||
if (recipient.role === RecipientRole.ASSISTANT) {
|
||||
return (
|
||||
<fieldset className="embed--DocumentWidgetForm dark:bg-background border-border rounded-2xl sm:border sm:p-3">
|
||||
<fieldset className="embed--DocumentWidgetForm rounded-2xl border-border sm:border sm:p-3 dark:bg-background">
|
||||
<RadioGroup
|
||||
className="gap-0 space-y-2 shadow-none sm:space-y-3"
|
||||
value={selectedAssistantRecipient?.id?.toString()}
|
||||
@@ -54,7 +54,7 @@ export default function EnvelopeSignerForm() {
|
||||
.map((r) => (
|
||||
<div
|
||||
key={r.id}
|
||||
className="bg-widget border-border relative flex flex-col gap-4 rounded-lg border p-4"
|
||||
className="relative flex flex-col gap-4 rounded-lg border border-border bg-widget p-4"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -69,15 +69,15 @@ export default function EnvelopeSignerForm() {
|
||||
{r.name}
|
||||
|
||||
{r.id === recipient.id && (
|
||||
<span className="text-muted-foreground ml-2">
|
||||
<span className="ml-2 text-muted-foreground">
|
||||
<Trans>(You)</Trans>
|
||||
</span>
|
||||
)}
|
||||
</Label>
|
||||
<p className="text-muted-foreground text-xs">{r.email}</p>
|
||||
<p className="text-xs text-muted-foreground">{r.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-muted-foreground text-xs leading-[inherit]">
|
||||
<div className="text-xs leading-[inherit] text-muted-foreground">
|
||||
<Plural
|
||||
value={assistantFields.filter((field) => field.recipientId === r.id).length}
|
||||
one="# field"
|
||||
@@ -103,7 +103,7 @@ export default function EnvelopeSignerForm() {
|
||||
<Input
|
||||
type="text"
|
||||
id="full-name"
|
||||
className="bg-background mt-2"
|
||||
className="mt-2 bg-background"
|
||||
value={fullName}
|
||||
disabled={isNameLocked}
|
||||
onChange={(e) => !isNameLocked && setFullName(e.target.value.trimStart())}
|
||||
@@ -119,6 +119,7 @@ export default function EnvelopeSignerForm() {
|
||||
<SignaturePadDialog
|
||||
className="mt-2"
|
||||
disabled={isSubmitting}
|
||||
fullName={fullName}
|
||||
value={signature ?? ''}
|
||||
onChange={(v) => setSignature(v ?? '')}
|
||||
typedSignatureEnabled={envelope.documentMeta.typedSignatureEnabled}
|
||||
|
||||
@@ -374,6 +374,7 @@ export default function EnvelopeSignerPageRenderer() {
|
||||
.with({ type: FieldType.SIGNATURE }, (field) => {
|
||||
handleSignatureFieldClick({
|
||||
field,
|
||||
fullName,
|
||||
signature,
|
||||
typedSignatureEnabled: envelope.documentMeta.typedSignatureEnabled,
|
||||
uploadSignatureEnabled: envelope.documentMeta.uploadSignatureEnabled,
|
||||
|
||||
@@ -123,14 +123,14 @@ export const EnvelopeDropZoneWrapper = ({
|
||||
const error = AppError.parseError(err);
|
||||
|
||||
const errorMessage = match(error.code)
|
||||
.with('INVALID_DOCUMENT_FILE', () => t`You cannot upload encrypted PDFs`)
|
||||
.with('INVALID_DOCUMENT_FILE', () => t`You cannot upload encrypted PDFs.`)
|
||||
.with(
|
||||
AppErrorCode.LIMIT_EXCEEDED,
|
||||
() => t`You have reached your document limit for this month. Please upgrade your plan.`,
|
||||
)
|
||||
.with(
|
||||
'ENVELOPE_ITEM_LIMIT_EXCEEDED',
|
||||
() => t`You have reached the limit of the number of files per envelope`,
|
||||
() => t`You have reached the limit of the number of files per envelope.`,
|
||||
)
|
||||
.otherwise(() => t`An error occurred during upload.`);
|
||||
|
||||
|
||||
@@ -126,14 +126,14 @@ export const EnvelopeUploadButton = ({ className, type, folderId }: EnvelopeUplo
|
||||
console.error(err);
|
||||
|
||||
const errorMessage = match(error.code)
|
||||
.with('INVALID_DOCUMENT_FILE', () => t`You cannot upload encrypted PDFs`)
|
||||
.with('INVALID_DOCUMENT_FILE', () => t`You cannot upload encrypted PDFs.`)
|
||||
.with(
|
||||
AppErrorCode.LIMIT_EXCEEDED,
|
||||
() => t`You have reached your document limit for this month. Please upgrade your plan.`,
|
||||
)
|
||||
.with(
|
||||
'ENVELOPE_ITEM_LIMIT_EXCEEDED',
|
||||
() => t`You have reached the limit of the number of files per envelope`,
|
||||
() => t`You have reached the limit of the number of files per envelope.`,
|
||||
)
|
||||
.otherwise(() => t`An error occurred while uploading your document.`);
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import { FolderCreateDialog } from '~/components/dialogs/folder-create-dialog';
|
||||
import { FolderDeleteDialog } from '~/components/dialogs/folder-delete-dialog';
|
||||
import { FolderMoveDialog } from '~/components/dialogs/folder-move-dialog';
|
||||
import { FolderUpdateDialog } from '~/components/dialogs/folder-update-dialog';
|
||||
import { TemplateCreateDialog } from '~/components/dialogs/template-create-dialog';
|
||||
import { DocumentUploadButtonLegacy } from '~/components/general/document/document-upload-button-legacy';
|
||||
import { FolderCard, FolderCardEmpty } from '~/components/general/folder/folder-card';
|
||||
import { useCurrentTeam } from '~/providers/team';
|
||||
@@ -70,7 +69,7 @@ export const FolderGrid = ({ type, parentId }: FolderGridProps) => {
|
||||
<div>
|
||||
<div className="mb-4 flex flex-col gap-4 md:flex-row md:items-end md:justify-between">
|
||||
<div
|
||||
className="text-muted-foreground hover:text-muted-foreground/80 flex flex-1 items-center text-sm font-medium"
|
||||
className="flex flex-1 items-center text-sm font-medium text-muted-foreground hover:text-muted-foreground/80"
|
||||
data-testid="folder-grid-breadcrumbs"
|
||||
>
|
||||
<Link to={formatRootPath()} className="flex items-center">
|
||||
@@ -100,10 +99,9 @@ export const FolderGrid = ({ type, parentId }: FolderGridProps) => {
|
||||
<div className="flex gap-4 sm:flex-row sm:justify-end">
|
||||
<EnvelopeUploadButton type={type} folderId={parentId || undefined} />
|
||||
|
||||
{type === FolderType.DOCUMENT ? (
|
||||
<DocumentUploadButtonLegacy /> // If you delete this, delete the component as well.
|
||||
) : (
|
||||
<TemplateCreateDialog folderId={parentId ?? undefined} /> // If you delete this, delete the component as well.
|
||||
{/* If you delete this, delete the component as well. */}
|
||||
{organisation.organisationClaim.flags.allowLegacyEnvelopes && (
|
||||
<DocumentUploadButtonLegacy type={type} />
|
||||
)}
|
||||
|
||||
<FolderCreateDialog type={type} />
|
||||
@@ -113,7 +111,7 @@ export const FolderGrid = ({ type, parentId }: FolderGridProps) => {
|
||||
{isPending ? (
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<div key={index} className="border-border bg-card h-full rounded-lg border px-4 py-5">
|
||||
<div key={index} className="h-full rounded-lg border border-border bg-card px-4 py-5">
|
||||
<div className="flex items-center gap-3">
|
||||
<Skeleton className="h-8 w-8 rounded" />
|
||||
<div className="flex w-full items-center justify-between">
|
||||
@@ -194,7 +192,7 @@ export const FolderGrid = ({ type, parentId }: FolderGridProps) => {
|
||||
{foldersData.folders.length > 12 && (
|
||||
<div className="mt-2 flex items-center justify-center">
|
||||
<Link
|
||||
className="text-muted-foreground hover:text-foreground text-sm font-medium"
|
||||
className="text-sm font-medium text-muted-foreground hover:text-foreground"
|
||||
to={formatViewAllFoldersPath()}
|
||||
>
|
||||
View all folders
|
||||
|
||||
@@ -178,7 +178,9 @@ const TeamDropdownMenu = ({ team }: { team: TGetOrganisationSessionResponse[0]['
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
|
||||
<MoreVerticalIcon className="h-4 w-4" />
|
||||
<span className="sr-only">Open menu</span>
|
||||
<span className="sr-only">
|
||||
<Trans>Open menu</Trans>
|
||||
</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" onClick={(e) => e.stopPropagation()}>
|
||||
|
||||
@@ -87,9 +87,9 @@ export default function OrganisationSettingsBrandingPage() {
|
||||
const settingsHeaderText = t`Branding Preferences`;
|
||||
|
||||
const settingsHeaderSubtitle = isPersonalLayoutMode
|
||||
? t`Here you can set your general branding preferences`
|
||||
? t`Here you can set your general branding preferences.`
|
||||
: team
|
||||
? t`Here you can set branding preferences for your team`
|
||||
? t`Here you can set branding preferences for your team.`
|
||||
: t`Here you can set branding preferences for your organisation. Teams will inherit these settings by default.`;
|
||||
|
||||
return (
|
||||
|
||||
@@ -112,7 +112,7 @@ export default function OrganisationSettingsDocumentPage() {
|
||||
|
||||
const settingsHeaderText = t`Document Preferences`;
|
||||
const settingsHeaderSubtitle = isPersonalLayoutMode
|
||||
? t`Here you can set your general document preferences`
|
||||
? t`Here you can set your general document preferences.`
|
||||
: t`Here you can set document preferences for your organisation. Teams will inherit these settings by default.`;
|
||||
|
||||
return (
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function OrganisationSettingsGeneral() {
|
||||
<div className="max-w-2xl">
|
||||
<SettingsHeader
|
||||
title={t`Email Preferences`}
|
||||
subtitle={t`You can manage your email preferences here`}
|
||||
subtitle={t`You can manage your email preferences here.`}
|
||||
/>
|
||||
|
||||
<section>
|
||||
|
||||
@@ -63,7 +63,7 @@ export default function TeamEmailSettingsGeneral() {
|
||||
<div className="max-w-2xl">
|
||||
<SettingsHeader
|
||||
title={t`Email Preferences`}
|
||||
subtitle={t`You can manage your email preferences here`}
|
||||
subtitle={t`You can manage your email preferences here.`}
|
||||
/>
|
||||
|
||||
<section>
|
||||
|
||||
@@ -185,6 +185,9 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
|
||||
(log) =>
|
||||
log.type === DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT && log.data.recipientId === recipientId,
|
||||
),
|
||||
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT]: auditLogs[
|
||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT
|
||||
].filter((log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT),
|
||||
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED]: auditLogs[
|
||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED
|
||||
].filter(
|
||||
@@ -245,11 +248,11 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
|
||||
<TableCell truncate={false} className="w-[min-content] max-w-[220px] align-top">
|
||||
<div className="hyphens-auto break-words font-medium">{recipient.name}</div>
|
||||
<div className="break-all">{recipient.email}</div>
|
||||
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
||||
<p className="mt-2 text-sm text-muted-foreground print:text-xs">
|
||||
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||
</p>
|
||||
|
||||
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
||||
<p className="mt-2 text-sm text-muted-foreground print:text-xs">
|
||||
<span className="font-medium">{_(msg`Authentication Level`)}:</span>{' '}
|
||||
<span className="block">{getAuthenticationLevel(recipient.id)}</span>
|
||||
</p>
|
||||
@@ -273,13 +276,13 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
|
||||
)}
|
||||
|
||||
{signature.signature?.typedSignature && (
|
||||
<p className="font-signature text-center text-sm">
|
||||
<p className="text-center font-signature text-sm">
|
||||
{signature.signature?.typedSignature}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
||||
<p className="mt-2 text-sm text-muted-foreground print:text-xs">
|
||||
<span className="font-medium">{_(msg`Signature ID`)}:</span>{' '}
|
||||
<span className="block font-mono uppercase">
|
||||
{signature.secondaryId}
|
||||
@@ -290,14 +293,14 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
|
||||
<p className="text-muted-foreground">N/A</p>
|
||||
)}
|
||||
|
||||
<p className="text-muted-foreground mt-2 text-sm print:text-xs">
|
||||
<p className="mt-2 text-sm text-muted-foreground print:text-xs">
|
||||
<span className="font-medium">{_(msg`IP Address`)}:</span>{' '}
|
||||
<span className="inline-block">
|
||||
{logs.DOCUMENT_RECIPIENT_COMPLETED[0]?.ipAddress ?? _(msg`Unknown`)}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p className="text-muted-foreground mt-1 text-sm print:text-xs">
|
||||
<p className="mt-1 text-sm text-muted-foreground print:text-xs">
|
||||
<span className="font-medium">{_(msg`Device`)}:</span>{' '}
|
||||
<span className="inline-block">
|
||||
{getDevice(logs.DOCUMENT_RECIPIENT_COMPLETED[0]?.userAgent)}
|
||||
@@ -307,18 +310,22 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
|
||||
|
||||
<TableCell truncate={false} className="w-[min-content] align-top">
|
||||
<div className="space-y-1">
|
||||
<p className="text-muted-foreground text-sm print:text-xs">
|
||||
<p className="text-sm text-muted-foreground print:text-xs">
|
||||
<span className="font-medium">{_(msg`Sent`)}:</span>{' '}
|
||||
<span className="inline-block">
|
||||
{logs.EMAIL_SENT[0]
|
||||
? DateTime.fromJSDate(logs.EMAIL_SENT[0].createdAt)
|
||||
.setLocale(APP_I18N_OPTIONS.defaultLocale)
|
||||
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')
|
||||
: _(msg`Unknown`)}
|
||||
: logs.DOCUMENT_SENT[0]
|
||||
? DateTime.fromJSDate(logs.DOCUMENT_SENT[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-sm text-muted-foreground print:text-xs">
|
||||
<span className="font-medium">{_(msg`Viewed`)}:</span>{' '}
|
||||
<span className="inline-block">
|
||||
{logs.DOCUMENT_OPENED[0]
|
||||
@@ -330,7 +337,7 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
|
||||
</p>
|
||||
|
||||
{logs.DOCUMENT_RECIPIENT_REJECTED[0] ? (
|
||||
<p className="text-muted-foreground text-sm print:text-xs">
|
||||
<p className="text-sm text-muted-foreground print:text-xs">
|
||||
<span className="font-medium">{_(msg`Rejected`)}:</span>{' '}
|
||||
<span className="inline-block">
|
||||
{logs.DOCUMENT_RECIPIENT_REJECTED[0]
|
||||
@@ -341,7 +348,7 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
|
||||
</span>
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-muted-foreground text-sm print:text-xs">
|
||||
<p className="text-sm text-muted-foreground print:text-xs">
|
||||
<span className="font-medium">{_(msg`Signed`)}:</span>{' '}
|
||||
<span className="inline-block">
|
||||
{logs.DOCUMENT_RECIPIENT_COMPLETED[0]
|
||||
@@ -355,7 +362,7 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
|
||||
</p>
|
||||
)}
|
||||
|
||||
<p className="text-muted-foreground text-sm print:text-xs">
|
||||
<p className="text-sm text-muted-foreground print:text-xs">
|
||||
<span className="font-medium">{_(msg`Reason`)}:</span>{' '}
|
||||
<span className="inline-block">
|
||||
{recipient.signingStatus === SigningStatus.REJECTED
|
||||
|
||||
@@ -371,8 +371,9 @@ const SigningPageV1 = ({ data }: { data: Awaited<ReturnType<typeof handleV1Loade
|
||||
to="https://documenso.com"
|
||||
className="text-documenso-700 hover:text-documenso-600"
|
||||
>
|
||||
Check out Documenso.
|
||||
Check out Documenso
|
||||
</Link>
|
||||
.
|
||||
</Trans>
|
||||
</p>
|
||||
)}
|
||||
@@ -470,8 +471,9 @@ const SigningPageV2 = ({ data }: { data: Awaited<ReturnType<typeof handleV2Loade
|
||||
to="https://documenso.com"
|
||||
className="text-documenso-700 hover:text-documenso-600"
|
||||
>
|
||||
Check out Documenso.
|
||||
Check out Documenso
|
||||
</Link>
|
||||
.
|
||||
</Trans>
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { SignFieldSignatureDialog } from '~/components/dialogs/sign-field-signat
|
||||
|
||||
type HandleSignatureFieldClickOptions = {
|
||||
field: TFieldSignature;
|
||||
fullName?: string;
|
||||
signature: string | null;
|
||||
typedSignatureEnabled?: boolean;
|
||||
uploadSignatureEnabled?: boolean;
|
||||
@@ -17,8 +18,14 @@ type HandleSignatureFieldClickOptions = {
|
||||
export const handleSignatureFieldClick = async (
|
||||
options: HandleSignatureFieldClickOptions,
|
||||
): Promise<Extract<TSignEnvelopeFieldValue, { type: typeof FieldType.SIGNATURE }> | null> => {
|
||||
const { field, signature, typedSignatureEnabled, uploadSignatureEnabled, drawSignatureEnabled } =
|
||||
options;
|
||||
const {
|
||||
field,
|
||||
fullName,
|
||||
signature,
|
||||
typedSignatureEnabled,
|
||||
uploadSignatureEnabled,
|
||||
drawSignatureEnabled,
|
||||
} = options;
|
||||
|
||||
if (field.type !== FieldType.SIGNATURE) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
@@ -37,6 +44,7 @@ export const handleSignatureFieldClick = async (
|
||||
|
||||
if (!signatureToInsert) {
|
||||
signatureToInsert = await SignFieldSignatureDialog.call({
|
||||
fullName,
|
||||
typedSignatureEnabled,
|
||||
uploadSignatureEnabled,
|
||||
drawSignatureEnabled,
|
||||
|
||||
@@ -107,5 +107,5 @@
|
||||
"vite-plugin-babel-macros": "^1.0.6",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"version": "2.2.6"
|
||||
"version": "2.2.7"
|
||||
}
|
||||
|
||||
Generated
+16
-16
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@documenso/root",
|
||||
"version": "2.2.6",
|
||||
"version": "2.2.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@documenso/root",
|
||||
"version": "2.2.6",
|
||||
"version": "2.2.7",
|
||||
"hasInstallScript": true,
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
@@ -78,7 +78,7 @@
|
||||
"@documenso/tailwind-config": "*",
|
||||
"@documenso/trpc": "*",
|
||||
"@documenso/ui": "*",
|
||||
"next": "^15.5.7",
|
||||
"next": "15.5.9",
|
||||
"next-plausible": "^3.12.5",
|
||||
"nextra": "^3",
|
||||
"nextra-theme-docs": "^3",
|
||||
@@ -99,7 +99,7 @@
|
||||
"dependencies": {
|
||||
"@documenso/prisma": "*",
|
||||
"luxon": "^3.7.2",
|
||||
"next": "^15.5.7"
|
||||
"next": "15.5.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
@@ -109,7 +109,7 @@
|
||||
},
|
||||
"apps/remix": {
|
||||
"name": "@documenso/remix",
|
||||
"version": "2.2.6",
|
||||
"version": "2.2.7",
|
||||
"dependencies": {
|
||||
"@cantoo/pdf-lib": "^2.5.3",
|
||||
"@documenso/api": "*",
|
||||
@@ -5036,9 +5036,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.7.tgz",
|
||||
"integrity": "sha512-4h6Y2NyEkIEN7Z8YxkA27pq6zTkS09bUSYC0xjd0NpwFxjnIKeZEeH591o5WECSmjpUhLn3H2QLJcDye3Uzcvg==",
|
||||
"version": "15.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.9.tgz",
|
||||
"integrity": "sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
@@ -26396,12 +26396,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jws": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
|
||||
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
|
||||
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jwa": "^2.0.0",
|
||||
"jwa": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
@@ -28912,12 +28912,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "15.5.7",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-15.5.7.tgz",
|
||||
"integrity": "sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ==",
|
||||
"version": "15.5.9",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-15.5.9.tgz",
|
||||
"integrity": "sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/env": "15.5.7",
|
||||
"@next/env": "15.5.9",
|
||||
"@swc/helpers": "0.5.15",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
"postcss": "8.4.31",
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
"apps/*",
|
||||
"packages/*"
|
||||
],
|
||||
"version": "2.2.6",
|
||||
"version": "2.2.7",
|
||||
"scripts": {
|
||||
"postinstall": "patch-package",
|
||||
"build": "turbo run build",
|
||||
|
||||
@@ -364,6 +364,25 @@ test('[TEAMS]: can create a template subfolder inside a template folder', async
|
||||
test('[TEAMS]: can create a template inside a template folder', async ({ page }) => {
|
||||
const { team, teamOwner } = await seedTeamDocuments();
|
||||
|
||||
const organisationClaim = await prisma.organisationClaim.findFirstOrThrow({
|
||||
where: {
|
||||
organisation: {
|
||||
id: team.organisationId,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.organisationClaim.update({
|
||||
where: {
|
||||
id: organisationClaim.id,
|
||||
},
|
||||
data: {
|
||||
flags: {
|
||||
allowLegacyEnvelopes: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const folder = await seedBlankFolder(teamOwner, team.id, {
|
||||
createFolderOptions: {
|
||||
name: 'Team Client Templates',
|
||||
@@ -380,16 +399,15 @@ test('[TEAMS]: can create a template inside a template folder', async ({ page })
|
||||
|
||||
await expect(page.getByText('Team Client Templates')).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Template (Legacy)' }).click();
|
||||
// Upload document.
|
||||
const [fileChooser] = await Promise.all([
|
||||
page.waitForEvent('filechooser'),
|
||||
page.getByRole('button', { name: 'Template (Legacy)' }).click(),
|
||||
]);
|
||||
|
||||
await page.getByText('Upload Template Document').click();
|
||||
|
||||
await page.locator('input[type="file"]').nth(0).waitFor({ state: 'attached' });
|
||||
|
||||
await page
|
||||
.locator('input[type="file"]')
|
||||
.nth(0)
|
||||
.setInputFiles(path.join(__dirname, '../../../assets/documenso-supporter-pledge.pdf'));
|
||||
await fileChooser.setFiles(
|
||||
path.join(__dirname, '../../../assets/documenso-supporter-pledge.pdf'),
|
||||
);
|
||||
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ auth.onError((err, c) => {
|
||||
}
|
||||
|
||||
// Handle other errors
|
||||
console.error('Unknown Error:', err);
|
||||
return c.json(
|
||||
{
|
||||
code: AppErrorCode.UNKNOWN_ERROR,
|
||||
|
||||
@@ -17,8 +17,9 @@ export const TemplateFooter = ({ isDocument = true }: TemplateFooterProps) => {
|
||||
<Trans>
|
||||
This document was sent using{' '}
|
||||
<Link className="text-[#7AC455]" href="https://documen.so/mail-footer">
|
||||
Documenso.
|
||||
Documenso
|
||||
</Link>
|
||||
.
|
||||
</Trans>
|
||||
</Text>
|
||||
)}
|
||||
|
||||
@@ -106,7 +106,7 @@ export const ConfirmTeamEmailTemplate = ({
|
||||
<Text className="mt-2 text-sm">
|
||||
<Trans>
|
||||
You can revoke access at any time in your team settings on Documenso{' '}
|
||||
<Link href={`${baseUrl}/settings/teams`}>here.</Link>
|
||||
<Link href={`${baseUrl}/settings/teams`}>here</Link>.
|
||||
</Trans>
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
@@ -73,8 +73,9 @@ export const ResetPasswordTemplate = ({
|
||||
Didn't request a password change? We are here to help you secure your account,
|
||||
just{' '}
|
||||
<Link className="text-documenso-700 font-normal" href="mailto:hi@documenso.com">
|
||||
contact us.
|
||||
contact us
|
||||
</Link>
|
||||
.
|
||||
</Trans>
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
@@ -63,6 +63,7 @@ export class LocalJobProvider extends BaseJobProvider {
|
||||
jobId: pendingJob.id,
|
||||
jobDefinitionId: pendingJob.jobId,
|
||||
data: options,
|
||||
isRetry: false,
|
||||
});
|
||||
}),
|
||||
);
|
||||
@@ -198,6 +199,7 @@ export class LocalJobProvider extends BaseJobProvider {
|
||||
jobId,
|
||||
jobDefinitionId: backgroundJob.jobId,
|
||||
data: options,
|
||||
isRetry: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,12 @@ export type PdfToImagesOptions = {
|
||||
export const pdfToImages = async (pdfBytes: Uint8Array, options: PdfToImagesOptions = {}) => {
|
||||
const { scale = 2 } = options;
|
||||
|
||||
const pdf = await pdfjsLib.getDocument({
|
||||
const task = await pdfjsLib.getDocument({
|
||||
data: pdfBytes,
|
||||
CanvasFactory: SkiaCanvasFactory,
|
||||
}).promise;
|
||||
});
|
||||
|
||||
const pdf = await task.promise;
|
||||
|
||||
const images = await pMap(
|
||||
Array.from({ length: pdf.numPages }),
|
||||
@@ -68,18 +70,23 @@ export const pdfToImages = async (pdfBytes: Uint8Array, options: PdfToImagesOpti
|
||||
viewport,
|
||||
}).promise;
|
||||
|
||||
return {
|
||||
const result = {
|
||||
pageNumber,
|
||||
image: await canvas.toBuffer('jpeg'),
|
||||
width: Math.floor(viewport.width),
|
||||
height: Math.floor(viewport.height),
|
||||
mimeType: 'image/jpeg',
|
||||
};
|
||||
|
||||
void page.cleanup();
|
||||
|
||||
return result;
|
||||
},
|
||||
{ concurrency: 10 },
|
||||
);
|
||||
|
||||
void pdf.destroy();
|
||||
void pdf.destroy().catch((e) => console.error(e));
|
||||
void task.destroy().catch((e) => console.error(e));
|
||||
|
||||
return images;
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ export const getDocumentCertificateAuditLogs = async ({
|
||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED,
|
||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
|
||||
DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
||||
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT,
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -37,6 +38,9 @@ export const getDocumentCertificateAuditLogs = async ({
|
||||
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED]: auditLogs.filter(
|
||||
(log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
|
||||
),
|
||||
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT]: auditLogs.filter(
|
||||
(log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT,
|
||||
),
|
||||
[DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED]: auditLogs.filter(
|
||||
(log) => log.type === DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED,
|
||||
),
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { DocumentStatus, EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { deletedAccountServiceAccount } from '../user/service-accounts/deleted-account';
|
||||
|
||||
export type OrphanEnvelopesOptions = {
|
||||
teamId: number;
|
||||
};
|
||||
|
||||
export const orphanEnvelopes = async ({ teamId }: OrphanEnvelopesOptions) => {
|
||||
const serviceAccount = await deletedAccountServiceAccount();
|
||||
|
||||
// Transfer all inflight and completed envelopes to the service account.
|
||||
await prisma.envelope.updateMany({
|
||||
where: {
|
||||
teamId,
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
status: {
|
||||
in: [DocumentStatus.PENDING, DocumentStatus.REJECTED, DocumentStatus.COMPLETED],
|
||||
},
|
||||
deletedAt: null,
|
||||
},
|
||||
data: {
|
||||
userId: serviceAccount.id,
|
||||
teamId: serviceAccount.ownedOrganisations[0].teams[0].id,
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
// Transfer any remaining deleted envelopes to the service account.
|
||||
await prisma.envelope.updateMany({
|
||||
where: {
|
||||
teamId,
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
status: {
|
||||
in: [DocumentStatus.PENDING, DocumentStatus.REJECTED, DocumentStatus.COMPLETED],
|
||||
},
|
||||
},
|
||||
data: {
|
||||
userId: serviceAccount.id,
|
||||
teamId: serviceAccount.ownedOrganisations[0].teams[0].id,
|
||||
},
|
||||
});
|
||||
|
||||
// Then delete anything remaining across documents and templates.
|
||||
await prisma.envelope.deleteMany({
|
||||
where: {
|
||||
teamId,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export type TransferTeamEnvelopesOptions = {
|
||||
sourceTeamId: number;
|
||||
targetTeamId: number;
|
||||
};
|
||||
|
||||
export const transferTeamEnvelopes = async ({
|
||||
sourceTeamId,
|
||||
targetTeamId,
|
||||
}: TransferTeamEnvelopesOptions) => {
|
||||
await prisma.envelope.updateMany({
|
||||
where: {
|
||||
teamId: sourceTeamId,
|
||||
},
|
||||
data: {
|
||||
teamId: targetTeamId,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -40,7 +40,10 @@ export const acceptOrganisationInvitation = async ({
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
email: organisationMemberInvite.email,
|
||||
email: {
|
||||
equals: organisationMemberInvite.email,
|
||||
mode: 'insensitive',
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
import { hashString } from '../auth/hash';
|
||||
|
||||
export const getApiTokenByToken = async ({ token }: { token: string }) => {
|
||||
@@ -38,11 +39,17 @@ export const getApiTokenByToken = async ({ token }: { token: string }) => {
|
||||
});
|
||||
|
||||
if (!apiToken) {
|
||||
throw new Error('Invalid token');
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid token',
|
||||
statusCode: 401,
|
||||
});
|
||||
}
|
||||
|
||||
if (apiToken.expires && apiToken.expires < new Date()) {
|
||||
throw new Error('Expired token');
|
||||
throw new AppError(AppErrorCode.EXPIRED_CODE, {
|
||||
message: 'Expired token',
|
||||
statusCode: 401,
|
||||
});
|
||||
}
|
||||
|
||||
// Handle a silly choice from many moons ago
|
||||
@@ -54,7 +61,10 @@ export const getApiTokenByToken = async ({ token }: { token: string }) => {
|
||||
|
||||
// This will never happen but we need to narrow types
|
||||
if (!user) {
|
||||
throw new Error('Invalid token');
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid token',
|
||||
statusCode: 401,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
import { hashString } from '../auth/hash';
|
||||
|
||||
export const getUserByApiToken = async ({ token }: { token: string }) => {
|
||||
@@ -19,14 +20,20 @@ export const getUserByApiToken = async ({ token }: { token: string }) => {
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new Error('Invalid token');
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid token',
|
||||
statusCode: 401,
|
||||
});
|
||||
}
|
||||
|
||||
const retrievedToken = user.apiTokens.find((apiToken) => apiToken.token === hashedToken);
|
||||
|
||||
// This should be impossible but we need to satisfy TypeScript
|
||||
if (!retrievedToken) {
|
||||
throw new Error('Invalid token');
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'Invalid token',
|
||||
statusCode: 401,
|
||||
});
|
||||
}
|
||||
|
||||
if (retrievedToken.expires && retrievedToken.expires < new Date()) {
|
||||
|
||||
@@ -185,7 +185,7 @@ export const createDocumentFromDirectTemplate = async ({
|
||||
documentAuth: directTemplateEnvelope.authOptions,
|
||||
});
|
||||
|
||||
const directRecipientName = user?.name || initialDirectRecipientName;
|
||||
let directRecipientName = user?.name || initialDirectRecipientName;
|
||||
|
||||
// Ensure typesafety when we add more options.
|
||||
const isAccessAuthValid = match(derivedRecipientAccessAuth.at(0))
|
||||
@@ -238,7 +238,7 @@ export const createDocumentFromDirectTemplate = async ({
|
||||
}
|
||||
|
||||
if (templateField.type === FieldType.NAME && directRecipientName === undefined) {
|
||||
directRecipientName === signedFieldValue?.value;
|
||||
directRecipientName = signedFieldValue?.value;
|
||||
}
|
||||
|
||||
const derivedRecipientActionAuth = await validateFieldAuth({
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { DocumentStatus, EnvelopeType } from '@prisma/client';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||
import { deletedAccountServiceAccount } from './service-accounts/deleted-account';
|
||||
import { orphanEnvelopes } from '../envelope/orphan-envelopes';
|
||||
|
||||
export type DeleteUserOptions = {
|
||||
id: number;
|
||||
@@ -14,6 +12,30 @@ export const deleteUser = async ({ id }: DeleteUserOptions) => {
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
include: {
|
||||
ownedOrganisations: {
|
||||
include: {
|
||||
teams: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
organisationMember: {
|
||||
include: {
|
||||
organisation: {
|
||||
include: {
|
||||
teams: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
@@ -22,22 +44,36 @@ export const deleteUser = async ({ id }: DeleteUserOptions) => {
|
||||
});
|
||||
}
|
||||
|
||||
const serviceAccount = await deletedAccountServiceAccount();
|
||||
// Get team IDs from organisations the user owns.
|
||||
const ownedTeamIds = user.ownedOrganisations.flatMap((org) => org.teams.map((team) => team.id));
|
||||
|
||||
// TODO: Send out cancellations for all pending docs
|
||||
await prisma.envelope.updateMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
type: EnvelopeType.DOCUMENT,
|
||||
status: {
|
||||
in: [DocumentStatus.PENDING, DocumentStatus.REJECTED, DocumentStatus.COMPLETED],
|
||||
},
|
||||
},
|
||||
data: {
|
||||
userId: serviceAccount.id,
|
||||
deletedAt: new Date(),
|
||||
},
|
||||
});
|
||||
// Get team IDs from organisations the user is a member of (but not owner).
|
||||
const memberTeams = user.organisationMember
|
||||
.filter((member) => member.organisation.ownerUserId !== user.id)
|
||||
.flatMap((member) =>
|
||||
member.organisation.teams.map((team) => ({
|
||||
teamId: team.id,
|
||||
orgOwnerId: member.organisation.ownerUserId,
|
||||
})),
|
||||
);
|
||||
|
||||
// For teams where user is the org owner - orphan their envelopes.
|
||||
await Promise.all(ownedTeamIds.map(async (teamId) => orphanEnvelopes({ teamId })));
|
||||
|
||||
// For teams where user is a member (not owner) - transfer envelopes to team owner.
|
||||
await Promise.all(
|
||||
memberTeams.map(async ({ teamId, orgOwnerId }) => {
|
||||
return prisma.envelope.updateMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
teamId,
|
||||
},
|
||||
data: {
|
||||
userId: orgOwnerId,
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
return await prisma.user.delete({
|
||||
where: {
|
||||
|
||||
@@ -5,6 +5,20 @@ export const deletedAccountServiceAccount = async () => {
|
||||
where: {
|
||||
email: 'deleted-account@documenso.com',
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
ownedOrganisations: {
|
||||
select: {
|
||||
id: true,
|
||||
teams: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!serviceAccount) {
|
||||
|
||||
+235
-177
File diff suppressed because it is too large
Load Diff
@@ -96,6 +96,11 @@ msgstr "{0, plural, one {# field} other {# fields}}"
|
||||
msgid "{0, plural, one {# folder} other {# folders}}"
|
||||
msgstr "{0, plural, one {# folder} other {# folders}}"
|
||||
|
||||
#. placeholder {0}: detectedRecipients.length
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
|
||||
msgid "{0, plural, one {# recipient have been added from AI detection.} other {# recipients have been added from AI detection.}}"
|
||||
msgstr "{0, plural, one {# recipient have been added from AI detection.} other {# recipients have been added from AI detection.}}"
|
||||
|
||||
#. placeholder {0}: template.recipients.length
|
||||
#: apps/remix/app/routes/_recipient+/d.$token+/_index.tsx
|
||||
msgid "{0, plural, one {# recipient} other {# recipients}}"
|
||||
@@ -146,11 +151,44 @@ msgstr "{0, plural, one {1 matching field} other {# matching fields}}"
|
||||
msgid "{0, plural, one {1 Recipient} other {# Recipients}}"
|
||||
msgstr "{0, plural, one {1 Recipient} other {# Recipients}}"
|
||||
|
||||
#. placeholder {0}: progress.fieldsDetected
|
||||
#. placeholder {1}: progress.pagesProcessed
|
||||
#. placeholder {2}: progress.totalPages
|
||||
#. placeholder {3}: progress.pagesProcessed
|
||||
#. placeholder {4}: progress.totalPages
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
msgid "{0, plural, one {Page {1} of {2} - # field found} other {Page {3} of {4} - # fields found}}"
|
||||
msgstr "{0, plural, one {Page {1} of {2} - # field found} other {Page {3} of {4} - # fields found}}"
|
||||
|
||||
#. placeholder {0}: progress.recipientsDetected
|
||||
#. placeholder {1}: progress.pagesProcessed
|
||||
#. placeholder {2}: progress.totalPages
|
||||
#. placeholder {3}: progress.pagesProcessed
|
||||
#. placeholder {4}: progress.totalPages
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
msgid "{0, plural, one {Page {1} of {2} - # recipient found} other {Page {3} of {4} - # recipients found}}"
|
||||
msgstr "{0, plural, one {Page {1} of {2} - # recipient found} other {Page {3} of {4} - # recipients found}}"
|
||||
|
||||
#. placeholder {0}: detectedRecipients.length
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
|
||||
msgid "{0, plural, one {Recipient added} other {Recipients added}}"
|
||||
msgstr "{0, plural, one {Recipient added} other {Recipients added}}"
|
||||
|
||||
#. placeholder {0}: pendingRecipients.length
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
|
||||
msgid "{0, plural, one {Waiting on 1 recipient} other {Waiting on # recipients}}"
|
||||
msgstr "{0, plural, one {Waiting on 1 recipient} other {Waiting on # recipients}}"
|
||||
|
||||
#. placeholder {0}: detectedFields.length
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
msgid "{0, plural, one {We found # field in your document.} other {We found # fields in your document.}}"
|
||||
msgstr "{0, plural, one {We found # field in your document.} other {We found # fields in your document.}}"
|
||||
|
||||
#. placeholder {0}: detectedRecipients.length
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
msgid "{0, plural, one {We found # recipient in your document.} other {We found # recipients in your document.}}"
|
||||
msgstr "{0, plural, one {We found # recipient in your document.} other {We found # recipients in your document.}}"
|
||||
|
||||
#. placeholder {0}: _(FRIENDLY_FIELD_TYPE[fieldType as FieldType])
|
||||
#. placeholder {0}: route.label
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-auto-sign.tsx
|
||||
@@ -195,11 +233,6 @@ msgstr "{0} of {1} documents remaining this month."
|
||||
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
|
||||
msgstr "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
|
||||
|
||||
#. placeholder {0}: detectedRecipients.length
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
|
||||
msgid "{0} recipient(s) have been added from AI detection."
|
||||
msgstr "{0} recipient(s) have been added from AI detection."
|
||||
|
||||
#. placeholder {0}: organisation.name
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "{0} Teams"
|
||||
@@ -846,9 +879,6 @@ msgid "A request to use your email has been initiated by {0} on Documenso"
|
||||
msgstr "A request to use your email has been initiated by {0} on Documenso"
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
|
||||
msgid "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso"
|
||||
msgstr "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso"
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
|
||||
msgid "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso."
|
||||
msgstr "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso."
|
||||
@@ -1283,6 +1313,10 @@ msgstr "After submission, a document will be automatically generated and added t
|
||||
msgid "AI Features"
|
||||
msgstr "AI Features"
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "AI features are disabled for your team. Please ask your team owner or organisation owner to enable them."
|
||||
msgstr "AI features are disabled for your team. Please ask your team owner or organisation owner to enable them."
|
||||
|
||||
#: apps/remix/app/components/general/document/document-status.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id._index.tsx
|
||||
msgid "All"
|
||||
@@ -2260,6 +2294,10 @@ msgstr "Charts"
|
||||
msgid "Checkbox"
|
||||
msgstr "Checkbox"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-content.tsx
|
||||
msgid "Checkbox option"
|
||||
msgstr "Checkbox option"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
|
||||
msgid "Checkbox Settings"
|
||||
msgstr "Checkbox Settings"
|
||||
@@ -2363,6 +2401,7 @@ msgstr "Client Secret"
|
||||
msgid "Client secret is required"
|
||||
msgstr "Client secret is required"
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
@@ -2375,7 +2414,6 @@ msgstr "Client secret is required"
|
||||
#: apps/remix/app/components/dialogs/team-group-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-use-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
|
||||
@@ -2386,7 +2424,9 @@ msgstr "Client secret is required"
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-auth-2fa.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
|
||||
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
|
||||
#: packages/ui/primitives/dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/missing-signature-field-dialog.tsx
|
||||
#: packages/ui/primitives/sheet.tsx
|
||||
msgid "Close"
|
||||
msgstr "Close"
|
||||
|
||||
@@ -3283,8 +3323,8 @@ msgid "Device"
|
||||
msgstr "Device"
|
||||
|
||||
#: packages/email/templates/reset-password.tsx
|
||||
msgid "Didn't request a password change? We are here to help you secure your account, just <0>contact us.</0>"
|
||||
msgstr "Didn't request a password change? We are here to help you secure your account, just <0>contact us.</0>"
|
||||
msgid "Didn't request a password change? We are here to help you secure your account, just <0>contact us</0>."
|
||||
msgstr "Didn't request a password change? We are here to help you secure your account, just <0>contact us</0>."
|
||||
|
||||
#: apps/remix/app/components/general/template/template-direct-link-badge.tsx
|
||||
#: apps/remix/app/components/tables/templates-table.tsx
|
||||
@@ -3825,6 +3865,11 @@ msgstr "Domain Name"
|
||||
msgid "Don't have an account? <0>Sign up</0>"
|
||||
msgstr "Don't have an account? <0>Sign up</0>"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
msgid "Don't transfer (Delete all documents)"
|
||||
msgstr "Don't transfer (Delete all documents)"
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/enable-authenticator-app-dialog.tsx
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
|
||||
@@ -4179,6 +4224,15 @@ msgstr "Enable account"
|
||||
msgid "Enable Account"
|
||||
msgstr "Enable Account"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
|
||||
msgid "Enable AI detection"
|
||||
msgstr "Enable AI detection"
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "Enable AI features"
|
||||
msgstr "Enable AI features"
|
||||
|
||||
#: apps/remix/app/components/forms/document-preferences-form.tsx
|
||||
msgid "Enable AI-powered features such as automatic recipient detection. When enabled, document content will be sent to AI providers. We only use providers that do not retain data for training and prefer European regions where available."
|
||||
msgstr "Enable AI-powered features such as automatic recipient detection. When enabled, document content will be sent to AI providers. We only use providers that do not retain data for training and prefer European regions where available."
|
||||
@@ -4817,10 +4871,26 @@ msgstr "Go home"
|
||||
msgid "Go to document"
|
||||
msgstr "Go to document"
|
||||
|
||||
#: packages/ui/primitives/data-table-pagination.tsx
|
||||
msgid "Go to first page"
|
||||
msgstr "Go to first page"
|
||||
|
||||
#: packages/ui/primitives/data-table-pagination.tsx
|
||||
msgid "Go to last page"
|
||||
msgstr "Go to last page"
|
||||
|
||||
#: packages/ui/primitives/data-table-pagination.tsx
|
||||
msgid "Go to next page"
|
||||
msgstr "Go to next page"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
msgid "Go to owner"
|
||||
msgstr "Go to owner"
|
||||
|
||||
#: packages/ui/primitives/data-table-pagination.tsx
|
||||
msgid "Go to previous page"
|
||||
msgstr "Go to previous page"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "Go to team"
|
||||
msgstr "Go to team"
|
||||
@@ -4901,8 +4971,8 @@ msgid "Here you can set branding preferences for your organisation. Teams will i
|
||||
msgstr "Here you can set branding preferences for your organisation. Teams will inherit these settings by default."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx
|
||||
msgid "Here you can set branding preferences for your team"
|
||||
msgstr "Here you can set branding preferences for your team"
|
||||
msgid "Here you can set branding preferences for your team."
|
||||
msgstr "Here you can set branding preferences for your team."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.document.tsx
|
||||
msgid "Here you can set document preferences for your organisation. Teams will inherit these settings by default."
|
||||
@@ -4917,12 +4987,12 @@ msgid "Here you can set preferences and defaults for your team."
|
||||
msgstr "Here you can set preferences and defaults for your team."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx
|
||||
msgid "Here you can set your general branding preferences"
|
||||
msgstr "Here you can set your general branding preferences"
|
||||
msgid "Here you can set your general branding preferences."
|
||||
msgstr "Here you can set your general branding preferences."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.document.tsx
|
||||
msgid "Here you can set your general document preferences"
|
||||
msgstr "Here you can set your general document preferences"
|
||||
msgid "Here you can set your general document preferences."
|
||||
msgstr "Here you can set your general document preferences."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
|
||||
msgid "Here's how it works:"
|
||||
@@ -5702,15 +5772,15 @@ msgstr "Member Since"
|
||||
msgid "Members"
|
||||
msgstr "Members"
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
|
||||
#: apps/remix/app/components/forms/support-ticket-form.tsx
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "Message"
|
||||
msgstr "Message"
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
|
||||
#: apps/remix/app/components/embed/authoring/configure-document-advanced-settings.tsx
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "Message <0>(Optional)</0>"
|
||||
msgstr "Message <0>(Optional)</0>"
|
||||
|
||||
@@ -5860,10 +5930,6 @@ msgstr "Never expire"
|
||||
msgid "New Password"
|
||||
msgstr "New Password"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "New Template"
|
||||
msgstr "New Template"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
|
||||
@@ -6096,8 +6162,8 @@ msgid "Once you have scanned the QR code or entered the code manually, enter the
|
||||
msgstr "Once you have scanned the QR code or entered the code manually, enter the code provided by your authenticator app below."
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
|
||||
msgid "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button"
|
||||
msgstr "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button"
|
||||
msgid "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button."
|
||||
msgstr "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button."
|
||||
|
||||
#: packages/lib/constants/template.ts
|
||||
msgid "Once your template is set up, share the link anywhere you want. The person who opens the link will be able to enter their information in the direct link recipient field and complete any other fields assigned to them."
|
||||
@@ -6131,6 +6197,10 @@ msgstr "Only PDF files are allowed"
|
||||
msgid "Oops! Something went wrong."
|
||||
msgstr "Oops! Something went wrong."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "Open menu"
|
||||
msgstr "Open menu"
|
||||
|
||||
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
|
||||
msgid "Opened"
|
||||
msgstr "Opened"
|
||||
@@ -6324,20 +6394,6 @@ msgstr "Ownership transferred to {organisationMemberName}."
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr "Page {0} of {1}"
|
||||
|
||||
#. placeholder {0}: progress.pagesProcessed
|
||||
#. placeholder {1}: progress.totalPages
|
||||
#. placeholder {2}: progress.fieldsDetected
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
msgid "Page {0} of {1} - {2} field(s) found"
|
||||
msgstr "Page {0} of {1} - {2} field(s) found"
|
||||
|
||||
#. placeholder {0}: progress.pagesProcessed
|
||||
#. placeholder {1}: progress.totalPages
|
||||
#. placeholder {2}: progress.recipientsDetected
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
msgid "Page {0} of {1} - {2} recipient(s) found"
|
||||
msgstr "Page {0} of {1} - {2} recipient(s) found"
|
||||
|
||||
#. placeholder {0}: i + 1
|
||||
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
|
||||
#: packages/ui/primitives/pdf-viewer/base.tsx
|
||||
@@ -6683,10 +6739,6 @@ msgstr "Please try a different domain."
|
||||
msgid "Please try again and make sure you enter the correct email address."
|
||||
msgstr "Please try again and make sure you enter the correct email address."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "Please try again later."
|
||||
msgstr "Please try again later."
|
||||
|
||||
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
|
||||
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
|
||||
#: packages/ui/primitives/pdf-viewer/base.tsx
|
||||
@@ -6964,10 +7016,6 @@ msgstr "Recipient updated"
|
||||
msgid "Recipients"
|
||||
msgstr "Recipients"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
|
||||
msgid "Recipients added"
|
||||
msgstr "Recipients added"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Recipients metrics"
|
||||
msgstr "Recipients metrics"
|
||||
@@ -7141,8 +7189,8 @@ msgstr "Reply to email"
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "Reply To Email"
|
||||
msgstr "Reply To Email"
|
||||
msgid "Reply To Email <0>(Optional)</0>"
|
||||
msgstr "Reply To Email <0>(Optional)</0>"
|
||||
|
||||
#: apps/remix/app/components/general/webhook-logs-sheet.tsx
|
||||
msgid "Request"
|
||||
@@ -7445,6 +7493,7 @@ msgid "See the background jobs tab for the status"
|
||||
msgstr "See the background jobs tab for the status"
|
||||
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-dropdown-field.tsx
|
||||
#: packages/ui/primitives/document-flow/field-content.tsx
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx
|
||||
#: packages/ui/primitives/document-flow/types.ts
|
||||
msgid "Select"
|
||||
@@ -8084,7 +8133,6 @@ msgstr "Some signers have not been assigned a signature field. Please assign at
|
||||
#: apps/remix/app/components/dialogs/team-email-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-inherit-member-disable-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-inherit-member-enable-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
|
||||
@@ -8247,14 +8295,14 @@ msgstr "Stripe customer created successfully"
|
||||
msgid "Stripe Customer ID"
|
||||
msgstr "Stripe Customer ID"
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
|
||||
#: apps/remix/app/components/forms/support-ticket-form.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
msgid "Subject"
|
||||
msgstr "Subject"
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
|
||||
#: apps/remix/app/components/embed/authoring/configure-document-advanced-settings.tsx
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "Subject <0>(Optional)</0>"
|
||||
msgstr "Subject <0>(Optional)</0>"
|
||||
@@ -8575,7 +8623,6 @@ msgstr "Teams that this organisation group is currently assigned to"
|
||||
msgid "Template"
|
||||
msgstr "Template"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: packages/ui/primitives/document-upload-button.tsx
|
||||
msgid "Template (Legacy)"
|
||||
msgstr "Template (Legacy)"
|
||||
@@ -8588,7 +8635,7 @@ msgstr "Template Created"
|
||||
msgid "Template deleted"
|
||||
msgstr "Template deleted"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Template document uploaded"
|
||||
msgstr "Template document uploaded"
|
||||
|
||||
@@ -8650,10 +8697,6 @@ msgstr "Template uploaded"
|
||||
msgid "Templates"
|
||||
msgstr "Templates"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "Templates allow you to quickly generate documents with pre-filled recipients and fields."
|
||||
msgstr "Templates allow you to quickly generate documents with pre-filled recipients and fields."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id._index.tsx
|
||||
msgid "Test"
|
||||
msgstr "Test"
|
||||
@@ -8821,8 +8864,8 @@ msgid "The email domain you are looking for may have been removed, renamed or ma
|
||||
msgstr "The email domain you are looking for may have been removed, renamed or may have never existed."
|
||||
|
||||
#: apps/remix/app/components/forms/signin.tsx
|
||||
msgid "The email or password provided is incorrect"
|
||||
msgstr "The email or password provided is incorrect"
|
||||
msgid "The email or password provided is incorrect."
|
||||
msgstr "The email or password provided is incorrect."
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
|
||||
@@ -9020,8 +9063,8 @@ msgid "The token you have used to reset your password is either expired or it ne
|
||||
msgstr "The token you have used to reset your password is either expired or it never existed. If you have still forgotten your password, please request a new reset link."
|
||||
|
||||
#: apps/remix/app/components/forms/signin.tsx
|
||||
msgid "The two-factor authentication code provided is incorrect"
|
||||
msgstr "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."
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-signature-form.tsx
|
||||
msgid "The typed signature font size"
|
||||
@@ -9191,8 +9234,8 @@ msgid "This document was created using a direct link."
|
||||
msgstr "This document was created using a direct link."
|
||||
|
||||
#: packages/email/template-components/template-footer.tsx
|
||||
msgid "This document was sent using <0>Documenso.</0>"
|
||||
msgstr "This document was sent using <0>Documenso.</0>"
|
||||
msgid "This document was sent using <0>Documenso</0>."
|
||||
msgstr "This document was sent using <0>Documenso</0>."
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-duplicate-dialog.tsx
|
||||
msgid "This document will be duplicated."
|
||||
@@ -9523,6 +9566,10 @@ msgstr "Total Signers that Signed Up"
|
||||
msgid "Total Users"
|
||||
msgstr "Total Users"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
msgid "Transfer documents to a different team"
|
||||
msgstr "Transfer documents to a different team"
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
|
||||
msgid "Triggers"
|
||||
@@ -9535,6 +9582,10 @@ msgstr "Triggers"
|
||||
msgid "Try again"
|
||||
msgstr "Try again"
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "Turn on AI detection to automatically find recipients and fields in your documents. AI providers do not retain your data for training."
|
||||
msgstr "Turn on AI detection to automatically find recipients and fields in your documents. AI providers do not retain your data for training."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Two factor authentication"
|
||||
msgstr "Two factor authentication"
|
||||
@@ -9681,6 +9732,10 @@ msgstr "Unauthorized"
|
||||
msgid "Uncompleted"
|
||||
msgstr "Uncompleted"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
|
||||
msgid "Undo"
|
||||
msgstr "Undo"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.linked-accounts.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
@@ -10293,8 +10348,8 @@ msgstr "Waiting for Your Turn"
|
||||
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
|
||||
msgid "Want to send slick signing links like this one? <0>Check out Documenso.</0>"
|
||||
msgstr "Want to send slick signing links like this one? <0>Check out Documenso.</0>"
|
||||
msgid "Want to send slick signing links like this one? <0>Check out Documenso</0>."
|
||||
msgstr "Want to send slick signing links like this one? <0>Check out Documenso</0>."
|
||||
|
||||
#: apps/remix/app/routes/_profile+/_layout.tsx
|
||||
msgid "Want your own public profile?"
|
||||
@@ -10329,6 +10384,10 @@ msgstr "We are unable to update this passkey at the moment. Please try again lat
|
||||
msgid "We couldn't create a Stripe customer. Please try again."
|
||||
msgstr "We couldn't create a Stripe customer. Please try again."
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "We couldn't enable AI features right now. Please try again."
|
||||
msgstr "We couldn't enable AI features right now. Please try again."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "We couldn't update the group. Please try again."
|
||||
msgstr "We couldn't update the group. Please try again."
|
||||
@@ -10529,16 +10588,6 @@ msgstr "We encountered an unknown error while attempting update the team email.
|
||||
msgid "We encountered an unknown error while attempting update your profile. Please try again later."
|
||||
msgstr "We encountered an unknown error while attempting update your profile. Please try again later."
|
||||
|
||||
#. placeholder {0}: detectedFields.length
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
msgid "We found {0} field(s) in your document."
|
||||
msgstr "We found {0} field(s) in your document."
|
||||
|
||||
#. placeholder {0}: detectedRecipients.length
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
msgid "We found {0} recipient(s) in your document."
|
||||
msgstr "We found {0} recipient(s) in your document."
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
|
||||
msgid "We have sent a confirmation email for verification."
|
||||
msgstr "We have sent a confirmation email for verification."
|
||||
@@ -10900,13 +10949,13 @@ msgid "You are currently updating <0>{0}</0>"
|
||||
msgstr "You are currently updating <0>{0}</0>"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
|
||||
msgid "You are currently updating <0>{memberName}.</0>"
|
||||
msgstr "You are currently updating <0>{memberName}.</0>"
|
||||
msgid "You are currently updating <0>{memberName}</0>."
|
||||
msgstr "You are currently updating <0>{memberName}</0>."
|
||||
|
||||
#: apps/remix/app/components/dialogs/admin-organisation-member-update-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
|
||||
msgid "You are currently updating <0>{organisationMemberName}.</0>"
|
||||
msgstr "You are currently updating <0>{organisationMemberName}.</0>"
|
||||
msgid "You are currently updating <0>{organisationMemberName}</0>."
|
||||
msgstr "You are currently updating <0>{organisationMemberName}</0>."
|
||||
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
|
||||
msgid "You are currently updating the <0>{passkeyName}</0> passkey."
|
||||
@@ -10967,16 +11016,16 @@ msgstr "You can enable access to allow all organisation members to access this t
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.email.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.email.tsx
|
||||
msgid "You can manage your email preferences here"
|
||||
msgstr "You can manage your email preferences here"
|
||||
msgid "You can manage your email preferences here."
|
||||
msgstr "You can manage your email preferences here."
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
|
||||
msgid "You can only detect fields in draft envelopes"
|
||||
msgstr "You can only detect fields in draft envelopes"
|
||||
|
||||
#: packages/email/templates/confirm-team-email.tsx
|
||||
msgid "You can revoke access at any time in your team settings on Documenso <0>here.</0>"
|
||||
msgstr "You can revoke access at any time in your team settings on Documenso <0>here.</0>"
|
||||
msgid "You can revoke access at any time in your team settings on Documenso <0>here</0>."
|
||||
msgstr "You can revoke access at any time in your team settings on Documenso <0>here</0>."
|
||||
|
||||
#: apps/remix/app/components/forms/public-profile-form.tsx
|
||||
msgid "You can update the profile URL by updating the team URL in the general settings page."
|
||||
@@ -11009,8 +11058,8 @@ msgid "You cannot delete a group which has a higher role than you."
|
||||
msgstr "You cannot delete a group which has a higher role than you."
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-item-delete-dialog.tsx
|
||||
msgid "You cannot delete this item because the document has been sent to recipients"
|
||||
msgstr "You cannot delete this item because the document has been sent to recipients"
|
||||
msgid "You cannot delete this item because the document has been sent to recipients."
|
||||
msgstr "You cannot delete this item because the document has been sent to recipients."
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
|
||||
msgid "You cannot modify a group which has a higher role than you."
|
||||
@@ -11036,8 +11085,8 @@ msgstr "You cannot upload documents at this time."
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
msgid "You cannot upload encrypted PDFs"
|
||||
msgstr "You cannot upload encrypted PDFs"
|
||||
msgid "You cannot upload encrypted PDFs."
|
||||
msgstr "You cannot upload encrypted PDFs."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.billing.tsx
|
||||
msgid "You currently have an inactive <0>{currentProductName}</0> subscription"
|
||||
@@ -11048,8 +11097,8 @@ msgid "You currently have no access to any teams within this organisation. Pleas
|
||||
msgstr "You currently have no access to any teams within this organisation. Please contact your organisation to request access."
|
||||
|
||||
#: apps/remix/app/components/forms/token.tsx
|
||||
msgid "You do not have permission to create a token for this team"
|
||||
msgstr "You do not have permission to create a token for this team"
|
||||
msgid "You do not have permission to create a token for this team."
|
||||
msgstr "You do not have permission to create a token for this team."
|
||||
|
||||
#: apps/remix/app/components/tables/user-billing-organisations-table.tsx
|
||||
msgid "You don't manage billing for any organisations."
|
||||
@@ -11118,8 +11167,8 @@ msgstr "You have not yet created or received any documents. To create a document
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
msgid "You have reached the limit of the number of files per envelope"
|
||||
msgstr "You have reached the limit of the number of files per envelope"
|
||||
msgid "You have reached the limit of the number of files per envelope."
|
||||
msgstr "You have reached the limit of the number of files per envelope."
|
||||
|
||||
#. placeholder {0}: quota.directTemplates
|
||||
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
|
||||
@@ -11274,6 +11323,10 @@ msgstr "You will now be required to enter a code from your authenticator app whe
|
||||
msgid "You will receive an email copy of the signed document once everyone has signed."
|
||||
msgstr "You will receive an email copy of the signed document once everyone has signed."
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "You're an admin. You can enable AI features for this team right away. Everyone on the team will see AI detection once enabled."
|
||||
msgstr "You're an admin. You can enable AI features for this team right away. Everyone on the team will see AI detection once enabled."
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
msgid "You've made too many detection requests. Please wait a minute before trying again."
|
||||
@@ -11332,6 +11385,10 @@ msgstr "Your current plan is past due."
|
||||
msgid "Your direct signing templates"
|
||||
msgstr "Your direct signing templates"
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "Your document content will be sent securely to our AI provider solely for detection and will not be stored or used for training."
|
||||
msgstr "Your document content will be sent securely to our AI provider solely for detection and will not be stored or used for training."
|
||||
|
||||
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Your document failed to upload."
|
||||
@@ -11367,7 +11424,7 @@ msgstr "Your document has been successfully duplicated."
|
||||
msgid "Your document has been uploaded successfully."
|
||||
msgstr "Your document has been uploaded successfully."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Your document has been uploaded successfully. You will be redirected to the template page."
|
||||
msgstr "Your document has been uploaded successfully. You will be redirected to the template page."
|
||||
|
||||
|
||||
+235
-177
File diff suppressed because it is too large
Load Diff
+235
-177
File diff suppressed because it is too large
Load Diff
+235
-177
File diff suppressed because it is too large
Load Diff
+231
-173
File diff suppressed because it is too large
Load Diff
+232
-174
File diff suppressed because it is too large
Load Diff
+233
-175
File diff suppressed because it is too large
Load Diff
+230
-172
File diff suppressed because it is too large
Load Diff
@@ -96,6 +96,11 @@ msgstr "{0, plural, one {# campo} other {# campos}}"
|
||||
msgid "{0, plural, one {# folder} other {# folders}}"
|
||||
msgstr "{0, plural, one {# pasta} other {# pastas}}"
|
||||
|
||||
#. placeholder {0}: detectedRecipients.length
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
|
||||
msgid "{0, plural, one {# recipient have been added from AI detection.} other {# recipients have been added from AI detection.}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: template.recipients.length
|
||||
#: apps/remix/app/routes/_recipient+/d.$token+/_index.tsx
|
||||
msgid "{0, plural, one {# recipient} other {# recipients}}"
|
||||
@@ -146,11 +151,44 @@ msgstr "{0, plural, one {1 campo correspondente} other {# campos correspondentes
|
||||
msgid "{0, plural, one {1 Recipient} other {# Recipients}}"
|
||||
msgstr "{0, plural, one {1 Destinatário} other {# Destinatários}}"
|
||||
|
||||
#. placeholder {0}: progress.fieldsDetected
|
||||
#. placeholder {1}: progress.pagesProcessed
|
||||
#. placeholder {2}: progress.totalPages
|
||||
#. placeholder {3}: progress.pagesProcessed
|
||||
#. placeholder {4}: progress.totalPages
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
msgid "{0, plural, one {Page {1} of {2} - # field found} other {Page {3} of {4} - # fields found}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: progress.recipientsDetected
|
||||
#. placeholder {1}: progress.pagesProcessed
|
||||
#. placeholder {2}: progress.totalPages
|
||||
#. placeholder {3}: progress.pagesProcessed
|
||||
#. placeholder {4}: progress.totalPages
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
msgid "{0, plural, one {Page {1} of {2} - # recipient found} other {Page {3} of {4} - # recipients found}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: detectedRecipients.length
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
|
||||
msgid "{0, plural, one {Recipient added} other {Recipients added}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: pendingRecipients.length
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
|
||||
msgid "{0, plural, one {Waiting on 1 recipient} other {Waiting on # recipients}}"
|
||||
msgstr "{0, plural, one {Aguardando 1 destinatário} other {Aguardando # destinatários}}"
|
||||
|
||||
#. placeholder {0}: detectedFields.length
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
msgid "{0, plural, one {We found # field in your document.} other {We found # fields in your document.}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: detectedRecipients.length
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
msgid "{0, plural, one {We found # recipient in your document.} other {We found # recipients in your document.}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: _(FRIENDLY_FIELD_TYPE[fieldType as FieldType])
|
||||
#. placeholder {0}: route.label
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-auto-sign.tsx
|
||||
@@ -195,11 +233,6 @@ msgstr "{0} de {1} documentos restantes este mês."
|
||||
msgid "{0} on behalf of \"{1}\" has invited you to {recipientActionVerb} the document \"{2}\"."
|
||||
msgstr "{0} em nome de \"{1}\" convidou você para {recipientActionVerb} o documento \"{2}\"."
|
||||
|
||||
#. placeholder {0}: detectedRecipients.length
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
|
||||
msgid "{0} recipient(s) have been added from AI detection."
|
||||
msgstr "{0} destinatário(s) foram adicionados pela detecção de IA."
|
||||
|
||||
#. placeholder {0}: organisation.name
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "{0} Teams"
|
||||
@@ -846,9 +879,6 @@ msgid "A request to use your email has been initiated by {0} on Documenso"
|
||||
msgstr "Uma solicitação para usar seu e-mail foi iniciada por {0} no Documenso"
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
|
||||
msgid "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso"
|
||||
msgstr "Um segredo que será enviado para sua URL para que você possa verificar se a solicitação foi enviada pelo Documenso"
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
|
||||
msgid "A secret that will be sent to your URL so you can verify that the request has been sent by Documenso."
|
||||
msgstr "Um segredo que será enviado para sua URL para que você possa verificar se a solicitação foi enviada pelo Documenso."
|
||||
@@ -1283,6 +1313,10 @@ msgstr "Após o envio, um documento será gerado automaticamente e adicionado à
|
||||
msgid "AI Features"
|
||||
msgstr "Recursos de IA"
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "AI features are disabled for your team. Please ask your team owner or organisation owner to enable them."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/document/document-status.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id._index.tsx
|
||||
msgid "All"
|
||||
@@ -2260,6 +2294,10 @@ msgstr "Gráficos"
|
||||
msgid "Checkbox"
|
||||
msgstr "Caixa de seleção"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-content.tsx
|
||||
msgid "Checkbox option"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
|
||||
msgid "Checkbox Settings"
|
||||
msgstr "Configurações da Caixa de Seleção"
|
||||
@@ -2363,6 +2401,7 @@ msgstr "Segredo do Cliente"
|
||||
msgid "Client secret is required"
|
||||
msgstr "Segredo do Cliente é obrigatório"
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
@@ -2375,7 +2414,6 @@ msgstr "Segredo do Cliente é obrigatório"
|
||||
#: apps/remix/app/components/dialogs/team-group-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-use-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/webhook-test-dialog.tsx
|
||||
@@ -2386,7 +2424,9 @@ msgstr "Segredo do Cliente é obrigatório"
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-auth-2fa.tsx
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
|
||||
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
|
||||
#: packages/ui/primitives/dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/missing-signature-field-dialog.tsx
|
||||
#: packages/ui/primitives/sheet.tsx
|
||||
msgid "Close"
|
||||
msgstr "Fechar"
|
||||
|
||||
@@ -3283,8 +3323,8 @@ msgid "Device"
|
||||
msgstr "Dispositivo"
|
||||
|
||||
#: packages/email/templates/reset-password.tsx
|
||||
msgid "Didn't request a password change? We are here to help you secure your account, just <0>contact us.</0>"
|
||||
msgstr "Não solicitou uma alteração de senha? Estamos aqui para ajudá-lo a proteger sua conta, basta <0>entrar em contato conosco.</0>"
|
||||
msgid "Didn't request a password change? We are here to help you secure your account, just <0>contact us</0>."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/template/template-direct-link-badge.tsx
|
||||
#: apps/remix/app/components/tables/templates-table.tsx
|
||||
@@ -3825,6 +3865,11 @@ msgstr "Nome do Domínio"
|
||||
msgid "Don't have an account? <0>Sign up</0>"
|
||||
msgstr "Não tem uma conta? <0>Inscreva-se</0>"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
msgid "Don't transfer (Delete all documents)"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/enable-authenticator-app-dialog.tsx
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
|
||||
@@ -4179,6 +4224,15 @@ msgstr "Ativar conta"
|
||||
msgid "Enable Account"
|
||||
msgstr "Ativar Conta"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
|
||||
msgid "Enable AI detection"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "Enable AI features"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/document-preferences-form.tsx
|
||||
msgid "Enable AI-powered features such as automatic recipient detection. When enabled, document content will be sent to AI providers. We only use providers that do not retain data for training and prefer European regions where available."
|
||||
msgstr "Ative recursos com tecnologia de IA, como detecção automática de destinatários. Quando ativado, o conteúdo do documento será enviado para provedores de IA. Usamos apenas provedores que não retêm dados para treinamento e preferimos regiões europeias quando disponíveis."
|
||||
@@ -4817,10 +4871,26 @@ msgstr "Ir para o início"
|
||||
msgid "Go to document"
|
||||
msgstr "Ir para o documento"
|
||||
|
||||
#: packages/ui/primitives/data-table-pagination.tsx
|
||||
msgid "Go to first page"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/data-table-pagination.tsx
|
||||
msgid "Go to last page"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/data-table-pagination.tsx
|
||||
msgid "Go to next page"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
msgid "Go to owner"
|
||||
msgstr "Ir para o proprietário"
|
||||
|
||||
#: packages/ui/primitives/data-table-pagination.tsx
|
||||
msgid "Go to previous page"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "Go to team"
|
||||
msgstr "Ir para a equipe"
|
||||
@@ -4901,8 +4971,8 @@ msgid "Here you can set branding preferences for your organisation. Teams will i
|
||||
msgstr "Aqui você pode definir preferências de marca para sua organização. As equipes herdarão essas configurações por padrão."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx
|
||||
msgid "Here you can set branding preferences for your team"
|
||||
msgstr "Aqui você pode definir preferências de marca para sua equipe"
|
||||
msgid "Here you can set branding preferences for your team."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.document.tsx
|
||||
msgid "Here you can set document preferences for your organisation. Teams will inherit these settings by default."
|
||||
@@ -4917,12 +4987,12 @@ msgid "Here you can set preferences and defaults for your team."
|
||||
msgstr "Aqui você pode definir preferências e padrões para sua equipe."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx
|
||||
msgid "Here you can set your general branding preferences"
|
||||
msgstr "Aqui você pode definir suas preferências gerais de marca"
|
||||
msgid "Here you can set your general branding preferences."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.document.tsx
|
||||
msgid "Here you can set your general document preferences"
|
||||
msgstr "Aqui você pode definir suas preferências gerais de documento"
|
||||
msgid "Here you can set your general document preferences."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
|
||||
msgid "Here's how it works:"
|
||||
@@ -5702,15 +5772,15 @@ msgstr "Membro Desde"
|
||||
msgid "Members"
|
||||
msgstr "Membros"
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
|
||||
#: apps/remix/app/components/forms/support-ticket-form.tsx
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "Message"
|
||||
msgstr "Mensagem"
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
|
||||
#: apps/remix/app/components/embed/authoring/configure-document-advanced-settings.tsx
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "Message <0>(Optional)</0>"
|
||||
msgstr "Mensagem <0>(Opcional)</0>"
|
||||
|
||||
@@ -5860,10 +5930,6 @@ msgstr "Nunca expirar"
|
||||
msgid "New Password"
|
||||
msgstr "Nova Senha"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "New Template"
|
||||
msgstr "Novo Modelo"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/embed/embed-direct-template-client-page.tsx
|
||||
@@ -6096,8 +6162,8 @@ msgid "Once you have scanned the QR code or entered the code manually, enter the
|
||||
msgstr "Depois de escanear o código QR ou inserir o código manualmente, insira o código fornecido pelo seu aplicativo autenticador abaixo."
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
|
||||
msgid "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button"
|
||||
msgstr "Depois de atualizar seus registros DNS, pode levar até 48 horas para que eles sejam propagados. Assim que a propagação do DNS estiver concluída, você precisará voltar e pressionar o botão \"Sincronizar\" domínios"
|
||||
msgid "Once you update your DNS records, it may take up to 48 hours for it to be propogated. Once the DNS propagation is complete you will need to come back and press the \"Sync\" domains button."
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/constants/template.ts
|
||||
msgid "Once your template is set up, share the link anywhere you want. The person who opens the link will be able to enter their information in the direct link recipient field and complete any other fields assigned to them."
|
||||
@@ -6131,6 +6197,10 @@ msgstr "Apenas arquivos PDF são permitidos"
|
||||
msgid "Oops! Something went wrong."
|
||||
msgstr "Ops! Algo deu errado."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "Open menu"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/stack-avatars-with-tooltip.tsx
|
||||
msgid "Opened"
|
||||
msgstr "Aberto"
|
||||
@@ -6324,20 +6394,6 @@ msgstr "Propriedade transferida para {organisationMemberName}."
|
||||
msgid "Page {0} of {1}"
|
||||
msgstr "Página {0} de {1}"
|
||||
|
||||
#. placeholder {0}: progress.pagesProcessed
|
||||
#. placeholder {1}: progress.totalPages
|
||||
#. placeholder {2}: progress.fieldsDetected
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
msgid "Page {0} of {1} - {2} field(s) found"
|
||||
msgstr "Página {0} de {1} - {2} campo(s) encontrado(s)"
|
||||
|
||||
#. placeholder {0}: progress.pagesProcessed
|
||||
#. placeholder {1}: progress.totalPages
|
||||
#. placeholder {2}: progress.recipientsDetected
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
msgid "Page {0} of {1} - {2} recipient(s) found"
|
||||
msgstr "Página {0} de {1} - {2} destinatário(s) encontrado(s)"
|
||||
|
||||
#. placeholder {0}: i + 1
|
||||
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
|
||||
#: packages/ui/primitives/pdf-viewer/base.tsx
|
||||
@@ -6683,10 +6739,6 @@ msgstr "Por favor, tente um domínio diferente."
|
||||
msgid "Please try again and make sure you enter the correct email address."
|
||||
msgstr "Por favor, tente novamente e certifique-se de inserir o endereço de e-mail correto."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "Please try again later."
|
||||
msgstr "Por favor, tente novamente mais tarde."
|
||||
|
||||
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
|
||||
#: packages/ui/components/pdf-viewer/pdf-viewer-konva.tsx
|
||||
#: packages/ui/primitives/pdf-viewer/base.tsx
|
||||
@@ -6964,10 +7016,6 @@ msgstr "Destinatário atualizado"
|
||||
msgid "Recipients"
|
||||
msgstr "Destinatários"
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-recipient-form.tsx
|
||||
msgid "Recipients added"
|
||||
msgstr "Destinatários adicionados"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Recipients metrics"
|
||||
msgstr "Métricas de destinatários"
|
||||
@@ -7141,8 +7189,8 @@ msgstr "Responder ao e-mail"
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "Reply To Email"
|
||||
msgstr "Responder Para E-mail"
|
||||
msgid "Reply To Email <0>(Optional)</0>"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/webhook-logs-sheet.tsx
|
||||
msgid "Request"
|
||||
@@ -7445,6 +7493,7 @@ msgid "See the background jobs tab for the status"
|
||||
msgstr "Veja a aba de trabalhos em segundo plano para o status"
|
||||
|
||||
#: apps/remix/app/components/general/document-signing/document-signing-dropdown-field.tsx
|
||||
#: packages/ui/primitives/document-flow/field-content.tsx
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/dropdown-field.tsx
|
||||
#: packages/ui/primitives/document-flow/types.ts
|
||||
msgid "Select"
|
||||
@@ -8084,7 +8133,6 @@ msgstr "Alguns signatários não receberam um campo de assinatura. Por favor, at
|
||||
#: apps/remix/app/components/dialogs/team-email-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-inherit-member-disable-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-inherit-member-enable-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
|
||||
@@ -8247,14 +8295,14 @@ msgstr "Cliente Stripe criado com sucesso"
|
||||
msgid "Stripe Customer ID"
|
||||
msgstr "ID do Cliente Stripe"
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
|
||||
#: apps/remix/app/components/forms/support-ticket-form.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
msgid "Subject"
|
||||
msgstr "Assunto"
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
|
||||
#: apps/remix/app/components/embed/authoring/configure-document-advanced-settings.tsx
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-settings-dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "Subject <0>(Optional)</0>"
|
||||
msgstr "Assunto <0>(Opcional)</0>"
|
||||
@@ -8575,7 +8623,6 @@ msgstr "Equipes às quais este grupo da organização está atualmente atribuíd
|
||||
msgid "Template"
|
||||
msgstr "Modelo"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: packages/ui/primitives/document-upload-button.tsx
|
||||
msgid "Template (Legacy)"
|
||||
msgstr "Modelo (Legado)"
|
||||
@@ -8588,7 +8635,7 @@ msgstr "Modelo Criado"
|
||||
msgid "Template deleted"
|
||||
msgstr "Modelo excluído"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Template document uploaded"
|
||||
msgstr "Documento de modelo enviado"
|
||||
|
||||
@@ -8650,10 +8697,6 @@ msgstr "Modelo enviado"
|
||||
msgid "Templates"
|
||||
msgstr "Modelos"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
msgid "Templates allow you to quickly generate documents with pre-filled recipients and fields."
|
||||
msgstr "Modelos permitem gerar documentos rapidamente com destinatários e campos pré-preenchidos."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.webhooks.$id._index.tsx
|
||||
msgid "Test"
|
||||
msgstr "Teste"
|
||||
@@ -8821,8 +8864,8 @@ msgid "The email domain you are looking for may have been removed, renamed or ma
|
||||
msgstr "O domínio de e-mail que você está procurando pode ter sido removido, renomeado ou nunca ter existido."
|
||||
|
||||
#: apps/remix/app/components/forms/signin.tsx
|
||||
msgid "The email or password provided is incorrect"
|
||||
msgstr "O e-mail ou senha fornecidos estão incorretos"
|
||||
msgid "The email or password provided is incorrect."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
|
||||
@@ -9020,8 +9063,8 @@ msgid "The token you have used to reset your password is either expired or it ne
|
||||
msgstr "O token que você usou para redefinir sua senha expirou ou nunca existiu. Se você ainda esqueceu sua senha, solicite um novo link de redefinição."
|
||||
|
||||
#: apps/remix/app/components/forms/signin.tsx
|
||||
msgid "The two-factor authentication code provided is incorrect"
|
||||
msgstr "O código de autenticação de dois fatores fornecido está incorreto"
|
||||
msgid "The two-factor authentication code provided is incorrect."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/editor/editor-field-signature-form.tsx
|
||||
msgid "The typed signature font size"
|
||||
@@ -9099,7 +9142,6 @@ msgstr "Esta conta foi desativada. Entre em contato com o suporte."
|
||||
#: apps/remix/app/components/forms/signin.tsx
|
||||
msgid "This account has not been verified. Please verify your account before signing in."
|
||||
msgstr "Esta conta não foi verificada. Verifique sua conta antes de entrar."
|
||||
msgstr "Esta conta não foi verificada. Verifique sua conta antes de entrar."
|
||||
|
||||
#: apps/remix/app/components/dialogs/admin-user-reset-two-factor-dialog.tsx
|
||||
msgid "This action is irreversible. Please ensure you have informed the user before proceeding."
|
||||
@@ -9192,8 +9234,8 @@ msgid "This document was created using a direct link."
|
||||
msgstr "Este documento foi criado usando um link direto."
|
||||
|
||||
#: packages/email/template-components/template-footer.tsx
|
||||
msgid "This document was sent using <0>Documenso.</0>"
|
||||
msgstr "Este documento foi enviado usando <0>Documenso.</0>"
|
||||
msgid "This document was sent using <0>Documenso</0>."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-duplicate-dialog.tsx
|
||||
msgid "This document will be duplicated."
|
||||
@@ -9524,6 +9566,10 @@ msgstr "Total de Signatários que se Inscreveram"
|
||||
msgid "Total Users"
|
||||
msgstr "Total de Usuários"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
msgid "Transfer documents to a different team"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/webhook-edit-dialog.tsx
|
||||
msgid "Triggers"
|
||||
@@ -9536,6 +9582,10 @@ msgstr "Gatilhos"
|
||||
msgid "Try again"
|
||||
msgstr "Tente novamente"
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "Turn on AI detection to automatically find recipients and fields in your documents. AI providers do not retain your data for training."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Two factor authentication"
|
||||
msgstr "Autenticação de dois fatores"
|
||||
@@ -9682,6 +9732,10 @@ msgstr "Não autorizado"
|
||||
msgid "Uncompleted"
|
||||
msgstr "Não concluído"
|
||||
|
||||
#: packages/ui/primitives/signature-pad/signature-pad-draw.tsx
|
||||
msgid "Undo"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.linked-accounts.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
@@ -10294,8 +10348,8 @@ msgstr "Aguardando Sua Vez"
|
||||
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/_index.tsx
|
||||
msgid "Want to send slick signing links like this one? <0>Check out Documenso.</0>"
|
||||
msgstr "Quer enviar links de assinatura elegantes como este? <0>Confira o Documenso.</0>"
|
||||
msgid "Want to send slick signing links like this one? <0>Check out Documenso</0>."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_profile+/_layout.tsx
|
||||
msgid "Want your own public profile?"
|
||||
@@ -10330,6 +10384,10 @@ msgstr "Não conseguimos atualizar esta passkey no momento. Por favor, tente nov
|
||||
msgid "We couldn't create a Stripe customer. Please try again."
|
||||
msgstr "Não conseguimos criar um cliente Stripe. Por favor, tente novamente."
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "We couldn't enable AI features right now. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "We couldn't update the group. Please try again."
|
||||
msgstr "Não conseguimos atualizar o grupo. Por favor, tente novamente."
|
||||
@@ -10530,16 +10588,6 @@ msgstr "Encontramos um erro desconhecido ao tentar atualizar o e-mail da equipe.
|
||||
msgid "We encountered an unknown error while attempting update your profile. Please try again later."
|
||||
msgstr "Encontramos um erro desconhecido ao tentar atualizar seu perfil. Por favor, tente novamente mais tarde."
|
||||
|
||||
#. placeholder {0}: detectedFields.length
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
msgid "We found {0} field(s) in your document."
|
||||
msgstr "Encontramos {0} campo(s) no seu documento."
|
||||
|
||||
#. placeholder {0}: detectedRecipients.length
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
msgid "We found {0} recipient(s) in your document."
|
||||
msgstr "Encontramos {0} destinatário(s) no seu documento."
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-email-add-dialog.tsx
|
||||
msgid "We have sent a confirmation email for verification."
|
||||
msgstr "Enviamos um e-mail de confirmação para verificação."
|
||||
@@ -10901,13 +10949,13 @@ msgid "You are currently updating <0>{0}</0>"
|
||||
msgstr "Você está atualizando <0>{0}</0>"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-update-dialog.tsx
|
||||
msgid "You are currently updating <0>{memberName}.</0>"
|
||||
msgstr "Você está atualizando <0>{memberName}.</0>"
|
||||
msgid "You are currently updating <0>{memberName}</0>."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/admin-organisation-member-update-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
|
||||
msgid "You are currently updating <0>{organisationMemberName}.</0>"
|
||||
msgstr "Você está atualizando <0>{organisationMemberName}.</0>"
|
||||
msgid "You are currently updating <0>{organisationMemberName}</0>."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
|
||||
msgid "You are currently updating the <0>{passkeyName}</0> passkey."
|
||||
@@ -10968,16 +11016,16 @@ msgstr "Você pode ativar o acesso para permitir que todos os membros da organiz
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.email.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.email.tsx
|
||||
msgid "You can manage your email preferences here"
|
||||
msgstr "Você pode gerenciar suas preferências de e-mail aqui"
|
||||
msgid "You can manage your email preferences here."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
|
||||
msgid "You can only detect fields in draft envelopes"
|
||||
msgstr "Você só pode detectar campos em envelopes de rascunho"
|
||||
|
||||
#: packages/email/templates/confirm-team-email.tsx
|
||||
msgid "You can revoke access at any time in your team settings on Documenso <0>here.</0>"
|
||||
msgstr "Você pode revogar o acesso a qualquer momento nas configurações da sua equipe no Documenso <0>aqui.</0>"
|
||||
msgid "You can revoke access at any time in your team settings on Documenso <0>here</0>."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/public-profile-form.tsx
|
||||
msgid "You can update the profile URL by updating the team URL in the general settings page."
|
||||
@@ -11010,8 +11058,8 @@ msgid "You cannot delete a group which has a higher role than you."
|
||||
msgstr "Você não pode excluir um grupo que tem uma função superior à sua."
|
||||
|
||||
#: apps/remix/app/components/dialogs/envelope-item-delete-dialog.tsx
|
||||
msgid "You cannot delete this item because the document has been sent to recipients"
|
||||
msgstr "Você não pode excluir este item porque o documento foi enviado aos destinatários"
|
||||
msgid "You cannot delete this item because the document has been sent to recipients."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-update-dialog.tsx
|
||||
msgid "You cannot modify a group which has a higher role than you."
|
||||
@@ -11037,8 +11085,8 @@ msgstr "Você não pode enviar documentos neste momento."
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
msgid "You cannot upload encrypted PDFs"
|
||||
msgstr "Você não pode enviar PDFs criptografados"
|
||||
msgid "You cannot upload encrypted PDFs."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.billing.tsx
|
||||
msgid "You currently have an inactive <0>{currentProductName}</0> subscription"
|
||||
@@ -11049,8 +11097,8 @@ msgid "You currently have no access to any teams within this organisation. Pleas
|
||||
msgstr "Você atualmente não tem acesso a nenhuma equipe dentro desta organização. Por favor, entre em contato com sua organização para solicitar acesso."
|
||||
|
||||
#: apps/remix/app/components/forms/token.tsx
|
||||
msgid "You do not have permission to create a token for this team"
|
||||
msgstr "Você não tem permissão para criar um token para esta equipe"
|
||||
msgid "You do not have permission to create a token for this team."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/user-billing-organisations-table.tsx
|
||||
msgid "You don't manage billing for any organisations."
|
||||
@@ -11119,8 +11167,8 @@ msgstr "Você ainda não criou ou recebeu nenhum documento. Para criar um docume
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-drop-zone-wrapper.tsx
|
||||
#: apps/remix/app/components/general/envelope/envelope-upload-button.tsx
|
||||
msgid "You have reached the limit of the number of files per envelope"
|
||||
msgstr "Você atingiu o limite do número de arquivos por envelope"
|
||||
msgid "You have reached the limit of the number of files per envelope."
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: quota.directTemplates
|
||||
#: apps/remix/app/components/dialogs/template-direct-link-dialog.tsx
|
||||
@@ -11275,6 +11323,10 @@ msgstr "Agora será necessário inserir um código do seu aplicativo autenticado
|
||||
msgid "You will receive an email copy of the signed document once everyone has signed."
|
||||
msgstr "Você receberá uma cópia por e-mail do documento assinado assim que todos assinarem."
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "You're an admin. You can enable AI features for this team right away. Everyone on the team will see AI detection once enabled."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-field-detection-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/ai-recipient-detection-dialog.tsx
|
||||
msgid "You've made too many detection requests. Please wait a minute before trying again."
|
||||
@@ -11333,6 +11385,10 @@ msgstr "Seu plano atual está vencido."
|
||||
msgid "Your direct signing templates"
|
||||
msgstr "Seus modelos de assinatura direta"
|
||||
|
||||
#: apps/remix/app/components/dialogs/ai-features-enable-dialog.tsx
|
||||
msgid "Your document content will be sent securely to our AI provider solely for detection and will not be stored or used for training."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/embed/authoring/configure-document-upload.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Your document failed to upload."
|
||||
@@ -11368,7 +11424,7 @@ msgstr "Seu documento foi duplicado com sucesso."
|
||||
msgid "Your document has been uploaded successfully."
|
||||
msgstr "Seu documento foi enviado com sucesso."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-create-dialog.tsx
|
||||
#: apps/remix/app/components/general/document/document-upload-button-legacy.tsx
|
||||
msgid "Your document has been uploaded successfully. You will be redirected to the template page."
|
||||
msgstr "Seu documento foi enviado com sucesso. Você será redirecionado para a página do modelo."
|
||||
|
||||
@@ -11559,4 +11615,4 @@ msgstr "Seu código de verificação:"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.sso.tsx
|
||||
msgid "your-domain.com another-domain.com"
|
||||
msgstr "seu-dominio.com outro-dominio.com"
|
||||
msgstr "seu-dominio.com outro-dominio.com"
|
||||
|
||||
+235
-177
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@ export const ZClaimFlagsSchema = z.object({
|
||||
|
||||
authenticationPortal: z.boolean().optional(),
|
||||
|
||||
allowEnvelopes: z.boolean().optional(),
|
||||
allowLegacyEnvelopes: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export type TClaimFlags = z.infer<typeof ZClaimFlagsSchema>;
|
||||
@@ -84,9 +84,9 @@ export const SUBSCRIPTION_CLAIM_FEATURE_FLAGS: Record<
|
||||
key: 'authenticationPortal',
|
||||
label: 'Authentication portal',
|
||||
},
|
||||
allowEnvelopes: {
|
||||
key: 'allowEnvelopes',
|
||||
label: 'Allow envelopes',
|
||||
allowLegacyEnvelopes: {
|
||||
key: 'allowLegacyEnvelopes',
|
||||
label: 'Allow Legacy Envelopes',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -60,6 +60,18 @@ export const seedUser = async ({
|
||||
},
|
||||
include: {
|
||||
teams: true,
|
||||
organisationClaim: true,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.organisationClaim.update({
|
||||
where: {
|
||||
id: organisation.organisationClaim.id,
|
||||
},
|
||||
data: {
|
||||
flags: {
|
||||
allowLegacyEnvelopes: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
ORGANISATION_USER_ACCOUNT_TYPE,
|
||||
} from '@documenso/lib/constants/organisations';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { orphanEnvelopes } from '@documenso/lib/server-only/envelope/orphan-envelopes';
|
||||
import { buildOrganisationWhereQuery } from '@documenso/lib/utils/organisations';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
@@ -32,6 +33,19 @@ export const deleteOrganisationRoute = authenticatedProcedure
|
||||
userId: user.id,
|
||||
roles: ORGANISATION_MEMBER_ROLE_PERMISSIONS_MAP['DELETE_ORGANISATION'],
|
||||
}),
|
||||
select: {
|
||||
id: true,
|
||||
owner: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
teams: {
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!organisation) {
|
||||
@@ -40,6 +54,9 @@ export const deleteOrganisationRoute = authenticatedProcedure
|
||||
});
|
||||
}
|
||||
|
||||
// Orphan all envelopes to get rid of foreign key constraints.
|
||||
await Promise.all(organisation.teams.map(async (team) => orphanEnvelopes({ teamId: team.id })));
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.account.deleteMany({
|
||||
where: {
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { TeamMemberRole } from '@prisma/client';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { orphanEnvelopes } from '@documenso/lib/server-only/envelope/orphan-envelopes';
|
||||
import { transferTeamEnvelopes } from '@documenso/lib/server-only/envelope/transfer-team-envelopes';
|
||||
import { deleteTeam } from '@documenso/lib/server-only/team/delete-team';
|
||||
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
|
||||
|
||||
import { authenticatedProcedure } from '../trpc';
|
||||
import { ZDeleteTeamRequestSchema, ZDeleteTeamResponseSchema } from './delete-team.types';
|
||||
@@ -8,15 +14,40 @@ export const deleteTeamRoute = authenticatedProcedure
|
||||
.input(ZDeleteTeamRequestSchema)
|
||||
.output(ZDeleteTeamResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId } = input;
|
||||
const { teamId, transferTeamId } = input;
|
||||
const { user } = ctx;
|
||||
|
||||
const team = await getTeamById({ userId: user.id, teamId });
|
||||
|
||||
if (team.currentTeamRole !== TeamMemberRole.ADMIN) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You are not allowed to delete this team',
|
||||
});
|
||||
}
|
||||
|
||||
ctx.logger.info({
|
||||
input: {
|
||||
teamId,
|
||||
},
|
||||
});
|
||||
|
||||
const transferTeam = transferTeamId
|
||||
? await getTeamById({ userId: user.id, teamId: transferTeamId }).catch(() => {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: 'Invalid transfer team ID',
|
||||
});
|
||||
})
|
||||
: undefined;
|
||||
|
||||
if (transferTeam) {
|
||||
await transferTeamEnvelopes({
|
||||
sourceTeamId: teamId,
|
||||
targetTeamId: transferTeam.id,
|
||||
});
|
||||
} else {
|
||||
await orphanEnvelopes({ teamId });
|
||||
}
|
||||
|
||||
await deleteTeam({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { z } from 'zod';
|
||||
|
||||
export const ZDeleteTeamRequestSchema = z.object({
|
||||
teamId: z.number(),
|
||||
transferTeamId: z.number().optional(),
|
||||
});
|
||||
|
||||
export const ZDeleteTeamResponseSchema = z.void();
|
||||
|
||||
@@ -17,7 +17,7 @@ const EnvelopePdfViewer = lazy(async () => import('./pdf-viewer-konva'));
|
||||
|
||||
export const PDFViewerKonvaLazy = (props: PDFViewerProps) => {
|
||||
return (
|
||||
<Suspense fallback={<div>Loading client component...</div>}>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<EnvelopePdfViewer {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
@@ -95,7 +95,9 @@ export function DataTablePagination<TData>({
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Go to first page</span>
|
||||
<span className="sr-only">
|
||||
<Trans>Go to first page</Trans>
|
||||
</span>
|
||||
<ChevronsLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
@@ -104,7 +106,9 @@ export function DataTablePagination<TData>({
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Go to previous page</span>
|
||||
<span className="sr-only">
|
||||
<Trans>Go to previous page</Trans>
|
||||
</span>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
@@ -113,7 +117,9 @@ export function DataTablePagination<TData>({
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Go to next page</span>
|
||||
<span className="sr-only">
|
||||
<Trans>Go to next page</Trans>
|
||||
</span>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
@@ -122,7 +128,9 @@ export function DataTablePagination<TData>({
|
||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Go to last page</span>
|
||||
<span className="sr-only">
|
||||
<Trans>Go to last page</Trans>
|
||||
</span>
|
||||
<ChevronsRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { X } from 'lucide-react';
|
||||
|
||||
@@ -81,7 +82,9 @@ const DialogContent = React.forwardRef<
|
||||
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
<span className="sr-only">
|
||||
<Trans>Close</Trans>
|
||||
</span>
|
||||
</DialogPrimitive.Close>
|
||||
)}
|
||||
</DialogPrimitive.Content>
|
||||
|
||||
@@ -257,8 +257,10 @@ export const AddSubjectFormPartial = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Reply To Email</Trans>{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
<Trans>
|
||||
Reply To Email{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
</Trans>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
@@ -295,8 +297,9 @@ export const AddSubjectFormPartial = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Subject</Trans>{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
<Trans>
|
||||
Subject <span className="text-muted-foreground">(Optional)</span>
|
||||
</Trans>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
@@ -313,8 +316,9 @@ export const AddSubjectFormPartial = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="flex flex-row items-center">
|
||||
<Trans>Message</Trans>{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
<Trans>
|
||||
Message <span className="text-muted-foreground">(Optional)</span>
|
||||
</Trans>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
|
||||
@@ -67,7 +67,7 @@ export const DocumentFlowFormContainerContent = ({
|
||||
}: DocumentFlowFormContainerContentProps) => {
|
||||
return (
|
||||
<div
|
||||
className={cn('custom-scrollbar -mx-2 flex flex-1 flex-col overflow-hidden px-2', className)}
|
||||
className={cn('custom-scrollbar -mx-2 flex flex-1 flex-col px-2', className)}
|
||||
{...props}
|
||||
>
|
||||
<div className="flex flex-1 flex-col">{children}</div>
|
||||
@@ -112,7 +112,7 @@ export const DocumentFlowFormContainerStep = ({
|
||||
<motion.div
|
||||
layout="size"
|
||||
layoutId="document-flow-container-step"
|
||||
className="bg-documenso absolute inset-y-0 left-0"
|
||||
className="bg-primary absolute inset-y-0 left-0"
|
||||
style={{
|
||||
width: `${(100 / maxStep) * step}%`,
|
||||
}}
|
||||
@@ -161,7 +161,7 @@ export const DocumentFlowFormContainerActions = ({
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
className="bg-documenso flex-1"
|
||||
className="bg-primary flex-1"
|
||||
size="lg"
|
||||
disabled={disabled || disableNextStep || loading || !canGoNext}
|
||||
loading={loading}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { DocumentMeta, Signature } from '@prisma/client';
|
||||
import { FieldType } from '@prisma/client';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
@@ -62,7 +63,7 @@ export const FieldContent = ({ field, documentMeta }: FieldIconProps) => {
|
||||
<div className="flex items-center">
|
||||
<Checkbox className="h-3 w-3" disabled />
|
||||
<Label className="text-foreground ml-1.5 text-xs font-normal opacity-50">
|
||||
Checkbox option
|
||||
<Trans>Checkbox option</Trans>
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -140,7 +141,9 @@ export const FieldContent = ({ field, documentMeta }: FieldIconProps) => {
|
||||
) {
|
||||
return (
|
||||
<div className="text-field-card-foreground flex flex-row items-center py-0.5 text-[clamp(0.07rem,25cqw,0.825rem)] text-sm">
|
||||
<p>Select</p>
|
||||
<p>
|
||||
<Trans>Select</Trans>
|
||||
</p>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import * as SheetPrimitive from '@radix-ui/react-dialog';
|
||||
import type { VariantProps } from 'class-variance-authority';
|
||||
import { cva } from 'class-variance-authority';
|
||||
@@ -160,7 +161,9 @@ const SheetContent = React.forwardRef<
|
||||
{children}
|
||||
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
<span className="sr-only">
|
||||
<Trans>Close</Trans>
|
||||
</span>
|
||||
</SheetPrimitive.Close>
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
|
||||
@@ -15,6 +15,7 @@ import { SignatureRender } from './signature-render';
|
||||
|
||||
export type SignaturePadDialogProps = Omit<HTMLAttributes<HTMLCanvasElement>, 'onChange'> & {
|
||||
disabled?: boolean;
|
||||
fullName?: string;
|
||||
value?: string;
|
||||
onChange: (_value: string) => void;
|
||||
dialogConfirmText?: MessageDescriptor | string;
|
||||
@@ -26,6 +27,7 @@ export type SignaturePadDialogProps = Omit<HTMLAttributes<HTMLCanvasElement>, 'o
|
||||
|
||||
export const SignaturePadDialog = ({
|
||||
className,
|
||||
fullName,
|
||||
value,
|
||||
onChange,
|
||||
disabled = false,
|
||||
@@ -43,7 +45,7 @@ export const SignaturePadDialog = ({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'aspect-signature-pad bg-background relative block w-full select-none rounded-lg border',
|
||||
'relative block aspect-signature-pad w-full select-none rounded-lg border bg-background',
|
||||
className,
|
||||
{
|
||||
'pointer-events-none opacity-50': disabled,
|
||||
@@ -112,6 +114,7 @@ export const SignaturePadDialog = ({
|
||||
<DialogContent hideClose={true} className="p-6 pt-4">
|
||||
<SignaturePad
|
||||
id="signature"
|
||||
fullName={fullName}
|
||||
value={value}
|
||||
className={className}
|
||||
disabled={disabled}
|
||||
|
||||
@@ -318,7 +318,9 @@ export const SignaturePadDraw = ({
|
||||
onClick={onUndoClick}
|
||||
>
|
||||
<Undo2 className="h-4 w-4" />
|
||||
<span className="sr-only">Undo</span>
|
||||
<span className="sr-only">
|
||||
<Trans>Undo</Trans>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
|
||||
@@ -7,13 +7,27 @@ import { cn } from '../../lib/utils';
|
||||
export type SignaturePadTypeProps = {
|
||||
className?: string;
|
||||
value?: string;
|
||||
defaultValue?: string;
|
||||
onChange: (_value: string) => void;
|
||||
};
|
||||
|
||||
export const SignaturePadType = ({ className, value, onChange }: SignaturePadTypeProps) => {
|
||||
export const SignaturePadType = ({
|
||||
className,
|
||||
value,
|
||||
defaultValue,
|
||||
onChange,
|
||||
}: SignaturePadTypeProps) => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const $isDirty = useRef(false);
|
||||
// Colors don't actually work for text.
|
||||
const [selectedColor, setSelectedColor] = useState('black');
|
||||
|
||||
useEffect(() => {
|
||||
if (!$isDirty.current && !value && defaultValue) {
|
||||
$isDirty.current = true;
|
||||
onChange(defaultValue);
|
||||
}
|
||||
}, [defaultValue, value, onChange]);
|
||||
|
||||
return (
|
||||
<div className={cn('flex h-full w-full items-center justify-center', className)}>
|
||||
@@ -23,7 +37,10 @@ export const SignaturePadType = ({ className, value, onChange }: SignaturePadTyp
|
||||
className="w-full bg-transparent px-4 text-center font-signature text-7xl text-black placeholder:text-4xl focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 dark:text-white"
|
||||
// style={{ color: selectedColor }}
|
||||
value={value}
|
||||
onChange={(event) => onChange(event.target.value.trimStart())}
|
||||
onChange={(event) => {
|
||||
onChange(event.target.value.trimStart());
|
||||
$isDirty.current = true;
|
||||
}}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
|
||||
@@ -21,6 +21,7 @@ export type SignaturePadValue = {
|
||||
};
|
||||
|
||||
export type SignaturePadProps = Omit<HTMLAttributes<HTMLCanvasElement>, 'onChange'> & {
|
||||
fullName?: string;
|
||||
value?: string;
|
||||
onChange?: (_value: SignaturePadValue) => void;
|
||||
|
||||
@@ -34,6 +35,7 @@ export type SignaturePadProps = Omit<HTMLAttributes<HTMLCanvasElement>, 'onChang
|
||||
};
|
||||
|
||||
export const SignaturePad = ({
|
||||
fullName,
|
||||
value = '',
|
||||
onChange,
|
||||
disabled = false,
|
||||
@@ -168,7 +170,7 @@ export const SignaturePad = ({
|
||||
|
||||
<TabsContent
|
||||
value="draw"
|
||||
className="border-border aspect-signature-pad dark:bg-background relative flex items-center justify-center rounded-md border bg-neutral-50 text-center"
|
||||
className="relative flex aspect-signature-pad items-center justify-center rounded-md border border-border bg-neutral-50 text-center dark:bg-background"
|
||||
>
|
||||
<SignaturePadDraw
|
||||
className="h-full w-full"
|
||||
@@ -179,15 +181,19 @@ export const SignaturePad = ({
|
||||
|
||||
<TabsContent
|
||||
value="text"
|
||||
className="border-border aspect-signature-pad dark:bg-background relative flex items-center justify-center rounded-md border bg-neutral-50 text-center"
|
||||
className="relative flex aspect-signature-pad items-center justify-center rounded-md border border-border bg-neutral-50 text-center dark:bg-background"
|
||||
>
|
||||
<SignaturePadType value={typedSignature} onChange={onTypedSignatureChange} />
|
||||
<SignaturePadType
|
||||
value={typedSignature}
|
||||
defaultValue={fullName}
|
||||
onChange={onTypedSignatureChange}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent
|
||||
value="image"
|
||||
className={cn(
|
||||
'border-border aspect-signature-pad dark:bg-background relative rounded-md border bg-neutral-50',
|
||||
'relative aspect-signature-pad rounded-md border border-border bg-neutral-50 dark:bg-background',
|
||||
{
|
||||
'bg-white': imageSignature,
|
||||
},
|
||||
|
||||
@@ -518,8 +518,10 @@ export const AddTemplateSettingsFormPartial = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Reply To Email</Trans>{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
<Trans>
|
||||
Reply To Email{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
</Trans>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
@@ -557,8 +559,9 @@ export const AddTemplateSettingsFormPartial = ({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel className="flex flex-row items-center">
|
||||
<Trans>Message</Trans>{' '}
|
||||
<span className="text-muted-foreground">(Optional)</span>
|
||||
<Trans>
|
||||
Message <span className="text-muted-foreground">(Optional)</span>
|
||||
</Trans>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
|
||||
Reference in New Issue
Block a user