import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } /** * Deterministic number formatters. * * We deliberately avoid `Intl.NumberFormat("en-ZA", …)` because Node and the * browser ship different ICU builds — on the server, `en-ZA` falls back to a * generic locale and renders "342,707.17", while the browser renders the * expected South African "342 707,17". That mismatch blows up SSR hydration * (https://react.dev/link/hydration-mismatch). Hard-coding the format keeps * server and client byte-identical. * * Style: space as thousand separator, comma as decimal point, exactly the * shape `Intl` produces in a real en-ZA browser. Always positive. */ function groupThousands(intPart: string): string { // Add a space every 3 digits from the right. return intPart.replace(/\B(?=(\d{3})+(?!\d))/g, " "); } export function formatNumber(value: number | null | undefined, decimals = 2): string { if (value == null || isNaN(value)) return "0"; // toFixed gives a stable, locale-independent string. Split on the decimal // point so we can rejoin with a comma. const [intPart, fracPart] = value.toFixed(decimals).split("."); const sign = intPart.startsWith("-") ? "-" : ""; const digits = sign ? intPart.slice(1) : intPart; const grouped = groupThousands(digits); return fracPart != null ? `${sign}${grouped},${fracPart}` : `${sign}${grouped}`; } export function formatCurrency(value: number | null | undefined): string { if (value == null || isNaN(value)) return "R0,00"; return `R ${formatNumber(value, 2)}`; } export function formatDate(value: string | Date | null | undefined): string { if (!value) return ""; const d = typeof value === "string" ? new Date(value) : value; if (isNaN(d.getTime())) return ""; // "21 Jul 2026" — stable across server and client. const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; return `${String(d.getDate()).padStart(2, "0")} ${months[d.getMonth()]} ${d.getFullYear()}`; } export function formatDateInput(value: string | Date | null | undefined): string { if (!value) return ""; const d = typeof value === "string" ? new Date(value) : value; if (isNaN(d.getTime())) return ""; // YYYY-MM-DD for — already locale-neutral. const yyyy = d.getFullYear(); const mm = String(d.getMonth() + 1).padStart(2, "0"); const dd = String(d.getDate()).padStart(2, "0"); return `${yyyy}-${mm}-${dd}`; }