Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | 1x 1x 333x 333x 1x 1x 1x 1x 1x 1x 1x 1x | import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
/**
* Merge Tailwind CSS classes with clsx
*/
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
/**
* Format a date string for display
*/
export function formatDate(date: string | Date, options?: Intl.DateTimeFormatOptions): string {
const d = typeof date === 'string' ? new Date(date) : date;
return d.toLocaleDateString('en-IN', {
year: 'numeric',
month: 'short',
day: 'numeric',
...options,
});
}
/**
* Format a relative time (e.g., "2 hours ago")
*/
export function formatRelativeTime(date: string | Date): string {
const d = typeof date === 'string' ? new Date(date) : date;
const now = new Date();
const diffInSeconds = Math.floor((now.getTime() - d.getTime()) / 1000);
if (diffInSeconds < 60) return 'just now';
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)}m ago`;
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)}h ago`;
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)}d ago`;
return formatDate(d);
}
/**
* Truncate text with ellipsis
*/
export function truncate(str: string, length: number): string {
if (str.length <= length) return str;
return str.slice(0, length) + '...';
}
/**
* Generate initials from a name
*/
export function getInitials(name: string): string {
const parts = name.trim().split(/\s+/);
if (parts.length === 1) {
return parts[0].slice(0, 2).toUpperCase();
}
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
}
/**
* Format a phone number for WhatsApp URL
*/
export function formatWhatsAppUrl(phone: string, message?: string): string {
// Remove any non-digit characters except leading +
const cleaned = phone.replace(/[^\d+]/g, '');
// Remove leading + if present (WhatsApp URLs don't need it)
const number = cleaned.startsWith('+') ? cleaned.slice(1) : cleaned;
let url = `https://wa.me/${number}`;
if (message) {
url += `?text=${encodeURIComponent(message)}`;
}
return url;
}
/**
* Copy text to clipboard
*/
export async function copyToClipboard(text: string): Promise<boolean> {
try {
await navigator.clipboard.writeText(text);
return true;
} catch {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand('copy');
return true;
} catch {
return false;
} finally {
document.body.removeChild(textArea);
}
}
}
/**
* Generate a random color based on a string (for avatars)
*/
export function stringToColor(str: string): string {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
const colors = [
'#0ea5e9', // sky-500
'#8b5cf6', // violet-500
'#ec4899', // pink-500
'#f97316', // orange-500
'#10b981', // emerald-500
'#06b6d4', // cyan-500
'#f59e0b', // amber-500
'#6366f1', // indigo-500
];
return colors[Math.abs(hash) % colors.length];
}
/**
* Debounce function
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeoutId: ReturnType<typeof setTimeout> | null = null;
return function (this: unknown, ...args: Parameters<T>) {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
func.apply(this, args);
}, wait);
};
}
/**
* Sleep/delay utility
*/
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
|