export { cn } from './cn' /** * Generate a random ID with optional prefix */ export function generateId(prefix = ''): string { const random = Math.random().toString(36).substring(2, 9) return prefix ? `${prefix}_${random}` : random } /** * Sleep for a given number of milliseconds */ export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)) } /** * Format a date as a short human-readable string (e.g. "Jan 5, 2024"). * Accepts a Date object or an ISO date string. */ export function formatDate(date: Date | string): string { const d = typeof date === 'string' ? new Date(date) : date return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric', }).format(d) } /** * Format a date relative to now using coarse buckets * (Today / Yesterday / N days ago / N weeks ago / formatted date). * Accepts a Date object or an ISO date string. */ export function formatRelativeTime(date: Date | string): string { const d = typeof date === 'string' ? new Date(date) : date const now = new Date() const diffMs = now.getTime() - d.getTime() const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)) if (diffDays === 0) return 'Today' if (diffDays === 1) return 'Yesterday' if (diffDays < 7) return `${diffDays} days ago` if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago` return formatDate(d) } /** * Format a date relative to now using fine-grained buckets * (just now / Xm ago / Xh ago / Xd ago / formatted date). */ export function formatRelativeDate(date: Date): string { const now = new Date() const diffMs = now.getTime() - date.getTime() const diffSecs = Math.floor(diffMs / 1000) const diffMins = Math.floor(diffSecs / 60) const diffHours = Math.floor(diffMins / 60) const diffDays = Math.floor(diffHours / 24) if (diffSecs < 60) return 'just now' if (diffMins < 60) return `${diffMins}m ago` if (diffHours < 24) return `${diffHours}h ago` if (diffDays < 7) return `${diffDays}d ago` return date.toLocaleDateString() } /** * Format a number as USD currency (no cents, e.g. "$1,000,000") */ export function formatCurrency(amount: number): string { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0, }).format(amount) } /** * Get initials from a display name (up to 2 characters) */ export function getInitials(name: string | null | undefined): string { if (!name) return '?' return name .split(' ') .map((word) => word[0]) .join('') .toUpperCase() .slice(0, 2) }