import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; /** Merge conditional class names and de-duplicate Tailwind utilities. */ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } /** Format a number of cents (e.g. 900) as a currency string ("$9"). */ export function formatPrice(cents: number, currency = "USD") { const dollars = cents / 100; return new Intl.NumberFormat("en-US", { style: "currency", currency, maximumFractionDigits: dollars % 1 === 0 ? 0 : 2, }).format(dollars); } /** The monthly usage bucket key, e.g. "2026-06". Pass a date for testability. */ export function periodKey(date: Date): string { const y = date.getUTCFullYear(); const m = String(date.getUTCMonth() + 1).padStart(2, "0"); return `${y}-${m}`; } /** * Returns `path` only if it is a safe same-origin relative path; otherwise * falls back to "/dashboard". Guards against open-redirect attacks by rejecting * protocol-relative ("//", "/\"), absolute ("https://…"), and backslash URLs. */ export function safeRedirect(path: string | null | undefined): string { if (!path) return "/dashboard"; // Must be a single-slash-rooted relative path with no scheme or backslash escapes. if ( !path.startsWith("/") || path.startsWith("//") || path.startsWith("/\\") || path.startsWith("\\") || path.includes("://") ) { return "/dashboard"; } return path; }