diff --git a/src/lib/order-color.ts b/src/lib/order-color.ts new file mode 100644 index 0000000..609ece1 --- /dev/null +++ b/src/lib/order-color.ts @@ -0,0 +1,44 @@ +/** + * Per-order color helper. + * + * Hashes an order id into one of six curated Tailwind colours so each + * order group renders with a stable, distinct accent on list pages + * (Collection Confirm, Invoicing). The same id always picks the same + * colour across every render and every page. + * + * FNV-1a gives an even distribution with no deps. Empty / null ids + * fall back to the first palette entry so the helper is total. + */ + +export type OrderColor = { + /** Left-edge stripe class, e.g. `border-emerald-500/40`. */ + borderClass: string; + /** Faint body tint, e.g. `bg-emerald-500/8`. Available for callers + * that want a coloured background; not used by the current pages. */ + bgClass: string; +}; + +export const ORDER_COLOR_PALETTE: ReadonlyArray = [ + { borderClass: "border-emerald-500/40", bgClass: "bg-emerald-500/8" }, + { borderClass: "border-sky-500/40", bgClass: "bg-sky-500/8" }, + { borderClass: "border-amber-500/40", bgClass: "bg-amber-500/8" }, + { borderClass: "border-rose-500/40", bgClass: "bg-rose-500/8" }, + { borderClass: "border-violet-500/40", bgClass: "bg-violet-500/8" }, + { borderClass: "border-teal-500/40", bgClass: "bg-teal-500/8" }, +]; + +const FNV_OFFSET = 0x811c9dc5; +const FNV_PRIME = 0x01000193; +const PALETTE_SIZE = ORDER_COLOR_PALETTE.length; + +export function colorForOrder(orderId: string | undefined | null): OrderColor { + if (!orderId) return ORDER_COLOR_PALETTE[0]; + let hash = FNV_OFFSET; + for (let i = 0; i < orderId.length; i++) { + hash ^= orderId.charCodeAt(i); + // `>>> 0` keeps the value in the uint32 range so Math.imul + // doesn't produce a negative result on overflow. + hash = Math.imul(hash, FNV_PRIME) >>> 0; + } + return ORDER_COLOR_PALETTE[hash % PALETTE_SIZE]; +}