mirror of
https://github.com/documenso/documenso.git
synced 2026-07-25 01:15:49 +10:00
Merge remote-tracking branch 'origin/main' into pr-2508
# Conflicts: # apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents._index.tsx # packages/lib/server-only/document/find-documents.ts
This commit is contained in:
@@ -41,7 +41,7 @@ export const CopyTextButton = ({
|
||||
<Button
|
||||
type="button"
|
||||
variant="none"
|
||||
className="ml-2 h-7 rounded border bg-neutral-50 px-0.5 font-normal dark:border dark:border-neutral-500 dark:bg-neutral-600"
|
||||
className="ml-2 h-7 rounded-md border-border bg-muted px-0.5 font-normal"
|
||||
onClick={async () => onCopy()}
|
||||
>
|
||||
<AnimatePresence mode="wait" initial={false}>
|
||||
@@ -56,7 +56,7 @@ export const CopyTextButton = ({
|
||||
|
||||
<div
|
||||
className={cn(
|
||||
'flex h-6 w-6 items-center justify-center rounded transition-all hover:bg-neutral-200 hover:active:bg-neutral-300 dark:hover:bg-neutral-500 dark:hover:active:bg-neutral-400',
|
||||
'flex h-6 w-6 items-center justify-center rounded transition-all hover:bg-muted-foreground/10 hover:active:bg-muted-foreground/20',
|
||||
{
|
||||
'ml-1': Boolean(badgeContentCopied || badgeContentUncopied),
|
||||
},
|
||||
|
||||
@@ -13,334 +13,361 @@ type DocumentEmailCheckboxesProps = {
|
||||
value: Value;
|
||||
onChange: (value: Value) => void;
|
||||
className?: string;
|
||||
hiddenEvents?: readonly DocumentEmailEvents[];
|
||||
};
|
||||
|
||||
export const DocumentEmailCheckboxes = ({ value, onChange, className }: DocumentEmailCheckboxesProps) => {
|
||||
export const DocumentEmailCheckboxes = ({ value, onChange, className, hiddenEvents }: DocumentEmailCheckboxesProps) => {
|
||||
const isHidden = (event: DocumentEmailEvents) => hiddenEvents?.includes(event) ?? false;
|
||||
|
||||
return (
|
||||
<div className={cn('space-y-3', className)}>
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.RecipientSigned}
|
||||
className="h-5 w-5"
|
||||
checked={value.recipientSigned}
|
||||
onCheckedChange={(checked) => onChange({ ...value, [DocumentEmailEvents.RecipientSigned]: Boolean(checked) })}
|
||||
/>
|
||||
{!isHidden(DocumentEmailEvents.RecipientSigned) && (
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.RecipientSigned}
|
||||
className="h-5 w-5"
|
||||
checked={value.recipientSigned}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.RecipientSigned]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.RecipientSigned}
|
||||
>
|
||||
<Trans>Email the owner when a recipient signs</Trans>
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.RecipientSigned}
|
||||
>
|
||||
<Trans>Email the owner when a recipient signs</Trans>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Recipient signed email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Recipient signed email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<Trans>This email is sent to the document owner when a recipient has signed the document.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<p>
|
||||
<Trans>This email is sent to the document owner when a recipient has signed the document.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.RecipientSigningRequest}
|
||||
className="h-5 w-5"
|
||||
checked={value.recipientSigningRequest}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.RecipientSigningRequest]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
{!isHidden(DocumentEmailEvents.RecipientSigningRequest) && (
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.RecipientSigningRequest}
|
||||
className="h-5 w-5"
|
||||
checked={value.recipientSigningRequest}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.RecipientSigningRequest]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.RecipientSigningRequest}
|
||||
>
|
||||
<Trans>Email recipients with a signing request</Trans>
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.RecipientSigningRequest}
|
||||
>
|
||||
<Trans>Email recipients with a signing request</Trans>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Recipient signing request email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Recipient signing request email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<Trans>This email is sent to the recipient requesting them to sign the document.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<p>
|
||||
<Trans>This email is sent to the recipient requesting them to sign the document.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.RecipientRemoved}
|
||||
className="h-5 w-5"
|
||||
checked={value.recipientRemoved}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.RecipientRemoved]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
{!isHidden(DocumentEmailEvents.RecipientRemoved) && (
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.RecipientRemoved}
|
||||
className="h-5 w-5"
|
||||
checked={value.recipientRemoved}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.RecipientRemoved]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.RecipientRemoved}
|
||||
>
|
||||
<Trans>Email recipients when they're removed from a pending document</Trans>
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.RecipientRemoved}
|
||||
>
|
||||
<Trans>Email recipients when they're removed from a pending document</Trans>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Recipient removed email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Recipient removed email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<Trans>This email is sent to the recipient if they are removed from a pending document.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<p>
|
||||
<Trans>This email is sent to the recipient if they are removed from a pending document.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.DocumentPending}
|
||||
className="h-5 w-5"
|
||||
checked={value.documentPending}
|
||||
onCheckedChange={(checked) => onChange({ ...value, [DocumentEmailEvents.DocumentPending]: Boolean(checked) })}
|
||||
/>
|
||||
{!isHidden(DocumentEmailEvents.DocumentPending) && (
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.DocumentPending}
|
||||
className="h-5 w-5"
|
||||
checked={value.documentPending}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.DocumentPending]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.DocumentPending}
|
||||
>
|
||||
<Trans>Email the signer if the document is still pending</Trans>
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.DocumentPending}
|
||||
>
|
||||
<Trans>Email the signer if the document is still pending</Trans>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Document pending email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Document pending email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<Trans>
|
||||
This email will be sent to the recipient who has just signed the document, if there are still other
|
||||
recipients who have not signed yet.
|
||||
</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<p>
|
||||
<Trans>
|
||||
This email will be sent to the recipient who has just signed the document, if there are still other
|
||||
recipients who have not signed yet.
|
||||
</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.DocumentCompleted}
|
||||
className="h-5 w-5"
|
||||
checked={value.documentCompleted}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.DocumentCompleted]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
{!isHidden(DocumentEmailEvents.DocumentCompleted) && (
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.DocumentCompleted}
|
||||
className="h-5 w-5"
|
||||
checked={value.documentCompleted}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.DocumentCompleted]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.DocumentCompleted}
|
||||
>
|
||||
<Trans>Email recipients when the document is completed</Trans>
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.DocumentCompleted}
|
||||
>
|
||||
<Trans>Email recipients when the document is completed</Trans>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Document completed email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Document completed email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<Trans>This will be sent to all recipients once the document has been fully completed.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<p>
|
||||
<Trans>This will be sent to all recipients once the document has been fully completed.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.DocumentDeleted}
|
||||
className="h-5 w-5"
|
||||
checked={value.documentDeleted}
|
||||
onCheckedChange={(checked) => onChange({ ...value, [DocumentEmailEvents.DocumentDeleted]: Boolean(checked) })}
|
||||
/>
|
||||
{!isHidden(DocumentEmailEvents.DocumentDeleted) && (
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.DocumentDeleted}
|
||||
className="h-5 w-5"
|
||||
checked={value.documentDeleted}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.DocumentDeleted]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.DocumentDeleted}
|
||||
>
|
||||
<Trans>Email recipients when a pending document is deleted</Trans>
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.DocumentDeleted}
|
||||
>
|
||||
<Trans>Email recipients when a pending document is deleted</Trans>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Document deleted email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Document deleted email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<Trans>This will be sent to all recipients if a pending document has been deleted.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<p>
|
||||
<Trans>This will be sent to all recipients if a pending document has been deleted.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.OwnerDocumentCompleted}
|
||||
className="h-5 w-5"
|
||||
checked={value.ownerDocumentCompleted}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.OwnerDocumentCompleted]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
{!isHidden(DocumentEmailEvents.OwnerDocumentCompleted) && (
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.OwnerDocumentCompleted}
|
||||
className="h-5 w-5"
|
||||
checked={value.ownerDocumentCompleted}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.OwnerDocumentCompleted]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.OwnerDocumentCompleted}
|
||||
>
|
||||
<Trans>Email the owner when the document is completed</Trans>
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.OwnerDocumentCompleted}
|
||||
>
|
||||
<Trans>Email the owner when the document is completed</Trans>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Document completed email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Document completed email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<Trans>This will be sent to the document owner once the document has been fully completed.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<p>
|
||||
<Trans>This will be sent to the document owner once the document has been fully completed.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.OwnerDocumentCreated}
|
||||
className="h-5 w-5"
|
||||
checked={value.ownerDocumentCreated}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.OwnerDocumentCreated]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
{!isHidden(DocumentEmailEvents.OwnerDocumentCreated) && (
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.OwnerDocumentCreated}
|
||||
className="h-5 w-5"
|
||||
checked={value.ownerDocumentCreated}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.OwnerDocumentCreated]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.OwnerDocumentCreated}
|
||||
>
|
||||
<Trans>Email the owner when a document is created from a direct template</Trans>
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.OwnerDocumentCreated}
|
||||
>
|
||||
<Trans>Email the owner when a document is created from a direct template</Trans>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Document created from direct template email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Document created from direct template email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<Trans>
|
||||
This email is sent to the document owner when a recipient creates a document via a direct template
|
||||
link.
|
||||
</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<p>
|
||||
<Trans>
|
||||
This email is sent to the document owner when a recipient creates a document via a direct template
|
||||
link.
|
||||
</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.OwnerRecipientExpired}
|
||||
className="h-5 w-5"
|
||||
checked={value.ownerRecipientExpired}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.OwnerRecipientExpired]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
{!isHidden(DocumentEmailEvents.OwnerRecipientExpired) && (
|
||||
<div className="flex flex-row items-center">
|
||||
<Checkbox
|
||||
id={DocumentEmailEvents.OwnerRecipientExpired}
|
||||
className="h-5 w-5"
|
||||
checked={value.ownerRecipientExpired}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ ...value, [DocumentEmailEvents.OwnerRecipientExpired]: Boolean(checked) })
|
||||
}
|
||||
/>
|
||||
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.OwnerRecipientExpired}
|
||||
>
|
||||
<Trans>Send recipient expired email to the owner</Trans>
|
||||
<label
|
||||
className="ml-2 flex flex-row items-center text-muted-foreground text-sm"
|
||||
htmlFor={DocumentEmailEvents.OwnerRecipientExpired}
|
||||
>
|
||||
<Trans>Send recipient expired email to the owner</Trans>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Recipient expired email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
|
||||
<h2>
|
||||
<strong>
|
||||
<Trans>Recipient expired email</Trans>
|
||||
</strong>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
<Trans>This will be sent to the document owner when a recipient's signing window has expired.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
<p>
|
||||
<Trans>This will be sent to the document owner when a recipient's signing window has expired.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import { type Field, FieldType } from '@prisma/client';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import { FIELD_ROOT_CONTAINER_CLASS_NAME } from '../../lib/field-root-container-classes';
|
||||
import type { RecipientColorStyles } from '../../lib/recipient-colors';
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
@@ -117,7 +118,7 @@ export function FieldRootContainer({ field, children, color, className, readonly
|
||||
data-inserted={field.inserted ? 'true' : 'false'}
|
||||
data-readonly={readonly ? 'true' : 'false'}
|
||||
className={cn(
|
||||
'field--FieldRootContainer field-card-container dark-mode-disabled group relative z-20 flex h-full w-full items-center rounded-[2px] bg-white/90 ring-2 ring-gray-200 transition-all',
|
||||
FIELD_ROOT_CONTAINER_CLASS_NAME,
|
||||
color?.base,
|
||||
{
|
||||
'px-2': field.type !== FieldType.SIGNATURE && field.type !== FieldType.FREE_SIGNATURE,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
const FIELD_ROOT_CONTAINER_SHARED_CLASS_NAME =
|
||||
'field--FieldRootContainer field-card-container dark-mode-disabled group rounded-[2px] bg-white/90 ring-2 ring-gray-200 transition-all';
|
||||
|
||||
export const FIELD_ROOT_CONTAINER_CLASS_NAME = `${FIELD_ROOT_CONTAINER_SHARED_CLASS_NAME} relative z-20 flex h-full w-full items-center`;
|
||||
|
||||
export const FIELD_ROOT_CONTAINER_PROBE_CLASS_NAME = `field--FieldRootContainerProbe ${FIELD_ROOT_CONTAINER_SHARED_CLASS_NAME}`;
|
||||
|
||||
/**
|
||||
* Selector for the element the probe is appended into when reading computed
|
||||
* field styles. It must be an ancestor of where real fields render so the probe
|
||||
* inherits the same CSS cascade (custom embed CSS is commonly scoped under
|
||||
* `.embed--Root` / `.embed--DocumentContainer`).
|
||||
*/
|
||||
export const FIELD_PROBE_ANCHOR_SELECTOR = '.embed--DocumentContainer';
|
||||
@@ -61,6 +61,7 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^1.2.1",
|
||||
"cmdk": "^0.2.1",
|
||||
"colord": "^2.9.3",
|
||||
"framer-motion": "^12.23.24",
|
||||
"lucide-react": "^0.554.0",
|
||||
"luxon": "^3.7.2",
|
||||
|
||||
@@ -83,7 +83,7 @@ const CommandTextInput = React.forwardRef<
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:font-medium file:text-sm placeholder:text-muted-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
{
|
||||
'!ring-red-500 ring-2 transition-all': props['aria-invalid'],
|
||||
'!ring-destructive ring-2 transition-all': props['aria-invalid'],
|
||||
},
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -92,7 +92,7 @@ export const DocumentDropzone = ({
|
||||
// Disabled State
|
||||
<div className="flex">
|
||||
<motion.div
|
||||
className="a z-10 flex aspect-[3/4] w-24 origin-top-right -rotate-[22deg] flex-col gap-y-1 rounded-lg border border-muted-foreground/20 bg-white/80 px-2 py-4 backdrop-blur-sm group-hover:border-destructive/10 group-hover:bg-destructive/2 dark:bg-muted/80"
|
||||
className="a z-10 flex aspect-[3/4] w-24 origin-top-right -rotate-[22deg] flex-col gap-y-1 rounded-lg border border-muted-foreground/20 bg-background/80 px-2 py-4 backdrop-blur-sm group-hover:border-destructive/10 group-hover:bg-destructive/2"
|
||||
variants={DocumentDropzoneDisabledCardLeftVariants}
|
||||
>
|
||||
<div className="h-2 w-full rounded-[2px] bg-muted-foreground/10 group-hover:bg-destructive/10" />
|
||||
@@ -100,7 +100,7 @@ export const DocumentDropzone = ({
|
||||
<div className="h-2 w-full rounded-[2px] bg-muted-foreground/10 group-hover:bg-destructive/10" />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="z-20 flex aspect-[3/4] w-24 flex-col items-center justify-center gap-y-1 rounded-lg border border-muted-foreground/20 bg-white/80 px-2 py-4 backdrop-blur-sm group-hover:border-destructive/50 group-hover:bg-destructive/5 dark:bg-muted/80"
|
||||
className="z-20 flex aspect-[3/4] w-24 flex-col items-center justify-center gap-y-1 rounded-lg border border-muted-foreground/20 bg-background/80 px-2 py-4 backdrop-blur-sm group-hover:border-destructive/50 group-hover:bg-destructive/5"
|
||||
variants={DocumentDropzoneDisabledCardCenterVariants}
|
||||
>
|
||||
<AlertTriangle
|
||||
@@ -109,7 +109,7 @@ export const DocumentDropzone = ({
|
||||
/>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="z-10 flex aspect-[3/4] w-24 origin-top-left rotate-[22deg] flex-col gap-y-1 rounded-lg border border-muted-foreground/20 bg-white/80 px-2 py-4 backdrop-blur-sm group-hover:border-destructive/10 group-hover:bg-destructive/2 dark:bg-muted/80"
|
||||
className="z-10 flex aspect-[3/4] w-24 origin-top-left rotate-[22deg] flex-col gap-y-1 rounded-lg border border-muted-foreground/20 bg-background/80 px-2 py-4 backdrop-blur-sm group-hover:border-destructive/10 group-hover:bg-destructive/2"
|
||||
variants={DocumentDropzoneDisabledCardRightVariants}
|
||||
>
|
||||
<div className="h-2 w-full rounded-[2px] bg-muted-foreground/10 group-hover:bg-destructive/10" />
|
||||
@@ -121,7 +121,7 @@ export const DocumentDropzone = ({
|
||||
// Non Disabled State
|
||||
<div className="flex">
|
||||
<motion.div
|
||||
className="a z-10 flex aspect-[3/4] w-24 origin-top-right -rotate-[22deg] flex-col gap-y-1 rounded-lg border border-muted-foreground/20 bg-white/80 px-2 py-4 backdrop-blur-sm group-hover:border-documenso/80 dark:bg-muted/80"
|
||||
className="a z-10 flex aspect-[3/4] w-24 origin-top-right -rotate-[22deg] flex-col gap-y-1 rounded-lg border border-muted-foreground/20 bg-background/80 px-2 py-4 backdrop-blur-sm group-hover:border-documenso/80"
|
||||
variants={DocumentDropzoneCardLeftVariants}
|
||||
>
|
||||
<div className="h-2 w-full rounded-[2px] bg-muted-foreground/20 group-hover:bg-documenso" />
|
||||
@@ -129,13 +129,13 @@ export const DocumentDropzone = ({
|
||||
<div className="h-2 w-full rounded-[2px] bg-muted-foreground/20 group-hover:bg-documenso" />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="z-20 flex aspect-[3/4] w-24 flex-col items-center justify-center gap-y-1 rounded-lg border border-muted-foreground/20 bg-white/80 px-2 py-4 backdrop-blur-sm group-hover:border-documenso/80 dark:bg-muted/80"
|
||||
className="z-20 flex aspect-[3/4] w-24 flex-col items-center justify-center gap-y-1 rounded-lg border border-muted-foreground/20 bg-background/80 px-2 py-4 backdrop-blur-sm group-hover:border-documenso/80"
|
||||
variants={DocumentDropzoneCardCenterVariants}
|
||||
>
|
||||
<Plus strokeWidth="2px" className="h-12 w-12 text-muted-foreground/20 group-hover:text-documenso" />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="z-10 flex aspect-[3/4] w-24 origin-top-left rotate-[22deg] flex-col gap-y-1 rounded-lg border border-muted-foreground/20 bg-white/80 px-2 py-4 backdrop-blur-sm group-hover:border-documenso/80 dark:bg-muted/80"
|
||||
className="z-10 flex aspect-[3/4] w-24 origin-top-left rotate-[22deg] flex-col gap-y-1 rounded-lg border border-muted-foreground/20 bg-background/80 px-2 py-4 backdrop-blur-sm group-hover:border-documenso/80"
|
||||
variants={DocumentDropzoneCardRightVariants}
|
||||
>
|
||||
<div className="h-2 w-full rounded-[2px] bg-muted-foreground/20 group-hover:bg-documenso" />
|
||||
|
||||
@@ -920,7 +920,7 @@ export const AddFieldsFormPartial = ({
|
||||
{hasErrors && (
|
||||
<div className="mt-4">
|
||||
<ul>
|
||||
<li className="text-red-500 text-sm">
|
||||
<li className="text-destructive text-sm">
|
||||
<Trans>
|
||||
To proceed further, please set at least one value for the{' '}
|
||||
{emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'}{' '}
|
||||
|
||||
@@ -29,6 +29,7 @@ import { prop, sortBy } from 'remeda';
|
||||
import { DocumentReadOnlyFields, mapFieldsWithRecipients } from '../../components/document/document-read-only-fields';
|
||||
import type { RecipientAutoCompleteOption } from '../../components/recipient/recipient-autocomplete-input';
|
||||
import { RecipientAutoCompleteInput } from '../../components/recipient/recipient-autocomplete-input';
|
||||
import { Alert, AlertDescription } from '../alert';
|
||||
import { Button } from '../button';
|
||||
import { Checkbox } from '../checkbox';
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '../form/form';
|
||||
@@ -491,10 +492,24 @@ export const AddSignersFormPartial = ({
|
||||
void handleAutoSave();
|
||||
}, [form]);
|
||||
|
||||
const recipientCountLimit = organisation.organisationClaim.recipientCount;
|
||||
const isOverRecipientLimit = recipientCountLimit > 0 && signers.length > recipientCountLimit;
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocumentFlowFormContainerHeader title={documentFlow.title} description={documentFlow.description} />
|
||||
<DocumentFlowFormContainerContent>
|
||||
{isOverRecipientLimit && (
|
||||
<Alert variant="destructive" className="mb-4">
|
||||
<AlertDescription>
|
||||
<Trans>
|
||||
This envelope cannot have more than {recipientCountLimit} recipients. Please contact support if you need
|
||||
more.
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{isDocumentPdfLoaded && (
|
||||
<DocumentReadOnlyFields
|
||||
showRecipientColors={true}
|
||||
|
||||
@@ -102,12 +102,14 @@ export const AddSubjectFormPartial = ({
|
||||
: msg`Send`,
|
||||
[DocumentStatus.COMPLETED]: msg`Update`,
|
||||
[DocumentStatus.REJECTED]: msg`Update`,
|
||||
[DocumentStatus.CANCELLED]: msg`Update`,
|
||||
},
|
||||
[DocumentDistributionMethod.NONE]: {
|
||||
[DocumentStatus.DRAFT]: msg`Generate Links`,
|
||||
[DocumentStatus.PENDING]: msg`View Document`,
|
||||
[DocumentStatus.COMPLETED]: msg`View Document`,
|
||||
[DocumentStatus.REJECTED]: msg`View Document`,
|
||||
[DocumentStatus.CANCELLED]: msg`View Document`,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -326,7 +326,7 @@ export const FieldAdvancedSettings = forwardRef<HTMLDivElement, FieldAdvancedSet
|
||||
<div className="mt-4">
|
||||
<ul>
|
||||
{errors.map((error, index) => (
|
||||
<li className="text-red-500 text-sm" key={index}>
|
||||
<li className="text-destructive text-sm" key={index}>
|
||||
{error}
|
||||
</li>
|
||||
))}
|
||||
|
||||
+1
-1
@@ -240,7 +240,7 @@ export const CheckboxFieldAdvancedSettings = ({
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center text-slate-500 hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center text-muted-foreground hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
onClick={() => removeValue(index)}
|
||||
>
|
||||
<Trash className="h-5 w-5" />
|
||||
|
||||
+1
-1
@@ -162,7 +162,7 @@ export const DropdownFieldAdvancedSettings = ({
|
||||
<Input className="w-1/2" value={value.value} onChange={(e) => handleValueChange(index, e.target.value)} />
|
||||
<button
|
||||
type="button"
|
||||
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center text-slate-500 hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center text-muted-foreground hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
onClick={() => removeValue(index)}
|
||||
>
|
||||
<Trash className="h-5 w-5" />
|
||||
|
||||
@@ -176,7 +176,7 @@ export const RadioFieldAdvancedSettings = ({
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center text-slate-500 hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50 dark:text-white"
|
||||
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center text-muted-foreground hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
onClick={() => removeValue(value.id)}
|
||||
>
|
||||
<Trash className="h-5 w-5" />
|
||||
|
||||
@@ -38,7 +38,7 @@ export const FormErrorMessage = ({ error, className }: FormErrorMessageProps) =>
|
||||
opacity: 0,
|
||||
y: 10,
|
||||
}}
|
||||
className={cn('text-red-500 text-xs', className)}
|
||||
className={cn('text-destructive text-xs', className)}
|
||||
>
|
||||
{errorMessage}
|
||||
</motion.p>
|
||||
|
||||
@@ -156,7 +156,7 @@ const FormMessage = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<
|
||||
y: 10,
|
||||
}}
|
||||
>
|
||||
<p ref={ref} id={formMessageId} className={cn('text-red-500 text-xs', className)} {...props}>
|
||||
<p ref={ref} id={formMessageId} className={cn('text-destructive text-xs', className)} {...props}>
|
||||
{body}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
@@ -12,7 +12,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({ className, type,
|
||||
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:font-medium file:text-sm placeholder:text-muted-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className,
|
||||
{
|
||||
'!ring-red-500 ring-2 transition-all': props['aria-invalid'],
|
||||
'!ring-destructive ring-2 transition-all': props['aria-invalid'],
|
||||
},
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
@@ -119,7 +119,7 @@ export function MultiSelectCombobox<T = OptionValue>({
|
||||
<AnimatePresence>
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center">
|
||||
<Loader className="h-5 w-5 animate-spin text-gray-500 dark:text-gray-100" />
|
||||
<Loader className="h-5 w-5 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
) : (
|
||||
<AnimateGenericFadeInOut className="flex w-full justify-between">
|
||||
@@ -142,7 +142,7 @@ export function MultiSelectCombobox<T = OptionValue>({
|
||||
{showClearButton && !loading && (
|
||||
<div className="absolute top-0 right-8 bottom-0 flex items-center justify-center">
|
||||
<button
|
||||
className="flex h-4 w-4 items-center justify-center rounded-full bg-gray-300 dark:bg-neutral-700"
|
||||
className="flex h-4 w-4 items-center justify-center rounded-full bg-muted-foreground/20"
|
||||
onClick={() => onChange([])}
|
||||
>
|
||||
<XIcon className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
|
||||
@@ -31,7 +31,7 @@ const SelectTrigger = React.forwardRef<
|
||||
<AnimatePresence>
|
||||
{loading ? (
|
||||
<div className="flex w-full items-center justify-center">
|
||||
<Loader className="h-5 w-5 animate-spin text-gray-500 dark:text-gray-100" />
|
||||
<Loader className="h-5 w-5 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
) : (
|
||||
<AnimateGenericFadeInOut className="flex w-full justify-between">
|
||||
|
||||
@@ -28,7 +28,7 @@ export const SignaturePadType = ({ className, value, defaultValue, onChange }: S
|
||||
<input
|
||||
data-testid="signature-pad-type-input"
|
||||
placeholder={t`Type your signature`}
|
||||
className="w-full bg-transparent px-4 text-center font-signature text-7xl text-black placeholder:text-4xl focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 dark:text-white"
|
||||
className="w-full bg-transparent px-4 text-center font-signature text-7xl text-foreground placeholder:text-4xl focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0"
|
||||
// style={{ color: selectedColor }}
|
||||
value={value}
|
||||
onChange={(event) => {
|
||||
|
||||
@@ -169,26 +169,23 @@ export const SignaturePad = ({
|
||||
|
||||
<TabsContent
|
||||
value="draw"
|
||||
className="relative flex aspect-signature-pad items-center justify-center rounded-md border border-border bg-neutral-50 text-center dark:bg-background"
|
||||
className="relative flex aspect-signature-pad items-center justify-center rounded-md border border-border bg-muted/25 text-center"
|
||||
>
|
||||
<SignaturePadDraw className="h-full w-full" onChange={onDrawSignatureChange} value={drawSignature} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent
|
||||
value="text"
|
||||
className="relative flex aspect-signature-pad items-center justify-center rounded-md border border-border bg-neutral-50 text-center dark:bg-background"
|
||||
className="relative flex aspect-signature-pad items-center justify-center rounded-md border border-border bg-muted/25 text-center"
|
||||
>
|
||||
<SignaturePadType value={typedSignature} defaultValue={fullName} onChange={onTypedSignatureChange} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent
|
||||
value="image"
|
||||
className={cn(
|
||||
'relative aspect-signature-pad rounded-md border border-border bg-neutral-50 dark:bg-background',
|
||||
{
|
||||
'bg-white': imageSignature,
|
||||
},
|
||||
)}
|
||||
className={cn('relative aspect-signature-pad rounded-md border border-border bg-muted/25', {
|
||||
'bg-background': imageSignature,
|
||||
})}
|
||||
>
|
||||
<SignaturePadUpload value={imageSignature} onChange={onImageSignatureChange} />
|
||||
</TabsContent>
|
||||
|
||||
@@ -3,6 +3,8 @@ import { useEffect, useRef } from 'react';
|
||||
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
const SIGNATURE_FONT_FAMILY = 'Caveat';
|
||||
|
||||
export type SignatureRenderProps = {
|
||||
className?: string;
|
||||
value: string;
|
||||
@@ -30,7 +32,6 @@ export const SignatureRender = ({ className, value }: SignatureRenderProps) => {
|
||||
|
||||
const canvasWidth = $el.current.width;
|
||||
const canvasHeight = $el.current.height;
|
||||
const fontFamily = 'Caveat';
|
||||
|
||||
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
||||
ctx.textAlign = 'center';
|
||||
@@ -42,7 +43,7 @@ export const SignatureRender = ({ className, value }: SignatureRenderProps) => {
|
||||
|
||||
// Start with a base font size
|
||||
let fontSize = 18;
|
||||
ctx.font = `${fontSize}px ${fontFamily}`;
|
||||
ctx.font = `${fontSize}px ${SIGNATURE_FONT_FAMILY}`;
|
||||
|
||||
// Measure 10 characters and calculate scale factor
|
||||
const characterWidth = ctx.measureText('m'.repeat(10)).width;
|
||||
@@ -52,7 +53,7 @@ export const SignatureRender = ({ className, value }: SignatureRenderProps) => {
|
||||
fontSize = fontSize * scaleFactor;
|
||||
|
||||
// Adjust font size if it exceeds canvas width
|
||||
ctx.font = `${fontSize}px ${fontFamily}`;
|
||||
ctx.font = `${fontSize}px ${SIGNATURE_FONT_FAMILY}`;
|
||||
|
||||
const textWidth = ctx.measureText(value).width;
|
||||
|
||||
@@ -61,7 +62,7 @@ export const SignatureRender = ({ className, value }: SignatureRenderProps) => {
|
||||
}
|
||||
|
||||
// Set final font and render text
|
||||
ctx.font = `${fontSize}px ${fontFamily}`;
|
||||
ctx.font = `${fontSize}px ${SIGNATURE_FONT_FAMILY}`;
|
||||
ctx.fillText(value, canvasWidth / 2, canvasHeight / 2);
|
||||
};
|
||||
|
||||
@@ -110,11 +111,28 @@ export const SignatureRender = ({ className, value }: SignatureRenderProps) => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
if (isBase64Image(value)) {
|
||||
renderImageSignature();
|
||||
} else {
|
||||
renderTypedSignature();
|
||||
return;
|
||||
}
|
||||
|
||||
const renderWhenFontIsReady = async () => {
|
||||
try {
|
||||
await document.fonts?.load(`18px ${SIGNATURE_FONT_FAMILY}`);
|
||||
} finally {
|
||||
if (isMounted) {
|
||||
renderTypedSignature();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void renderWhenFontIsReady();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -944,7 +944,7 @@ export const AddTemplateFieldsFormPartial = ({
|
||||
{hasErrors && (
|
||||
<div className="mt-4">
|
||||
<ul>
|
||||
<li className="text-red-500 text-sm">
|
||||
<li className="text-destructive text-sm">
|
||||
<Trans>
|
||||
To proceed further, please set at least one value for the{' '}
|
||||
{emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'}{' '}
|
||||
|
||||
@@ -725,7 +725,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
|
||||
|
||||
{isSignerDirectRecipient(signer) ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger className="col-span-1 mt-auto inline-flex h-10 w-10 items-center justify-center text-slate-500 hover:opacity-80">
|
||||
<TooltipTrigger className="col-span-1 mt-auto inline-flex h-10 w-10 items-center justify-center text-muted-foreground hover:opacity-80">
|
||||
<Link2Icon className="h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="z-9999 max-w-md p-4 text-foreground">
|
||||
@@ -744,7 +744,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center justify-center text-slate-500 hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center justify-center text-muted-foreground hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
disabled={isSubmitting || signers.length === 1}
|
||||
onClick={() => onRemoveSigner(index)}
|
||||
data-testid="remove-placeholder-recipient-button"
|
||||
|
||||
@@ -11,7 +11,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({ classNa
|
||||
'flex h-20 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
{
|
||||
'!ring-red-500 ring-2 transition-all': props['aria-invalid'],
|
||||
'!ring-destructive ring-2 transition-all': props['aria-invalid'],
|
||||
},
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
Reference in New Issue
Block a user