fix(09-01): fix propertyAddress pre-seed and polish PreparePanel text fill UI

- TextFillForm: add initialData prop; buildInitialRows seeds rows from
  pre-seeded data so propertyAddress row renders populated on mount
- PreparePanel: pass initialData={...} to TextFillForm so the lazy
  useState in PreparePanel correctly flows through to the visible UI
- TextFillForm: replace AcroForm jargon instruction with friendly copy
- TextFillForm: add Field name / Value column headers for clear layout
- TextFillForm: improve spacing (py-1.5), softer remove button (gray→red on hover)
- TypeScript: npx tsc --noEmit passes clean
This commit is contained in:
Chandler Copeland
2026-03-21 12:20:08 -06:00
parent fa9981edd9
commit 11f2b80217
2 changed files with 28 additions and 11 deletions

View File

@@ -151,7 +151,10 @@ export function PreparePanel({ docId, defaultEmail, clientName, currentStatus, a
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2">Text fill fields</label> <label className="block text-sm font-medium text-gray-700 mb-2">Text fill fields</label>
<TextFillForm onChange={setTextFillData} /> <TextFillForm
onChange={setTextFillData}
initialData={clientPropertyAddress ? { propertyAddress: clientPropertyAddress } : undefined}
/>
</div> </div>
<button <button

View File

@@ -5,10 +5,19 @@ interface TextRow { label: string; value: string; }
interface TextFillFormProps { interface TextFillFormProps {
onChange: (data: Record<string, string>) => void; onChange: (data: Record<string, string>) => void;
initialData?: Record<string, string>;
} }
export function TextFillForm({ onChange }: TextFillFormProps) { function buildInitialRows(initialData?: Record<string, string>): TextRow[] {
const [rows, setRows] = useState<TextRow[]>([{ label: '', value: '' }]); if (initialData && Object.keys(initialData).length > 0) {
const seeded = Object.entries(initialData).map(([label, value]) => ({ label, value }));
return [...seeded, { label: '', value: '' }];
}
return [{ label: '', value: '' }];
}
export function TextFillForm({ onChange, initialData }: TextFillFormProps) {
const [rows, setRows] = useState<TextRow[]>(() => buildInitialRows(initialData));
function updateRow(index: number, field: 'label' | 'value', val: string) { function updateRow(index: number, field: 'label' | 'value', val: string) {
const next = rows.map((r, i) => i === index ? { ...r, [field]: val } : r); const next = rows.map((r, i) => i === index ? { ...r, [field]: val } : r);
@@ -29,26 +38,31 @@ export function TextFillForm({ onChange }: TextFillFormProps) {
return ( return (
<div className="space-y-2"> <div className="space-y-2">
<p className="text-xs text-gray-500"> <p className="text-xs text-gray-400 mt-0.5">
Field label = AcroForm field name in the PDF (e.g. &quot;PropertyAddress&quot;). Leave blank to skip. Pre-fill text fields in the PDF before sending. Leave blank to skip a field.
</p> </p>
<div className="flex gap-2 items-center px-1">
<span className="flex-1 text-xs font-medium text-gray-500 uppercase tracking-wide">Field name</span>
<span className="flex-1 text-xs font-medium text-gray-500 uppercase tracking-wide">Value</span>
<span className="w-6" />
</div>
{rows.map((row, i) => ( {rows.map((row, i) => (
<div key={i} className="flex gap-2 items-center"> <div key={i} className="flex gap-2 items-center">
<input <input
placeholder="Field label" placeholder="e.g. PropertyAddress"
value={row.label} value={row.label}
onChange={e => updateRow(i, 'label', e.target.value)} onChange={e => updateRow(i, 'label', e.target.value)}
className="flex-1 border rounded px-2 py-1 text-sm" className="flex-1 border rounded px-2 py-1.5 text-sm focus:border-[var(--gold)] focus:ring-[var(--gold)]"
/> />
<input <input
placeholder="Value" placeholder="Value to fill in"
value={row.value} value={row.value}
onChange={e => updateRow(i, 'value', e.target.value)} onChange={e => updateRow(i, 'value', e.target.value)}
className="flex-1 border rounded px-2 py-1 text-sm" className="flex-1 border rounded px-2 py-1.5 text-sm focus:border-[var(--gold)] focus:ring-[var(--gold)]"
/> />
<button <button
onClick={() => removeRow(i)} onClick={() => removeRow(i)}
className="text-red-400 hover:text-red-600 text-sm px-1" className="w-6 text-gray-300 hover:text-red-500 text-base leading-none flex items-center justify-center"
aria-label="Remove row" aria-label="Remove row"
type="button" type="button"
>&times;</button> >&times;</button>
@@ -57,7 +71,7 @@ export function TextFillForm({ onChange }: TextFillFormProps) {
{rows.length < 10 && ( {rows.length < 10 && (
<button <button
onClick={addRow} onClick={addRow}
className="text-blue-600 hover:text-blue-800 text-sm" className="text-sm text-gray-500 hover:text-gray-700"
type="button" type="button"
> >
+ Add field + Add field