fix: envelope styling (#2102)

This commit is contained in:
David Nguyen
2025-10-27 16:11:10 +11:00
committed by GitHub
parent 47bdcd833f
commit 5cdd7f8623
42 changed files with 1037 additions and 586 deletions

View File

@ -15,18 +15,16 @@ import {
import { AnimatePresence, motion } from 'framer-motion';
import { InfoIcon } from 'lucide-react';
import { useForm } from 'react-hook-form';
import { useNavigate } from 'react-router';
import { match } from 'ts-pattern';
import * as z from 'zod';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
import type { TEnvelope } from '@documenso/lib/types/envelope';
import { formatSigningLink } from '@documenso/lib/utils/recipients';
import { trpc, trpc as trpcReact } from '@documenso/trpc/react';
import { CopyTextButton } from '@documenso/ui/components/common/copy-text-button';
import { DocumentSendEmailMessageHelper } from '@documenso/ui/components/document/document-send-email-message-helper';
import { cn } from '@documenso/ui/lib/utils';
import { Alert, AlertDescription } from '@documenso/ui/primitives/alert';
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
import { Button } from '@documenso/ui/primitives/button';
import {
Dialog,
@ -65,6 +63,7 @@ export type EnvelopeDistributeDialogProps = {
fields: Pick<Field, 'type' | 'recipientId'>[];
};
onDistribute?: () => Promise<void>;
documentRootPath: string;
trigger?: React.ReactNode;
};
@ -89,6 +88,7 @@ export type TEnvelopeDistributeFormSchema = z.infer<typeof ZEnvelopeDistributeFo
export const EnvelopeDistributeDialog = ({
envelope,
trigger,
documentRootPath,
onDistribute,
}: EnvelopeDistributeDialogProps) => {
const organisation = useCurrentOrganisation();
@ -97,6 +97,7 @@ export const EnvelopeDistributeDialog = ({
const { toast } = useToast();
const { t } = useLingui();
const navigate = useNavigate();
const [isOpen, setIsOpen] = useState(false);
@ -163,6 +164,14 @@ export const EnvelopeDistributeDialog = ({
await onDistribute?.();
let redirectPath = `${documentRootPath}/${envelope.id}`;
if (meta.distributionMethod === DocumentDistributionMethod.NONE) {
redirectPath += '?action=copy-links';
}
await navigate(redirectPath);
toast({
title: t`Envelope distributed`,
description: t`Your envelope has been distributed successfully.`,
@ -198,6 +207,7 @@ export const EnvelopeDistributeDialog = ({
<Trans>Recipients will be able to sign the document once sent</Trans>
</DialogDescription>
</DialogHeader>
{!invalidEnvelopeCode ? (
<Form {...form}>
<form onSubmit={handleSubmit(onFormSubmit)}>
@ -220,7 +230,11 @@ export const EnvelopeDistributeDialog = ({
</TabsList>
</Tabs>
<div className="min-h-72">
<div
className={cn('min-h-72', {
'min-h-[23rem]': organisation.organisationClaim.flags.emailDomains,
})}
>
<AnimatePresence initial={false} mode="wait">
{distributionMethod === DocumentDistributionMethod.EMAIL && (
<motion.div
@ -355,73 +369,18 @@ export const EnvelopeDistributeDialog = ({
exit={{ opacity: 0, transition: { duration: 0.15 } }}
className="min-h-60 rounded-lg border"
>
{envelope.status === DocumentStatus.DRAFT ? (
<div className="text-muted-foreground py-24 text-center text-sm">
<p>
<Trans>We won't send anything to notify recipients.</Trans>
</p>
<div className="text-muted-foreground py-24 text-center text-sm">
<p>
<Trans>We won't send anything to notify recipients.</Trans>
</p>
<p className="mt-2">
<Trans>
We will generate signing links for you, which you can send to the
recipients through your method of choice.
</Trans>
</p>
</div>
) : (
<ul className="text-muted-foreground divide-y">
{/* Todo: Envelopes - I don't think this section shows up */}
{recipients.length === 0 && (
<li className="flex flex-col items-center justify-center py-6 text-sm">
<Trans>No recipients</Trans>
</li>
)}
{recipients.map((recipient) => (
<li
key={recipient.id}
className="flex items-center justify-between px-4 py-3 text-sm"
>
<AvatarWithText
avatarFallback={recipient.email.slice(0, 1).toUpperCase()}
primaryText={
<p className="text-muted-foreground text-sm">
{recipient.email}
</p>
}
secondaryText={
<p className="text-muted-foreground/70 text-xs">
{t(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
</p>
}
/>
{recipient.role !== RecipientRole.CC && (
<CopyTextButton
value={formatSigningLink(recipient.token)}
onCopySuccess={() => {
toast({
title: t`Copied to clipboard`,
description: t`The signing link has been copied to your clipboard.`,
});
}}
badgeContentUncopied={
<p className="ml-1 text-xs">
<Trans>Copy</Trans>
</p>
}
badgeContentCopied={
<p className="ml-1 text-xs">
<Trans>Copied</Trans>
</p>
}
/>
)}
</li>
))}
</ul>
)}
<p className="mt-2">
<Trans>
We will generate signing links for you, which you can send to the
recipients through your method of choice.
</Trans>
</p>
</div>
</motion.div>
)}
</AnimatePresence>

View File

@ -213,8 +213,6 @@ export const EnvelopeDownloadDialog = ({
</div>
))
)}
{/* Todo: Envelopes - Download all button */}
</div>
</DialogContent>
</Dialog>

View File

@ -8,11 +8,13 @@ import { Popover, PopoverContent, PopoverTrigger } from '@documenso/ui/primitive
export type DocumentSigningAttachmentsPopoverProps = {
envelopeId: string;
token: string;
trigger?: React.ReactNode;
};
export const DocumentSigningAttachmentsPopover = ({
envelopeId,
token,
trigger,
}: DocumentSigningAttachmentsPopoverProps) => {
const { data: attachments } = trpc.envelope.attachment.find.useQuery({
envelopeId,
@ -26,15 +28,17 @@ export const DocumentSigningAttachmentsPopover = ({
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="gap-2">
<PaperclipIcon className="h-4 w-4" />
<span>
<Trans>Attachments</Trans>{' '}
{attachments && attachments.data.length > 0 && (
<span className="ml-1">({attachments.data.length})</span>
)}
</span>
</Button>
{trigger ?? (
<Button variant="outline" className="gap-2">
<PaperclipIcon className="h-4 w-4" />
<span>
<Trans>Attachments</Trans>{' '}
{attachments && attachments.data.length > 0 && (
<span className="ml-1">({attachments.data.length})</span>
)}
</span>
</Button>
)}
</PopoverTrigger>
<PopoverContent className="w-96" align="start">

View File

@ -3,7 +3,7 @@ import { lazy, useMemo } from 'react';
import { Plural, Trans } from '@lingui/react/macro';
import { EnvelopeType, RecipientRole } from '@prisma/client';
import { motion } from 'framer-motion';
import { ArrowLeftIcon, BanIcon, DownloadCloudIcon } from 'lucide-react';
import { ArrowLeftIcon, BanIcon, DownloadCloudIcon, PaperclipIcon } from 'lucide-react';
import { Link } from 'react-router';
import { match } from 'ts-pattern';
@ -75,7 +75,7 @@ export const DocumentSigningPageViewV2 = () => {
<EnvelopeSignerHeader />
{/* Main Content Area */}
<div className="flex h-[calc(100vh-73px)] w-screen">
<div className="flex h-[calc(100vh-4rem)] w-screen">
{/* Left Section - Step Navigation */}
<div className="bg-background border-border hidden w-80 flex-shrink-0 flex-col overflow-y-auto border-r py-4 lg:flex">
<div className="px-4">
@ -121,12 +121,16 @@ export const DocumentSigningPageViewV2 = () => {
<Trans>Actions</Trans>
</h4>
<div className="w-full">
<DocumentSigningAttachmentsPopover
envelopeId={envelope.id}
token={recipient.token}
/>
</div>
<DocumentSigningAttachmentsPopover
envelopeId={envelope.id}
token={recipient.token}
trigger={
<Button variant="ghost" size="sm" className="w-full justify-start">
<PaperclipIcon className="mr-2 h-4 w-4" />
<Trans>Attachments</Trans>
</Button>
}
/>
<EnvelopeDownloadDialog
envelopeId={envelope.id}

View File

@ -8,6 +8,7 @@ import {
RecipientRole,
SigningStatus,
} from '@prisma/client';
import { prop, sortBy } from 'remeda';
import { isBase64Image } from '@documenso/lib/constants/signatures';
import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
@ -165,7 +166,29 @@ export const EnvelopeSigningProvider = ({
* The fields that are still required to be signed by the actual recipient.
*/
const recipientFieldsRemaining = useMemo(() => {
return envelopeData.recipient.fields.filter((field) => isFieldUnsignedAndRequired(field));
const requiredFields = envelopeData.recipient.fields
.filter((field) => isFieldUnsignedAndRequired(field))
.map((field) => {
const envelopeItem = envelope.envelopeItems.find(
(item) => item.id === field.envelopeItemId,
);
if (!envelopeItem) {
throw new Error('Missing envelope item');
}
return {
...field,
envelopeItemOrder: envelopeItem.order,
};
});
return sortBy(
requiredFields,
[prop('envelopeItemOrder'), 'asc'],
[prop('page'), 'asc'],
[prop('positionY'), 'asc'],
);
}, [envelopeData.recipient.fields]);
/**

View File

@ -4,7 +4,10 @@ import { Trans } from '@lingui/react/macro';
import type { DocumentData, EnvelopeItem } from '@prisma/client';
import { DateTime } from 'luxon';
import { EnvelopeRenderProvider } from '@documenso/lib/client-only/providers/envelope-render-provider';
import {
EnvelopeRenderProvider,
useCurrentEnvelopeRender,
} from '@documenso/lib/client-only/providers/envelope-render-provider';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react';
import PDFViewerKonvaLazy from '@documenso/ui/components/pdf-viewer/pdf-viewer-konva-lazy';
@ -92,6 +95,60 @@ export const DocumentCertificateQRView = ({
</Dialog>
)}
{internalVersion === 2 ? (
<EnvelopeRenderProvider envelope={{ envelopeItems }}>
<DocumentCertificateQrV2
title={title}
recipientCount={recipientCount}
formattedDate={formattedDate}
/>
</EnvelopeRenderProvider>
) : (
<>
<div className="flex w-full flex-col justify-between gap-4 md:flex-row md:items-end">
<div className="space-y-1">
<h1 className="text-xl font-medium">{title}</h1>
<div className="text-muted-foreground flex flex-col gap-0.5 text-sm">
<p>
<Trans>{recipientCount} recipients</Trans>
</p>
<p>
<Trans>Completed on {formattedDate}</Trans>
</p>
</div>
</div>
<ShareDocumentDownloadButton
title={title}
documentData={envelopeItems[0].documentData}
/>
</div>
<div className="mt-12 w-full">
<PDFViewer key={envelopeItems[0].id} documentData={envelopeItems[0].documentData} />
</div>
</>
)}
</div>
);
};
type DocumentCertificateQrV2Props = {
title: string;
recipientCount: number;
formattedDate: string;
};
const DocumentCertificateQrV2 = ({
title,
recipientCount,
formattedDate,
}: DocumentCertificateQrV2Props) => {
const { currentEnvelopeItem } = useCurrentEnvelopeRender();
return (
<div className="flex min-h-screen flex-col items-start">
<div className="flex w-full flex-col justify-between gap-4 md:flex-row md:items-end">
<div className="space-y-1">
<h1 className="text-xl font-medium">{title}</h1>
@ -106,21 +163,18 @@ export const DocumentCertificateQRView = ({
</div>
</div>
<ShareDocumentDownloadButton title={title} documentData={envelopeItems[0].documentData} />
{currentEnvelopeItem && (
<ShareDocumentDownloadButton
title={title}
documentData={currentEnvelopeItem.documentData}
/>
)}
</div>
<div className="mt-12 w-full">
{internalVersion === 2 ? (
<EnvelopeRenderProvider envelope={{ envelopeItems }}>
<EnvelopeRendererFileSelector className="mb-4 p-0" fields={[]} secondaryOverride={''} />
<EnvelopeRendererFileSelector className="mb-4 p-0" fields={[]} secondaryOverride={''} />
<PDFViewerKonvaLazy customPageRenderer={EnvelopeGenericPageRenderer} />
</EnvelopeRenderProvider>
) : (
<>
<PDFViewer key={envelopeItems[0].id} documentData={envelopeItems[0].documentData} />
</>
)}
<PDFViewerKonvaLazy customPageRenderer={EnvelopeGenericPageRenderer} />
</div>
</div>
);

View File

@ -1,7 +1,10 @@
import { useEffect, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { DocumentStatus, RecipientRole, SigningStatus } from '@prisma/client';
import { TooltipArrow } from '@radix-ui/react-tooltip';
import {
AlertTriangle,
CheckIcon,
@ -12,7 +15,7 @@ import {
PlusIcon,
UserIcon,
} from 'lucide-react';
import { Link } from 'react-router';
import { Link, useSearchParams } from 'react-router';
import { match } from 'ts-pattern';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
@ -24,6 +27,12 @@ import { SignatureIcon } from '@documenso/ui/icons/signature';
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
import { Badge } from '@documenso/ui/primitives/badge';
import { PopoverHover } from '@documenso/ui/primitives/popover';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '@documenso/ui/primitives/tooltip';
import { useToast } from '@documenso/ui/primitives/use-toast';
export type DocumentPageViewRecipientsProps = {
@ -37,8 +46,24 @@ export const DocumentPageViewRecipients = ({
}: DocumentPageViewRecipientsProps) => {
const { _ } = useLingui();
const { toast } = useToast();
const [searchParams, setSearchParams] = useSearchParams();
const recipients = envelope.recipients;
const [shouldHighlightCopyButtons, setShouldHighlightCopyButtons] = useState(false);
// Check for action=view-tokens query parameter and set highlighting state
useEffect(() => {
const hasViewTokensAction = searchParams.get('action') === 'copy-links';
if (hasViewTokensAction) {
setShouldHighlightCopyButtons(true);
// Remove the query parameter immediately
const params = new URLSearchParams(searchParams);
params.delete('action');
setSearchParams(params);
}
}, [searchParams, setSearchParams]);
return (
<section className="dark:bg-background border-border bg-widget flex flex-col rounded-xl border">
@ -69,7 +94,7 @@ export const DocumentPageViewRecipients = ({
</li>
)}
{recipients.map((recipient) => (
{recipients.map((recipient, i) => (
<li key={recipient.id} className="flex items-center justify-between px-4 py-2.5 text-sm">
<AvatarWithText
avatarFallback={recipient.email.slice(0, 1).toUpperCase()}
@ -159,15 +184,33 @@ export const DocumentPageViewRecipients = ({
{envelope.status === DocumentStatus.PENDING &&
recipient.signingStatus === SigningStatus.NOT_SIGNED &&
recipient.role !== RecipientRole.CC && (
<CopyTextButton
value={formatSigningLink(recipient.token)}
onCopySuccess={() => {
toast({
title: _(msg`Copied to clipboard`),
description: _(msg`The signing link has been copied to your clipboard.`),
});
}}
/>
<TooltipProvider>
<Tooltip open={shouldHighlightCopyButtons && i === 0}>
<TooltipTrigger asChild>
<div
className={shouldHighlightCopyButtons ? 'animate-pulse' : ''}
onClick={() => setShouldHighlightCopyButtons(false)}
>
<CopyTextButton
value={formatSigningLink(recipient.token)}
onCopySuccess={() => {
toast({
title: _(msg`Copied to clipboard`),
description: _(
msg`The signing link has been copied to your clipboard.`,
),
});
setShouldHighlightCopyButtons(false);
}}
/>
</div>
</TooltipTrigger>
<TooltipContent sideOffset={2}>
<Trans>Copy Signing Links</Trans>
<TooltipArrow className="fill-background" />
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
</li>

View File

@ -5,6 +5,7 @@ import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { FieldType, RecipientRole } from '@prisma/client';
import { FileTextIcon } from 'lucide-react';
import { Link } from 'react-router';
import { isDeepEqual } from 'remeda';
import { match } from 'ts-pattern';
@ -61,7 +62,7 @@ const FieldSettingsTypeTranslations: Record<FieldType, MessageDescriptor> = {
};
export const EnvelopeEditorFieldsPage = () => {
const { envelope, editorFields } = useCurrentEnvelopeEditor();
const { envelope, editorFields, relativePath } = useCurrentEnvelopeEditor();
const { currentEnvelopeItem } = useCurrentEnvelopeRender();
@ -104,12 +105,12 @@ export const EnvelopeEditorFieldsPage = () => {
return (
<div className="relative flex h-full">
<div className="flex w-full flex-col">
<div className="flex w-full flex-col overflow-y-auto">
{/* Horizontal envelope item selector */}
<EnvelopeRendererFileSelector fields={editorFields.localFields} />
{/* Document View */}
<div className="mt-4 flex justify-center p-4">
<div className="mt-4 flex h-full justify-center p-4">
{currentEnvelopeItem !== null ? (
<PDFViewerKonvaLazy customPageRenderer={EnvelopeEditorFieldsPageRenderer} />
) : (
@ -128,7 +129,7 @@ export const EnvelopeEditorFieldsPage = () => {
{/* Right Section - Form Fields Panel */}
{currentEnvelopeItem && (
<div className="bg-background border-border sticky top-0 h-[calc(100vh-73px)] w-80 flex-shrink-0 overflow-y-auto border-l py-4">
<div className="bg-background border-border sticky top-0 h-full w-80 flex-shrink-0 overflow-y-auto border-l py-4">
{/* Recipient selector section. */}
<section className="px-4">
<h3 className="text-foreground mb-2 text-sm font-semibold">
@ -137,8 +138,14 @@ export const EnvelopeEditorFieldsPage = () => {
{envelope.recipients.length === 0 ? (
<Alert variant="warning">
<AlertDescription>
<AlertDescription className="flex flex-col gap-2">
<Trans>You need at least one recipient to add fields</Trans>
<Link to={`${relativePath.editorPath}`} className="text-sm">
<p>
<Trans>Click here to add a recipient</Trans>
</p>
</Link>
</AlertDescription>
</Alert>
) : (

View File

@ -37,7 +37,6 @@ export default function EnvelopeEditorHeader() {
updateEnvelope,
autosaveError,
relativePath,
syncEnvelope,
editorFields,
} = useCurrentEnvelopeEditor();
@ -152,7 +151,7 @@ export default function EnvelopeEditorHeader() {
...envelope,
fields: editorFields.localFields,
}}
onDistribute={syncEnvelope}
documentRootPath={relativePath.documentRootPath}
trigger={
<Button size="sm">
<SendIcon className="mr-2 h-4 w-4" />

View File

@ -33,7 +33,7 @@ export const EnvelopeEditorPreviewPage = () => {
return (
<div className="relative flex h-full">
<div className="flex w-full flex-col">
<div className="flex w-full flex-col overflow-y-auto">
{/* Horizontal envelope item selector */}
<EnvelopeRendererFileSelector fields={editorFields.localFields} />
@ -82,7 +82,7 @@ export const EnvelopeEditorPreviewPage = () => {
{/* Right Section - Form Fields Panel */}
{currentEnvelopeItem && false && (
<div className="sticky top-0 h-[calc(100vh-73px)] w-80 flex-shrink-0 overflow-y-auto border-l border-gray-200 bg-white py-4">
<div className="sticky top-0 h-full w-80 flex-shrink-0 overflow-y-auto border-l border-gray-200 bg-white py-4">
{/* Add fields section. */}
<section className="px-4">
{/* <h3 className="mb-2 text-sm font-semibold text-gray-900">

View File

@ -14,7 +14,7 @@ import { DocumentSigningOrder, EnvelopeType, RecipientRole, SendStatus } from '@
import { motion } from 'framer-motion';
import { GripVerticalIcon, HelpCircleIcon, PlusIcon, TrashIcon } from 'lucide-react';
import { useFieldArray, useForm, useWatch } from 'react-hook-form';
import { prop, sortBy } from 'remeda';
import { isDeepEqual, prop, sortBy } from 'remeda';
import { z } from 'zod';
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
@ -148,8 +148,7 @@ export const EnvelopeEditorRecipientForm = () => {
},
});
// Always show advanced settings if any recipient has auth options.
const alwaysShowAdvancedSettings = useMemo(() => {
const recipientHasAuthSettings = useMemo(() => {
const recipientHasAuthOptions = recipients.find((recipient) => {
const recipientAuthOptions = ZRecipientAuthOptionsSchema.parse(recipient.authOptions);
@ -165,7 +164,7 @@ export const EnvelopeEditorRecipientForm = () => {
return recipientHasAuthOptions !== undefined || formHasActionAuth !== undefined;
}, [recipients, form]);
const [showAdvancedSettings, setShowAdvancedSettings] = useState(alwaysShowAdvancedSettings);
const [showAdvancedSettings, setShowAdvancedSettings] = useState(recipientHasAuthSettings);
const [showSigningOrderConfirmation, setShowSigningOrderConfirmation] = useState(false);
const {
@ -464,7 +463,7 @@ export const EnvelopeEditorRecipientForm = () => {
const formValueSigners = formValues.signers || [];
// Remove the last signer if it's empty.
const recipients = formValueSigners.filter((signer, i) => {
const nonEmptyRecipients = formValueSigners.filter((signer, i) => {
if (i === formValueSigners.length - 1 && signer.email === '') {
return false;
}
@ -474,26 +473,48 @@ export const EnvelopeEditorRecipientForm = () => {
const validatedFormValues = ZEnvelopeRecipientsForm.safeParse({
...formValues,
signers: recipients,
signers: nonEmptyRecipients,
});
if (validatedFormValues.success) {
console.log('validatedFormValues', validatedFormValues);
if (!validatedFormValues.success) {
return;
}
const { data } = validatedFormValues;
const hasSigningOrderChanged = envelope.documentMeta.signingOrder !== data.signingOrder;
const hasAllowDictateNextSignerChanged =
envelope.documentMeta.allowDictateNextSigner !== data.allowDictateNextSigner;
const hasSignersChanged =
data.signers.length !== recipients.length ||
data.signers.some((signer) => {
const recipient = recipients.find((recipient) => recipient.id === signer.id);
if (!recipient) {
return true;
}
return (
signer.email !== recipient.email ||
signer.name !== recipient.name ||
signer.role !== recipient.role ||
signer.signingOrder !== recipient.signingOrder ||
!isDeepEqual(signer.actionAuth, recipient.authOptions?.actionAuth)
);
});
if (hasSignersChanged) {
setRecipientsDebounced(validatedFormValues.data.signers);
}
if (
validatedFormValues.data.signingOrder !== envelope.documentMeta.signingOrder ||
validatedFormValues.data.allowDictateNextSigner !==
envelope.documentMeta.allowDictateNextSigner
) {
updateEnvelope({
meta: {
signingOrder: validatedFormValues.data.signingOrder,
allowDictateNextSigner: validatedFormValues.data.allowDictateNextSigner,
},
});
}
if (hasSigningOrderChanged || hasAllowDictateNextSignerChanged) {
updateEnvelope({
meta: {
signingOrder: validatedFormValues.data.signingOrder,
allowDictateNextSigner: validatedFormValues.data.allowDictateNextSigner,
},
});
}
}, [formValues]);
@ -534,17 +555,16 @@ export const EnvelopeEditorRecipientForm = () => {
<AnimateGenericFadeInOut motionKey={showAdvancedSettings ? 'Show' : 'Hide'}>
<Form {...form}>
<div className="bg-accent/50 -mt-2 mb-2 space-y-4 rounded-md p-4">
{!alwaysShowAdvancedSettings && organisation.organisationClaim.flags.cfr21 && (
{organisation.organisationClaim.flags.cfr21 && (
<div className="flex flex-row items-center">
<Checkbox
id="showAdvancedRecipientSettings"
className="h-5 w-5"
checked={showAdvancedSettings}
onCheckedChange={(value) => setShowAdvancedSettings(Boolean(value))}
/>
<label
className="text-muted-foreground ml-2 text-sm"
className="ml-2 text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
htmlFor="showAdvancedRecipientSettings"
>
<Trans>Show advanced settings</Trans>
@ -703,171 +723,48 @@ export const EnvelopeEditorRecipientForm = () => {
<motion.fieldset
data-native-id={signer.id}
disabled={isSubmitting || !canRecipientBeModified(signer.id)}
className={cn('grid grid-cols-10 items-end gap-2 pb-2', {
'border-b pt-2': showAdvancedSettings,
'grid-cols-12 pr-3': isSigningOrderSequential,
className={cn('pb-2', {
'border-b pb-4':
showAdvancedSettings && index !== signers.length - 1,
'pt-2': showAdvancedSettings && index === 0,
'pr-3': isSigningOrderSequential,
})}
>
{isSigningOrderSequential && (
<FormField
control={form.control}
name={`signers.${index}.signingOrder`}
render={({ field }) => (
<FormItem
className={cn(
'col-span-1 mt-auto flex items-center gap-x-1 space-y-0',
{
'mb-6':
form.formState.errors.signers?.[index] &&
!form.formState.errors.signers[index]?.signingOrder,
},
)}
>
<GripVerticalIcon className="h-5 w-5 flex-shrink-0 opacity-40" />
<FormControl>
<Input
type="number"
max={signers.length}
data-testid="signing-order-input"
className={cn(
'w-full text-center',
'[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',
)}
{...field}
onChange={(e) => {
field.onChange(e);
handleSigningOrderChange(index, e.target.value);
}}
onBlur={(e) => {
field.onBlur();
handleSigningOrderChange(index, e.target.value);
}}
disabled={
snapshot.isDragging ||
isSubmitting ||
!canRecipientBeModified(signer.id)
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
<FormField
control={form.control}
name={`signers.${index}.email`}
render={({ field }) => (
<FormItem
className={cn('relative', {
'mb-6':
form.formState.errors.signers?.[index] &&
!form.formState.errors.signers[index]?.email,
'col-span-4': !showAdvancedSettings,
'col-span-5': showAdvancedSettings,
})}
>
{!showAdvancedSettings && index === 0 && (
<FormLabel required>
<Trans>Email</Trans>
</FormLabel>
)}
<FormControl>
<RecipientAutoCompleteInput
type="email"
placeholder={t`Email`}
value={field.value}
disabled={
snapshot.isDragging ||
isSubmitting ||
!canRecipientBeModified(signer.id)
}
options={recipientSuggestions}
onSelect={(suggestion) =>
handleRecipientAutoCompleteSelect(index, suggestion)
}
onSearchQueryChange={(query) => {
field.onChange(query);
setRecipientSearchQuery(query);
}}
loading={isLoading}
data-testid="signer-email-input"
maxLength={254}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name={`signers.${index}.name`}
render={({ field }) => (
<FormItem
className={cn({
'mb-6':
form.formState.errors.signers?.[index] &&
!form.formState.errors.signers[index]?.name,
'col-span-4': !showAdvancedSettings,
'col-span-5': showAdvancedSettings,
})}
>
{!showAdvancedSettings && index === 0 && (
<FormLabel>
<Trans>Name</Trans>
</FormLabel>
)}
<FormControl>
<RecipientAutoCompleteInput
type="text"
placeholder={t`Name`}
{...field}
disabled={
snapshot.isDragging ||
isSubmitting ||
!canRecipientBeModified(signer.id)
}
options={recipientSuggestions}
onSelect={(suggestion) =>
handleRecipientAutoCompleteSelect(index, suggestion)
}
onSearchQueryChange={(query) => {
field.onChange(query);
setRecipientSearchQuery(query);
}}
loading={isLoading}
maxLength={255}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{showAdvancedSettings &&
organisation.organisationClaim.flags.cfr21 && (
<div className="flex flex-row items-center gap-x-2">
{isSigningOrderSequential && (
<FormField
control={form.control}
name={`signers.${index}.actionAuth`}
name={`signers.${index}.signingOrder`}
render={({ field }) => (
<FormItem
className={cn('col-span-8', {
'mb-6':
form.formState.errors.signers?.[index] &&
!form.formState.errors.signers[index]?.actionAuth,
'col-span-10': isSigningOrderSequential,
})}
className={cn(
'mt-auto flex items-center gap-x-1 space-y-0',
{
'mb-6':
form.formState.errors.signers?.[index] &&
!form.formState.errors.signers[index]?.signingOrder,
},
)}
>
<GripVerticalIcon className="h-5 w-5 flex-shrink-0 opacity-40" />
<FormControl>
<RecipientActionAuthSelect
<Input
type="number"
max={signers.length}
data-testid="signing-order-input"
className={cn(
'w-10 text-center',
'[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',
)}
{...field}
onValueChange={field.onChange}
onChange={(e) => {
field.onChange(e);
handleSigningOrderChange(index, e.target.value);
}}
onBlur={(e) => {
field.onBlur();
handleSigningOrderChange(index, e.target.value);
}}
disabled={
snapshot.isDragging ||
isSubmitting ||
@ -875,20 +772,109 @@ export const EnvelopeEditorRecipientForm = () => {
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
<div className="col-span-2 flex gap-x-2">
<FormField
control={form.control}
name={`signers.${index}.email`}
render={({ field }) => (
<FormItem
className={cn('relative w-full', {
'mb-6':
form.formState.errors.signers?.[index] &&
!form.formState.errors.signers[index]?.email,
})}
>
{!showAdvancedSettings && index === 0 && (
<FormLabel required>
<Trans>Email</Trans>
</FormLabel>
)}
<FormControl>
<RecipientAutoCompleteInput
type="email"
placeholder={t`Email`}
value={field.value}
disabled={
snapshot.isDragging ||
isSubmitting ||
!canRecipientBeModified(signer.id)
}
options={recipientSuggestions}
onSelect={(suggestion) =>
handleRecipientAutoCompleteSelect(index, suggestion)
}
onSearchQueryChange={(query) => {
field.onChange(query);
setRecipientSearchQuery(query);
}}
loading={isLoading}
data-testid="signer-email-input"
maxLength={254}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name={`signers.${index}.name`}
render={({ field }) => (
<FormItem
className={cn('w-full', {
'mb-6':
form.formState.errors.signers?.[index] &&
!form.formState.errors.signers[index]?.name,
})}
>
{!showAdvancedSettings && index === 0 && (
<FormLabel>
<Trans>Name</Trans>
</FormLabel>
)}
<FormControl>
<RecipientAutoCompleteInput
type="text"
placeholder={t`Name`}
{...field}
disabled={
snapshot.isDragging ||
isSubmitting ||
!canRecipientBeModified(signer.id)
}
options={recipientSuggestions}
onSelect={(suggestion) =>
handleRecipientAutoCompleteSelect(index, suggestion)
}
onSearchQueryChange={(query) => {
field.onChange(query);
setRecipientSearchQuery(query);
}}
loading={isLoading}
maxLength={255}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name={`signers.${index}.role`}
render={({ field }) => (
<FormItem
className={cn('mt-auto', {
className={cn('mt-auto w-fit', {
'mb-6':
form.formState.errors.signers?.[index] &&
!form.formState.errors.signers[index]?.role,
@ -916,14 +902,11 @@ export const EnvelopeEditorRecipientForm = () => {
)}
/>
<button
type="button"
className={cn(
'mt-auto inline-flex h-10 w-10 items-center justify-center hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50',
{
'mb-6': form.formState.errors.signers?.[index],
},
)}
<Button
variant="ghost"
className={cn('mt-auto px-2', {
'mb-6': form.formState.errors.signers?.[index],
})}
data-testid="remove-signer-button"
disabled={
snapshot.isDragging ||
@ -934,8 +917,40 @@ export const EnvelopeEditorRecipientForm = () => {
onClick={() => onRemoveSigner(index)}
>
<TrashIcon className="h-4 w-4" />
</button>
</Button>
</div>
{showAdvancedSettings &&
organisation.organisationClaim.flags.cfr21 && (
<FormField
control={form.control}
name={`signers.${index}.actionAuth`}
render={({ field }) => (
<FormItem
className={cn('mt-2 w-full', {
'mb-6':
form.formState.errors.signers?.[index] &&
!form.formState.errors.signers[index]?.actionAuth,
'pl-6': isSigningOrderSequential,
})}
>
<FormControl>
<RecipientActionAuthSelect
{...field}
onValueChange={field.onChange}
disabled={
snapshot.isDragging ||
isSubmitting ||
!canRecipientBeModified(signer.id)
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
</motion.fieldset>
</div>
)}

View File

@ -355,7 +355,7 @@ export const EnvelopeEditorSettingsDialog = ({
<Form {...form}>
<form onSubmit={form.handleSubmit(onFormSubmit)}>
<fieldset
className="flex min-h-[45rem] w-full flex-col space-y-6 px-6 pt-6"
className="flex h-[45rem] max-h-[calc(100vh-14rem)] w-full flex-col space-y-6 overflow-y-auto px-6 pt-6"
disabled={form.formState.isSubmitting}
key={activeTab}
>

View File

@ -81,7 +81,6 @@ export default function EnvelopeEditor() {
isAutosaving,
flushAutosave,
relativePath,
syncEnvelope,
editorFields,
} = useCurrentEnvelopeEditor();
@ -157,7 +156,7 @@ export default function EnvelopeEditor() {
<EnvelopeEditorHeader />
{/* Main Content Area */}
<div className="flex h-[calc(100vh-73px)] w-screen">
<div className="flex h-[calc(100vh-4rem)] w-screen">
{/* Left Section - Step Navigation */}
<div className="bg-background border-border flex w-80 flex-shrink-0 flex-col overflow-y-auto border-r py-4">
{/* Left section step selector. */}
@ -251,7 +250,7 @@ export default function EnvelopeEditor() {
...envelope,
fields: editorFields.localFields,
}}
onDistribute={syncEnvelope}
documentRootPath={relativePath.documentRootPath}
trigger={
<Button variant="ghost" size="sm" className="w-full justify-start">
<SendIcon className="mr-2 h-4 w-4" />
@ -369,16 +368,14 @@ export default function EnvelopeEditor() {
</div>
{/* Main Content - Changes based on current step */}
<div className="flex-1 overflow-y-auto">
<AnimateGenericFadeInOut key={currentStep}>
{match({ currentStep, isStepLoading })
.with({ isStepLoading: true }, () => <SpinnerBox className="py-32" />)
.with({ currentStep: 'upload' }, () => <EnvelopeEditorUploadPage />)
.with({ currentStep: 'addFields' }, () => <EnvelopeEditorFieldsPage />)
.with({ currentStep: 'preview' }, () => <EnvelopeEditorPreviewPage />)
.exhaustive()}
</AnimateGenericFadeInOut>
</div>
<AnimateGenericFadeInOut className="flex-1 overflow-y-auto" key={currentStep}>
{match({ currentStep, isStepLoading })
.with({ isStepLoading: true }, () => <SpinnerBox className="py-32" />)
.with({ currentStep: 'upload' }, () => <EnvelopeEditorUploadPage />)
.with({ currentStep: 'addFields' }, () => <EnvelopeEditorFieldsPage />)
.with({ currentStep: 'preview' }, () => <EnvelopeEditorPreviewPage />)
.exhaustive()}
</AnimateGenericFadeInOut>
</div>
</div>
);

View File

@ -20,7 +20,8 @@ export const EnvelopeItemSelector = ({
}: EnvelopeItemSelectorProps) => {
return (
<button
className={`flex min-w-0 flex-shrink-0 cursor-pointer items-center space-x-3 rounded-lg border px-4 py-3 transition-colors ${
title={typeof primaryText === 'string' ? primaryText : undefined}
className={`flex h-fit max-w-72 flex-shrink-0 cursor-pointer items-center space-x-3 rounded-lg border px-4 py-3 transition-colors ${
isSelected
? 'border-green-200 bg-green-50 text-green-900 dark:border-green-400/30 dark:bg-green-400/10 dark:text-green-400'
: 'border-border bg-muted/50 hover:bg-muted/70'
@ -39,7 +40,7 @@ export const EnvelopeItemSelector = ({
<div className="text-xs text-gray-500">{secondaryText}</div>
</div>
<div
className={cn('h-2 w-2 rounded-full', {
className={cn('h-2 w-2 flex-shrink-0 rounded-full', {
'bg-green-500': isSelected,
})}
></div>
@ -61,7 +62,7 @@ export const EnvelopeRendererFileSelector = ({
const { envelopeItems, currentEnvelopeItem, setCurrentEnvelopeItem } = useCurrentEnvelopeRender();
return (
<div className={cn('flex h-fit space-x-2 overflow-x-auto p-4', className)}>
<div className={cn('flex h-fit flex-shrink-0 space-x-2 overflow-x-auto p-4', className)}>
{envelopeItems.map((doc, i) => (
<EnvelopeItemSelector
key={doc.id}

View File

@ -12,7 +12,7 @@ import { getClientSideFieldTranslations } from '@documenso/lib/utils/fields';
export default function EnvelopeGenericPageRenderer() {
const { i18n } = useLingui();
const { currentEnvelopeItem, fields } = useCurrentEnvelopeRender();
const { currentEnvelopeItem, fields, getRecipientColorKey } = useCurrentEnvelopeRender();
const {
stage,
@ -60,8 +60,7 @@ export default function EnvelopeGenericPageRenderer() {
translations: getClientSideFieldTranslations(i18n),
pageWidth: unscaledViewport.width,
pageHeight: unscaledViewport.height,
// color: getRecipientColorKey(field.recipientId),
color: 'purple', // Todo
color: getRecipientColorKey(field.recipientId),
editable: false,
mode: 'sign',
});
@ -80,7 +79,7 @@ export default function EnvelopeGenericPageRenderer() {
};
/**
* Render fields when they are added or removed from the localFields.
* Render fields when they are added or removed
*/
useEffect(() => {
if (!pageLayer.current || !stage.current) {
@ -93,14 +92,12 @@ export default function EnvelopeGenericPageRenderer() {
group.name() === 'field-group' &&
!localPageFields.some((field) => field.id.toString() === group.id())
) {
console.log('Field removed, removing from canvas');
group.destroy();
}
});
// If it exists, rerender.
localPageFields.forEach((field) => {
console.log('Field created/updated, rendering on canvas');
renderFieldOnLayer(field);
});

View File

@ -15,7 +15,6 @@ export type ShareDocumentDownloadButtonProps = {
documentData: DocumentData;
};
// Todo: Envelopes - Support multiple item downloads.
export const ShareDocumentDownloadButton = ({
title,
documentData,

View File

@ -148,6 +148,7 @@ export default function DocumentPage({ params }: Route.ComponentProps) {
<EnvelopeRenderProvider
envelope={envelope}
fields={envelope.status == DocumentStatus.COMPLETED ? [] : envelope.fields}
recipientIds={envelope.recipients.map((recipient) => recipient.id)}
>
{isMultiEnvelopeItem && (
<EnvelopeRendererFileSelector fields={envelope.fields} className="mb-4 p-0" />

View File

@ -99,7 +99,11 @@ export default function EnvelopeEditorPage({ params }: Route.ComponentProps) {
return (
<EnvelopeEditorProvider initialEnvelope={envelope}>
<EnvelopeRenderProvider envelope={envelope}>
<EnvelopeRenderProvider
envelope={envelope}
fields={envelope.fields}
recipientIds={envelope.recipients.map((recipient) => recipient.id)}
>
<EnvelopeEditor />
</EnvelopeRenderProvider>
</EnvelopeEditorProvider>

View File

@ -168,7 +168,11 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
<div className="mt-6 grid w-full grid-cols-12 gap-8">
{envelope.internalVersion === 2 ? (
<div className="relative col-span-12 lg:col-span-6 xl:col-span-7">
<EnvelopeRenderProvider envelope={envelope} fields={envelope.fields}>
<EnvelopeRenderProvider
envelope={envelope}
fields={envelope.fields}
recipientIds={envelope.recipients.map((recipient) => recipient.id)}
>
{isMultiEnvelopeItem && (
<EnvelopeRendererFileSelector fields={envelope.fields} className="mb-4 p-0" />
)}

View File

@ -3,6 +3,9 @@ import React from 'react';
import type { DocumentData } from '@prisma/client';
import type { TRecipientColor } from '@documenso/ui/lib/recipient-colors';
import { AVAILABLE_RECIPIENT_COLORS } from '@documenso/ui/lib/recipient-colors';
import type { TEnvelope } from '../../types/envelope';
import { getFile } from '../../universal/upload/get-file';
@ -23,6 +26,7 @@ type EnvelopeRenderProviderValue = {
currentEnvelopeItem: EnvelopeRenderItem | null;
setCurrentEnvelopeItem: (envelopeItemId: string) => void;
fields: TEnvelope['fields'];
getRecipientColorKey: (recipientId: number) => TRecipientColor;
};
interface EnvelopeRenderProviderProps {
@ -35,6 +39,13 @@ interface EnvelopeRenderProviderProps {
* Only pass if the CustomRenderer you are passing in wants fields.
*/
fields?: TEnvelope['fields'];
/**
* Optional recipient IDs used to determine the color of the fields.
*
* Only required for generic page renderers.
*/
recipientIds?: number[];
}
const EnvelopeRenderContext = createContext<EnvelopeRenderProviderValue | null>(null);
@ -56,6 +67,7 @@ export const EnvelopeRenderProvider = ({
children,
envelope,
fields,
recipientIds = [],
}: EnvelopeRenderProviderProps) => {
// Indexed by documentDataId.
const [files, setFiles] = useState<Record<string, FileData>>({});
@ -132,6 +144,17 @@ export const EnvelopeRenderProvider = ({
}
}, [envelope.envelopeItems]);
const getRecipientColorKey = useCallback(
(recipientId: number) => {
const recipientIndex = recipientIds.findIndex((id) => id === recipientId);
return AVAILABLE_RECIPIENT_COLORS[
Math.max(recipientIndex, 0) % AVAILABLE_RECIPIENT_COLORS.length
];
},
[recipientIds],
);
return (
<EnvelopeRenderContext.Provider
value={{
@ -140,6 +163,7 @@ export const EnvelopeRenderProvider = ({
currentEnvelopeItem: currentItem,
setCurrentEnvelopeItem,
fields: fields ?? [],
getRecipientColorKey,
}}
>
{children}

View File

@ -189,7 +189,6 @@ export const run = async ({
settings,
});
// Todo: Envelopes - Is it okay to have dynamic IDs?
const newDocumentData = await Promise.all(
envelopeItems.map(async (envelopeItem) =>
io.runTask(`decorate-and-sign-envelope-item-${envelopeItem.id}`, async () => {

View File

@ -256,11 +256,10 @@ export const sendDocument = async ({
});
}
// Todo: Envelopes - [AUDIT_LOGS]
if (envelope.internalVersion === 2) {
await Promise.all(
const autoInsertedFields = await Promise.all(
fieldsToAutoInsert.map(async (field) => {
await tx.field.update({
return await tx.field.update({
where: {
id: field.fieldId,
},
@ -271,6 +270,21 @@ export const sendDocument = async ({
});
}),
);
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELDS_AUTO_INSERTED,
envelopeId: envelope.id,
data: {
fields: autoInsertedFields.map((field) => ({
fieldId: field.id,
fieldType: field.type,
recipientId: field.recipientId,
})),
},
// Don't put metadata or user here since it's a system event.
}),
});
}
return await tx.envelope.update({

View File

@ -262,10 +262,20 @@ msgstr "{prefix} hat ein Feld hinzugefügt"
msgid "{prefix} added a recipient"
msgstr "{prefix} hat einen Empfänger hinzugefügt"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created an envelope item with title {0}"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created the document"
msgstr "{prefix} hat das Dokument erstellt"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted an envelope item with title {0}"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted the document"
msgstr "{prefix} hat das Dokument gelöscht"
@ -356,6 +366,7 @@ msgstr "{recipientActionVerb} Dokument"
msgid "{recipientActionVerb} the document to complete the process."
msgstr "{recipientActionVerb} das Dokument, um den Prozess abzuschließen."
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "{recipientCount} recipients"
msgstr "{recipientCount} Empfänger"
@ -1742,8 +1753,9 @@ msgstr ""
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
msgid "Attachment removed successfully."
msgstr ""
msgstr "<<<<<<< Updated upstream======="
#: apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
@ -2158,6 +2170,10 @@ msgstr "Filter löschen"
msgid "Clear Signature"
msgstr "Unterschrift löschen"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
msgid "Click here to add a recipient"
msgstr ""
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
msgid "Click here to get started"
msgstr "Klicken Sie hier, um zu beginnen"
@ -2280,6 +2296,7 @@ msgstr "Abgeschlossene Dokumente"
msgid "Completed Documents"
msgstr "Abgeschlossene Dokumente"
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "Completed on {formattedDate}"
msgstr "Abgeschlossen am {formattedDate}"
@ -2479,7 +2496,6 @@ msgid "Controls which signatures are allowed to be used when signing a document.
msgstr "Bestimmt, welche Signaturen beim Unterschreiben eines Dokuments verwendet werden dürfen."
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied"
msgstr "Kopiert"
@ -2497,14 +2513,12 @@ msgstr "Kopiert"
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/components/document/document-share-button.tsx
msgid "Copied to clipboard"
msgstr "In die Zwischenablage kopiert"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copy"
msgstr "Kopieren"
@ -2522,6 +2536,7 @@ msgid "Copy Shareable Link"
msgstr "Kopiere den teilbaren Link"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Copy Signing Links"
msgstr "Signierlinks kopieren"
@ -3646,7 +3661,6 @@ msgstr "Legen Sie Ihr Dokument hier ab"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
#: packages/lib/utils/fields.ts
msgid "Dropdown"
msgstr "Dropdown"
@ -4035,6 +4049,14 @@ msgstr ""
msgid "Envelope Item Count"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item created"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item deleted"
msgstr ""
#: apps/remix/app/components/dialogs/envelope-redistribute-dialog.tsx
msgid "Envelope resent"
msgstr ""
@ -5544,7 +5566,6 @@ msgstr "Kein passender Empfänger mit dieser Beschreibung gefunden."
#: apps/remix/app/components/general/template/template-page-view-recipients.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "No recipients"
msgstr "Keine Empfänger"
@ -7066,6 +7087,10 @@ msgstr "Wählen Sie Mitglieder oder Gruppen von Mitgliedern, die dem Team hinzug
msgid "Select members to add to this team"
msgstr "Wählen Sie Mitglieder aus, die diesem Team hinzugefügt werden sollen"
#: packages/lib/utils/fields.ts
msgid "Select Option"
msgstr ""
#: apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
msgid "Select passkey"
msgstr "Passkey auswählen"
@ -7870,6 +7895,15 @@ msgstr "E-Mail-Domains synchronisieren"
msgid "Sync failed, changes not saved"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgctxt "Audit log format"
msgid "System auto inserted fields"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "System auto inserted fields"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "System Requirements"
msgstr "Systemanforderungen"
@ -8412,7 +8446,6 @@ msgstr "Der Name des Unterzeichners"
#: apps/remix/app/components/general/avatar-with-recipient.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "The signing link has been copied to your clipboard."
msgstr "Der Signierlink wurde in die Zwischenablage kopiert."

View File

@ -257,10 +257,20 @@ msgstr "{prefix} added a field"
msgid "{prefix} added a recipient"
msgstr "{prefix} added a recipient"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created an envelope item with title {0}"
msgstr "{prefix} created an envelope item with title {0}"
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created the document"
msgstr "{prefix} created the document"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted an envelope item with title {0}"
msgstr "{prefix} deleted an envelope item with title {0}"
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted the document"
msgstr "{prefix} deleted the document"
@ -351,6 +361,7 @@ msgstr "{recipientActionVerb} document"
msgid "{recipientActionVerb} the document to complete the process."
msgstr "{recipientActionVerb} the document to complete the process."
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "{recipientCount} recipients"
msgstr "{recipientCount} recipients"
@ -1737,8 +1748,9 @@ msgstr "Attachment added successfully."
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
msgid "Attachment removed successfully."
msgstr "Attachment removed successfully."
msgstr "Attachment removed successfully.<<<<<<< Updated upstream======="
#: apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
@ -2153,6 +2165,10 @@ msgstr "Clear filters"
msgid "Clear Signature"
msgstr "Clear Signature"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
msgid "Click here to add a recipient"
msgstr "Click here to add a recipient"
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
msgid "Click here to get started"
msgstr "Click here to get started"
@ -2275,6 +2291,7 @@ msgstr "Completed documents"
msgid "Completed Documents"
msgstr "Completed Documents"
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "Completed on {formattedDate}"
msgstr "Completed on {formattedDate}"
@ -2474,7 +2491,6 @@ msgid "Controls which signatures are allowed to be used when signing a document.
msgstr "Controls which signatures are allowed to be used when signing a document."
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied"
msgstr "Copied"
@ -2492,14 +2508,12 @@ msgstr "Copied"
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/components/document/document-share-button.tsx
msgid "Copied to clipboard"
msgstr "Copied to clipboard"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copy"
msgstr "Copy"
@ -2517,6 +2531,7 @@ msgid "Copy Shareable Link"
msgstr "Copy Shareable Link"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Copy Signing Links"
msgstr "Copy Signing Links"
@ -3641,7 +3656,6 @@ msgstr "Drop your document here"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
#: packages/lib/utils/fields.ts
msgid "Dropdown"
msgstr "Dropdown"
@ -4030,6 +4044,14 @@ msgstr "Envelope ID"
msgid "Envelope Item Count"
msgstr "Envelope Item Count"
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item created"
msgstr "Envelope item created"
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item deleted"
msgstr "Envelope item deleted"
#: apps/remix/app/components/dialogs/envelope-redistribute-dialog.tsx
msgid "Envelope resent"
msgstr "Envelope resent"
@ -5539,7 +5561,6 @@ msgstr "No recipient matching this description was found."
#: apps/remix/app/components/general/template/template-page-view-recipients.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "No recipients"
msgstr "No recipients"
@ -7061,6 +7082,10 @@ msgstr "Select members or groups of members to add to the team."
msgid "Select members to add to this team"
msgstr "Select members to add to this team"
#: packages/lib/utils/fields.ts
msgid "Select Option"
msgstr "Select Option"
#: apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
msgid "Select passkey"
msgstr "Select passkey"
@ -7865,6 +7890,15 @@ msgstr "Sync Email Domains"
msgid "Sync failed, changes not saved"
msgstr "Sync failed, changes not saved"
#: packages/lib/utils/document-audit-logs.ts
msgctxt "Audit log format"
msgid "System auto inserted fields"
msgstr "System auto inserted fields"
#: packages/lib/utils/document-audit-logs.ts
msgid "System auto inserted fields"
msgstr "System auto inserted fields"
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "System Requirements"
msgstr "System Requirements"
@ -8417,7 +8451,6 @@ msgstr "The signer's name"
#: apps/remix/app/components/general/avatar-with-recipient.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "The signing link has been copied to your clipboard."
msgstr "The signing link has been copied to your clipboard."

View File

@ -262,10 +262,20 @@ msgstr "{prefix} agregó un campo"
msgid "{prefix} added a recipient"
msgstr "{prefix} agregó un destinatario"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created an envelope item with title {0}"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created the document"
msgstr "{prefix} creó el documento"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted an envelope item with title {0}"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted the document"
msgstr "{prefix} eliminó el documento"
@ -356,6 +366,7 @@ msgstr "{recipientActionVerb} documento"
msgid "{recipientActionVerb} the document to complete the process."
msgstr "{recipientActionVerb} el documento para completar el proceso."
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "{recipientCount} recipients"
msgstr "{recipientCount} destinatarios"
@ -1742,8 +1753,9 @@ msgstr ""
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
msgid "Attachment removed successfully."
msgstr ""
msgstr "<<<<<<< Updated upstream======="
#: apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
@ -2158,6 +2170,10 @@ msgstr "Limpiar filtros"
msgid "Clear Signature"
msgstr "Limpiar firma"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
msgid "Click here to add a recipient"
msgstr ""
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
msgid "Click here to get started"
msgstr "Haga clic aquí para comenzar"
@ -2280,6 +2296,7 @@ msgstr "Documentos completados"
msgid "Completed Documents"
msgstr "Documentos Completados"
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "Completed on {formattedDate}"
msgstr "Completado el {formattedDate}"
@ -2479,7 +2496,6 @@ msgid "Controls which signatures are allowed to be used when signing a document.
msgstr "Controla qué firmas están permitidas al firmar un documento."
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied"
msgstr "Copiado"
@ -2497,14 +2513,12 @@ msgstr "Copiado"
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/components/document/document-share-button.tsx
msgid "Copied to clipboard"
msgstr "Copiado al portapapeles"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copy"
msgstr "Copiar"
@ -2522,6 +2536,7 @@ msgid "Copy Shareable Link"
msgstr "Copiar enlace compartible"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Copy Signing Links"
msgstr "Copiar enlaces de firma"
@ -3646,7 +3661,6 @@ msgstr "Suelta tu documento aquí"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
#: packages/lib/utils/fields.ts
msgid "Dropdown"
msgstr "Menú desplegable"
@ -4035,6 +4049,14 @@ msgstr ""
msgid "Envelope Item Count"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item created"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item deleted"
msgstr ""
#: apps/remix/app/components/dialogs/envelope-redistribute-dialog.tsx
msgid "Envelope resent"
msgstr ""
@ -5544,7 +5566,6 @@ msgstr "No se encontró ningún destinatario que coincidiera con esta descripci
#: apps/remix/app/components/general/template/template-page-view-recipients.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "No recipients"
msgstr "Sin destinatarios"
@ -7066,6 +7087,10 @@ msgstr "Seleccione miembros o grupos de miembros para agregar al equipo."
msgid "Select members to add to this team"
msgstr "Seleccione los miembros para añadir a este equipo"
#: packages/lib/utils/fields.ts
msgid "Select Option"
msgstr ""
#: apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
msgid "Select passkey"
msgstr "Seleccionar clave de acceso"
@ -7870,6 +7895,15 @@ msgstr "Sincronizar dominios de correo electrónico"
msgid "Sync failed, changes not saved"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgctxt "Audit log format"
msgid "System auto inserted fields"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "System auto inserted fields"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "System Requirements"
msgstr "Requisitos del Sistema"
@ -8412,7 +8446,6 @@ msgstr "El nombre del firmante"
#: apps/remix/app/components/general/avatar-with-recipient.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "The signing link has been copied to your clipboard."
msgstr "El enlace de firma ha sido copiado a tu portapapeles."

View File

@ -262,10 +262,20 @@ msgstr "{prefix} a ajouté un champ"
msgid "{prefix} added a recipient"
msgstr "{prefix} a ajouté un destinataire"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created an envelope item with title {0}"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created the document"
msgstr "{prefix} a créé le document"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted an envelope item with title {0}"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted the document"
msgstr "{prefix} a supprimé le document"
@ -356,6 +366,7 @@ msgstr "{recipientActionVerb} document"
msgid "{recipientActionVerb} the document to complete the process."
msgstr "{recipientActionVerb} the document to complete the process."
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "{recipientCount} recipients"
msgstr "{recipientCount} destinataires"
@ -1742,8 +1753,9 @@ msgstr ""
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
msgid "Attachment removed successfully."
msgstr ""
msgstr "<<<<<<< Updated upstream======="
#: apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
@ -2158,6 +2170,10 @@ msgstr "Effacer les filtres"
msgid "Clear Signature"
msgstr "Effacer la signature"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
msgid "Click here to add a recipient"
msgstr ""
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
msgid "Click here to get started"
msgstr "Cliquez ici pour commencer"
@ -2280,6 +2296,7 @@ msgstr "Documents complétés"
msgid "Completed Documents"
msgstr "Documents Complétés"
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "Completed on {formattedDate}"
msgstr "Terminé le {formattedDate}"
@ -2479,7 +2496,6 @@ msgid "Controls which signatures are allowed to be used when signing a document.
msgstr "Contrôle quelles signatures sont autorisées lors de la signature d'un document."
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied"
msgstr "Copié"
@ -2497,14 +2513,12 @@ msgstr "Copié"
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/components/document/document-share-button.tsx
msgid "Copied to clipboard"
msgstr "Copié dans le presse-papiers"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copy"
msgstr "Copier"
@ -2522,6 +2536,7 @@ msgid "Copy Shareable Link"
msgstr "Copier le lien partageable"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Copy Signing Links"
msgstr "Copier les liens de signature"
@ -3646,7 +3661,6 @@ msgstr "Déposez votre document ici"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
#: packages/lib/utils/fields.ts
msgid "Dropdown"
msgstr "Liste déroulante"
@ -4035,6 +4049,14 @@ msgstr ""
msgid "Envelope Item Count"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item created"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item deleted"
msgstr ""
#: apps/remix/app/components/dialogs/envelope-redistribute-dialog.tsx
msgid "Envelope resent"
msgstr ""
@ -5544,7 +5566,6 @@ msgstr "Aucun destinataire correspondant à cette description n'a été trouvé.
#: apps/remix/app/components/general/template/template-page-view-recipients.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "No recipients"
msgstr "Aucun destinataire"
@ -7066,6 +7087,10 @@ msgstr "Sélectionnez des membres ou groupes de membres à ajouter à l'équipe.
msgid "Select members to add to this team"
msgstr "Sélectionnez des membres à ajouter à cette équipe"
#: packages/lib/utils/fields.ts
msgid "Select Option"
msgstr ""
#: apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
msgid "Select passkey"
msgstr "Sélectionner la clé d'authentification"
@ -7870,6 +7895,15 @@ msgstr "Synchroniser les domaines de messagerie"
msgid "Sync failed, changes not saved"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgctxt "Audit log format"
msgid "System auto inserted fields"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "System auto inserted fields"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "System Requirements"
msgstr "Exigences du système"
@ -8412,7 +8446,6 @@ msgstr "Le nom du signataire"
#: apps/remix/app/components/general/avatar-with-recipient.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "The signing link has been copied to your clipboard."
msgstr "Le lien de signature a été copié dans votre presse-papiers."

View File

@ -262,10 +262,20 @@ msgstr "{prefix} ha aggiunto un campo"
msgid "{prefix} added a recipient"
msgstr "{prefix} ha aggiunto un destinatario"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created an envelope item with title {0}"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created the document"
msgstr "{prefix} ha creato il documento"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted an envelope item with title {0}"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted the document"
msgstr "{prefix} ha eliminato il documento"
@ -356,6 +366,7 @@ msgstr "{recipientActionVerb} documento"
msgid "{recipientActionVerb} the document to complete the process."
msgstr "{recipientActionVerb} il documento per completare il processo."
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "{recipientCount} recipients"
msgstr "{recipientCount} destinatari"
@ -1742,8 +1753,9 @@ msgstr ""
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
msgid "Attachment removed successfully."
msgstr ""
msgstr "<<<<<<< Updated upstream======="
#: apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
@ -2158,6 +2170,10 @@ msgstr "Cancella filtri"
msgid "Clear Signature"
msgstr "Cancella firma"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
msgid "Click here to add a recipient"
msgstr ""
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
msgid "Click here to get started"
msgstr "Clicca qui per iniziare"
@ -2280,6 +2296,7 @@ msgstr "Documenti Completati"
msgid "Completed Documents"
msgstr "Documenti Completati"
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "Completed on {formattedDate}"
msgstr "Completato il {formattedDate}"
@ -2479,7 +2496,6 @@ msgid "Controls which signatures are allowed to be used when signing a document.
msgstr "Controlla quali firme sono consentite per firmare un documento."
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied"
msgstr "Copiato"
@ -2497,14 +2513,12 @@ msgstr "Copiato"
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/components/document/document-share-button.tsx
msgid "Copied to clipboard"
msgstr "Copiato negli appunti"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copy"
msgstr "Copia"
@ -2522,6 +2536,7 @@ msgid "Copy Shareable Link"
msgstr "Copia il Link Condivisibile"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Copy Signing Links"
msgstr "Copia link di firma"
@ -3646,7 +3661,6 @@ msgstr "Rilascia qui il tuo documento"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
#: packages/lib/utils/fields.ts
msgid "Dropdown"
msgstr "Menu a tendina"
@ -4035,6 +4049,14 @@ msgstr ""
msgid "Envelope Item Count"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item created"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item deleted"
msgstr ""
#: apps/remix/app/components/dialogs/envelope-redistribute-dialog.tsx
msgid "Envelope resent"
msgstr ""
@ -5544,7 +5566,6 @@ msgstr "Nessun destinatario corrispondente a questa descrizione è stato trovato
#: apps/remix/app/components/general/template/template-page-view-recipients.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "No recipients"
msgstr "Nessun destinatario"
@ -7066,6 +7087,10 @@ msgstr "Seleziona membri o gruppi di membri da aggiungere al team."
msgid "Select members to add to this team"
msgstr "Seleziona membri da aggiungere a questo team"
#: packages/lib/utils/fields.ts
msgid "Select Option"
msgstr ""
#: apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
msgid "Select passkey"
msgstr "Seleziona una chiave di accesso"
@ -7870,6 +7895,15 @@ msgstr "Sincronizza Domini Email"
msgid "Sync failed, changes not saved"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgctxt "Audit log format"
msgid "System auto inserted fields"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "System auto inserted fields"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "System Requirements"
msgstr "Requisiti di sistema"
@ -8420,7 +8454,6 @@ msgstr "Il nome del firmatario"
#: apps/remix/app/components/general/avatar-with-recipient.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "The signing link has been copied to your clipboard."
msgstr "Il link di firma è stato copiato negli appunti."

View File

@ -262,10 +262,20 @@ msgstr "Użytkownik {prefix} dodał pole"
msgid "{prefix} added a recipient"
msgstr "Użytkownik {prefix} dodał odbiorcę"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created an envelope item with title {0}"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} created the document"
msgstr "Użytkownik {prefix} utworzył dokument"
#. placeholder {0}: data.envelopeItemTitle
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted an envelope item with title {0}"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "{prefix} deleted the document"
msgstr "Użytkownik {prefix} usunął dokument"
@ -356,6 +366,7 @@ msgstr "{recipientActionVerb} dokument"
msgid "{recipientActionVerb} the document to complete the process."
msgstr "Sprawdź i {recipientActionVerb} dokument, aby zakończyć proces."
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "{recipientCount} recipients"
msgstr "{recipientCount} odbiorców"
@ -1742,8 +1753,9 @@ msgstr ""
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
msgid "Attachment removed successfully."
msgstr ""
msgstr "<<<<<<< Updated upstream======="
#: apps/remix/app/components/general/document-signing/document-signing-page-view-v2.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document-signing/document-signing-attachments-popover.tsx
#: apps/remix/app/components/general/document/document-attachments-popover.tsx
@ -2158,6 +2170,10 @@ msgstr "Wyczyść filtry"
msgid "Clear Signature"
msgstr "Wyczyść podpis"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-page.tsx
msgid "Click here to add a recipient"
msgstr ""
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
msgid "Click here to get started"
msgstr "Kliknij, aby rozpocząć"
@ -2280,6 +2296,7 @@ msgstr "Dokumenty zakończone"
msgid "Completed Documents"
msgstr "Zakończone dokumenty"
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
#: apps/remix/app/components/general/document/document-certificate-qr-view.tsx
msgid "Completed on {formattedDate}"
msgstr "Zakończono {formattedDate}"
@ -2479,7 +2496,6 @@ msgid "Controls which signatures are allowed to be used when signing a document.
msgstr "Kontroluje, które podpisy są dozwolone do użycia podczas podpisywania dokumentu."
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copied"
msgstr "Skopiowano"
@ -2497,14 +2513,12 @@ msgstr "Skopiowano"
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/organisation-email-domain-records-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
#: packages/ui/components/document/document-share-button.tsx
msgid "Copied to clipboard"
msgstr "Skopiowano do schowka"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "Copy"
msgstr "Kopiuj"
@ -2522,6 +2536,7 @@ msgid "Copy Shareable Link"
msgstr "Kopiuj udostępniany link"
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
msgid "Copy Signing Links"
msgstr "Kopiuj linki do podpisania"
@ -3646,7 +3661,6 @@ msgstr "Upuść swój dokument tutaj"
#: apps/remix/app/components/general/envelope-editor/envelope-editor-fields-drag-drop.tsx
#: packages/ui/primitives/template-flow/add-template-fields.tsx
#: packages/ui/primitives/document-flow/add-fields.tsx
#: packages/lib/utils/fields.ts
msgid "Dropdown"
msgstr "Lista rozwijana"
@ -4035,6 +4049,14 @@ msgstr ""
msgid "Envelope Item Count"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item created"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "Envelope item deleted"
msgstr ""
#: apps/remix/app/components/dialogs/envelope-redistribute-dialog.tsx
msgid "Envelope resent"
msgstr ""
@ -5544,7 +5566,6 @@ msgstr "Nie znaleziono odbiorcy pasującego do tego opisu."
#: apps/remix/app/components/general/template/template-page-view-recipients.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "No recipients"
msgstr "Brak odbiorców"
@ -7066,6 +7087,10 @@ msgstr "Wybierz członków lub grupy członków, aby dodać do zespołu."
msgid "Select members to add to this team"
msgstr "Wybierz członków, aby dodać do tego zespołu"
#: packages/lib/utils/fields.ts
msgid "Select Option"
msgstr ""
#: apps/remix/app/components/general/document-signing/document-signing-auth-passkey.tsx
msgid "Select passkey"
msgstr "Wybierz klucz uwierzytelniający"
@ -7870,6 +7895,15 @@ msgstr "Synchronizuj domeny e-mail"
msgid "Sync failed, changes not saved"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgctxt "Audit log format"
msgid "System auto inserted fields"
msgstr ""
#: packages/lib/utils/document-audit-logs.ts
msgid "System auto inserted fields"
msgstr ""
#: apps/remix/app/routes/_unauthenticated+/articles.signature-disclosure.tsx
msgid "System Requirements"
msgstr "Wymagania systemowe"
@ -8412,7 +8446,6 @@ msgstr "Nazwa podpisującego"
#: apps/remix/app/components/general/avatar-with-recipient.tsx
#: apps/remix/app/components/general/document/document-recipient-link-copy-dialog.tsx
#: apps/remix/app/components/general/document/document-page-view-recipients.tsx
#: apps/remix/app/components/dialogs/envelope-distribute-dialog.tsx
#: packages/ui/primitives/document-flow/add-subject.tsx
msgid "The signing link has been copied to your clipboard."
msgstr "Link do podpisu został skopiowany do schowka."

View File

@ -21,10 +21,14 @@ export const ZDocumentAuditLogTypeSchema = z.enum([
'RECIPIENT_DELETED',
'RECIPIENT_UPDATED',
'ENVELOPE_ITEM_CREATED',
'ENVELOPE_ITEM_DELETED',
// Document events.
'DOCUMENT_COMPLETED', // When the document is sealed and fully completed.
'DOCUMENT_CREATED', // When the document is created.
'DOCUMENT_DELETED', // When the document is soft deleted.
'DOCUMENT_FIELDS_AUTO_INSERTED', // When a field is auto inserted during send due to default values (radio/dropdown/checkbox).
'DOCUMENT_FIELD_INSERTED', // When a field is inserted (signed/approved/etc) by a recipient.
'DOCUMENT_FIELD_UNINSERTED', // When a field is uninserted by a recipient.
'DOCUMENT_FIELD_PREFILLED', // When a field is prefilled by an assistant.
@ -181,6 +185,28 @@ const ZBaseRecipientDataSchema = z.object({
recipientRole: z.string(),
});
/**
* Event: Envelope item created.
*/
export const ZDocumentAuditLogEventEnvelopeItemCreatedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.ENVELOPE_ITEM_CREATED),
data: z.object({
envelopeItemId: z.string(),
envelopeItemTitle: z.string(),
}),
});
/**
* Event: Envelope item deleted.
*/
export const ZDocumentAuditLogEventEnvelopeItemDeletedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.ENVELOPE_ITEM_DELETED),
data: z.object({
envelopeItemId: z.string(),
envelopeItemTitle: z.string(),
}),
});
/**
* Event: Email sent.
*/
@ -315,6 +341,22 @@ export const ZDocumentAuditLogEventDocumentFieldInsertedSchema = z.object({
}),
});
/**
* Event: Document field auto inserted.
*/
export const ZDocumentAuditLogEventDocumentFieldsAutoInsertedSchema = z.object({
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELDS_AUTO_INSERTED),
data: z.object({
fields: z.array(
z.object({
fieldId: z.number(),
fieldType: z.nativeEnum(FieldType),
recipientId: z.number(),
}),
),
}),
});
/**
* Event: Document field uninserted.
*/
@ -652,11 +694,14 @@ export const ZDocumentAuditLogBaseSchema = z.object({
export const ZDocumentAuditLogSchema = ZDocumentAuditLogBaseSchema.and(
z.union([
ZDocumentAuditLogEventEnvelopeItemCreatedSchema,
ZDocumentAuditLogEventEnvelopeItemDeletedSchema,
ZDocumentAuditLogEventEmailSentSchema,
ZDocumentAuditLogEventDocumentCompletedSchema,
ZDocumentAuditLogEventDocumentCreatedSchema,
ZDocumentAuditLogEventDocumentDeletedSchema,
ZDocumentAuditLogEventDocumentMovedToTeamSchema,
ZDocumentAuditLogEventDocumentFieldsAutoInsertedSchema,
ZDocumentAuditLogEventDocumentFieldInsertedSchema,
ZDocumentAuditLogEventDocumentFieldUninsertedSchema,
ZDocumentAuditLogEventDocumentFieldPrefilledSchema,

View File

@ -71,7 +71,6 @@ export const ZFieldHeightSchema = z.number().min(1).describe('The height of the
// ---------------------------------------------
// Todo: Envelopes - dunno man
const PrismaDecimalSchema = z.preprocess(
(val) => (typeof val === 'string' ? new Prisma.Decimal(val) : val),
z.instanceof(Prisma.Decimal, { message: 'Must be a Decimal' }),

View File

@ -129,3 +129,58 @@ export const createSpinner = ({
return loadingGroup;
};
type CreateFieldHoverInteractionOptions = {
options: RenderFieldElementOptions;
fieldGroup: Konva.Group;
fieldRect: Konva.Rect;
};
/**
* Adds smooth transition-like behavior for hover effects to the field group and rectangle.
*/
export const createFieldHoverInteraction = ({
options,
fieldGroup,
fieldRect,
}: CreateFieldHoverInteractionOptions) => {
const { mode } = options;
if (mode === 'export' || !options.color) {
return;
}
const hoverColor = RECIPIENT_COLOR_STYLES[options.color].baseRingHover;
fieldGroup.on('mouseover', () => {
new Konva.Tween({
node: fieldRect,
duration: 0.3,
fill: hoverColor,
}).play();
});
fieldGroup.on('mouseout', () => {
new Konva.Tween({
node: fieldRect,
duration: 0.3,
fill: DEFAULT_RECT_BACKGROUND,
}).play();
});
fieldGroup.on('transformstart', () => {
new Konva.Tween({
node: fieldRect,
duration: 0.3,
fill: hoverColor,
}).play();
});
fieldGroup.on('transformend', () => {
new Konva.Tween({
node: fieldRect,
duration: 0.3,
fill: DEFAULT_RECT_BACKGROUND,
}).play();
});
};

View File

@ -4,6 +4,7 @@ import { match } from 'ts-pattern';
import { DEFAULT_STANDARD_FONT_SIZE } from '../../constants/pdf';
import type { TCheckboxFieldMeta } from '../../types/field-meta';
import {
createFieldHoverInteraction,
konvaTextFill,
konvaTextFontFamily,
upsertFieldGroup,
@ -26,25 +27,27 @@ export const renderCheckboxFieldElement = (
) => {
const { pageWidth, pageHeight, pageLayer, mode } = options;
const isFirstRender = !pageLayer.findOne(`#${field.renderId}`);
const fieldGroup = upsertFieldGroup(field, options);
// Clear previous children and listeners to re-render fresh.
fieldGroup.removeChildren();
fieldGroup.off('transform');
fieldGroup.add(upsertFieldRect(field, options));
const { fieldWidth, fieldHeight } = calculateFieldPosition(field, pageWidth, pageHeight);
const checkboxMeta: TCheckboxFieldMeta | null = (field.fieldMeta as TCheckboxFieldMeta) || null;
const checkboxValues = checkboxMeta?.values || [];
const fontSize = checkboxMeta?.fontSize || DEFAULT_STANDARD_FONT_SIZE;
const isFirstRender = !pageLayer.findOne(`#${field.renderId}`);
// Clear previous children and listeners to re-render fresh.
const fieldGroup = upsertFieldGroup(field, options);
fieldGroup.removeChildren();
fieldGroup.off('transform');
if (isFirstRender) {
pageLayer.add(fieldGroup);
}
const fieldRect = upsertFieldRect(field, options);
fieldGroup.add(fieldRect);
const fontSize = checkboxMeta?.fontSize || DEFAULT_STANDARD_FONT_SIZE;
// Handle rescaling items during transforms.
fieldGroup.on('transform', () => {
const groupScaleX = fieldGroup.scaleX();
@ -127,11 +130,9 @@ export const renderCheckboxFieldElement = (
pageLayer.batchDraw();
});
const { fieldWidth, fieldHeight } = calculateFieldPosition(field, pageWidth, pageHeight);
const checkedValues: number[] = field.customText ? JSON.parse(field.customText) : [];
checkboxValues.forEach(({ id, value, checked }, index) => {
checkboxValues.forEach(({ value, checked }, index) => {
const isCheckboxChecked = match(mode)
.with('edit', () => checked)
.with('sign', () => checkedValues.includes(index))
@ -145,8 +146,6 @@ export const renderCheckboxFieldElement = (
})
.exhaustive();
console.log('wtf?');
const itemSize = calculateCheckboxSize(fontSize);
const { itemInputX, itemInputY, textX, textY, textWidth, textHeight } =
@ -211,6 +210,8 @@ export const renderCheckboxFieldElement = (
fieldGroup.add(text);
});
createFieldHoverInteraction({ fieldGroup, fieldRect, options });
return {
fieldGroup,
isFirstRender,

View File

@ -1,8 +1,10 @@
import { FieldType } from '@prisma/client';
import Konva from 'konva';
import { DEFAULT_STANDARD_FONT_SIZE } from '../../constants/pdf';
import type { TDropdownFieldMeta } from '../../types/field-meta';
import {
createFieldHoverInteraction,
konvaTextFill,
konvaTextFontFamily,
upsertFieldGroup,
@ -48,79 +50,30 @@ export const renderDropdownFieldElement = (
field: FieldToRender,
options: RenderFieldElementOptions,
) => {
const { pageWidth, pageHeight, pageLayer, mode } = options;
const { pageWidth, pageHeight, pageLayer, mode, translations } = options;
const { fieldWidth, fieldHeight } = calculateFieldPosition(field, pageWidth, pageHeight);
const dropdownMeta: TDropdownFieldMeta | null = (field.fieldMeta as TDropdownFieldMeta) || null;
let selectedValue = translations?.[FieldType.DROPDOWN] || 'Select Option';
const isFirstRender = !pageLayer.findOne(`#${field.renderId}`);
const fieldGroup = upsertFieldGroup(field, options);
// Clear previous children to re-render fresh.
const fieldGroup = upsertFieldGroup(field, options);
fieldGroup.removeChildren();
fieldGroup.off('transform');
fieldGroup.add(upsertFieldRect(field, options));
const fieldRect = upsertFieldRect(field, options);
fieldGroup.add(fieldRect);
if (isFirstRender) {
pageLayer.add(fieldGroup);
fieldGroup.on('transform', () => {
const groupScaleX = fieldGroup.scaleX();
const groupScaleY = fieldGroup.scaleY();
const fieldRect = fieldGroup.findOne('.field-rect');
const text = fieldGroup.findOne('.dropdown-selected-text');
const arrow = fieldGroup.findOne('.dropdown-arrow');
if (!fieldRect || !text || !arrow) {
console.log('fieldRect or text or arrow not found');
return;
}
const rectWidth = fieldRect.width() * groupScaleX;
const rectHeight = fieldRect.height() * groupScaleY;
const { arrowX, arrowY, textX, textY, textWidth, textHeight } = calculateDropdownPosition({
fieldWidth: rectWidth,
fieldHeight: rectHeight,
});
arrow.setAttrs({
x: arrowX,
y: arrowY,
scaleX: 1,
scaleY: 1,
});
text.setAttrs({
scaleX: 1,
scaleY: 1,
x: textX,
y: textY,
width: textWidth,
height: textHeight,
});
fieldRect.setAttrs({
width: rectWidth,
height: rectHeight,
});
fieldGroup.scale({
x: 1,
y: 1,
});
pageLayer.batchDraw();
});
}
const dropdownMeta: TDropdownFieldMeta | null = (field.fieldMeta as TDropdownFieldMeta) || null;
const { fieldWidth, fieldHeight } = calculateFieldPosition(field, pageWidth, pageHeight);
const fontSize = dropdownMeta?.fontSize || DEFAULT_STANDARD_FONT_SIZE;
// Todo: Envelopes - Translations
let selectedValue = 'Select Option';
if (field.inserted) {
selectedValue = field.customText;
}
@ -158,27 +111,63 @@ export const renderDropdownFieldElement = (
visible: mode !== 'export',
});
// Add hover state for dropdown
fieldGroup.on('mouseenter', () => {
// dropdownContainer.stroke('#2563EB');
// dropdownContainer.strokeWidth(2);
document.body.style.cursor = 'pointer';
pageLayer.batchDraw();
});
fieldGroup.on('mouseleave', () => {
// dropdownContainer.stroke('#374151');
// dropdownContainer.strokeWidth(2);
document.body.style.cursor = 'default';
pageLayer.batchDraw();
});
fieldGroup.add(selectedText);
if (!field.inserted || mode === 'export') {
fieldGroup.add(arrow);
}
fieldGroup.on('transform', () => {
const groupScaleX = fieldGroup.scaleX();
const groupScaleY = fieldGroup.scaleY();
const fieldRect = fieldGroup.findOne('.field-rect');
const text = fieldGroup.findOne('.dropdown-selected-text');
const arrow = fieldGroup.findOne('.dropdown-arrow');
if (!fieldRect || !text || !arrow) {
return;
}
const rectWidth = fieldRect.width() * groupScaleX;
const rectHeight = fieldRect.height() * groupScaleY;
const { arrowX, arrowY, textX, textY, textWidth, textHeight } = calculateDropdownPosition({
fieldWidth: rectWidth,
fieldHeight: rectHeight,
});
arrow.setAttrs({
x: arrowX,
y: arrowY,
scaleX: 1,
scaleY: 1,
});
text.setAttrs({
scaleX: 1,
scaleY: 1,
x: textX,
y: textY,
width: textWidth,
height: textHeight,
});
fieldRect.setAttrs({
width: rectWidth,
height: rectHeight,
});
fieldGroup.scale({
x: 1,
y: 1,
});
pageLayer.batchDraw();
});
createFieldHoverInteraction({ fieldGroup, fieldRect, options });
return {
fieldGroup,
isFirstRender,

View File

@ -4,6 +4,7 @@ import { match } from 'ts-pattern';
import { DEFAULT_STANDARD_FONT_SIZE } from '../../constants/pdf';
import type { TRadioFieldMeta } from '../../types/field-meta';
import {
createFieldHoverInteraction,
konvaTextFill,
konvaTextFontFamily,
upsertFieldGroup,
@ -26,25 +27,24 @@ export const renderRadioFieldElement = (
) => {
const { pageWidth, pageHeight, pageLayer, mode } = options;
const isFirstRender = !pageLayer.findOne(`#${field.renderId}`);
const fieldGroup = upsertFieldGroup(field, options);
// Clear previous children to re-render fresh
fieldGroup.removeChildren();
fieldGroup.add(upsertFieldRect(field, options));
const radioMeta: TRadioFieldMeta | null = (field.fieldMeta as TRadioFieldMeta) || null;
const radioValues = radioMeta?.values || [];
const fontSize = radioMeta?.fontSize || DEFAULT_STANDARD_FONT_SIZE;
const isFirstRender = !pageLayer.findOne(`#${field.renderId}`);
// Clear previous children and listeners to re-render fresh
const fieldGroup = upsertFieldGroup(field, options);
fieldGroup.removeChildren();
fieldGroup.off('transform');
if (isFirstRender) {
pageLayer.add(fieldGroup);
}
fieldGroup.off('transform');
const fieldRect = upsertFieldRect(field, options);
fieldGroup.add(fieldRect);
const fontSize = radioMeta?.fontSize || DEFAULT_STANDARD_FONT_SIZE;
// Handle rescaling items during transforms.
fieldGroup.on('transform', () => {
@ -195,6 +195,8 @@ export const renderRadioFieldElement = (
fieldGroup.add(text);
});
createFieldHoverInteraction({ fieldGroup, fieldRect, options });
return {
fieldGroup,
isFirstRender,

View File

@ -1,13 +1,12 @@
import Konva from 'konva';
import {
DEFAULT_RECT_BACKGROUND,
RECIPIENT_COLOR_STYLES,
} from '@documenso/ui/lib/recipient-colors';
import { DEFAULT_SIGNATURE_TEXT_FONT_SIZE } from '../../constants/pdf';
import { AppError } from '../../errors/app-error';
import { upsertFieldGroup, upsertFieldRect } from './field-generic-items';
import {
createFieldHoverInteraction,
upsertFieldGroup,
upsertFieldRect,
} from './field-generic-items';
import { calculateFieldPosition } from './field-renderer';
import type { FieldToRender, RenderFieldElementOptions } from './field-renderer';
@ -212,33 +211,7 @@ export const renderSignatureFieldElement = (
fieldRect.opacity(0);
}
// Todo: Doesn't work.
if (mode !== 'export') {
const hoverColor = options.color
? RECIPIENT_COLOR_STYLES[options.color].baseRingHover
: '#e5e7eb';
// Todo: Envelopes - On hover add text color
// Add smooth transition-like behavior for hover effects
fieldGroup.on('mouseover', () => {
new Konva.Tween({
node: fieldRect,
duration: 0.3,
fill: hoverColor,
}).play();
});
fieldGroup.on('mouseout', () => {
new Konva.Tween({
node: fieldRect,
duration: 0.3,
fill: DEFAULT_RECT_BACKGROUND,
}).play();
});
fieldGroup.add(fieldRect);
}
createFieldHoverInteraction({ fieldGroup, fieldRect, options });
return {
fieldGroup,

View File

@ -1,13 +1,9 @@
import Konva from 'konva';
import {
DEFAULT_RECT_BACKGROUND,
RECIPIENT_COLOR_STYLES,
} from '@documenso/ui/lib/recipient-colors';
import { DEFAULT_STANDARD_FONT_SIZE } from '../../constants/pdf';
import type { TTextFieldMeta } from '../../types/field-meta';
import {
createFieldHoverInteraction,
konvaTextFill,
konvaTextFontFamily,
upsertFieldGroup,
@ -19,12 +15,12 @@ import { calculateFieldPosition } from './field-renderer';
const upsertFieldText = (field: FieldToRender, options: RenderFieldElementOptions): Konva.Text => {
const { pageWidth, pageHeight, mode = 'edit', pageLayer, translations } = options;
const fieldTypeName = translations?.[field.type] || field.type;
const { fieldWidth, fieldHeight } = calculateFieldPosition(field, pageWidth, pageHeight);
const textMeta = field.fieldMeta as TTextFieldMeta | undefined;
const fieldTypeName = translations?.[field.type] || field.type;
const fieldText: Konva.Text =
pageLayer.findOne(`#${field.renderId}-text`) ||
new Konva.Text({
@ -118,9 +114,8 @@ export const renderTextFieldElement = (
const isFirstRender = !pageLayer.findOne(`#${field.renderId}`);
const fieldGroup = upsertFieldGroup(field, options);
// Clear previous children and listeners to re-render fresh.
const fieldGroup = upsertFieldGroup(field, options);
fieldGroup.removeChildren();
fieldGroup.off('transform');
@ -183,33 +178,7 @@ export const renderTextFieldElement = (
fieldRect.opacity(0);
}
// Todo: Doesn't work.
if (mode !== 'export') {
const hoverColor = options.color
? RECIPIENT_COLOR_STYLES[options.color].baseRingHover
: '#e5e7eb';
// Todo: Envelopes - On hover add text color
// Add smooth transition-like behavior for hover effects
fieldGroup.on('mouseover', () => {
new Konva.Tween({
node: fieldRect,
duration: 0.3,
fill: hoverColor,
}).play();
});
fieldGroup.on('mouseout', () => {
new Konva.Tween({
node: fieldRect,
duration: 0.3,
fill: DEFAULT_RECT_BACKGROUND,
}).play();
});
fieldGroup.add(fieldRect);
}
createFieldHoverInteraction({ fieldGroup, fieldRect, options });
return {
fieldGroup,

View File

@ -353,6 +353,13 @@ export const formatDocumentAuditLogAction = (
}),
identified: msg`${prefix} deleted the document`,
}))
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELDS_AUTO_INSERTED }, () => ({
anonymous: msg({
message: `System auto inserted fields`,
context: `Audit log format`,
}),
identified: msg`System auto inserted fields`,
}))
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED }, () => ({
anonymous: msg({
message: `Field signed`,
@ -515,6 +522,14 @@ export const formatDocumentAuditLogAction = (
context: `Audit log format`,
}),
}))
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.ENVELOPE_ITEM_CREATED }, ({ data }) => ({
anonymous: msg`Envelope item created`,
identified: msg`${prefix} created an envelope item with title ${data.envelopeItemTitle}`,
}))
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.ENVELOPE_ITEM_DELETED }, ({ data }) => ({
anonymous: msg`Envelope item deleted`,
identified: msg`${prefix} deleted an envelope item with title ${data.envelopeItemTitle}`,
}))
.exhaustive();
return {

View File

@ -101,7 +101,7 @@ export const getClientSideFieldTranslations = ({ t }: I18n): Record<FieldType, s
[FieldType.TEXT]: t(msg`Text`),
[FieldType.CHECKBOX]: t(msg`Checkbox`),
[FieldType.RADIO]: t(msg`Radio`),
[FieldType.DROPDOWN]: t(msg`Dropdown`),
[FieldType.DROPDOWN]: t(msg`Select Option`),
[FieldType.SIGNATURE]: t(msg`Signature`),
[FieldType.FREE_SIGNATURE]: t(msg`Free Signature`),
[FieldType.INITIALS]: t(msg`Initials`),

View File

@ -1,6 +1,8 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import { prefixedId } from '@documenso/lib/universal/id';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
@ -14,7 +16,7 @@ export const createEnvelopeItemsRoute = authenticatedProcedure
.input(ZCreateEnvelopeItemsRequestSchema)
.output(ZCreateEnvelopeItemsResponseSchema)
.mutation(async ({ input, ctx }) => {
const { user, teamId } = ctx;
const { user, teamId, metadata } = ctx;
const { envelopeId, items } = input;
ctx.logger.info({
@ -110,17 +112,39 @@ export const createEnvelopeItemsRoute = authenticatedProcedure
const currentHighestOrderValue =
envelope.envelopeItems[envelope.envelopeItems.length - 1]?.order ?? 1;
const result = await prisma.envelopeItem.createManyAndReturn({
data: items.map((item) => ({
id: prefixedId('envelope_item'),
envelopeId,
title: item.title,
documentDataId: item.documentDataId,
order: currentHighestOrderValue + 1,
})),
include: {
documentData: true,
},
const result = await prisma.$transaction(async (tx) => {
const createdItems = await tx.envelopeItem.createManyAndReturn({
data: items.map((item) => ({
id: prefixedId('envelope_item'),
envelopeId,
title: item.title,
documentDataId: item.documentDataId,
order: currentHighestOrderValue + 1,
})),
include: {
documentData: true,
},
});
await tx.documentAuditLog.createMany({
data: createdItems.map((item) =>
createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.ENVELOPE_ITEM_CREATED,
envelopeId: envelope.id,
data: {
envelopeItemId: item.id,
envelopeItemTitle: item.title,
},
user: {
name: user.name,
email: user.email,
},
requestMetadata: metadata.requestMetadata,
}),
),
});
return createdItems;
});
return {

View File

@ -1,5 +1,7 @@
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getEnvelopeWhereInput } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { canEnvelopeItemsBeModified } from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
@ -13,7 +15,7 @@ export const deleteEnvelopeItemRoute = authenticatedProcedure
.input(ZDeleteEnvelopeItemRequestSchema)
.output(ZDeleteEnvelopeItemResponseSchema)
.mutation(async ({ input, ctx }) => {
const { user, teamId } = ctx;
const { user, teamId, metadata } = ctx;
const { envelopeId, envelopeItemId } = input;
ctx.logger.info({
@ -52,29 +54,48 @@ export const deleteEnvelopeItemRoute = authenticatedProcedure
});
}
const deletedEnvelopeItem = await prisma.envelopeItem.delete({
where: {
id: envelopeItemId,
envelopeId: envelope.id,
},
select: {
documentData: {
select: {
id: true,
const result = await prisma.$transaction(async (tx) => {
const deletedEnvelopeItem = await tx.envelopeItem.delete({
where: {
id: envelopeItemId,
envelopeId: envelope.id,
},
select: {
id: true,
title: true,
documentData: {
select: {
id: true,
},
},
},
},
});
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.ENVELOPE_ITEM_DELETED,
envelopeId: envelope.id,
data: {
envelopeItemId: deletedEnvelopeItem.id,
envelopeItemTitle: deletedEnvelopeItem.title,
},
user: {
name: user.name,
email: user.email,
},
requestMetadata: metadata.requestMetadata,
}),
});
return deletedEnvelopeItem;
});
// Todo: Envelopes [ASK] - Should we delete the document data?
await prisma.documentData.delete({
where: {
id: deletedEnvelopeItem.documentData.id,
id: result.documentData.id,
envelopeItem: {
is: null,
},
},
});
// Todo: Envelope [AUDIT_LOGS]
});

View File

@ -10,6 +10,8 @@ import { RecipientActionAuth } from '@documenso/lib/types/document-auth';
import { MultiSelect, type Option } from '@documenso/ui/primitives/multiselect';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { cn } from '../../lib/utils';
export interface RecipientActionAuthSelectProps {
value?: string[];
defaultValue?: string[];
@ -73,7 +75,11 @@ export const RecipientActionAuthSelect = ({
/>
<Tooltip>
<TooltipTrigger className="absolute right-2 top-1/2 -translate-y-1/2">
<TooltipTrigger
className={cn('absolute right-2 top-1/2 -translate-y-1/2', {
'right-8': selectedOptions.length > 0,
})}
>
<InfoIcon className="h-4 w-4" />
</TooltipTrigger>

View File

@ -9,6 +9,7 @@ export type RecipientColorStyles = {
base: string;
baseRing: string;
baseRingHover: string;
baseTextHover: string;
fieldButton: string;
fieldItem: string;
fieldItemInitials: string;
@ -24,6 +25,7 @@ export const RECIPIENT_COLOR_STYLES = {
base: 'ring-neutral-400',
baseRing: 'rgba(176, 176, 176, 1)',
baseRingHover: 'rgba(176, 176, 176, 1)',
baseTextHover: 'rgba(176, 176, 176, 1)',
fieldButton: 'border-neutral-400 hover:border-neutral-400',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: '',
@ -36,6 +38,7 @@ export const RECIPIENT_COLOR_STYLES = {
base: 'ring-recipient-green hover:bg-recipient-green/30',
baseRing: 'rgba(122, 195, 85, 1)',
baseRingHover: 'rgba(122, 195, 85, 0.3)',
baseTextHover: 'rgba(122, 195, 85, 1)',
fieldButton: 'hover:border-recipient-green hover:bg-recipient-green/30 ',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-green',
@ -48,6 +51,7 @@ export const RECIPIENT_COLOR_STYLES = {
base: 'ring-recipient-blue hover:bg-recipient-blue/30',
baseRing: 'rgba(56, 123, 199, 1)',
baseRingHover: 'rgba(56, 123, 199, 0.3)',
baseTextHover: 'rgba(56, 123, 199, 1)',
fieldButton: 'hover:border-recipient-blue hover:bg-recipient-blue/30',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-blue',
@ -60,6 +64,7 @@ export const RECIPIENT_COLOR_STYLES = {
base: 'ring-recipient-purple hover:bg-recipient-purple/30',
baseRing: 'rgba(151, 71, 255, 1)',
baseRingHover: 'rgba(151, 71, 255, 0.3)',
baseTextHover: 'rgba(151, 71, 255, 1)',
fieldButton: 'hover:border-recipient-purple hover:bg-recipient-purple/30',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-purple',
@ -72,6 +77,7 @@ export const RECIPIENT_COLOR_STYLES = {
base: 'ring-recipient-orange hover:bg-recipient-orange/30',
baseRing: 'rgba(246, 159, 30, 1)',
baseRingHover: 'rgba(246, 159, 30, 0.3)',
baseTextHover: 'rgba(246, 159, 30, 1)',
fieldButton: 'hover:border-recipient-orange hover:bg-recipient-orange/30',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-orange',
@ -84,6 +90,7 @@ export const RECIPIENT_COLOR_STYLES = {
base: 'ring-recipient-yellow hover:bg-recipient-yellow/30',
baseRing: 'rgba(219, 186, 0, 1)',
baseRingHover: 'rgba(219, 186, 0, 0.3)',
baseTextHover: 'rgba(219, 186, 0, 1)',
fieldButton: 'hover:border-recipient-yellow hover:bg-recipient-yellow/30',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-yellow',
@ -96,6 +103,7 @@ export const RECIPIENT_COLOR_STYLES = {
base: 'ring-recipient-pink hover:bg-recipient-pink/30',
baseRing: 'rgba(217, 74, 186, 1)',
baseRingHover: 'rgba(217, 74, 186, 0.3)',
baseTextHover: 'rgba(217, 74, 186, 1)',
fieldButton: 'hover:border-recipient-pink hover:bg-recipient-pink/30',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-pink',