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:
ephraimduncan
2026-07-02 06:48:23 +00:00
640 changed files with 42155 additions and 7972 deletions
@@ -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>
);
};
+2 -1
View File
@@ -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';
+1
View File
@@ -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",
+1 -1
View File
@@ -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}
+6 -6
View File
@@ -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>
))}
@@ -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" />
@@ -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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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" />
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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}