import type { ReactNode } from 'react';
import { Ban, CloudUpload, ListChecks, Receipt, ShoppingCart, TrendingDown, TrendingUp } from 'lucide-react';
import { count, money } from '@/features/store-admin/format';
import type { ConsoleTotals } from '../consoleModel';
import type { DaySeries, Trend } from '../useConsoleSeries';
/**
* The five figures the board opens with.
*
* Each card carries the number, the shape of how it got there, and whether that
* is better or worse than the period before. The sparkline is not decoration:
* ₹12,480 taken evenly across a week and ₹12,480 taken in one afternoon are the
* same number and completely different news, and the line is the only part of
* the card that can tell them apart.
*
* Trend arrows are drawn only where a real comparison exists. `Trend.isNew`
* marks the case where the previous period was zero — "↑ ∞%" is not a fact, so
* it reads "new" instead.
*/
export interface KpiStripProps {
totals: ConsoleTotals;
days: readonly DaySeries[];
trends: {
online: Trend;
counter: Trend;
orders: Trend;
cancelled: Trend;
};
comparisonLabel: string;
isLoading: boolean;
}
export function KpiStrip({ totals, days, trends, comparisonLabel, isLoading }: KpiStripProps) {
if (isLoading) {
return (
{[0, 1, 2, 3, 4].map((i) => (
))}
);
}
return (
}
label="Online Sales"
value={money(totals.onlineRevenue)}
trend={trends.online}
comparisonLabel={comparisonLabel}
series={days.map((d) => d.online)}
/>
}
label="Counter Sales"
value={money(totals.counterRevenue)}
trend={trends.counter}
comparisonLabel={comparisonLabel}
series={days.map((d) => d.counter)}
/>
}
label="Total Orders"
value={count(totals.totalOrders)}
trend={trends.orders}
comparisonLabel={comparisonLabel}
series={days.map((d) => d.onlineOrders + d.counterBills)}
/>
}
label="Cancelled Orders"
value={count(totals.cancelled)}
trend={trends.cancelled}
comparisonLabel={comparisonLabel}
series={days.map(() => 0)}
// The only card where a rise is bad news, so the colours invert.
isInverted
/>
}
label="Unsynced Bills"
value={count(totals.pendingBills)}
// A live queue depth, not a period figure: there is nothing last week
// to compare it against, so it states what it is instead.
note={totals.pendingBills === 0 ? 'all bills are in the books' : 'waiting on a till'}
series={days.map(() => 0)}
isInverted
/>
);
}
function Kpi({
icon, label, value, trend, comparisonLabel, series, note, isInverted,
}: {
icon: ReactNode;
label: string;
value: string;
trend?: Trend;
comparisonLabel?: string;
series: readonly number[];
note?: string;
isInverted?: boolean;
}) {
// Up is green unless a rise is the bad outcome — cancellations, unsent bills.
const isGood = trend ? (isInverted ? trend.direction === 'down' : trend.direction === 'up') : true;
// A flat line on an empty period is not bad news, so it is not drawn in red.
const hasShape = series.some((value) => value > 0);
return (
{/* Icon and figure only. The sparkline sits on the row below rather than
beside the label: at the widths this strip actually gets — five cards
sharing the page with Nearle Buddy — a 52px chart alongside truncates
"Cancelled Orders" to "Cance…", and a label that has stopped naming
its number is worse than no chart. */}