Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-18 02:05:39 +00:00
co-authored by renee-png
parent 6224ddd812
commit 9522143f51
@@ -0,0 +1,232 @@
import { useEffect, useState } from "react";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
fetchClients,
fetchHomeowners,
type ClientLite,
type HomeownerLite,
type FormFieldDef,
} from "@/lib/forms-shared";
import { FL_COUNTIES } from "@/lib/florida";
export function FormFieldsFillPanel({
fields,
values,
onChange,
}: {
fields: FormFieldDef[];
values: Record<string, string>;
onChange: (next: Record<string, string>) => void;
}) {
const [clients, setClients] = useState<ClientLite[]>([]);
const [homeownersByClient, setHomeownersByClient] = useState<Record<string, HomeownerLite[]>>({});
// Track which client a homeowner field is filtered by (per-field-key)
const [hoClientByField, setHoClientByField] = useState<Record<string, string>>({});
useEffect(() => {
if (fields.some((f) => f.type === "client" || f.type === "homeowner")) {
fetchClients().then(setClients);
}
}, [fields]);
const set = (key: string, v: string) => onChange({ ...values, [key]: v });
const ensureHomeowners = async (clientId: string) => {
if (!clientId || homeownersByClient[clientId]) return;
const hs = await fetchHomeowners(clientId);
setHomeownersByClient((prev) => ({ ...prev, [clientId]: hs }));
};
if (fields.length === 0) {
return (
<p className="text-xs text-muted-foreground italic px-1 py-2">
No fillable fields. Click "Edit fields" to define inputs.
</p>
);
}
return (
<div className="space-y-3">
{fields.map((f) => {
const v = values[f.key] ?? f.default ?? "";
const labelEl = (
<Label className="text-xs">
{f.label}
{f.required && <span className="text-destructive ml-1">*</span>}
</Label>
);
if (f.type === "textarea") {
return (
<div key={f.key}>
{labelEl}
<Textarea
rows={3}
value={v}
placeholder={f.placeholder}
onChange={(e) => set(f.key, e.target.value)}
/>
</div>
);
}
if (f.type === "number") {
return (
<div key={f.key}>
{labelEl}
<Input
type="number"
value={v}
placeholder={f.placeholder}
onChange={(e) => set(f.key, e.target.value)}
/>
</div>
);
}
if (f.type === "date") {
return (
<div key={f.key}>
{labelEl}
<Input
type="date"
value={v}
onChange={(e) => set(f.key, e.target.value)}
/>
</div>
);
}
if (f.type === "dropdown") {
return (
<div key={f.key}>
{labelEl}
<Select value={v} onValueChange={(val) => set(f.key, val)}>
<SelectTrigger className="h-9">
<SelectValue placeholder={f.placeholder ?? "Select..."} />
</SelectTrigger>
<SelectContent>
{(f.options ?? []).map((o) => (
<SelectItem key={o} value={o}>
{o}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
}
if (f.type === "county") {
return (
<div key={f.key}>
{labelEl}
<Select value={v} onValueChange={(val) => set(f.key, val)}>
<SelectTrigger className="h-9">
<SelectValue placeholder="Select Florida county" />
</SelectTrigger>
<SelectContent>
{FL_COUNTIES.map((c) => (
<SelectItem key={c} value={c}>
{c}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
}
if (f.type === "client") {
return (
<div key={f.key}>
{labelEl}
<Select
value={v}
onValueChange={(val) => {
const c = clients.find((x) => x.name === val);
// store the name as the substituted value
set(f.key, c?.name ?? val);
}}
>
<SelectTrigger className="h-9">
<SelectValue placeholder="Select client" />
</SelectTrigger>
<SelectContent>
{clients.map((c) => (
<SelectItem key={c.id} value={c.name}>
{c.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
);
}
if (f.type === "homeowner") {
const filterClientId = hoClientByField[f.key] ?? "";
const hos = filterClientId ? homeownersByClient[filterClientId] ?? [] : [];
return (
<div key={f.key} className="space-y-1">
{labelEl}
<Select
value={filterClientId}
onValueChange={(val) => {
setHoClientByField((p) => ({ ...p, [f.key]: val }));
ensureHomeowners(val);
}}
>
<SelectTrigger className="h-8 text-xs">
<SelectValue placeholder="Filter by client" />
</SelectTrigger>
<SelectContent>
{clients.map((c) => (
<SelectItem key={c.id} value={c.id}>
{c.name}
</SelectItem>
))}
</SelectContent>
</Select>
<Select
value={v}
onValueChange={(val) => set(f.key, val)}
disabled={!filterClientId}
>
<SelectTrigger className="h-9">
<SelectValue
placeholder={filterClientId ? "Select homeowner" : "Pick client first"}
/>
</SelectTrigger>
<SelectContent>
{hos.map((h) => {
const name = `${h.first_name} ${h.last_name}${h.unit_number ? ` — Unit ${h.unit_number}` : ""}`;
return (
<SelectItem key={h.id} value={name}>
{name}
</SelectItem>
);
})}
</SelectContent>
</Select>
</div>
);
}
// text (default)
return (
<div key={f.key}>
{labelEl}
<Input
value={v}
placeholder={f.placeholder}
onChange={(e) => set(f.key, e.target.value)}
/>
</div>
);
})}
</div>
);
}