/* Admin → Billing & Invoices: revenue, invoices, collection status. */ function BillingScreen() { const [invoices, setInvoices] = React.useState([ { id:'INV-2043', family:'Hatten Family', c:'green', amount:120.00, item:'Aftercare · May', status:'paid', due:'May 28' }, { id:'INV-2044', family:'Carter Family', c:'blue', amount:15.00, item:'Field Trip Fee', status:'due', due:'Jun 10' }, { id:'INV-2045', family:'Mitchell Family', c:'red', amount:48.50, item:'Lunch reload + Art fee', status:'overdue', due:'May 30' }, { id:'INV-2046', family:'Perez Family', c:'gold', amount:80.00, item:'Coding Club · Jun', status:'paid', due:'Jun 1' }, { id:'INV-2047', family:'Bennett Family', c:'cyan', amount:120.00, item:'Aftercare · Jun', status:'due', due:'Jun 15' }, ]); const ST = { paid: { label:'Paid', bg:'var(--success-100)', fg:'var(--success-600)' }, due: { label:'Due', bg:'var(--warning-100)', fg:'var(--warning-600)' }, overdue: { label:'Overdue', bg:'var(--danger-100)', fg:'var(--danger-600)' }, }; const money = (n) => '$' + n.toFixed(2); const markPaid = (id) => { setInvoices(iv => iv.map(x => x.id===id ? { ...x, status:'paid' } : x)); jlToast(id + ' marked paid', { icon:'check-check', color:'#1F9D57' }); }; const remind = (x) => jlToast('Reminder sent to ' + x.family, { icon:'send' }); const collected = invoices.filter(i=>i.status==='paid').reduce((s,i)=>s+i.amount,0); const outstanding = invoices.filter(i=>i.status!=='paid').reduce((s,i)=>s+i.amount,0); const overdue = invoices.filter(i=>i.status==='overdue').length; return (
+11% YoY}>
'$'+(v/1000).toFixed(0)+'k'} />
{[['Lunch accounts','$9,240','var(--gold-500)'],['Aftercare','$7,180','var(--blue-500)'],['Fees & trips','$5,030','var(--success-500)'],['Marketplace','$3,200','var(--danger-500)']].map(([l,v,c],i)=>(
{l} {v}
))}
jlToast('New invoice form opened', { icon:'plus' })} style={{ display:'flex', alignItems:'center', gap:6, background:'var(--gold-400)', color:'var(--on-gold)', border:0, borderRadius:9, padding:'8px 13px', fontWeight:700, fontSize:12.5, cursor:'pointer', fontFamily:'var(--font-sans)', boxShadow:'var(--shadow-gold)' }}>New Invoice} pad={false}>
InvoiceFamily / ItemAmountDueStatus
{invoices.map((x,i) => { const st = ST[x.status]; return (
{x.id} {x.family}{x.item} {money(x.amount)} {x.due} {x.status==='paid' ? {st.label} : {st.label} }
); })}
); } Object.assign(window, { BillingScreen });