import type { ReactNode } from 'react'; import './tabBar.css'; /** * The console's view switcher — Orders / Deliveries / Counter sales, Catalogue * / My stock / My requests, Store users / Terminal accounts / Riders. * * ── There were six of these ───────────────────────────────────────────────── * * `TabButton` was defined privately in `StoresPage`, `InventoryPage`, * `ReportsPage`, `SalesPage`, `UsersPage` and `store-user/ui.tsx` — six copies * of the same button, each styled inline, and they had already drifted: three * of them filled the count badge with `--color-warning`, so "85" under Orders * was drawn in the colour this console reserves for something being wrong. A * count is not an alarm. Sales had the sensible version. Nobody could see the * difference because no two of them are ever on screen together. * * ── Why it is a track and not loose pills ─────────────────────────────────── * * The old buttons were transparent until selected, so an unselected set read as * four pieces of text floating above the page with no indication they were one * control or that picking one deselects the others. They sit in a recessed * track now, with the selected tab raised out of it on the card surface — the * standard segmented control, and the one shape that says "these are the views, * you are in this one" without needing a label to say so. */ export interface TabBarProps { children: ReactNode; /** `sm` for a secondary strip — a filter under a primary switcher. */ size?: 'md' | 'sm'; 'aria-label'?: string; } export function TabBar({ children, size = 'md', ...rest }: TabBarProps) { return (
{children}
); } export interface TabProps { label: string; icon?: ReactNode; isActive: boolean; onClick: () => void; /** * The count beside the label. Omit it and no count is drawn. * * Whether ZERO is worth drawing depends on what the tab is, so the caller * decides rather than this component: * * - A VIEW passes `x.length || undefined`. "Deliveries 0" beside "Orders * 85" reads as a broken tab; "Deliveries" alone says nothing and lets the * empty state explain. * - A STATUS in a ladder passes the raw count. "Cancelled 0" is the answer * to a question the reader is asking — nothing was cancelled — and a rung * that loses its number when it empties makes the ladder jump about as * the day goes on. */ count?: number | undefined; /** * Draws a hairline before this tab, dividing the track into groups. * * Sales needs it: Orders and Deliveries are two halves of the app's own * trade, and Counter sales is a different channel entirely. The rule says so * without spending a tab on a heading. */ isGroupStart?: boolean; /** * A pulsing dot after the label, for a view that updates by itself. * * Dispatch's Active Fleet is the one: it reads rider positions live, and the * dot is how the board says so. A named prop rather than a general-purpose * slot — a `trailing` that took any node would be the seam this component * exists to close, and there is exactly one live view in the console. */ isLive?: boolean; } export function Tab({ label, icon, isActive, onClick, count, isGroupStart, isLive }: TabProps) { return ( ); }