// PriceList.jsx — Account & Finance › Customer Price List // Pick a customer (name + code) on the left, see their negotiated day-rate // for every machine type on the right: current given price + the date it // took effect, plus the previous price and its date. const _fmtRM = (n) => 'RM ' + Number(n).toLocaleString('en-MY'); const _fmtPriceDate = (iso) => iso ? new Date(iso + 'T00:00:00').toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }) : '—'; function PriceDelta({ current, last, changed }) { if (!changed || current === last) { return No change; } const up = current > last; const pct = Math.round(Math.abs(current - last) / last * 100); const Ico = up ? window.TrendUp : window.TrendDown; const color = up ? '#15803d' : 'var(--color-danger)'; return ( {up ? '+' : '−'}{pct}% ); } function PriceListPage({ role }) { const canEditPrices = role === 'Admin' || role === 'Finance'; const [selId, setSelId] = React.useState(CUSTOMERS[0].id); const [q, setQ] = React.useState(''); const customer = CUSTOMERS.find(c => c.id === selId) || CUSTOMERS[0]; const rows = React.useMemo(() => priceListFor(customer.id), [customer.id]); const filtered = CUSTOMERS.filter(c => !q.trim() || c.name.toLowerCase().includes(q.toLowerCase()) || c.id.toLowerCase().includes(q.toLowerCase()) ); // Catalogue-wide stats for the selected customer const avgVsBase = (() => { const types = Object.fromEntries(PRICE_MACHINE_TYPES.map(t => [t.id, t.base])); const diffs = rows.map(r => (r.current - types[r.typeId]) / types[r.typeId]); const avg = diffs.reduce((a, b) => a + b, 0) / diffs.length; return Math.round(avg * 100); })(); const lastUpdated = rows.reduce((a, r) => (r.priceDate > a ? r.priceDate : a), rows[0].priceDate); return (
Customer Price List — negotiated day-rates per machine type, by customer. Each rate shows the current given price and effective date alongside the previous price. {canEditPrices ? 'You can revise prices.' : 'View-only — only Admin & Finance can revise prices.'}
{/* Customer picker */} {/* Price table for selected customer */}

{customer.name}

Customer code {customer.id} · {rows.length} machine types priced
{canEditPrices ? : Admin & Finance only}
Avg vs. standard rate
0 ? '#15803d' : avgVsBase < 0 ? 'var(--color-danger)' : 'var(--color-navy)'}}> {avgVsBase > 0 ? '+' : ''}{avgVsBase}%
Machine types priced
{rows.length}
Last price update
{_fmtPriceDate(lastUpdated)}
{rows.map(r => { const cat = CATEGORIES.find(c => c.name === r.category); return ( ); })}
Machine Type Given Price Price Date Last Price Last Price Date Change
{r.category} {r.spec}
{_fmtRM(r.current)} /day {_fmtPriceDate(r.priceDate)} {r.changed ? _fmtRM(r.last) : '—'} {r.changed ? _fmtPriceDate(r.lastDate) : '—'}
); } Object.assign(window, { PriceListPage });