'use client'; import { forwardRef, useEffect, useState } from 'react'; import { useParams } from 'next/navigation'; import { usePathname } from 'next/navigation'; import type { MessageDescriptor } from '@lingui/core'; import { msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { match } from 'ts-pattern'; import { type TBaseFieldMeta as BaseFieldMeta, type TCheckboxFieldMeta as CheckboxFieldMeta, type TDropdownFieldMeta as DropdownFieldMeta, type TFieldMetaSchema as FieldMeta, type TNumberFieldMeta as NumberFieldMeta, type TRadioFieldMeta as RadioFieldMeta, type TTextFieldMeta as TextFieldMeta, ZFieldMetaSchema, } from '@documenso/lib/types/field-meta'; import { FieldType } from '@documenso/prisma/client'; import { trpc } from '@documenso/trpc/react'; import { useToast } from '@documenso/ui/primitives/use-toast'; import type { FieldFormType } from './add-fields'; import { DocumentFlowFormContainerActions, DocumentFlowFormContainerContent, DocumentFlowFormContainerFooter, DocumentFlowFormContainerHeader, } from './document-flow-root'; import { FieldItem } from './field-item'; import { CheckboxFieldAdvancedSettings } from './field-items-advanced-settings/checkbox-field'; import { DropdownFieldAdvancedSettings } from './field-items-advanced-settings/dropdown-field'; import { NumberFieldAdvancedSettings } from './field-items-advanced-settings/number-field'; import { RadioFieldAdvancedSettings } from './field-items-advanced-settings/radio-field'; import { TextFieldAdvancedSettings } from './field-items-advanced-settings/text-field'; export type FieldAdvancedSettingsProps = { teamId?: number; title: MessageDescriptor; description: MessageDescriptor; field: FieldFormType; fields: FieldFormType[]; onAdvancedSettings?: () => void; isDocumentPdfLoaded?: boolean; onSave?: (fieldState: FieldMeta) => void; }; export type FieldMetaKeys = | keyof BaseFieldMeta | keyof TextFieldMeta | keyof NumberFieldMeta | keyof RadioFieldMeta | keyof CheckboxFieldMeta | keyof DropdownFieldMeta; const getDefaultState = (fieldType: FieldType): FieldMeta => { switch (fieldType) { case FieldType.TEXT: return { type: 'text', label: '', placeholder: '', text: '', characterLimit: 0, required: false, readOnly: false, }; case FieldType.NUMBER: return { type: 'number', label: '', placeholder: '', numberFormat: '', value: '0', minValue: 0, maxValue: 0, required: false, readOnly: false, }; case FieldType.RADIO: return { type: 'radio', values: [], required: false, readOnly: false, }; case FieldType.CHECKBOX: return { type: 'checkbox', values: [], validationRule: '', validationLength: 0, required: false, readOnly: false, }; case FieldType.DROPDOWN: return { type: 'dropdown', values: [], defaultValue: '', required: false, readOnly: false, }; default: throw new Error(`Unsupported field type: ${fieldType}`); } }; export const FieldAdvancedSettings = forwardRef( ( { title, description, field, fields, onAdvancedSettings, isDocumentPdfLoaded = true, onSave, teamId, }, ref, ) => { const { _ } = useLingui(); const { toast } = useToast(); const params = useParams(); const pathname = usePathname(); const id = params?.id; const isTemplatePage = pathname?.includes('template'); const isDocumentPage = pathname?.includes('document'); const [errors, setErrors] = useState([]); const { data: template } = trpc.template.getTemplateWithDetailsById.useQuery( { id: Number(id), }, { enabled: isTemplatePage, }, ); const { data: document } = trpc.document.getDocumentById.useQuery( { id: Number(id), teamId, }, { enabled: isDocumentPage, }, ); const doesFieldExist = (!!document || !!template) && field.nativeId !== undefined; const { data: fieldData } = trpc.field.getField.useQuery( { fieldId: Number(field.nativeId), teamId, }, { enabled: doesFieldExist, }, ); const fieldMeta = fieldData?.fieldMeta; const localStorageKey = `field_${field.formId}_${field.type}`; const defaultState: FieldMeta = getDefaultState(field.type); const [fieldState, setFieldState] = useState(() => { const savedState = localStorage.getItem(localStorageKey); return savedState ? { ...defaultState, ...JSON.parse(savedState) } : defaultState; }); useEffect(() => { if (fieldMeta && typeof fieldMeta === 'object') { const parsedFieldMeta = ZFieldMetaSchema.parse(fieldMeta); setFieldState({ ...defaultState, ...parsedFieldMeta, }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [fieldMeta]); const handleFieldChange = ( key: FieldMetaKeys, value: string | { checked: boolean; value: string }[] | { value: string }[] | boolean, ) => { setFieldState((prevState: FieldMeta) => { if (['characterLimit', 'minValue', 'maxValue', 'validationLength'].includes(key)) { const parsedValue = Number(value); return { ...prevState, [key]: isNaN(parsedValue) ? undefined : parsedValue, }; } else { return { ...prevState, [key]: value, }; } }); }; const handleOnGoNextClick = () => { try { if (errors.length > 0) { return; } else { localStorage.setItem(localStorageKey, JSON.stringify(fieldState)); onSave?.(fieldState); onAdvancedSettings?.(); } } catch (error) { console.error('Failed to save to localStorage:', error); toast({ title: _(msg`Error`), description: _(msg`Failed to save settings.`), variant: 'destructive', }); } }; return (
{isDocumentPdfLoaded && fields.map((field, index) => ( ))} {match(field.type) .with(FieldType.TEXT, () => ( )) .with(FieldType.NUMBER, () => ( )) .with(FieldType.RADIO, () => ( )) .with(FieldType.CHECKBOX, () => ( )) .with(FieldType.DROPDOWN, () => ( )) .otherwise(() => null)} {errors.length > 0 && (
    {errors.map((error, index) => (
  • {error}
  • ))}
)}
0} />
); }, ); FieldAdvancedSettings.displayName = 'FieldAdvancedSettings';