fix: lint project (#2693)

This commit is contained in:
David Nguyen
2026-05-08 16:04:22 +10:00
committed by GitHub
parent edbf65969b
commit 8671f269e8
1414 changed files with 12867 additions and 24335 deletions
@@ -1,33 +1,8 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { Field } from '@prisma/client';
import { FieldType, Prisma, RecipientRole, SendStatus } from '@prisma/client';
import {
CalendarDays,
CheckSquare,
ChevronDown,
Contact,
Disc,
Hash,
Mail,
Type,
User,
} from 'lucide-react';
import { useFieldArray, useForm } from 'react-hook-form';
import { useHotkeys } from 'react-hotkeys-hook';
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
import { useAutoSave } from '@documenso/lib/client-only/hooks/use-autosave';
import { useDocumentElement } from '@documenso/lib/client-only/hooks/use-document-element';
import { PDF_VIEWER_PAGE_SELECTOR, getPdfPagesCount } from '@documenso/lib/constants/pdf-viewer';
import {
type TFieldMetaSchema as FieldMeta,
ZFieldMetaSchema,
} from '@documenso/lib/types/field-meta';
import { getPdfPagesCount, PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { type TFieldMetaSchema as FieldMeta, ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import type { TRecipientLite } from '@documenso/lib/types/recipient';
import { nanoid } from '@documenso/lib/universal/id';
import { ADVANCED_FIELD_TYPES_WITH_OPTIONAL_SETTING } from '@documenso/lib/utils/advanced-fields-helpers';
@@ -38,6 +13,16 @@ import {
canRecipientFieldsBeModified,
getRecipientsWithMissingFields,
} from '@documenso/lib/utils/recipients';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { Field } from '@prisma/client';
import { FieldType, Prisma, RecipientRole, SendStatus } from '@prisma/client';
import { CalendarDays, CheckSquare, ChevronDown, Contact, Disc, Hash, Mail, Type, User } from 'lucide-react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { useHotkeys } from 'react-hotkeys-hook';
import { FieldToolTip } from '../../components/field/field-tooltip';
import { getRecipientColorStyles } from '../../lib/recipient-colors';
@@ -111,8 +96,7 @@ export const AddFieldsFormPartial = ({
const { isWithinPageBounds, getFieldPosition, getPage } = useDocumentElement();
const { currentStep, totalSteps, previousStep } = useStep();
const canRenderBackButtonAsRemove =
currentStep === 1 && typeof documentFlow.onBackStep === 'function' && canGoBack;
const canRenderBackButtonAsRemove = currentStep === 1 && typeof documentFlow.onBackStep === 'function' && canGoBack;
const [showAdvancedSettings, setShowAdvancedSettings] = useState(false);
const [currentField, setCurrentField] = useState<FieldFormType>();
const [activeFieldId, setActiveFieldId] = useState<string | null>(null);
@@ -128,8 +112,7 @@ export const AddFieldsFormPartial = ({
pageY: Number(field.positionY),
pageWidth: Number(field.width),
pageHeight: Number(field.height),
signerEmail:
recipients.find((recipient) => recipient.id === field.recipientId)?.email ?? '',
signerEmail: recipients.find((recipient) => recipient.id === field.recipientId)?.email ?? '',
recipientId: field.recipientId,
fieldMeta: field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : undefined,
})),
@@ -174,12 +157,8 @@ export const AddFieldsFormPartial = ({
const [selectedField, setSelectedField] = useState<FieldType | null>(null);
const [selectedSigner, setSelectedSigner] = useState<TRecipientLite | null>(null);
const [lastActiveField, setLastActiveField] = useState<TAddFieldsFormSchema['fields'][0] | null>(
null,
);
const [fieldClipboard, setFieldClipboard] = useState<TAddFieldsFormSchema['fields'][0] | null>(
null,
);
const [lastActiveField, setLastActiveField] = useState<TAddFieldsFormSchema['fields'][0] | null>(null);
const [fieldClipboard, setFieldClipboard] = useState<TAddFieldsFormSchema['fields'][0] | null>(null);
const selectedSignerIndex = recipients.findIndex((r) => r.id === selectedSigner?.id);
const selectedSignerStyles = getRecipientColorStyles(selectedSignerIndex);
@@ -214,15 +193,12 @@ export const AddFieldsFormPartial = ({
[localFields],
);
const hasErrors =
emptyCheckboxFields.length > 0 || emptyRadioFields.length > 0 || emptySelectFields.length > 0;
const hasErrors = emptyCheckboxFields.length > 0 || emptyRadioFields.length > 0 || emptySelectFields.length > 0;
const fieldsWithError = useMemo(() => {
const fields = localFields.filter((field) => {
const hasError =
((field.type === FieldType.CHECKBOX ||
field.type === FieldType.RADIO ||
field.type === FieldType.DROPDOWN) &&
((field.type === FieldType.CHECKBOX || field.type === FieldType.RADIO || field.type === FieldType.DROPDOWN) &&
field.fieldMeta === undefined) ||
(field.fieldMeta && 'values' in field.fieldMeta && field?.fieldMeta?.values?.length === 0);
@@ -271,12 +247,7 @@ export const AddFieldsFormPartial = ({
const onMouseMove = useCallback(
(event: MouseEvent) => {
setIsFieldWithinBounds(
isWithinPageBounds(
event,
PDF_VIEWER_PAGE_SELECTOR,
fieldBounds.current.width,
fieldBounds.current.height,
),
isWithinPageBounds(event, PDF_VIEWER_PAGE_SELECTOR, fieldBounds.current.width, fieldBounds.current.height),
);
setCoords({
@@ -297,12 +268,7 @@ export const AddFieldsFormPartial = ({
if (
!$page ||
!isWithinPageBounds(
event,
PDF_VIEWER_PAGE_SELECTOR,
fieldBounds.current.width,
fieldBounds.current.height,
)
!isWithinPageBounds(event, PDF_VIEWER_PAGE_SELECTOR, fieldBounds.current.width, fieldBounds.current.height)
) {
setSelectedField(null);
return;
@@ -365,12 +331,7 @@ export const AddFieldsFormPartial = ({
return;
}
const {
x: pageX,
y: pageY,
width: pageWidth,
height: pageHeight,
} = getFieldPosition($page, node);
const { x: pageX, y: pageY, width: pageWidth, height: pageHeight } = getFieldPosition($page, node);
update(index, {
...field,
@@ -526,13 +487,11 @@ export const AddFieldsFormPartial = ({
useEffect(() => {
const recipientsByRoleToDisplay = recipients.filter(
(recipient) =>
recipient.role !== RecipientRole.CC && recipient.role !== RecipientRole.ASSISTANT,
(recipient) => recipient.role !== RecipientRole.CC && recipient.role !== RecipientRole.ASSISTANT,
);
setSelectedSigner(
recipientsByRoleToDisplay.find((r) => r.sendStatus !== SendStatus.SENT) ??
recipientsByRoleToDisplay[0],
recipientsByRoleToDisplay.find((r) => r.sendStatus !== SendStatus.SENT) ?? recipientsByRoleToDisplay[0],
);
}, [recipients]);
@@ -595,10 +554,7 @@ export const AddFieldsFormPartial = ({
{showAdvancedSettings && currentField ? (
<FieldAdvancedSettings
title={msg`Advanced settings`}
description={msg`Configure the ${parseMessageDescriptor(
_,
FRIENDLY_FIELD_TYPE[currentField.type],
)} field`}
description={msg`Configure the ${parseMessageDescriptor(_, FRIENDLY_FIELD_TYPE[currentField.type])} field`}
field={currentField}
fields={localFields}
onAdvancedSettings={handleAdvancedSettings}
@@ -614,17 +570,14 @@ export const AddFieldsFormPartial = ({
/>
) : (
<>
<DocumentFlowFormContainerHeader
title={documentFlow.title}
description={documentFlow.description}
/>
<DocumentFlowFormContainerHeader title={documentFlow.title} description={documentFlow.description} />
<DocumentFlowFormContainerContent>
<div className="flex flex-col">
{selectedField && (
<div
className={cn(
'dark:text-muted-background pointer-events-none fixed z-50 flex cursor-pointer flex-col items-center justify-center rounded-[2px] bg-white text-muted-foreground ring-2 transition duration-200 [container-type:size]',
'pointer-events-none fixed z-50 flex cursor-pointer flex-col items-center justify-center rounded-[2px] bg-white text-muted-foreground ring-2 transition duration-200 [container-type:size] dark:text-muted-background',
selectedSignerStyles?.base,
{
'-rotate-6 scale-90 opacity-50 dark:bg-black/20': !isFieldWithinBounds,
@@ -658,8 +611,7 @@ export const AddFieldsFormPartial = ({
recipientIndex={recipientIndex === -1 ? 0 : recipientIndex}
field={field}
disabled={
selectedSigner?.email !== field.signerEmail ||
!canRecipientBeModified(selectedSigner, fields)
selectedSigner?.email !== field.signerEmail || !canRecipientBeModified(selectedSigner, fields)
}
minHeight={MIN_HEIGHT_PX}
minWidth={MIN_WIDTH_PX}
@@ -704,7 +656,7 @@ export const AddFieldsFormPartial = ({
selectedRecipient={selectedSigner}
onSelectedRecipientChange={setSelectedSigner}
recipients={recipients}
className="mb-12 mt-2"
className="mt-2 mb-12"
/>
)}
@@ -726,7 +678,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 font-signature text-lg font-normal text-muted-foreground group-data-[selected]:text-foreground',
'flex items-center justify-center gap-x-1.5 font-normal font-signature text-lg text-muted-foreground group-data-[selected]:text-foreground',
)}
>
<Trans>Signature</Trans>
@@ -750,7 +702,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'flex items-center justify-center gap-x-1.5 font-normal text-muted-foreground text-sm group-data-[selected]:text-foreground',
)}
>
<Contact className="h-4 w-4" />
@@ -775,7 +727,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'flex items-center justify-center gap-x-1.5 font-normal text-muted-foreground text-sm group-data-[selected]:text-foreground',
)}
>
<Mail className="h-4 w-4" />
@@ -800,7 +752,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'flex items-center justify-center gap-x-1.5 font-normal text-muted-foreground text-sm group-data-[selected]:text-foreground',
)}
>
<User className="h-4 w-4" />
@@ -825,7 +777,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'flex items-center justify-center gap-x-1.5 font-normal text-muted-foreground text-sm group-data-[selected]:text-foreground',
)}
>
<CalendarDays className="h-4 w-4" />
@@ -850,7 +802,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'flex items-center justify-center gap-x-1.5 font-normal text-muted-foreground text-sm group-data-[selected]:text-foreground',
)}
>
<Type className="h-4 w-4" />
@@ -875,7 +827,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'flex items-center justify-center gap-x-1.5 font-normal text-muted-foreground text-sm group-data-[selected]:text-foreground',
)}
>
<Hash className="h-4 w-4" />
@@ -900,7 +852,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'flex items-center justify-center gap-x-1.5 font-normal text-muted-foreground text-sm group-data-[selected]:text-foreground',
)}
>
<Disc className="h-4 w-4" />
@@ -925,7 +877,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'flex items-center justify-center gap-x-1.5 font-normal text-muted-foreground text-sm group-data-[selected]:text-foreground',
)}
>
<CheckSquare className="h-4 w-4" />
@@ -950,7 +902,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'flex items-center justify-center gap-x-1.5 font-normal text-muted-foreground text-sm group-data-[selected]:text-foreground',
)}
>
<ChevronDown className="h-4 w-4" />
@@ -968,14 +920,10 @@ export const AddFieldsFormPartial = ({
{hasErrors && (
<div className="mt-4">
<ul>
<li className="text-sm text-red-500">
<li className="text-red-500 text-sm">
<Trans>
To proceed further, please set at least one value for the{' '}
{emptyCheckboxFields.length > 0
? 'Checkbox'
: emptyRadioFields.length > 0
? 'Radio'
: 'Select'}{' '}
{emptyCheckboxFields.length > 0 ? 'Checkbox' : emptyRadioFields.length > 0 ? 'Radio' : 'Select'}{' '}
field.
</Trans>
</li>
@@ -987,8 +935,7 @@ export const AddFieldsFormPartial = ({
<Alert variant="warning">
<AlertDescription>
<Trans>
This recipient can no longer be modified as they have signed a field, or completed
the document.
This recipient can no longer be modified as they have signed a field, or completed the document.
</Trans>
</AlertDescription>
</Alert>
@@ -1,8 +1,7 @@
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { FieldType } from '@prisma/client';
import { z } from 'zod';
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
export const ZAddFieldsFormSchema = z.object({
fields: z.array(
z.object({
@@ -1,18 +1,3 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import {
DocumentStatus,
DocumentVisibility,
type Field,
SendStatus,
TeamMemberRole,
} from '@prisma/client';
import { InfoIcon } from 'lucide-react';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
import { useAutoSave } from '@documenso/lib/client-only/hooks/use-autosave';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
@@ -39,21 +24,16 @@ import {
DocumentVisibilitySelect,
DocumentVisibilityTooltip,
} from '@documenso/ui/components/document/document-visibility-select';
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from '@documenso/ui/primitives/accordion';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@documenso/ui/primitives/accordion';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { MultiSelectCombobox } from '@documenso/ui/primitives/multi-select-combobox';
import { zodResolver } from '@hookform/resolvers/zod';
import { Trans, useLingui } from '@lingui/react/macro';
import { DocumentStatus, DocumentVisibility, type Field, SendStatus, TeamMemberRole } from '@prisma/client';
import { InfoIcon } from 'lucide-react';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
import { DocumentSignatureSettingsTooltip } from '../../components/document/document-signature-settings-tooltip';
import { Combobox } from '../combobox';
@@ -112,11 +92,10 @@ export const AddSettingsFormPartial = ({
meta: {
timezone:
TIME_ZONES.find((timezone) => timezone === document.documentMeta?.timezone) ??
DEFAULT_DOCUMENT_TIME_ZONE,
TIME_ZONES.find((timezone) => timezone === document.documentMeta?.timezone) ?? DEFAULT_DOCUMENT_TIME_ZONE,
dateFormat:
DATE_FORMATS.find((format) => format.value === document.documentMeta?.dateFormat)
?.value ?? DEFAULT_DOCUMENT_DATE_FORMAT,
DATE_FORMATS.find((format) => format.value === document.documentMeta?.dateFormat)?.value ??
DEFAULT_DOCUMENT_DATE_FORMAT,
redirectUrl: document.documentMeta?.redirectUrl ?? '',
language: document.documentMeta?.language ?? 'en',
signatureTypes: extractTeamSignatureSettings(document.documentMeta),
@@ -126,9 +105,7 @@ export const AddSettingsFormPartial = ({
const { stepIndex, currentStep, totalSteps, previousStep } = useStep();
const documentHasBeenSent = recipients.some(
(recipient) => recipient.sendStatus === SendStatus.SENT,
);
const documentHasBeenSent = recipients.some((recipient) => recipient.sendStatus === SendStatus.SENT);
const canUpdateVisibility = match(currentTeamMemberRole)
.with(TeamMemberRole.ADMIN, () => true)
@@ -149,11 +126,7 @@ export const AddSettingsFormPartial = ({
// We almost always want to set the timezone to the user's local timezone to avoid confusion
// when the document is signed.
useEffect(() => {
if (
!form.formState.touchedFields.meta?.timezone &&
!documentHasBeenSent &&
!document.documentMeta?.timezone
) {
if (!form.formState.touchedFields.meta?.timezone && !documentHasBeenSent && !document.documentMeta?.timezone) {
form.setValue('meta.timezone', Intl.DateTimeFormat().resolvedOptions().timeZone);
}
}, [
@@ -188,10 +161,7 @@ export const AddSettingsFormPartial = ({
return (
<>
<DocumentFlowFormContainerHeader
title={documentFlow.title}
description={documentFlow.description}
/>
<DocumentFlowFormContainerHeader title={documentFlow.title} description={documentFlow.description} />
<DocumentFlowFormContainerContent>
{isDocumentPdfLoaded && (
@@ -203,10 +173,7 @@ export const AddSettingsFormPartial = ({
)}
<Form {...form}>
<fieldset
className="flex h-full flex-col space-y-6"
disabled={form.formState.isSubmitting}
>
<fieldset className="flex h-full flex-col space-y-6" disabled={form.formState.isSubmitting}>
<FormField
control={form.control}
name="title"
@@ -244,9 +211,8 @@ export const AddSettingsFormPartial = ({
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
<Trans>
Controls the language for the document, including the language to be used
for email notifications, and the final certificate that is generated and
attached to the document.
Controls the language for the document, including the language to be used for email
notifications, and the final certificate that is generated and attached to the document.
</Trans>
</TooltipContent>
</Tooltip>
@@ -365,7 +331,7 @@ export const AddSettingsFormPartial = ({
<Trans>Advanced Options</Trans>
</AccordionTrigger>
<AccordionContent className="-mx-1 px-1 pt-2 text-sm leading-relaxed text-muted-foreground">
<AccordionContent className="-mx-1 px-1 pt-2 text-muted-foreground text-sm leading-relaxed">
<div className="flex flex-col space-y-6">
<FormField
control={form.control}
@@ -381,8 +347,8 @@ export const AddSettingsFormPartial = ({
<TooltipContent className="max-w-xs text-muted-foreground">
<Trans>
Add an external ID to the document. This can be used to identify
the document in external systems.
Add an external ID to the document. This can be used to identify the document in
external systems.
</Trans>
</TooltipContent>
</Tooltip>
@@ -507,9 +473,7 @@ export const AddSettingsFormPartial = ({
</TooltipTrigger>
<TooltipContent className="max-w-xs text-muted-foreground">
<Trans>
Add a URL to redirect the user to once the document is signed
</Trans>
<Trans>Add a URL to redirect the user to once the document is signed</Trans>
</TooltipContent>
</Tooltip>
</FormLabel>
@@ -1,26 +1,16 @@
import { msg } from '@lingui/core/macro';
import { DocumentVisibility } from '@prisma/client';
import { z } from 'zod';
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import { DocumentSignatureType } from '@documenso/lib/constants/document';
import { SUPPORTED_LANGUAGE_CODES } from '@documenso/lib/constants/i18n';
import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones';
import {
ZDocumentAccessAuthTypesSchema,
ZDocumentActionAuthTypesSchema,
} from '@documenso/lib/types/document-auth';
import {
ZDocumentMetaDateFormatSchema,
ZDocumentMetaTimezoneSchema,
} from '@documenso/lib/types/document-meta';
import { ZDocumentAccessAuthTypesSchema, ZDocumentActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
import { ZDocumentMetaDateFormatSchema, ZDocumentMetaTimezoneSchema } from '@documenso/lib/types/document-meta';
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
import { msg } from '@lingui/core/macro';
import { DocumentVisibility } from '@prisma/client';
import { z } from 'zod';
export const ZAddSettingsFormSchema = z.object({
title: z
.string()
.trim()
.min(1, { message: msg`Title cannot be empty`.id }),
title: z.string().trim().min(1, { message: msg`Title cannot be empty`.id }),
externalId: z.string().optional(),
visibility: z.nativeEnum(DocumentVisibility).optional(),
globalAccessAuth: z
@@ -36,8 +26,7 @@ export const ZAddSettingsFormSchema = z.object({
.string()
.optional()
.refine((value) => value === undefined || value === '' || isValidRedirectUrl(value), {
message:
'Please enter a valid URL, make sure you include http:// or https:// part of the url.',
message: 'Please enter a valid URL, make sure you include http:// or https:// part of the url.',
}),
language: z
.union([z.string(), z.enum(SUPPORTED_LANGUAGE_CODES)])
@@ -1,18 +1,3 @@
import { useCallback, useId, useMemo, useRef, useState } from 'react';
import type { DropResult, SensorAPI } from '@hello-pangea/dnd';
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { Field } from '@prisma/client';
import { DocumentSigningOrder, RecipientRole, SendStatus } from '@prisma/client';
import { motion } from 'framer-motion';
import { GripVerticalIcon, HelpCircle, Plus, Trash } from 'lucide-react';
import { useFieldArray, useForm } from 'react-hook-form';
import { prop, sortBy } from 'remeda';
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
import { useAutoSave } from '@documenso/lib/client-only/hooks/use-autosave';
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
@@ -27,11 +12,21 @@ import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animat
import { RecipientActionAuthSelect } from '@documenso/ui/components/recipient/recipient-action-auth-select';
import { RecipientRoleSelect } from '@documenso/ui/components/recipient/recipient-role-select';
import { cn } from '@documenso/ui/lib/utils';
import type { DropResult, SensorAPI } from '@hello-pangea/dnd';
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { Field } from '@prisma/client';
import { DocumentSigningOrder, RecipientRole, SendStatus } from '@prisma/client';
import { motion } from 'framer-motion';
import { GripVerticalIcon, HelpCircle, Plus, Trash } from 'lucide-react';
import { useCallback, useId, useMemo, useRef, useState } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { prop, sortBy } from 'remeda';
import {
DocumentReadOnlyFields,
mapFieldsWithRecipients,
} from '../../components/document/document-read-only-fields';
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 { Button } from '../button';
@@ -130,8 +125,7 @@ export const AddSignersFormPartial = ({
email: recipient.email,
role: recipient.role,
signingOrder: recipient.signingOrder ?? index + 1,
actionAuth:
ZRecipientAuthOptionsSchema.parse(recipient.authOptions)?.actionAuth ?? undefined,
actionAuth: ZRecipientAuthOptionsSchema.parse(recipient.authOptions)?.actionAuth ?? undefined,
})),
[prop('signingOrder'), 'asc'],
[prop('nativeId'), 'asc'],
@@ -147,14 +141,10 @@ export const AddSignersFormPartial = ({
const recipientHasAuthOptions = recipients.find((recipient) => {
const recipientAuthOptions = ZRecipientAuthOptionsSchema.parse(recipient.authOptions);
return (
recipientAuthOptions.accessAuth.length > 0 || recipientAuthOptions.actionAuth.length > 0
);
return recipientAuthOptions.accessAuth.length > 0 || recipientAuthOptions.actionAuth.length > 0;
});
const formHasActionAuth = form
.getValues('signers')
.find((signer) => signer.actionAuth.length > 0);
const formHasActionAuth = form.getValues('signers').find((signer) => signer.actionAuth.length > 0);
return recipientHasAuthOptions !== undefined || formHasActionAuth !== undefined;
}, [recipients, form]);
@@ -193,10 +183,7 @@ export const AddSignersFormPartial = ({
name: 'signers',
});
const emptySigners = useCallback(
() => form.getValues('signers').filter((signer) => signer.email === ''),
[form],
);
const emptySigners = useCallback(() => form.getValues('signers').filter((signer) => signer.email === ''), [form]);
const { scheduleSave } = useAutoSave(onAutoSave);
@@ -220,9 +207,7 @@ export const AddSignersFormPartial = ({
const updatedSigners = currentSigners.map((signer) => {
// Find the matching recipient from the response using nativeId
const matchingRecipient = response.recipients.find(
(recipient) => recipient.id === signer.nativeId,
);
const matchingRecipient = response.recipients.find((recipient) => recipient.id === signer.nativeId);
if (matchingRecipient) {
// Update the signer with the server-returned data, especially the ID
@@ -233,9 +218,7 @@ export const AddSignersFormPartial = ({
} else if (!signer.nativeId) {
// For new signers without nativeId, match by email and update with server ID
// Bug: Multiple recipients with the same email will be matched incorrectly here.
const newRecipient = response.recipients.find(
(recipient) => recipient.email === signer.email,
);
const newRecipient = response.recipients.find((recipient) => recipient.email === signer.email);
if (newRecipient) {
return {
...signer,
@@ -258,9 +241,7 @@ export const AddSignersFormPartial = ({
(signer) => signer.email.toLowerCase() === user?.email?.toLowerCase(),
);
const hasDocumentBeenSent = recipients.some(
(recipient) => recipient.sendStatus === SendStatus.SENT,
);
const hasDocumentBeenSent = recipients.some((recipient) => recipient.sendStatus === SendStatus.SENT);
const canRecipientBeModified = (recipientId?: number) => {
if (recipientId === undefined) {
@@ -335,8 +316,7 @@ export const AddSignersFormPartial = ({
email: user?.email ?? '',
role: RecipientRole.SIGNER,
actionAuth: [],
signingOrder:
signers.length > 0 ? (signers[signers.length - 1]?.signingOrder ?? 0) + 1 : 1,
signingOrder: signers.length > 0 ? (signers[signers.length - 1]?.signingOrder ?? 0) + 1 : 1,
},
{
shouldFocus: true,
@@ -347,10 +327,7 @@ export const AddSignersFormPartial = ({
}
};
const handleRecipientAutoCompleteSelect = (
index: number,
suggestion: RecipientAutoCompleteOption,
) => {
const handleRecipientAutoCompleteSelect = (index: number, suggestion: RecipientAutoCompleteOption) => {
setValue(`signers.${index}.email`, suggestion.email, {
shouldValidate: true,
shouldDirty: true,
@@ -363,7 +340,9 @@ export const AddSignersFormPartial = ({
const onDragEnd = useCallback(
async (result: DropResult) => {
if (!result.destination) return;
if (!result.destination) {
return;
}
const items = Array.from(watchedSigners);
const [reorderedSigner] = items.splice(result.source.index, 1);
@@ -514,10 +493,7 @@ export const AddSignersFormPartial = ({
return (
<>
<DocumentFlowFormContainerHeader
title={documentFlow.title}
description={documentFlow.description}
/>
<DocumentFlowFormContainerHeader title={documentFlow.title} description={documentFlow.description} />
<DocumentFlowFormContainerContent>
{isDocumentPdfLoaded && (
<DocumentReadOnlyFields
@@ -545,9 +521,7 @@ export const AddSignersFormPartial = ({
return;
}
field.onChange(
checked ? DocumentSigningOrder.SEQUENTIAL : DocumentSigningOrder.PARALLEL,
);
field.onChange(checked ? DocumentSigningOrder.SEQUENTIAL : DocumentSigningOrder.PARALLEL);
// If sequential signing is turned off, disable dictate next signer
if (!checked) {
@@ -623,8 +597,8 @@ export const AddSignersFormPartial = ({
<TooltipContent className="max-w-80 p-4">
<p>
<Trans>
When enabled, signers can choose who should sign next in the sequence
instead of following the predefined order.
When enabled, signers can choose who should sign next in the sequence instead of following
the predefined order.
</Trans>
</p>
</TooltipContent>
@@ -644,11 +618,7 @@ export const AddSignersFormPartial = ({
>
<Droppable droppableId="signers">
{(provided) => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
className="flex w-full flex-col gap-y-2"
>
<div {...provided.droppableProps} ref={provided.innerRef} className="flex w-full flex-col gap-y-2">
{signers.map((signer, index) => (
<Draggable
key={`${signer.id}-${signer.signingOrder}`}
@@ -667,8 +637,7 @@ export const AddSignersFormPartial = ({
{...provided.draggableProps}
{...provided.dragHandleProps}
className={cn('py-1', {
'pointer-events-none rounded-md bg-widget-foreground pt-2':
snapshot.isDragging,
'pointer-events-none rounded-md bg-widget-foreground pt-2': snapshot.isDragging,
})}
>
<motion.fieldset
@@ -685,14 +654,11 @@ export const AddSignersFormPartial = ({
name={`signers.${index}.signingOrder`}
render={({ field }) => (
<FormItem
className={cn(
'col-span-2 mt-auto flex items-center gap-x-1 space-y-0',
{
'mb-6':
form.formState.errors.signers?.[index] &&
!form.formState.errors.signers[index]?.signingOrder,
},
)}
className={cn('col-span-2 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>
@@ -758,9 +724,7 @@ export const AddSignersFormPartial = ({
!canRecipientBeModified(signer.nativeId)
}
options={recipientSuggestions}
onSelect={(suggestion) =>
handleRecipientAutoCompleteSelect(index, suggestion)
}
onSelect={(suggestion) => handleRecipientAutoCompleteSelect(index, suggestion)}
onSearchQueryChange={(query) => {
field.onChange(query);
setRecipientSearchQuery(query);
@@ -807,9 +771,7 @@ export const AddSignersFormPartial = ({
!canRecipientBeModified(signer.nativeId)
}
options={recipientSuggestions}
onSelect={(suggestion) =>
handleRecipientAutoCompleteSelect(index, suggestion)
}
onSelect={(suggestion) => handleRecipientAutoCompleteSelect(index, suggestion)}
onSearchQueryChange={(query) => {
field.onChange(query);
setRecipientSearchQuery(query);
@@ -825,37 +787,36 @@ export const AddSignersFormPartial = ({
)}
/>
{showAdvancedSettings &&
organisation.organisationClaim.flags.cfr21 && (
<FormField
control={form.control}
name={`signers.${index}.actionAuth`}
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,
})}
>
<FormControl>
<RecipientActionAuthSelect
{...field}
onValueChange={field.onChange}
disabled={
snapshot.isDragging ||
isSubmitting ||
!canRecipientBeModified(signer.nativeId)
}
/>
</FormControl>
{showAdvancedSettings && organisation.organisationClaim.flags.cfr21 && (
<FormField
control={form.control}
name={`signers.${index}.actionAuth`}
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,
})}
>
<FormControl>
<RecipientActionAuthSelect
{...field}
onValueChange={field.onChange}
disabled={
snapshot.isDragging ||
isSubmitting ||
!canRecipientBeModified(signer.nativeId)
}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
)}
<FormMessage />
</FormItem>
)}
/>
)}
<div className="col-span-2 flex gap-x-2">
<FormField
@@ -940,7 +901,7 @@ export const AddSignersFormPartial = ({
disabled={isSubmitting || signers.length >= remaining.recipients}
onClick={() => onAddSigner()}
>
<Plus className="-ml-1 mr-2 h-5 w-5" />
<Plus className="mr-2 -ml-1 h-5 w-5" />
<Trans>Add Signer</Trans>
</Button>
@@ -951,7 +912,7 @@ export const AddSignersFormPartial = ({
disabled={isSubmitting || isUserAlreadyARecipient}
onClick={() => onAddSelfSigner()}
>
<Plus className="-ml-1 mr-2 h-5 w-5" />
<Plus className="mr-2 -ml-1 h-5 w-5" />
<Trans>Add myself</Trans>
</Button>
</div>
@@ -965,10 +926,7 @@ export const AddSignersFormPartial = ({
onCheckedChange={(value) => setShowAdvancedSettings(Boolean(value))}
/>
<label
className="ml-2 text-sm text-muted-foreground"
htmlFor="showAdvancedRecipientSettings"
>
<label className="ml-2 text-muted-foreground text-sm" htmlFor="showAdvancedRecipientSettings">
<Trans>Show advanced settings</Trans>
</label>
</div>
@@ -1,10 +1,9 @@
import { ZRecipientActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
import { zEmail } from '@documenso/lib/utils/zod';
import { msg } from '@lingui/core/macro';
import { DocumentSigningOrder, RecipientRole } from '@prisma/client';
import { z } from 'zod';
import { ZRecipientActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
import { zEmail } from '@documenso/lib/utils/zod';
export const ZAddSignersFormSchema = z.object({
signers: z.array(
z.object({
@@ -1,15 +1,3 @@
import { useEffect } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { Field } from '@prisma/client';
import { DocumentDistributionMethod, DocumentStatus, RecipientRole } from '@prisma/client';
import { AnimatePresence, motion } from 'framer-motion';
import { InfoIcon } from 'lucide-react';
import { useForm } from 'react-hook-form';
import { useAutoSave } from '@documenso/lib/client-only/hooks/use-autosave';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
@@ -19,29 +7,23 @@ import type { TRecipientLite } from '@documenso/lib/types/recipient';
import { formatSigningLink } from '@documenso/lib/utils/recipients';
import { trpc } from '@documenso/trpc/react';
import { DocumentSendEmailMessageHelper } from '@documenso/ui/components/document/document-send-email-message-helper';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { Field } from '@prisma/client';
import { DocumentDistributionMethod, DocumentStatus, RecipientRole } from '@prisma/client';
import { AnimatePresence, motion } from 'framer-motion';
import { InfoIcon } from 'lucide-react';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { CopyTextButton } from '../../components/common/copy-text-button';
import { DocumentEmailCheckboxes } from '../../components/document/document-email-checkboxes';
import {
DocumentReadOnlyFields,
mapFieldsWithRecipients,
} from '../../components/document/document-read-only-fields';
import { DocumentReadOnlyFields, mapFieldsWithRecipients } from '../../components/document/document-read-only-fields';
import { AvatarWithText } from '../avatar';
import { Input } from '../input';
import { useStep } from '../stepper';
@@ -70,8 +52,8 @@ export type AddSubjectFormProps = {
export const AddSubjectFormPartial = ({
documentFlow,
recipients: recipients,
fields: fields,
recipients,
fields,
document,
onSubmit,
onAutoSave,
@@ -89,8 +71,7 @@ export const AddSubjectFormPartial = ({
// emailReplyName: document.documentMeta?.emailReplyName || undefined,
subject: document.documentMeta?.subject ?? '',
message: document.documentMeta?.message ?? '',
distributionMethod:
document.documentMeta?.distributionMethod || DocumentDistributionMethod.EMAIL,
distributionMethod: document.documentMeta?.distributionMethod || DocumentDistributionMethod.EMAIL,
emailSettings: ZDocumentEmailSettingsSchema.parse(document?.documentMeta?.emailSettings),
},
},
@@ -106,11 +87,10 @@ export const AddSubjectFormPartial = ({
formState: { isSubmitting },
} = form;
const { data: emailData, isLoading: isLoadingEmails } =
trpc.enterprise.organisation.email.find.useQuery({
organisationId: organisation.id,
perPage: 100,
});
const { data: emailData, isLoading: isLoadingEmails } = trpc.enterprise.organisation.email.find.useQuery({
organisationId: organisation.id,
perPage: 100,
});
const emails = emailData?.data || [];
@@ -168,10 +148,7 @@ export const AddSubjectFormPartial = ({
return (
<>
<DocumentFlowFormContainerHeader
title={documentFlow.title}
description={documentFlow.description}
/>
<DocumentFlowFormContainerHeader title={documentFlow.title} description={documentFlow.description} />
<DocumentFlowFormContainerContent>
<div className="flex flex-col">
{isDocumentPdfLoaded && (
@@ -226,9 +203,7 @@ export const AddSubjectFormPartial = ({
<Select
{...field}
value={field.value === null ? '-1' : field.value}
onValueChange={(value) =>
field.onChange(value === '-1' ? null : value)
}
onValueChange={(value) => field.onChange(value === '-1' ? null : value)}
>
<SelectTrigger loading={isLoadingEmails} className="bg-background">
<SelectValue />
@@ -259,8 +234,7 @@ export const AddSubjectFormPartial = ({
<FormItem>
<FormLabel>
<Trans>
Reply To Email{' '}
<span className="text-muted-foreground">(Optional)</span>
Reply To Email <span className="text-muted-foreground">(Optional)</span>
</Trans>
</FormLabel>
@@ -331,11 +305,7 @@ export const AddSubjectFormPartial = ({
</FormLabel>
<FormControl>
<Textarea
className="mt-2 h-16 resize-none bg-background"
{...field}
maxLength={5000}
/>
<Textarea className="mt-2 h-16 resize-none bg-background" {...field} maxLength={5000} />
</FormControl>
<FormMessage />
</FormItem>
@@ -361,15 +331,15 @@ export const AddSubjectFormPartial = ({
className="rounded-lg border"
>
{document.status === DocumentStatus.DRAFT ? (
<div className="py-16 text-center text-sm text-muted-foreground">
<div className="py-16 text-center text-muted-foreground 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.
We will generate signing links for you, which you can send to the recipients through your method
of choice.
</Trans>
</p>
</div>
@@ -382,17 +352,12 @@ export const AddSubjectFormPartial = ({
)}
{recipients.map((recipient) => (
<li
key={recipient.id}
className="flex items-center justify-between px-4 py-3 text-sm"
>
<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-sm text-muted-foreground">{recipient.email}</p>
}
primaryText={<p className="text-muted-foreground text-sm">{recipient.email}</p>}
secondaryText={
<p className="text-xs text-muted-foreground/70">
<p className="text-muted-foreground/70 text-xs">
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
</p>
}
@@ -404,9 +369,7 @@ export const AddSubjectFormPartial = ({
onCopySuccess={() => {
toast({
title: _(msg`Copied to clipboard`),
description: _(
msg`The signing link has been copied to your clipboard.`,
),
description: _(msg`The signing link has been copied to your clipboard.`),
});
}}
badgeContentUncopied={
@@ -1,8 +1,7 @@
import { DocumentDistributionMethod } from '@prisma/client';
import { z } from 'zod';
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
import { zEmail } from '@documenso/lib/utils/zod';
import { DocumentDistributionMethod } from '@prisma/client';
import { z } from 'zod';
export const ZAddSubjectFormSchema = z.object({
meta: z.object({
@@ -11,10 +10,7 @@ export const ZAddSubjectFormSchema = z.object({
// emailReplyName: z.string().optional(),
subject: z.string(),
message: z.string(),
distributionMethod: z
.nativeEnum(DocumentDistributionMethod)
.optional()
.default(DocumentDistributionMethod.EMAIL),
distributionMethod: z.nativeEnum(DocumentDistributionMethod).optional().default(DocumentDistributionMethod.EMAIL),
emailSettings: ZDocumentEmailSettingsSchema,
}),
});
@@ -1,11 +1,10 @@
import type { HTMLAttributes } from 'react';
import React from 'react';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { motion } from 'framer-motion';
import type React from 'react';
import type { HTMLAttributes } from 'react';
import { cn } from '../../lib/utils';
import { Button } from '../button';
@@ -39,19 +38,16 @@ export type DocumentFlowFormContainerHeaderProps = {
description: MessageDescriptor;
};
export const DocumentFlowFormContainerHeader = ({
title,
description,
}: DocumentFlowFormContainerHeaderProps) => {
export const DocumentFlowFormContainerHeader = ({ title, description }: DocumentFlowFormContainerHeaderProps) => {
const { _ } = useLingui();
return (
<>
<h3 className="text-2xl font-semibold text-foreground">{_(title)}</h3>
<h3 className="font-semibold text-2xl text-foreground">{_(title)}</h3>
<p className="mt-2 text-sm text-muted-foreground">{_(description)}</p>
<p className="mt-2 text-muted-foreground text-sm">{_(description)}</p>
<hr className="mb-8 mt-4 border-border" />
<hr className="mt-4 mb-8 border-border" />
</>
);
};
@@ -93,13 +89,10 @@ export type DocumentFlowFormContainerStepProps = {
maxStep: number;
};
export const DocumentFlowFormContainerStep = ({
step,
maxStep,
}: DocumentFlowFormContainerStepProps) => {
export const DocumentFlowFormContainerStep = ({ step, maxStep }: DocumentFlowFormContainerStepProps) => {
return (
<div>
<p className="text-sm text-muted-foreground">
<p className="text-muted-foreground text-sm">
<Trans>
Step <span>{`${step} of ${maxStep}`}</span>
</Trans>
@@ -1,16 +1,12 @@
import { convertToLocalSystemFormat, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import type { TFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { fromCheckboxValue } from '@documenso/lib/universal/field-checkbox';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { DocumentMeta, Signature } from '@prisma/client';
import { FieldType } from '@prisma/client';
import { ChevronDown } from 'lucide-react';
import {
DEFAULT_DOCUMENT_DATE_FORMAT,
convertToLocalSystemFormat,
} from '@documenso/lib/constants/date-formats';
import type { TFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { fromCheckboxValue } from '@documenso/lib/universal/field-checkbox';
import { cn } from '../../lib/utils';
import { Checkbox } from '../checkbox';
import { Label } from '../label';
@@ -62,7 +58,7 @@ export const FieldContent = ({ field, documentMeta }: FieldIconProps) => {
>
<div className="flex items-center">
<Checkbox className="h-3 w-3" disabled />
<Label className="ml-1.5 text-xs font-normal text-foreground opacity-50">
<Label className="ml-1.5 font-normal text-foreground text-xs opacity-50">
<Trans>Checkbox option</Trans>
</Label>
</div>
@@ -88,10 +84,7 @@ export const FieldContent = ({ field, documentMeta }: FieldIconProps) => {
/>
{item.value && (
<Label
htmlFor={`checkbox-${index}`}
className="ml-1.5 text-xs font-normal text-foreground"
>
<Label htmlFor={`checkbox-${index}`} className="ml-1.5 font-normal text-foreground text-xs">
{item.value}
</Label>
)}
@@ -120,10 +113,7 @@ export const FieldContent = ({ field, documentMeta }: FieldIconProps) => {
checked={item.value === field.customText}
/>
{item.value && (
<Label
htmlFor={`option-${index}`}
className="ml-1.5 text-xs font-normal text-foreground"
>
<Label htmlFor={`option-${index}`} className="ml-1.5 font-normal text-foreground text-xs">
{item.value}
</Label>
)}
@@ -134,13 +124,9 @@ export const FieldContent = ({ field, documentMeta }: FieldIconProps) => {
);
}
if (
field.type === FieldType.DROPDOWN &&
field.fieldMeta?.type === 'dropdown' &&
!field.inserted
) {
if (field.type === FieldType.DROPDOWN && field.fieldMeta?.type === 'dropdown' && !field.inserted) {
return (
<div className="flex flex-row items-center py-0.5 text-[clamp(0.07rem,25cqw,0.825rem)] text-sm text-field-card-foreground">
<div className="flex flex-row items-center py-0.5 text-[clamp(0.07rem,25cqw,0.825rem)] text-field-card-foreground text-sm">
<p>
<Trans>Select</Trans>
</p>
@@ -149,25 +135,16 @@ export const FieldContent = ({ field, documentMeta }: FieldIconProps) => {
);
}
if (
field.type === FieldType.SIGNATURE &&
field.signature?.signatureImageAsBase64 &&
field.inserted
) {
if (field.type === FieldType.SIGNATURE && field.signature?.signatureImageAsBase64 && field.inserted) {
return (
<img
src={field.signature.signatureImageAsBase64}
alt="Signature"
className="h-full w-full object-contain"
/>
<img src={field.signature.signatureImageAsBase64} alt="Signature" className="h-full w-full object-contain" />
);
}
const labelToDisplay = fieldMeta?.label || _(FRIENDLY_FIELD_TYPE[type]) || '';
let textToDisplay: string | undefined;
const isSignatureField =
field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE;
const isSignatureField = field.type === FieldType.SIGNATURE || field.type === FieldType.FREE_SIGNATURE;
if (field.type === FieldType.TEXT && field.fieldMeta?.type === 'text' && field.fieldMeta?.text) {
textToDisplay = field.fieldMeta.text;
@@ -1,18 +1,10 @@
import { forwardRef, useEffect, useState } from 'react';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { FieldType } from '@prisma/client';
import { match } from 'ts-pattern';
import { useAutoSave } from '@documenso/lib/client-only/hooks/use-autosave';
import {
type TBaseFieldMeta as BaseFieldMeta,
type TCheckboxFieldMeta as CheckboxFieldMeta,
type TDateFieldMeta as DateFieldMeta,
DEFAULT_DATE_OVERFLOW_MODE,
DEFAULT_EMAIL_OVERFLOW_MODE,
type TDateFieldMeta as DateFieldMeta,
type TDropdownFieldMeta as DropdownFieldMeta,
type TEmailFieldMeta as EmailFieldMeta,
type TFieldMetaSchema as FieldMeta,
@@ -24,6 +16,12 @@ import {
ZFieldMetaSchema,
} from '@documenso/lib/types/field-meta';
import { useToast } from '@documenso/ui/primitives/use-toast';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { FieldType } from '@prisma/client';
import { forwardRef, useEffect, useState } from 'react';
import { match } from 'ts-pattern';
import type { FieldFormType } from './add-fields';
import {
@@ -152,19 +150,7 @@ const getDefaultState = (fieldType: FieldType): FieldMeta => {
};
export const FieldAdvancedSettings = forwardRef<HTMLDivElement, FieldAdvancedSettingsProps>(
(
{
title,
description,
field,
fields,
onAdvancedSettings,
isDocumentPdfLoaded = true,
onSave,
onAutoSave,
},
ref,
) => {
({ title, description, field, fields, onAdvancedSettings, isDocumentPdfLoaded = true, onSave, onAutoSave }, ref) => {
const { _ } = useLingui();
const { toast } = useToast();
@@ -212,17 +198,10 @@ export const FieldAdvancedSettings = forwardRef<HTMLDivElement, FieldAdvancedSet
const handleFieldChange = (
key: FieldMetaKeys,
value:
| string
| { checked: boolean; value: string }[]
| { value: string }[]
| boolean
| number,
value: string | { checked: boolean; value: string }[] | { value: string }[] | boolean | number,
) => {
setFieldState((prevState: FieldMeta) => {
if (
['characterLimit', 'minValue', 'maxValue', 'validationLength', 'fontSize'].includes(key)
) {
if (['characterLimit', 'minValue', 'maxValue', 'validationLength', 'fontSize'].includes(key)) {
const parsedValue = Number(value);
return {
@@ -271,9 +250,7 @@ export const FieldAdvancedSettings = forwardRef<HTMLDivElement, FieldAdvancedSet
key={index}
field={localField}
disabled={true}
fieldClassName={
localField.formId === field.formId ? 'ring-red-400' : 'ring-neutral-200'
}
fieldClassName={localField.formId === field.formId ? 'ring-red-400' : 'ring-neutral-200'}
/>
</span>
))}
@@ -349,7 +326,7 @@ export const FieldAdvancedSettings = forwardRef<HTMLDivElement, FieldAdvancedSet
<div className="mt-4">
<ul>
{errors.map((error, index) => (
<li className="text-sm text-red-500" key={index}>
<li className="text-red-500 text-sm" key={index}>
{error}
</li>
))}
@@ -358,10 +335,7 @@ export const FieldAdvancedSettings = forwardRef<HTMLDivElement, FieldAdvancedSet
)}
</DocumentFlowFormContainerContent>
<DocumentFlowFormContainerFooter
className="mt-auto"
data-testid="field-advanced-settings-footer"
>
<DocumentFlowFormContainerFooter className="mt-auto" data-testid="field-advanced-settings-footer">
<DocumentFlowFormContainerActions
goNextLabel={msg`Save`}
goBackLabel={msg`Cancel`}
@@ -1,19 +1,17 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { FieldType } from '@prisma/client';
import { CopyPlus, Settings2, SquareStack, Trash } from 'lucide-react';
import { createPortal } from 'react-dom';
import { Rnd } from 'react-rnd';
import { useSearchParams } from 'react-router';
import { useElementBounds } from '@documenso/lib/client-only/hooks/use-element-bounds';
import { useIsPageInDom } from '@documenso/lib/client-only/hooks/use-is-page-in-dom';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import type { TFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { ZCheckboxFieldMeta, ZRadioFieldMeta } from '@documenso/lib/types/field-meta';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { FieldType } from '@prisma/client';
import { CopyPlus, Settings2, SquareStack, Trash } from 'lucide-react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { Rnd } from 'react-rnd';
import { useSearchParams } from 'react-router';
import { getRecipientColorStyles } from '../../lib/recipient-colors';
import { cn } from '../../lib/utils';
@@ -96,9 +94,7 @@ const FieldItemInner = ({
const [settingsActive, setSettingsActive] = useState(false);
const $el = useRef<HTMLDivElement>(null);
const $pageBounds = useElementBounds(
`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`,
);
const $pageBounds = useElementBounds(`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`);
const signerStyles = getRecipientColorStyles(recipientIndex);
@@ -275,20 +271,16 @@ const FieldItemInner = ({
onMove?.(d.node);
}}
>
{(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) &&
field.fieldMeta?.label && (
<div
className={cn(
'absolute -top-16 left-0 right-0 rounded-md p-2 text-center text-xs text-gray-700',
{
'border border-primary bg-foreground/5': !fieldHasCheckedValues,
'border border-primary bg-documenso-200': fieldHasCheckedValues,
},
)}
>
{field.fieldMeta.label}
</div>
)}
{(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) && field.fieldMeta?.label && (
<div
className={cn('absolute -top-16 right-0 left-0 rounded-md p-2 text-center text-gray-700 text-xs', {
'border border-primary bg-foreground/5': !fieldHasCheckedValues,
'border border-primary bg-documenso-200': fieldHasCheckedValues,
})}
>
{field.fieldMeta.label}
</div>
)}
<div
className={cn(
@@ -317,18 +309,17 @@ const FieldItemInner = ({
<FieldContent field={field} />
{/* On hover, display recipient initials on side of field. */}
<div className="absolute -right-5 top-0 z-20 hidden h-full w-5 items-center justify-center group-hover:flex">
<div className="absolute top-0 -right-5 z-20 hidden h-full w-5 items-center justify-center group-hover:flex">
<div
className={cn(
'flex h-5 w-5 flex-col items-center justify-center rounded-r-md text-[0.5rem] font-bold text-white opacity-0 transition duration-200 group-hover/field-item:opacity-100',
'flex h-5 w-5 flex-col items-center justify-center rounded-r-md font-bold text-[0.5rem] text-white opacity-0 transition duration-200 group-hover/field-item:opacity-100',
signerStyles.fieldItemInitials,
{
'!opacity-50': disabled || passive,
},
)}
>
{(field.signerEmail?.charAt(0)?.toUpperCase() ?? '') +
(field.signerEmail?.charAt(1)?.toUpperCase() ?? '')}
{(field.signerEmail?.charAt(0)?.toUpperCase() ?? '') + (field.signerEmail?.charAt(1)?.toUpperCase() ?? '')}
</div>
</div>
@@ -1,24 +1,16 @@
import { useEffect, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { ChevronDown, ChevronUp, Trash } from 'lucide-react';
import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
import { type TCheckboxFieldMeta as CheckboxFieldMeta } from '@documenso/lib/types/field-meta';
import type { TCheckboxFieldMeta as CheckboxFieldMeta } from '@documenso/lib/types/field-meta';
import { Button } from '@documenso/ui/primitives/button';
import { Checkbox } from '@documenso/ui/primitives/checkbox';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Switch } from '@documenso/ui/primitives/switch';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { ChevronDown, ChevronUp, Trash } from 'lucide-react';
import { useEffect, useState } from 'react';
import { checkboxValidationLength, checkboxValidationRules } from './constants';
@@ -44,19 +36,14 @@ export const CheckboxFieldAdvancedSettings = ({
const [required, setRequired] = useState(fieldState.required ?? false);
const [validationLength, setValidationLength] = useState(fieldState.validationLength ?? 0);
const [validationRule, setValidationRule] = useState(fieldState.validationRule ?? '');
const [direction, setDirection] = useState<'vertical' | 'horizontal'>(
fieldState.direction ?? 'vertical',
);
const [direction, setDirection] = useState<'vertical' | 'horizontal'>(fieldState.direction ?? 'vertical');
const handleToggleChange = (field: keyof CheckboxFieldMeta, value: string | boolean) => {
const readOnly = field === 'readOnly' ? Boolean(value) : Boolean(fieldState.readOnly);
const required = field === 'required' ? Boolean(value) : Boolean(fieldState.required);
const validationRule =
field === 'validationRule' ? String(value) : String(fieldState.validationRule);
const validationLength =
field === 'validationLength' ? Number(value) : Number(fieldState.validationLength);
const currentDirection =
field === 'direction' && String(value) === 'horizontal' ? 'horizontal' : 'vertical';
const validationRule = field === 'validationRule' ? String(value) : String(fieldState.validationRule);
const validationLength = field === 'validationLength' ? Number(value) : Number(fieldState.validationLength);
const currentDirection = field === 'direction' && String(value) === 'horizontal' ? 'horizontal' : 'vertical';
setReadOnly(readOnly);
setRequired(required);
@@ -102,7 +89,9 @@ export const CheckboxFieldAdvancedSettings = ({
}, [values]);
const removeValue = (index: number) => {
if (values.length === 1) return;
if (values.length === 1) {
return;
}
const newValues = [...values];
newValues.splice(index, 1);
@@ -110,11 +99,7 @@ export const CheckboxFieldAdvancedSettings = ({
handleFieldChange('values', newValues);
};
const handleCheckboxValue = (
index: number,
property: 'value' | 'checked',
newValue: string | boolean,
) => {
const handleCheckboxValue = (index: number, property: 'value' | 'checked', newValue: string | boolean) => {
const newValues = [...values];
if (property === 'checked') {
@@ -139,7 +124,7 @@ export const CheckboxFieldAdvancedSettings = ({
</Label>
<Input
id="label"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={_(msg`Field label`)}
value={fieldState.label}
onChange={(e) => handleFieldChange('label', e.target.value)}
@@ -154,7 +139,7 @@ export const CheckboxFieldAdvancedSettings = ({
value={fieldState.direction ?? 'vertical'}
onValueChange={(val) => handleToggleChange('direction', val)}
>
<SelectTrigger className="text-muted-foreground bg-background mt-2 w-full">
<SelectTrigger className="mt-2 w-full bg-background text-muted-foreground">
<SelectValue placeholder={_(msg`Select direction`)} />
</SelectTrigger>
<SelectContent position="popper">
@@ -173,11 +158,8 @@ export const CheckboxFieldAdvancedSettings = ({
<Label>
<Trans>Validation</Trans>
</Label>
<Select
value={fieldState.validationRule}
onValueChange={(val) => handleToggleChange('validationRule', val)}
>
<SelectTrigger className="text-muted-foreground bg-background mt-2 w-full">
<Select value={fieldState.validationRule} onValueChange={(val) => handleToggleChange('validationRule', val)}>
<SelectTrigger className="mt-2 w-full bg-background text-muted-foreground">
<SelectValue placeholder={_(msg`Select at least`)} />
</SelectTrigger>
<SelectContent position="popper">
@@ -194,7 +176,7 @@ export const CheckboxFieldAdvancedSettings = ({
value={fieldState.validationLength ? String(fieldState.validationLength) : ''}
onValueChange={(val) => handleToggleChange('validationLength', val)}
>
<SelectTrigger className="text-muted-foreground bg-background mt-2 w-full">
<SelectTrigger className="mt-2 w-full bg-background text-muted-foreground">
<SelectValue placeholder={_(msg`Pick a number`)} />
</SelectTrigger>
<SelectContent position="popper">
@@ -230,7 +212,7 @@ export const CheckboxFieldAdvancedSettings = ({
</div>
</div>
<Button
className="bg-foreground/10 hover:bg-foreground/5 border-foreground/10 mt-2 border"
className="mt-2 border border-foreground/10 bg-foreground/10 hover:bg-foreground/5"
variant="outline"
onClick={() => setShowValidation((prev) => !prev)}
>
@@ -247,7 +229,7 @@ export const CheckboxFieldAdvancedSettings = ({
{values.map((value, index) => (
<div key={index} className="mt-2 flex items-center gap-4">
<Checkbox
className="data-[state=checked]:bg-primary border-foreground/30 h-5 w-5"
className="h-5 w-5 border-foreground/30 data-[state=checked]:bg-primary"
checked={value.checked}
onCheckedChange={(checked) => handleCheckboxValue(index, 'checked', checked)}
/>
@@ -266,7 +248,7 @@ export const CheckboxFieldAdvancedSettings = ({
</div>
))}
<Button
className="bg-foreground/10 hover:bg-foreground/5 border-foreground/10 ml-9 mt-4 border"
className="mt-4 ml-9 border border-foreground/10 bg-foreground/10 hover:bg-foreground/5"
variant="outline"
onClick={addValue}
>
@@ -1,19 +1,9 @@
import { Trans, useLingui } from '@lingui/react/macro';
import { validateFields as validateDateFields } from '@documenso/lib/advanced-fields-validation/validate-fields';
import {
DEFAULT_DATE_OVERFLOW_MODE,
type TDateFieldMeta as DateFieldMeta,
} from '@documenso/lib/types/field-meta';
import { type TDateFieldMeta as DateFieldMeta, DEFAULT_DATE_OVERFLOW_MODE } from '@documenso/lib/types/field-meta';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Trans, useLingui } from '@lingui/react/macro';
type DateFieldAdvancedSettingsProps = {
fieldState: DateFieldMeta;
@@ -82,10 +72,7 @@ export const DateFieldAdvancedSettings = ({
<Trans>Text Align</Trans>
</Label>
<Select
value={fieldState.textAlign}
onValueChange={(value) => handleInput('textAlign', value)}
>
<Select value={fieldState.textAlign} onValueChange={(value) => handleInput('textAlign', value)}>
<SelectTrigger className="mt-2 bg-background">
<SelectValue placeholder={t`Select text align`} />
</SelectTrigger>
@@ -1,30 +1,19 @@
import { useEffect, useState } from 'react';
import { validateDropdownField } from '@documenso/lib/advanced-fields-validation/validate-dropdown';
import type { TDropdownFieldMeta as DropdownFieldMeta } from '@documenso/lib/types/field-meta';
import { Button } from '@documenso/ui/primitives/button';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Switch } from '@documenso/ui/primitives/switch';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { ChevronDown, ChevronUp, Trash } from 'lucide-react';
import { validateDropdownField } from '@documenso/lib/advanced-fields-validation/validate-dropdown';
import { type TDropdownFieldMeta as DropdownFieldMeta } from '@documenso/lib/types/field-meta';
import { Button } from '@documenso/ui/primitives/button';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Switch } from '@documenso/ui/primitives/switch';
import { useEffect, useState } from 'react';
type DropdownFieldAdvancedSettingsProps = {
fieldState: DropdownFieldMeta;
handleFieldChange: (
key: keyof DropdownFieldMeta,
value: string | { value: string }[] | boolean,
) => void;
handleFieldChange: (key: keyof DropdownFieldMeta, value: string | { value: string }[] | boolean) => void;
handleErrors: (errors: string[]) => void;
};
@@ -47,7 +36,9 @@ export const DropdownFieldAdvancedSettings = ({
};
const removeValue = (index: number) => {
if (values.length === 1) return;
if (values.length === 1) {
return;
}
const newValues = [...values];
newValues.splice(index, 1);
@@ -98,7 +89,7 @@ export const DropdownFieldAdvancedSettings = ({
}, [fieldState.defaultValue]);
return (
<div className="text-dark flex flex-col gap-4">
<div className="flex flex-col gap-4 text-dark">
<div>
<Label>
<Trans>Select default option</Trans>
@@ -168,11 +159,7 @@ export const DropdownFieldAdvancedSettings = ({
<div>
{values.map((value, index) => (
<div key={index} className="mt-2 flex items-center gap-4">
<Input
className="w-1/2"
value={value.value}
onChange={(e) => handleValueChange(index, e.target.value)}
/>
<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"
@@ -183,7 +170,7 @@ export const DropdownFieldAdvancedSettings = ({
</div>
))}
<Button
className="ml-9 mt-4 border border-foreground/10 bg-foreground/10 hover:bg-foreground/5"
className="mt-4 ml-9 border border-foreground/10 bg-foreground/10 hover:bg-foreground/5"
variant="outline"
onClick={addValue}
>
@@ -1,19 +1,9 @@
import { Trans, useLingui } from '@lingui/react/macro';
import { validateFields as validateEmailFields } from '@documenso/lib/advanced-fields-validation/validate-fields';
import {
DEFAULT_EMAIL_OVERFLOW_MODE,
type TEmailFieldMeta as EmailFieldMeta,
} from '@documenso/lib/types/field-meta';
import { DEFAULT_EMAIL_OVERFLOW_MODE, type TEmailFieldMeta as EmailFieldMeta } from '@documenso/lib/types/field-meta';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Trans, useLingui } from '@lingui/react/macro';
type EmailFieldAdvancedSettingsProps = {
fieldState: EmailFieldMeta;
@@ -64,10 +54,7 @@ export const EmailFieldAdvancedSettings = ({
<Trans>Text Align</Trans>
</Label>
<Select
value={fieldState.textAlign}
onValueChange={(value) => handleInput('textAlign', value)}
>
<Select value={fieldState.textAlign} onValueChange={(value) => handleInput('textAlign', value)}>
<SelectTrigger className="mt-2 bg-background">
<SelectValue placeholder={t`Select text align`} />
</SelectTrigger>
@@ -1,9 +1,8 @@
import { Trans, useLingui } from '@lingui/react/macro';
import { validateFields as validateInitialsFields } from '@documenso/lib/advanced-fields-validation/validate-fields';
import { type TInitialsFieldMeta as InitialsFieldMeta } from '@documenso/lib/types/field-meta';
import type { TInitialsFieldMeta as InitialsFieldMeta } from '@documenso/lib/types/field-meta';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import { Trans, useLingui } from '@lingui/react/macro';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../select';
@@ -41,7 +40,7 @@ export const InitialsFieldAdvancedSettings = ({
<Input
id="fontSize"
type="number"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Field font size`}
value={fieldState.fontSize}
onChange={(e) => handleInput('fontSize', e.target.value)}
@@ -55,11 +54,8 @@ export const InitialsFieldAdvancedSettings = ({
<Trans>Text Align</Trans>
</Label>
<Select
value={fieldState.textAlign}
onValueChange={(value) => handleInput('textAlign', value)}
>
<SelectTrigger className="bg-background mt-2">
<Select value={fieldState.textAlign} onValueChange={(value) => handleInput('textAlign', value)}>
<SelectTrigger className="mt-2 bg-background">
<SelectValue placeholder={t`Select text align`} />
</SelectTrigger>
@@ -1,16 +1,9 @@
import { Trans, useLingui } from '@lingui/react/macro';
import { validateFields as validateNameFields } from '@documenso/lib/advanced-fields-validation/validate-fields';
import { type TNameFieldMeta as NameFieldMeta } from '@documenso/lib/types/field-meta';
import type { TNameFieldMeta as NameFieldMeta } from '@documenso/lib/types/field-meta';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Trans, useLingui } from '@lingui/react/macro';
type NameFieldAdvancedSettingsProps = {
fieldState: NameFieldMeta;
@@ -46,7 +39,7 @@ export const NameFieldAdvancedSettings = ({
<Input
id="fontSize"
type="number"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Field font size`}
value={fieldState.fontSize}
onChange={(e) => handleInput('fontSize', e.target.value)}
@@ -60,11 +53,8 @@ export const NameFieldAdvancedSettings = ({
<Trans>Text Align</Trans>
</Label>
<Select
value={fieldState.textAlign}
onValueChange={(value) => handleInput('textAlign', value)}
>
<SelectTrigger className="bg-background mt-2">
<Select value={fieldState.textAlign} onValueChange={(value) => handleInput('textAlign', value)}>
<SelectTrigger className="mt-2 bg-background">
<SelectValue placeholder={t`Select text align`} />
</SelectTrigger>
@@ -1,21 +1,13 @@
import { useState } from 'react';
import { Trans, useLingui } from '@lingui/react/macro';
import { ChevronDown, ChevronUp } from 'lucide-react';
import { validateNumberField } from '@documenso/lib/advanced-fields-validation/validate-number';
import { type TNumberFieldMeta as NumberFieldMeta } from '@documenso/lib/types/field-meta';
import type { TNumberFieldMeta as NumberFieldMeta } from '@documenso/lib/types/field-meta';
import { Button } from '@documenso/ui/primitives/button';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Switch } from '@documenso/ui/primitives/switch';
import { Trans, useLingui } from '@lingui/react/macro';
import { ChevronDown, ChevronUp } from 'lucide-react';
import { useState } from 'react';
import { numberFormatValues } from './constants';
@@ -65,7 +57,7 @@ export const NumberFieldAdvancedSettings = ({
</Label>
<Input
id="label"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Label`}
value={fieldState.label}
onChange={(e) => handleFieldChange('label', e.target.value)}
@@ -77,7 +69,7 @@ export const NumberFieldAdvancedSettings = ({
</Label>
<Input
id="placeholder"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Placeholder`}
value={fieldState.placeholder}
onChange={(e) => handleFieldChange('placeholder', e.target.value)}
@@ -89,7 +81,7 @@ export const NumberFieldAdvancedSettings = ({
</Label>
<Input
id="value"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Value`}
value={fieldState.value}
onChange={(e) => handleInput('value', e.target.value)}
@@ -99,11 +91,8 @@ export const NumberFieldAdvancedSettings = ({
<Label>
<Trans>Number format</Trans>
</Label>
<Select
value={fieldState.numberFormat ?? ''}
onValueChange={(val) => handleInput('numberFormat', val)}
>
<SelectTrigger className="text-muted-foreground bg-background mt-2 w-full">
<Select value={fieldState.numberFormat ?? ''} onValueChange={(val) => handleInput('numberFormat', val)}>
<SelectTrigger className="mt-2 w-full bg-background text-muted-foreground">
<SelectValue placeholder={t`Field format`} />
</SelectTrigger>
<SelectContent position="popper">
@@ -123,7 +112,7 @@ export const NumberFieldAdvancedSettings = ({
<Input
id="fontSize"
type="number"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Field font size`}
value={fieldState.fontSize}
onChange={(e) => handleInput('fontSize', e.target.value)}
@@ -137,11 +126,8 @@ export const NumberFieldAdvancedSettings = ({
<Trans>Text Align</Trans>
</Label>
<Select
value={fieldState.textAlign}
onValueChange={(value) => handleInput('textAlign', value)}
>
<SelectTrigger className="bg-background mt-2">
<Select value={fieldState.textAlign} onValueChange={(value) => handleInput('textAlign', value)}>
<SelectTrigger className="mt-2 bg-background">
<SelectValue placeholder={t`Select text align`} />
</SelectTrigger>
@@ -176,7 +162,7 @@ export const NumberFieldAdvancedSettings = ({
</div>
</div>
<Button
className="bg-foreground/10 hover:bg-foreground/5 border-foreground/10 mt-2 border"
className="mt-2 border border-foreground/10 bg-foreground/10 hover:bg-foreground/5"
variant="outline"
onClick={() => setShowValidation((prev) => !prev)}
>
@@ -195,7 +181,7 @@ export const NumberFieldAdvancedSettings = ({
</Label>
<Input
id="minValue"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`E.g. 0`}
value={fieldState.minValue ?? ''}
onChange={(e) => handleInput('minValue', e.target.value)}
@@ -207,7 +193,7 @@ export const NumberFieldAdvancedSettings = ({
</Label>
<Input
id="maxValue"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`E.g. 100`}
value={fieldState.maxValue ?? ''}
onChange={(e) => handleInput('maxValue', e.target.value)}
@@ -1,17 +1,15 @@
import { useEffect, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { ChevronDown, ChevronUp, Trash } from 'lucide-react';
import { validateRadioField } from '@documenso/lib/advanced-fields-validation/validate-radio';
import { type TRadioFieldMeta as RadioFieldMeta } from '@documenso/lib/types/field-meta';
import type { TRadioFieldMeta as RadioFieldMeta } from '@documenso/lib/types/field-meta';
import { Button } from '@documenso/ui/primitives/button';
import { Checkbox } from '@documenso/ui/primitives/checkbox';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import { Switch } from '@documenso/ui/primitives/switch';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { ChevronDown, ChevronUp, Trash } from 'lucide-react';
import { useEffect, useState } from 'react';
export type RadioFieldAdvancedSettingsProps = {
fieldState: RadioFieldMeta;
@@ -30,9 +28,7 @@ export const RadioFieldAdvancedSettings = ({
const { _ } = useLingui();
const [showValidation, setShowValidation] = useState(false);
const [values, setValues] = useState(
fieldState.values ?? [{ id: 1, checked: false, value: _(msg`Default value`) }],
);
const [values, setValues] = useState(fieldState.values ?? [{ id: 1, checked: false, value: _(msg`Default value`) }]);
const [readOnly, setReadOnly] = useState(fieldState.readOnly ?? false);
const [required, setRequired] = useState(fieldState.required ?? false);
@@ -46,7 +42,9 @@ export const RadioFieldAdvancedSettings = ({
};
const removeValue = (id: number) => {
if (values.length === 1) return;
if (values.length === 1) {
return;
}
const newValues = values.filter((val) => val.id !== id);
setValues(newValues);
@@ -186,7 +184,7 @@ export const RadioFieldAdvancedSettings = ({
</div>
))}
<Button
className="ml-9 mt-4 border border-foreground/10 bg-foreground/10 hover:bg-foreground/5"
className="mt-4 ml-9 border border-foreground/10 bg-foreground/10 hover:bg-foreground/5"
variant="outline"
onClick={addValue}
>
@@ -1,18 +1,11 @@
import { Trans, useLingui } from '@lingui/react/macro';
import { validateTextField } from '@documenso/lib/advanced-fields-validation/validate-text';
import { type TTextFieldMeta as TextFieldMeta } from '@documenso/lib/types/field-meta';
import type { TTextFieldMeta as TextFieldMeta } from '@documenso/lib/types/field-meta';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
import { Switch } from '@documenso/ui/primitives/switch';
import { Textarea } from '@documenso/ui/primitives/textarea';
import { Trans, useLingui } from '@lingui/react/macro';
type TextFieldAdvancedSettingsProps = {
fieldState: TextFieldMeta;
@@ -29,8 +22,7 @@ export const TextFieldAdvancedSettings = ({
const handleInput = (field: keyof TextFieldMeta, value: string | boolean) => {
const text = field === 'text' ? String(value) : (fieldState.text ?? '');
const limit =
field === 'characterLimit' ? Number(value) : Number(fieldState.characterLimit ?? 0);
const limit = field === 'characterLimit' ? Number(value) : Number(fieldState.characterLimit ?? 0);
const fontSize = field === 'fontSize' ? Number(value) : Number(fieldState.fontSize ?? 14);
const readOnly = field === 'readOnly' ? Boolean(value) : Boolean(fieldState.readOnly);
const required = field === 'required' ? Boolean(value) : Boolean(fieldState.required);
@@ -55,7 +47,7 @@ export const TextFieldAdvancedSettings = ({
</Label>
<Input
id="label"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Field label`}
value={fieldState.label}
onChange={(e) => handleFieldChange('label', e.target.value)}
@@ -67,7 +59,7 @@ export const TextFieldAdvancedSettings = ({
</Label>
<Input
id="placeholder"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Field placeholder`}
value={fieldState.placeholder}
onChange={(e) => handleFieldChange('placeholder', e.target.value)}
@@ -80,7 +72,7 @@ export const TextFieldAdvancedSettings = ({
</Label>
<Textarea
id="text"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Add text to the field`}
value={fieldState.text}
onChange={(e) => handleInput('text', e.target.value)}
@@ -95,7 +87,7 @@ export const TextFieldAdvancedSettings = ({
id="characterLimit"
type="number"
min={0}
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Field character limit`}
value={fieldState.characterLimit}
onChange={(e) => handleInput('characterLimit', e.target.value)}
@@ -109,7 +101,7 @@ export const TextFieldAdvancedSettings = ({
<Input
id="fontSize"
type="number"
className="bg-background mt-2"
className="mt-2 bg-background"
placeholder={t`Field font size`}
value={fieldState.fontSize}
onChange={(e) => handleInput('fontSize', e.target.value)}
@@ -133,7 +125,7 @@ export const TextFieldAdvancedSettings = ({
handleInput('textAlign', value);
}}
>
<SelectTrigger className="bg-background mt-2">
<SelectTrigger className="mt-2 bg-background">
<SelectValue placeholder={t`Select text align`} />
</SelectTrigger>
@@ -1,6 +1,3 @@
import { Trans } from '@lingui/react/macro';
import { DialogClose } from '@radix-ui/react-dialog';
import { Button } from '@documenso/ui/primitives/button';
import {
Dialog,
@@ -10,16 +7,15 @@ import {
DialogHeader,
DialogTitle,
} from '@documenso/ui/primitives/dialog';
import { Trans } from '@lingui/react/macro';
import { DialogClose } from '@radix-ui/react-dialog';
export type MissingSignatureFieldDialogProps = {
isOpen: boolean;
onOpenChange: (value: boolean) => void;
};
export const MissingSignatureFieldDialog = ({
isOpen,
onOpenChange,
}: MissingSignatureFieldDialogProps) => {
export const MissingSignatureFieldDialog = ({ isOpen, onOpenChange }: MissingSignatureFieldDialogProps) => {
return (
<Dialog open={isOpen} onOpenChange={onOpenChange}>
<DialogContent className="max-w-lg" position="center">
@@ -30,8 +26,8 @@ export const MissingSignatureFieldDialog = ({
<DialogDescription>
<p className="mt-2">
<Trans>
Some signers have not been assigned a signature field. Please assign at least 1
signature field to each signer before proceeding.
Some signers have not been assigned a signature field. Please assign at least 1 signature field to each
signer before proceeding.
</Trans>
</p>
</DialogDescription>
@@ -1,7 +1,6 @@
import { useState } from 'react';
import { Trans } from '@lingui/react/macro';
import { Loader } from 'lucide-react';
import { useState } from 'react';
import type { ButtonProps } from '../button';
import { Button } from '../button';
@@ -19,38 +18,31 @@ export type SendDocumentActionDialogProps = ButtonProps & {
loading?: boolean;
};
export const SendDocumentActionDialog = ({
loading,
className,
...props
}: SendDocumentActionDialogProps) => {
export const SendDocumentActionDialog = ({ loading, className, ...props }: SendDocumentActionDialogProps) => {
const [open, setOpen] = useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button type="button" className={className}>
{loading && <Loader className="text-documenso mr-2 h-5 w-5 animate-spin" />}
{loading && <Loader className="mr-2 h-5 w-5 animate-spin text-documenso" />}
<Trans>Send</Trans>
</Button>
</DialogTrigger>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="text-center text-lg font-semibold">
<DialogTitle className="text-center font-semibold text-lg">
<Trans>Send Document</Trans>
</DialogTitle>
<DialogDescription className="text-center text-base">
<Trans>
You are about to send this document to the recipients. Are you sure you want to
continue?
</Trans>
<Trans>You are about to send this document to the recipients. Are you sure you want to continue?</Trans>
</DialogDescription>
</DialogHeader>
<DialogFooter className="mt-4 flex items-center gap-x-4">
<Button
className="dark:bg-muted dark:hover:bg-muted/80 dark:focus-visible:ring-muted/80 flex-1 border-none bg-black/5 hover:bg-black/10 focus-visible:ring-black/10"
className="flex-1 border-none bg-black/5 hover:bg-black/10 focus-visible:ring-black/10 dark:bg-muted dark:focus-visible:ring-muted/80 dark:hover:bg-muted/80"
type="button"
variant="secondary"
onClick={() => setOpen(false)}
@@ -15,19 +15,15 @@ export type SigningOrderConfirmationProps = {
onConfirm: () => void;
};
export function SigningOrderConfirmation({
open,
onOpenChange,
onConfirm,
}: SigningOrderConfirmationProps) {
export function SigningOrderConfirmation({ open, onOpenChange, onConfirm }: SigningOrderConfirmationProps) {
return (
<AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Warning</AlertDialogTitle>
<AlertDialogDescription>
You have an assistant role on the signers list, removing the signing order will change
the assistant role to signer.
You have an assistant role on the signers list, removing the signing order will change the assistant role to
signer.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
@@ -1,11 +1,10 @@
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { zEmail } from '@documenso/lib/utils/zod';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { FieldType } from '@prisma/client';
import { z } from 'zod';
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import { zEmail } from '@documenso/lib/utils/zod';
export const ZDocumentFlowFormSchema = z.object({
title: z.string().min(1),