import { useState, useEffect } from 'react' import { Routes, Route, NavLink, Navigate, useNavigate } from 'react-router-dom' import { api } from '../api' import { PageHeader, Card, Btn } from '../components/Layout' import { GBP_DENOMINATIONS, fmtGBP } from '../types' import type { FloatCount, FloatDenomination, FloatReceipt } from '../types' type CountType = 'petty_cash' | 'change_tin' | 'safe_cash' const TYPE_LABELS: Record = { petty_cash: 'Petty Cash', change_tin: 'Change Tin', safe_cash: 'Safe Cash', } // Denominations relevant for each type (change_tin uses bags, no £0.02/£0.01) const CHANGE_TIN_DENOMS = GBP_DENOMINATIONS.filter(d => d.value >= 0.05) function FloatCountForm({ type }: { type: CountType }) { const navigate = useNavigate() const [denomQtys, setDenomQtys] = useState>({}) const [receipts, setReceipts] = useState>([]) const [notes, setNotes] = useState('') const [saving, setSaving] = useState(false) const [msg, setMsg] = useState<{ text: string; ok: boolean } | null>(null) const denoms = type === 'change_tin' ? CHANGE_TIN_DENOMS : GBP_DENOMINATIONS // Load settings for change tin targets const [changeTinTargets, setChangeTinTargets] = useState>({}) const [pettyTarget, setPettyTarget] = useState(200) useEffect(() => { api.get>('/settings').then(s => { if (type === 'change_tin') { try { setChangeTinTargets(JSON.parse(s.change_tin_breakdown || '{}')) } catch {} } if (type === 'petty_cash') { setPettyTarget(parseFloat(s.petty_cash_float || '200')) } }).catch(() => {}) }, [type]) const totalCounted = denoms.reduce((s, d) => s + d.value * (denomQtys[d.value] ?? 0), 0) const totalReceipts = receipts.reduce((s, r) => s + (parseFloat(r.amount) || 0), 0) const targetAmount = type === 'petty_cash' ? pettyTarget : type === 'change_tin' ? Object.entries(changeTinTargets).reduce((s, [, v]) => s + (v || 0), 0) : 0 const variance = type === 'petty_cash' ? totalCounted + totalReceipts - pettyTarget : type === 'change_tin' ? totalCounted - targetAmount : 0 async function save() { setSaving(true) try { await api.post('/floats/save', { count_type: type, count_date: new Date().toISOString(), denominations: denoms.filter(d => (denomQtys[d.value] ?? 0) > 0).map(d => ({ denomination: d.value, quantity: denomQtys[d.value] ?? 0, total: d.value * (denomQtys[d.value] ?? 0), })), receipts: type === 'petty_cash' ? receipts.filter(r => r.amount) : [], total_counted: totalCounted, total_receipts: totalReceipts, target_amount: targetAmount, variance, notes, }) setMsg({ text: 'Count saved.', ok: true }) setDenomQtys({}) setReceipts([]) setNotes('') } catch (e: unknown) { setMsg({ text: e instanceof Error ? e.message : 'Save failed.', ok: false }) } finally { setSaving(false) } } return (
{msg && (
{msg.text}
)}

DENOMINATIONS

{denoms.map(d => { const qty = denomQtys[d.value] ?? 0 const target = type === 'change_tin' ? parseFloat(String(changeTinTargets[d.value.toFixed(2)] ?? '0')) : undefined const rowTotal = d.value * qty return (
{d.label} setDenomQtys(prev => ({ ...prev, [d.value]: parseInt(e.target.value) || 0 }))} placeholder="0" style={inpSt} /> {target !== undefined && ( tgt {fmtGBP(target)} )} {rowTotal > 0 ? fmtGBP(rowTotal) : '—'}
) })}
Total Counted {fmtGBP(totalCounted)}
{type === 'petty_cash' && (

RECEIPTS

setReceipts(r => [...r, { amount: '', description: '' }])}>+ Add
{receipts.map((r, i) => (
setReceipts(prev => prev.map((x, j) => j === i ? { ...x, amount: e.target.value } : x))} style={inpSt} /> setReceipts(prev => prev.map((x, j) => j === i ? { ...x, description: e.target.value } : x))} style={inpSt} />
))} {receipts.length > 0 && (
Total Receipts{fmtGBP(totalReceipts)}
)}
)} {type !== 'safe_cash' && (
Target Amount {fmtGBP(targetAmount)}
Variance 0 ? 'var(--success)' : 'var(--danger)' }}> {Math.abs(variance) < 0.01 ? '£0.00' : (variance > 0 ? '+' : '') + fmtGBP(Math.abs(variance))}
)}