export const formatMoney = (amount: number, currency = "usd") => new Intl.NumberFormat(undefined, { style: "currency", currency: currency.toUpperCase() }).format(amount / 100); export const formatDay = (value: string, zone: "UTC" | "local" = "local") => new Date(value).toLocaleDateString(undefined, { day: "numeric", month: "long", year: "numeric", ...(zone === "UTC" ? { timeZone: "UTC" } : {}), }); // A billing boundary is one Stripe instant, shown in the practice zone with its time so no two screens // land on different days. A bare day, all the subscription summary sends, is a UTC day and says so. export const formatBillingDate = (value: string, timeZone: string | null) => /^\d{4}-\d{2}-\d{2}$/.test(value) ? `${formatDay(value, "UTC")} (UTC)` : new Date(value).toLocaleString(undefined, { day: "numeric", month: "long", year: "numeric", hour: "numeric", minute: "2-digit", timeZone: timeZone ?? undefined, timeZoneName: "short", }); // The server rule: exact after trimming and collapsing spaces. const tidy = (value: string) => value.trim().replace(/\s+/g, " "); export const sameName = (typed: string, name: string) => tidy(name) !== "" && tidy(typed) === tidy(name); export const counted = (count: number, one: string, many: string) => `${count.toLocaleString()} ${count === 1 ? one : many}`; // Outside every workspace and permission gate: after a closure it is the one page staff can still use. export const PRACTICE_CLOSURE_PATH = "/dashboard/practice-closure"; // The Owner's account while closed; under the closure path, so the closed gate already serves it. export const PRACTICE_CLOSURE_ACCOUNT_PATH = `${PRACTICE_CLOSURE_PATH}/account`; // The shared panel card (Card), lifted like every card; the class only stacks its children tight. export const CLOSURE_CARD = "practice-closure__card"; // A closure outcome is a shared notice in its tone: a refusal in red, a wait in amber, done in green. export const NOTICE_TONE = { stop: "ak-notice ak-notice--error", wait: "ak-notice", done: "ak-notice ak-notice--success" } as const;