"use client"; import { useState } from "react"; /** A single editable field in a child row. */ export type FieldDef = { key: string; label: string; type?: "text" | "number" | "date" | "checkbox" | "select"; options?: { value: string; label: string }[]; width?: number; /** Numeric granularity. Defaults to money (0.01); a tax rate stored as a * fraction needs finer, or the browser rejects 0.0825 as off-step. */ step?: string; }; export type ChildConfig = { /** URL segment: installments | vehicles | drivers | beneficiaries | claims */ apiKind: string; title: string; fields: FieldDef[]; }; type RowValues = Record; function toDateInput(v: unknown): string { if (!v || typeof v !== "string") return ""; const d = new Date(v); return isNaN(d.getTime()) ? "" : d.toISOString().slice(0, 10); } /** Build editable values for a field from an existing row (or blank). */ function rowToValues(fields: FieldDef[], row?: Record): RowValues { const v: RowValues = {}; for (const f of fields) { const raw = row?.[f.key]; if (f.type === "checkbox") v[f.key] = !!raw; else if (f.type === "date") v[f.key] = toDateInput(raw); else v[f.key] = raw == null ? "" : String(raw); } return v; } /** Coerce editable values into an API payload (numbers/blanks handled). */ function valuesToPayload(fields: FieldDef[], v: RowValues): Record { const out: Record = {}; for (const f of fields) { const val = v[f.key]; if (f.type === "checkbox") out[f.key] = !!val; else if (f.type === "number") { const s = String(val).trim(); out[f.key] = s === "" ? undefined : Number(s); } else { const s = String(val).trim(); out[f.key] = s === "" ? undefined : s; } } return out; } /** * Generic add/edit/remove editor for a policy's child collection. The parent * owns the API calls (so it can reload the policy afterward); this component is * pure UI over `rows` plus add/save/remove callbacks. */ export function ChildCollection({ config, rows, canEdit, onAdd, onSave, onRemove, }: { config: ChildConfig; rows: Record[]; canEdit: boolean; onAdd: (payload: Record) => Promise; onSave: (id: string, payload: Record) => Promise; onRemove: (id: string) => Promise; }) { const [editingId, setEditingId] = useState(null); const [adding, setAdding] = useState(false); const [values, setValues] = useState({}); const [busy, setBusy] = useState(false); function startAdd() { setEditingId(null); setAdding(true); setValues(rowToValues(config.fields)); } function startEdit(row: Record) { setAdding(false); setEditingId(String(row.id)); setValues(rowToValues(config.fields, row)); } function cancel() { setAdding(false); setEditingId(null); } async function submit() { setBusy(true); try { const payload = valuesToPayload(config.fields, values); if (editingId) await onSave(editingId, payload); else await onAdd(payload); cancel(); } catch (e) { window.alert((e as Error)?.message ?? "No se pudo guardar."); } finally { setBusy(false); } } async function remove(id: string) { if (!window.confirm("¿Eliminar este registro?")) return; try { await onRemove(id); } catch (e) { window.alert((e as Error)?.message ?? "No se pudo eliminar."); } } const colCount = config.fields.length + (canEdit ? 1 : 0); function editorRow() { return ( {editor()} ); } function editor() { return (
{config.fields.map((f) => ( ))}
); } return (

{config.title} {rows.length}

{canEdit && !adding && editingId === null && ( )}
{rows.length === 0 && !adding ? (
Sin registros.
) : (
{config.fields.map((f) => ( ))} {canEdit && } {adding && editorRow()} {rows.map((row) => editingId === String(row.id) ? ( ) : ( {config.fields.map((f) => ( ))} {canEdit && ( )} ), )}
{f.label}Acciones
{editor()}
{cellText(f, row[f.key])}
)}
); } function cellText(f: FieldDef, raw: unknown): string { if (f.type === "checkbox") return raw ? "Sí" : "No"; if (f.type === "date") return toDateInput(raw) || "—"; if (f.type === "select") { const opt = f.options?.find((o) => o.value === String(raw)); return opt ? opt.label : "—"; } return raw == null || raw === "" ? "—" : String(raw); }