import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function formatDate(date: Date | string): string {
const d = new Date(date)
const now = new Date()
const diff = now.getTime() - d.getTime()
// Less than 1 minute
if (diff < 60000) {
return 'Just now'
}
// Less than 1 hour
if (diff < 3600000) {
const minutes = Math.floor(diff / 60000)
return `${minutes}m ago`
}
// Less than 24 hours
if (diff < 86400000) {
const hours = Math.floor(diff / 3600000)
return `${hours}h ago`
}
// Less than 7 days
if (diff < 604800000) {
const days = Math.floor(diff / 86400000)
return `${days}d ago`
}
// More than 7 days
return d.toLocaleDateString()
}
export function formatTime(date: Date | string): string {
const d = new Date(date)
return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
}
export function formatFileSize(bytes: number): string {
if (bytes === 0) return '0 Bytes'
const k = 1024
const sizes = ['Bytes', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
export function getFileIcon(mimeType: string): string {
if (mimeType.startsWith('image/')) return '🖼️'
if (mimeType.startsWith('video/')) return '🎥'
if (mimeType.startsWith('audio/')) return '🎵'
if (mimeType.includes('pdf')) return '📄'
if (mimeType.includes('word')) return '📝'
if (mimeType.includes('excel') || mimeType.includes('spreadsheet')) return '📊'
if (mimeType.includes('powerpoint') || mimeType.includes('presentation')) return '📽️'
if (mimeType.includes('zip') || mimeType.includes('rar') || mimeType.includes('archive')) return '📦'
return '📎'
}
export function generateAvatar(name: string): string {
const colors = [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9'
]
const initials = name
.split(' ')
.map(word => word[0])
.join('')
.toUpperCase()
.slice(0, 2)
const colorIndex = name.charCodeAt(0) % colors.length
const color = colors[colorIndex]
return `data:image/svg+xml,${encodeURIComponent(`
`)}`
}
export function debounce any>(
func: T,
wait: number
): (...args: Parameters) => void {
let timeout: NodeJS.Timeout | null = null
return (...args: Parameters) => {
if (timeout) {
clearTimeout(timeout)
}
timeout = setTimeout(() => {
func(...args)
}, wait)
}
}
export function throttle any>(
func: T,
limit: number
): (...args: Parameters) => void {
let inThrottle: boolean
return (...args: Parameters) => {
if (!inThrottle) {
func(...args)
inThrottle = true
setTimeout(() => inThrottle = false, limit)
}
}
}
export function isValidEmail(email: string): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
return emailRegex.test(email)
}
export function isValidUsername(username: string): boolean {
const usernameRegex = /^[a-zA-Z0-9_]{3,20}$/
return usernameRegex.test(username)
}
export function truncateText(text: string, maxLength: number): string {
if (text.length <= maxLength) return text
return text.slice(0, maxLength) + '...'
}
export function copyToClipboard(text: string): Promise {
if (navigator.clipboard) {
return navigator.clipboard.writeText(text)
} else {
// Fallback for older browsers
const textArea = document.createElement('textarea')
textArea.value = text
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
try {
document.execCommand('copy')
return Promise.resolve()
} catch (err) {
return Promise.reject(err)
} finally {
document.body.removeChild(textArea)
}
}
}
export function downloadFile(url: string, filename: string): void {
const link = document.createElement('a')
link.href = url
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
export function isImageFile(file: File): boolean {
return file.type.startsWith('image/')
}
export function isVideoFile(file: File): boolean {
return file.type.startsWith('video/')
}
export function isAudioFile(file: File): boolean {
return file.type.startsWith('audio/')
}
export function getInitials(name: string): string {
return name
.split(' ')
.map(word => word[0])
.join('')
.toUpperCase()
.slice(0, 2)
}