diff --git a/frontend/src/pages/DailyCashUp.tsx b/frontend/src/pages/DailyCashUp.tsx index 6074b9c..ae6a1c2 100644 --- a/frontend/src/pages/DailyCashUp.tsx +++ b/frontend/src/pages/DailyCashUp.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useState, useEffect } from 'react' import { RefreshCw, Save, CheckCircle, Loader } from 'lucide-react' import { api } from '../api' import { PageHeader, Card, Btn, StatusBadge } from '../components/Layout' @@ -31,8 +31,12 @@ function denomTotal(denoms: Denomination[]) { interface Props { user: User } +// flow: checking → empty (no record) | editing (draft) | locked (final) +type PageState = 'checking' | 'empty' | 'editing' | 'locked' + export function DailyCashUp({ user: _user }: Props) { const [date, setDate] = useState(today()) + const [pageState, setPageState] = useState('checking') const [cashUp, setCashUp] = useState(null) const [takings, setTakings] = useState(initDenominations('takings')) const [float, setFloat] = useState(initDenominations('float')) @@ -43,60 +47,49 @@ export function DailyCashUp({ user: _user }: Props) { const [tillPayments, setTillPayments] = useState([]) const [fetching, setFetching] = useState(false) const [saving, setSaving] = useState(false) - const [loading, setLoading] = useState(false) const [msg, setMsg] = useState<{ text: string; ok: boolean } | null>(null) const [showFloat, setShowFloat] = useState(false) - const isFinal = cashUp?.status === 'final' + const isFinal = pageState === 'locked' function flash(text: string, ok = true) { setMsg({ text, ok }) setTimeout(() => setMsg(null), 4000) } - async function loadExisting() { - setLoading(true) - try { - const data = await api.get<{ - cash_up: CashUp; denominations: Denomination[]; card_machines: CardMachine[]; - reconciliation: ReconciliationRow[]; attachments: Attachment[] - }>(`/cashup?date=${date}`) + function applyLoaded(data: { + cash_up: CashUp; denominations: Denomination[]; card_machines: CardMachine[]; attachments: Attachment[] + }) { + setCashUp(data.cash_up) + setNotes(data.cash_up.notes || '') + setAttachments(data.attachments || []) - setCashUp(data.cash_up) - setNotes(data.cash_up.notes || '') - setAttachments(data.attachments || []) + const rebuild = (ct: 'takings' | 'float') => + GBP_DENOMINATIONS.map(d => { + const saved = data.denominations.find( + s => s.count_type === ct && parseFloat(String(s.denomination_value)) === d.value + ) + return saved + ? { ...saved, denomination_value: d.value } + : { count_type: ct, denomination_type: d.type, denomination_value: d.value, quantity: null, value_entered: null, total_amount: 0 } + }) - // Rebuild denomination grids from saved data - const rebuild = (ct: 'takings' | 'float') => - GBP_DENOMINATIONS.map(d => { - const saved = data.denominations.find( - s => s.count_type === ct && parseFloat(String(s.denomination_value)) === d.value - ) - return saved - ? { ...saved, denomination_value: d.value } - : { count_type: ct, denomination_type: d.type, denomination_value: d.value, quantity: null, value_entered: null, total_amount: 0 } - }) + setTakings(rebuild('takings')) + setFloat(rebuild('float')) - setTakings(rebuild('takings')) - setFloat(rebuild('float')) - - if (data.card_machines.length) { - setMachines(MACHINES.map(name => { - const m = data.card_machines.find(c => c.machine_name === name) - return m ?? { machine_name: name, total_amount: 0, amex_amount: 0, visa_mc_amount: 0 } - })) - } - - flash('Loaded existing cash up.') - } catch (e: unknown) { - if (e instanceof Error && e.message === 'Not found') flash('No cash up for this date.', false) - else flash('Failed to load.', false) - } finally { - setLoading(false) + if (data.card_machines.length) { + setMachines(MACHINES.map(name => { + const m = data.card_machines.find(c => c.machine_name === name) + return m ?? { machine_name: name, total_amount: 0, amex_amount: 0, visa_mc_amount: 0 } + })) } + + setPageState(data.cash_up.status === 'final' ? 'locked' : 'editing') } - function reset() { + // Auto-check on mount and whenever date changes + useEffect(() => { + setPageState('checking') setCashUp(null) setTakings(initDenominations('takings')) setFloat(initDenominations('float')) @@ -105,7 +98,17 @@ export function DailyCashUp({ user: _user }: Props) { setAttachments([]) setNewbookTotals(null) setTillPayments([]) - } + + api.get<{ + cash_up: CashUp; denominations: Denomination[]; card_machines: CardMachine[]; + reconciliation: ReconciliationRow[]; attachments: Attachment[] + }>(`/cashup?date=${date}`) + .then(data => applyLoaded(data)) + .catch(e => { + if (e instanceof Error && e.message === 'Not found') setPageState('empty') + else { flash('Failed to load data for this date.', false); setPageState('empty') } + }) + }, [date]) function updateDenom(list: Denomination[], setList: (d: Denomination[]) => void, idx: number, field: 'quantity' | 'value_entered', raw: string) { const val = raw === '' ? null : parseFloat(raw) @@ -174,6 +177,7 @@ export function DailyCashUp({ user: _user }: Props) { flash(result.message) if (status === 'final') { setCashUp(prev => prev ? { ...prev, status: 'final' } : null) + setPageState('locked') } } catch (e: unknown) { flash(e instanceof Error ? e.message : 'Save failed.', false) @@ -196,8 +200,7 @@ export function DailyCashUp({ user: _user }: Props) { return (
- - {cashUp &&
} + {msg && (
- { setDate(e.target.value); reset() }} + setDate(e.target.value)} style={{ border: '1px solid var(--card-border)', borderRadius: '6px', padding: '0.45rem 0.75rem', fontSize: '0.9rem' }} />
-
- - {loading ? 'Loading…' : 'Load Existing'} - - {cashUp && New} -
+ {cashUp &&
} + {/* Checking */} + {pageState === 'checking' && ( +
Loading…
+ )} + + {/* No cash up for this date */} + {pageState === 'empty' && ( + +

No cash up recorded for this date.

+ setPageState('editing')}>Start Cash Up +
+ )} + + {/* Locked banner */} + {pageState === 'locked' && ( +
+ This cash up has been finalised and is locked. +
+ )} + + {(pageState === 'editing' || pageState === 'locked') && <> + {/* Cash denomination — Takings */}
@@ -363,7 +387,7 @@ export function DailyCashUp({ user: _user }: Props) { {/* Action buttons */} - {!isFinal && ( + {pageState === 'editing' && (
save('draft')} disabled={saving} variant="secondary"> @@ -376,11 +400,7 @@ export function DailyCashUp({ user: _user }: Props) {
)} - {isFinal && ( -
- This cash up has been finalised and cannot be edited. -
- )} + /* end editing | locked */}