// 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.'}