import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } export function formatDateTime(value?: string | null) { if (!value) return "Never"; return new Intl.DateTimeFormat(undefined, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", }).format(new Date(value)); } export function relativeTime(value?: string | null) { if (!value) return "Never"; const deltaSeconds = Math.round((new Date(value).getTime() - Date.now()) / 1000); const units: Array<[Intl.RelativeTimeFormatUnit, number]> = [ ["year", 31536000], ["month", 2592000], ["day", 86400], ["hour", 3600], ["minute", 60], ["second", 1], ]; const formatter = new Intl.RelativeTimeFormat(undefined, { numeric: "auto" }); const [unit, seconds] = units.find(([, amount]) => Math.abs(deltaSeconds) >= amount) ?? ["second", 1]; return formatter.format(Math.round(deltaSeconds / seconds), unit); } export function truncateMiddle(value: string, max = 18) { if (value.length <= max) return value; const keep = Math.max(4, Math.floor((max - 3) / 2)); return `${value.slice(0, keep)}...${value.slice(-keep)}`; } export async function copyToClipboard(value: string) { await navigator.clipboard.writeText(value); } export function slugifyName(value: string, fallback = "item") { const slug = value .trim() .toLowerCase() .replace(/[^a-z0-9]+/g, "_") .replace(/^_+|_+$/g, ""); return slug || `${fallback}_${Date.now().toString(36)}`; }