import { useState, useEffect } from 'react'; function fmt(value) { const num = parseFloat(value) || 0; return '$' + num.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } function ProgressBar({ paid, total }) { const pct = total > 0 ? Math.min(100, (paid / total) * 100) : 0; return (
= 100 ? 'var(--success)' : 'var(--accent)', borderRadius: '999px', transition: 'width 0.3s ease', }} />
); } const TODAY = new Date().toISOString().slice(0, 10); const EMPTY_FORM = { name: '', total_amount: '', due_date: '', start_date: TODAY, assigned_paycheck: 'both', }; function PlanCard({ plan, onEdit, onDelete }) { const pct = plan.total_amount > 0 ? Math.min(100, (plan.paid_total / plan.total_amount) * 100) : 0; return (
{plan.name} {plan.overdue && ( overdue )} {!plan.active && ( paid off )}
Due {new Date(plan.due_date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC' })}  ·  {plan.assigned_paycheck == null ? 'Split across both paychecks' : `Paycheck ${plan.assigned_paycheck} only`}
{fmt(plan.paid_total)} paid 0 ? 'var(--text)' : 'var(--success)' }}> {plan.remaining > 0 ? `${fmt(plan.remaining)} remaining` : 'Paid off'} / {fmt(plan.total_amount)}
{plan.active && (
)} {!plan.active && ( )}
); } export default function Financing() { const [plans, setPlans] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState(EMPTY_FORM); const [formError, setFormError] = useState(null); const [saving, setSaving] = useState(false); async function load() { try { setLoading(true); const res = await fetch('/api/financing'); if (!res.ok) throw new Error('Failed to load'); setPlans(await res.json()); setError(null); } catch (err) { setError(err.message); } finally { setLoading(false); } } useEffect(() => { load(); }, []); function openAdd() { setEditingId(null); setForm(EMPTY_FORM); setFormError(null); setShowForm(true); } function openEdit(plan) { setEditingId(plan.id); setForm({ name: plan.name, total_amount: plan.total_amount, due_date: plan.due_date, start_date: plan.start_date || TODAY, assigned_paycheck: plan.assigned_paycheck == null ? 'both' : String(plan.assigned_paycheck), }); setFormError(null); setShowForm(true); } function cancel() { setShowForm(false); setEditingId(null); setForm(EMPTY_FORM); setFormError(null); } function handleChange(e) { const { name, value } = e.target; setForm(prev => ({ ...prev, [name]: value })); } async function handleSave(e) { e.preventDefault(); setFormError(null); setSaving(true); try { const payload = { name: form.name, total_amount: parseFloat(form.total_amount), due_date: form.due_date, start_date: form.start_date || TODAY, assigned_paycheck: form.assigned_paycheck === 'both' ? null : parseInt(form.assigned_paycheck, 10), }; const url = editingId ? `/api/financing/${editingId}` : '/api/financing'; const method = editingId ? 'PUT' : 'POST'; const res = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const data = await res.json(); if (!res.ok) { setFormError(data.error || 'Failed to save'); return; } await load(); cancel(); } catch (err) { setFormError(err.message); } finally { setSaving(false); } } async function handleDelete(plan) { if (!window.confirm(`Delete "${plan.name}"? This cannot be undone.`)) return; try { const res = await fetch(`/api/financing/${plan.id}`, { method: 'DELETE' }); if (!res.ok) throw new Error('Failed to delete'); await load(); } catch (err) { alert(err.message); } } const active = plans.filter(p => p.active); const inactive = plans.filter(p => !p.active); return (

Financing

{!showForm && ( )}
{showForm && (

{editingId ? 'Edit Financing Plan' : 'New Financing Plan'}

{formError &&
{formError}
}
)} {loading &&

Loading…

} {error &&
Error: {error}
} {!loading && !error && ( <> {active.length === 0 && !showForm && (
No active financing plans. Click "Add Plan" to get started.
)}
{active.map(plan => ( ))}
{inactive.length > 0 && ( <>
Paid Off
{inactive.map(plan => ( ))}
)} )}
); }