diff --git a/src/components/documents/upload-for-signing-dialog.tsx b/src/components/documents/upload-for-signing-dialog.tsx index 8f708098..1dcad29e 100644 --- a/src/components/documents/upload-for-signing-dialog.tsx +++ b/src/components/documents/upload-for-signing-dialog.tsx @@ -93,6 +93,19 @@ interface PlacedField { pageY: number; pageWidth: number; pageHeight: number; + /** Per-instance default value the signer sees on render. For TEXT/NUMBER + * this is the prefilled string; for DROPDOWN it's the option `value` + * string; for CHECKBOX/RADIO the meta.values entries carry their own + * `checked` flags so this is left null. */ + defaultValue?: string | null; + /** Documenso v2 fieldMeta passed through verbatim. Per-type shape: + * TEXT/SIGNATURE/INITIALS: { text?, label?, required?, readOnly? } + * NUMBER: { numberFormat?, min?, max?, required? } + * DATE: { dateFormat?, required? } + * CHECKBOX/RADIO: { values: [{ value, checked? }] } + * DROPDOWN: { values: [{ value }], defaultValue? } + * Ignored on v1 instances. */ + fieldMeta?: Record; } interface DetectedFieldResponse { @@ -516,6 +529,9 @@ function DialogBody({ pageY: f.pageY, pageWidth: f.pageWidth, pageHeight: f.pageHeight, + ...(f.fieldMeta && Object.keys(f.fieldMeta).length > 0 + ? { fieldMeta: f.fieldMeta } + : {}), })), ), ); @@ -1421,9 +1437,227 @@ function FieldSidePanel({ /> + + ); } + +/** + * Editable list for CHECKBOX / RADIO / DROPDOWN options. Kept top-level + * so React doesn't recreate the component subtree on every keystroke + * (react-hooks/static-components rule). Single-select variants render + * radio inputs that mutually exclude defaults; multi-select renders + * checkboxes. + */ +function ChoiceMetaEditor({ + options, + onChange, + singleSelect, +}: { + options: Array<{ value: string; checked?: boolean }>; + onChange: (next: Array<{ value: string; checked?: boolean }>) => void; + singleSelect: boolean; +}) { + return ( +
+
+ + +
+ {options.length === 0 ? ( +

No options yet, add at least one.

+ ) : ( +
    + {options.map((opt, idx) => ( +
  • + { + const next = options.map((o, i) => + singleSelect + ? { ...o, checked: i === idx } + : i === idx + ? { ...o, checked: !o.checked } + : o, + ); + onChange(next); + }} + aria-label={`${opt.value} default ${singleSelect ? 'selection' : 'checked'}`} + className="size-3.5 shrink-0" + /> + { + const next = options.map((o, i) => + i === idx ? { ...o, value: e.target.value } : o, + ); + onChange(next); + }} + className="h-7 text-xs" + /> + +
  • + ))} +
+ )} +
+ ); +} + +/** + * Per-field-type config (Documenso v2 fieldMeta). Surfaces inputs only + * when the field type carries per-instance config; SIGNATURE / INITIALS / + * DATE / EMAIL / NAME fields render nothing here. Edits write into + * `field.fieldMeta` shallowly so the v2 create-many payload receives + * the shape Documenso expects. + */ +function FieldMetaSubPanel({ + field, + onUpdate, +}: { + field: PlacedField; + onUpdate: (patch: Partial) => void; +}) { + const meta: Record = (field.fieldMeta as Record) ?? {}; + function patchMeta(diff: Record) { + onUpdate({ fieldMeta: { ...meta, ...diff } }); + } + + if (field.type === 'TEXT') { + return ( +
+

+ Text settings +

+
+ + patchMeta({ text: e.target.value })} + className="h-7 text-xs" + placeholder="Pre-filled value the signer sees" + /> +
+
+ + patchMeta({ label: e.target.value })} + className="h-7 text-xs" + placeholder="Helper text above the field" + /> +
+ +
+ ); + } + + if (field.type === 'NUMBER') { + return ( +
+

+ Number settings +

+
+ + patchMeta({ numberFormat: e.target.value })} + className="h-7 text-xs" + placeholder="e.g. 0.00, $#,##0" + /> +
+
+
+ + + patchMeta({ min: e.target.value === '' ? undefined : Number(e.target.value) }) + } + className="h-7 text-xs" + /> +
+
+ + + patchMeta({ max: e.target.value === '' ? undefined : Number(e.target.value) }) + } + className="h-7 text-xs" + /> +
+
+ +
+ ); + } + + if (field.type === 'CHECKBOX' || field.type === 'RADIO' || field.type === 'DROPDOWN') { + const rawValues = (meta.values as Array<{ value: string; checked?: boolean }>) ?? []; + return ( +
+

+ {field.type === 'CHECKBOX' + ? 'Checkbox options' + : field.type === 'RADIO' + ? 'Radio options' + : 'Dropdown options'} +

+ patchMeta({ values: next })} + singleSelect={field.type !== 'CHECKBOX'} + /> +
+ ); + } + + // SIGNATURE / INITIALS / DATE / EMAIL / NAME carry no per-instance + // configuration in Documenso v2 today. + return null; +}