import * as React from "react"; import { useState } from "react"; import { Pencil, Plus, Trash2 } from "lucide-react"; import { cn } from "@/lib/utils"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { Button } from "@/components/ui/button"; import { Field, FieldLabel } from "@/components/ui/field"; import { Input } from "@/components/ui/input"; import { LoanOptionGroup } from "@/components/ui/loan-option-group"; import { CurrencyInputWithSlider, OwnershipSplit, type OwnershipOwner, } from "@/components/ui/form-primitives"; import { FREQUENCY_OPTIONS } from "@/lib/loan-constants"; import { formatCurrency } from "@/lib/format-currency"; // ─── Types ────────────────────────────────────────────────────────────────── export type DebtOwner = OwnershipOwner; export type DebtItem = { id: string; /** Summary label shown in the collapsed row. */ description: string; currentOwing: number; monthlyRepayment?: number; interestRate?: number; subLabel?: string; // ── Debt detail fields (edited via the inline form) ── originalLoanAmount?: number; loanRepaymentAmount?: number; repaymentFrequency?: "monthly" | "weekly"; detail?: string; lenderName?: string; accountName?: string; bsb?: string; accountNumber?: string; owners?: DebtOwner[]; }; export type DebtGroup = { debtType: string; label: string; items: DebtItem[]; }; export type DebtAccordionProps = { groups: DebtGroup[]; openGroups?: string[]; onToggleGroup?: (debtType: string) => void; /** Create/update an item. `item.id === ""` means a newly-added item. */ onSaveItem?: (debtType: string, item: DebtItem) => void; onDeleteItem?: (debtType: string, itemId: string) => void; /** Applicants used to seed the liability-ownership split on a new item. */ owners?: DebtOwner[]; /** Accessible label for the accordion landmark region. */ "aria-label"?: string; className?: string; }; const NEW_ITEM_ID = "__new__"; function blankDebtItem(owners: DebtOwner[]): DebtItem { return { id: "", description: "", currentOwing: 0, originalLoanAmount: 0, loanRepaymentAmount: 0, repaymentFrequency: "monthly", detail: "", lenderName: "", accountName: "", bsb: "", accountNumber: "", owners, }; } // ─── Component ────────────────────────────────────────────────────────────── export function DebtAccordion({ groups, openGroups, onToggleGroup, onSaveItem, onDeleteItem, owners = [{ id: "main", name: "Main Applicant", share: 100 }], "aria-label": ariaLabel = "Debt list", className, }: DebtAccordionProps) { const [editing, setEditing] = useState<{ debtType: string; itemId: string; } | null>(null); const [draft, setDraft] = useState(null); const totalOwing = groups .flatMap((g) => g.items) .reduce((sum, item) => sum + item.currentOwing, 0); const startAdd = (debtType: string) => { setDraft(blankDebtItem(owners)); setEditing({ debtType, itemId: NEW_ITEM_ID }); }; const startEdit = (debtType: string, item: DebtItem) => { setDraft({ ...item, owners: item.owners ?? owners }); setEditing({ debtType, itemId: item.id }); }; const cancel = () => { setEditing(null); setDraft(null); }; const save = (debtType: string, item: DebtItem) => { onSaveItem?.(debtType, item); cancel(); }; return (

Debts

Total owing: {formatCurrency(totalOwing)}
{ const prev = new Set(openGroups ?? []); const next = new Set(vals as string[]); for (const v of next) { if (!prev.has(v)) onToggleGroup?.(v); } for (const v of prev) { if (!next.has(v)) onToggleGroup?.(v); } }} > {groups.map((group) => { const isAdding = editing?.debtType === group.debtType && editing.itemId === NEW_ITEM_ID; // One form element per group — at most one is shown at a time // (editing an existing item, or the new draft). const form = editing?.debtType === group.debtType && draft ? ( save(group.debtType, updated)} onCancel={cancel} /> ) : null; return (
{group.label} {group.items.length}{" "} {group.items.length === 1 ? "item" : "items"} ·{" "} {formatCurrency( group.items.reduce((s, i) => s + i.currentOwing, 0), )}{" "} owing
{group.items.length === 0 && !isAdding ? (

No {group.label.toLowerCase()} added yet.

) : ( group.items.map((item) => editing?.debtType === group.debtType && editing.itemId === item.id && form ? ( React.cloneElement(form, { key: item.id }) ) : ( startEdit(group.debtType, item)} onDelete={() => onDeleteItem?.(group.debtType, item.id) } /> ), ) )} {isAdding && form} {!isAdding && ( )}
); })}
); } // ─── Collapsed summary row ────────────────────────────────────────────────── function DebtItemRow({ item, onEdit, onDelete, }: { item: DebtItem; onEdit?: () => void; onDelete?: () => void; }) { const repayment = item.loanRepaymentAmount ?? item.monthlyRepayment; return (
{item.description || "Untitled debt"} {item.subLabel && ( {item.subLabel} )}
Owing: {formatCurrency(item.currentOwing)} {repayment != null && repayment > 0 && ( Repayment: {formatCurrency(repayment)}/ {item.repaymentFrequency === "weekly" ? "wk" : "mo"} )} {item.interestRate != null && ( Rate: {item.interestRate}% )}
); } // ─── Inline add / edit form ───────────────────────────────────────────────── function DebtItemForm({ debtType, initial, onSave, onCancel, }: { debtType: string; initial: DebtItem; onSave: (item: DebtItem) => void; onCancel: () => void; }) { const [item, setItem] = useState(initial); const set = (patch: Partial) => setItem((prev) => ({ ...prev, ...patch })); const isCreditCard = debtType === "credit-card"; const canSave = item.currentOwing > 0 && (item.originalLoanAmount ?? 0) > 0 && (item.loanRepaymentAmount ?? 0) > 0; const handleSave = () => { const description = isCreditCard ? item.lenderName || item.description : item.detail || item.description; onSave({ ...item, description: description || "Untitled debt", monthlyRepayment: item.loanRepaymentAmount, }); }; return (
How much is owing? set({ currentOwing: n })} /> Original loan amount set({ originalLoanAmount: n })} /> How much are your loan repayments?
set({ loanRepaymentAmount: n })} /> set({ repaymentFrequency: v as DebtItem["repaymentFrequency"] }) } />
{isCreditCard ? ( <> Name of lender set({ lenderName: e.target.value })} /> Account Name set({ accountName: e.target.value })} />
BSB set({ bsb: e.target.value })} /> Account Number set({ accountNumber: e.target.value })} />
) : ( Details set({ detail: e.target.value })} /> )} {item.owners && item.owners.length > 1 && ( Who owns the liability? set({ owners: ow })} /> )}
); }