import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { api } from '../api' import { PageHeader, Card, Btn, StatusBadge } from '../components/Layout' import { GBP_DENOMINATIONS, fmtGBP, today } from '../types' interface DayRow { session_date: string status: 'draft' | 'final' total_cash_counted: string submitted_by: string | null } interface DenomDateRow { denomination_value: string session_date: string total_amount: string } interface SummaryResult { by_date: DayRow[] by_date_denom: DenomDateRow[] period: { from: string; to: string } } function fmtDateShort(d: string) { return new Date(d.slice(0, 10) + 'T12:00:00').toLocaleDateString('en-GB', { weekday: 'short', day: '2-digit', month: 'short', }) } export function CashSummary() { const navigate = useNavigate() const [from, setFrom] = useState(() => { const d = new Date() d.setDate(d.getDate() - 6) return d.toISOString().slice(0, 10) }) const [to, setTo] = useState(today) const [result, setResult] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState('') async function generate() { setLoading(true); setError(''); setResult(null) try { const data = await api.get(`/reports/cash-summary?from=${from}&to=${to}`) setResult(data) } catch (e: unknown) { setError(e instanceof Error ? e.message : 'Failed to generate summary') } finally { setLoading(false) } } function addToSafe() { if (!result) return const adjustDenoms: Record = {} result.by_date_denom.forEach(r => { adjustDenoms[r.denomination_value] = (adjustDenoms[r.denomination_value] || 0) + parseFloat(r.total_amount) }) navigate('/safe', { state: { adjustDenoms } }) } // Cross-tab lookups: denomMap[date_str][denom_float] = amount const denomMap: Record> = {} const rowTotals: Record = {} result?.by_date_denom.forEach(r => { const date = r.session_date.slice(0, 10) const dv = parseFloat(r.denomination_value) const amt = parseFloat(r.total_amount) if (!denomMap[date]) denomMap[date] = {} denomMap[date][dv] = amt rowTotals[dv] = (rowTotals[dv] || 0) + amt }) const grandTotal = Object.values(rowTotals).reduce((s, v) => s + v, 0) return (
setFrom(e.target.value)} style={inpSt} />
setTo(e.target.value)} style={inpSt} />
{loading ? 'Loading…' : 'Generate'} {result && result.by_date.length > 0 && ( + Add to Safe )}
{error && (
{error}
)} {result && ( result.by_date.length === 0 ? (

No cash ups recorded in this period.

) : ( {result.by_date.map(d => ( ))} {result.by_date.map(d => ( ))} {GBP_DENOMINATIONS.map(d => { if (!rowTotals[d.value]) return null return ( {result.by_date.map(row => { const date = row.session_date.slice(0, 10) const amt = denomMap[date]?.[d.value] || 0 return ( ) })} ) })} {result.by_date.map(row => ( ))}
Status
Denomination {fmtDateShort(d.session_date)} Total
{d.label} {amt > 0 ? fmtGBP(amt) : '—'} {fmtGBP(rowTotals[d.value])}
Cash Total {fmtGBP(row.total_cash_counted)} {fmtGBP(grandTotal)}
) )}
) } const lbl: React.CSSProperties = { fontSize: '0.75rem', color: 'var(--text-mid)', display: 'block', marginBottom: '0.25rem' } const inpSt: React.CSSProperties = { border: '1px solid var(--card-border)', borderRadius: '6px', padding: '0.4rem 0.6rem', fontSize: '0.875rem' } const th: React.CSSProperties = { padding: '0.45rem 0.5rem', textAlign: 'right', fontWeight: 600, color: 'var(--text-mid)' } const td: React.CSSProperties = { padding: '0.4rem 0.5rem', textAlign: 'right' }