Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-05-14 23:18:05 +00:00
co-authored by renee-png
parent a8169fc82a
commit f75f2d5094
+124 -1
View File
@@ -312,4 +312,127 @@ function TrustLedgerPage() {
</Card>
</PageContainer>
);
}
}
function RecordEntryDialog({
open, type, clients, onClose, onSaved,
}: {
open: boolean;
type: "deposit" | "withdrawal";
clients: ClientLite[];
onClose: () => void;
onSaved: (entry: Entry) => void;
}) {
const { user } = useAuth();
const [clientId, setClientId] = useState<string>("");
const [caseId, setCaseId] = useState<string>("_pool");
const [cases, setCases] = useState<{ id: string; case_number: string | null; title: string | null }[]>([]);
const [date, setDate] = useState<string>(new Date().toISOString().slice(0, 10));
const [amount, setAmount] = useState<string>("");
const [note, setNote] = useState<string>("");
const [saving, setSaving] = useState(false);
useEffect(() => {
if (!open) {
setClientId(""); setCaseId("_pool"); setCases([]);
setAmount(""); setNote(""); setDate(new Date().toISOString().slice(0, 10));
}
}, [open]);
useEffect(() => {
if (!clientId) { setCases([]); return; }
(async () => {
const { data } = await supabase
.from("cases")
.select("id, case_number, title")
.eq("client_id", clientId)
.is("archived_at", null)
.order("opened_at", { ascending: false });
setCases(data ?? []);
})();
}, [clientId]);
const sortedClients = useMemo(
() => [...clients].filter((c) => !c.archived_at).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? "")),
[clients],
);
const save = async () => {
if (!clientId) return toast.error("Pick a client");
const amt = Number(amount);
if (!amt || amt <= 0) return toast.error("Enter a valid amount");
setSaving(true);
const { data, error } = await supabase
.from("trust_ledger_entries")
.insert({
client_id: clientId,
case_id: caseId === "_pool" ? null : caseId,
entry_date: date,
entry_type: type,
amount: amt,
note: note || null,
created_by: user?.id ?? null,
})
.select("*")
.single();
setSaving(false);
if (error) return toast.error(error.message);
toast.success(`${type === "deposit" ? "Deposit" : "Withdrawal"} recorded`);
onSaved(data as Entry);
};
return (
<Dialog open={open} onOpenChange={(v) => !v && onClose()}>
<DialogContent>
<DialogHeader>
<DialogTitle>Record {type}</DialogTitle>
</DialogHeader>
<div className="space-y-3">
<div>
<Label>Client</Label>
<Select value={clientId} onValueChange={setClientId}>
<SelectTrigger><SelectValue placeholder="Select client…" /></SelectTrigger>
<SelectContent>
{sortedClients.map((c) => (
<SelectItem key={c.id} value={c.id}>{c.name ?? "Unnamed"}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label>Case (optional)</Label>
<Select value={caseId} onValueChange={setCaseId} disabled={!clientId}>
<SelectTrigger><SelectValue placeholder="Client trust pool" /></SelectTrigger>
<SelectContent>
<SelectItem value="_pool">Client trust pool (no case)</SelectItem>
{cases.map((c) => (
<SelectItem key={c.id} value={c.id}>{c.case_number ?? c.title}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<Label>Date</Label>
<Input type="date" value={date} onChange={(e) => setDate(e.target.value)} />
</div>
<div>
<Label>Amount</Label>
<Input type="number" step="0.01" placeholder="0.00" value={amount} onChange={(e) => setAmount(e.target.value)} />
</div>
</div>
<div>
<Label>Note (optional)</Label>
<Textarea rows={2} value={note} onChange={(e) => setNote(e.target.value)} placeholder="Reference, source, purpose…" />
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={onClose}>Cancel</Button>
<Button onClick={save} disabled={saving}>
{saving ? <Loader2 className="h-4 w-4 mr-2 animate-spin" /> : null}
Record {type}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}