import { DEFAULTS } from './constants'; // Debounce function for search inputs and API calls export function debounce void>( func: T, wait: number = DEFAULTS.SEARCH_DEBOUNCE ): (...args: Parameters) => void { let timeout: NodeJS.Timeout; return (...args: Parameters) => { clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } // Generate unique ID export function generateId(): string { return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } // Check if value is empty export function isEmpty(value: unknown): boolean { if (value == null) return true; if (typeof value === 'string') return value.trim().length === 0; if (Array.isArray(value)) return value.length === 0; if (typeof value === 'object') return Object.keys(value).length === 0; return false; } // Sleep function for testing and delays export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } // Type-safe object keys export function objectKeys>( obj: T ): (keyof T)[] { return Object.keys(obj) as (keyof T)[]; } // Safe JSON parse with fallback export function safeJsonParse(json: string, fallback: T): T { try { return JSON.parse(json) as T; } catch { return fallback; } } // Deep clone object export function deepClone(obj: T): T { if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Date) return new Date(obj.getTime()) as T; if (Array.isArray(obj)) return obj.map(deepClone) as T; const cloned = {} as T; for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { cloned[key] = deepClone(obj[key]); } } return cloned; } // Capitalize first letter export function capitalize(str: string): string { return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase(); } // Convert camelCase to kebab-case export function camelToKebab(str: string): string { return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase(); } // Remove duplicates from array export function removeDuplicates(array: T[]): T[] { return [...new Set(array)]; } // Group array by key export function groupBy( array: T[], key: K ): Record { return array.reduce( (groups, item) => { const group = String(item[key]); groups[group] = groups[group] || []; groups[group].push(item); return groups; }, {} as Record ); } // Get full image URL for display export function getImageUrl(imageUrl: string | null | undefined): string | null { if (!imageUrl) return null; // If it's already a full URL, return as is if (imageUrl.startsWith('http')) return imageUrl; // If it's a relative URL, prepend the server base URL (not API base URL) // Images are served directly from the server root, not through the API const apiBaseUrl = (import.meta.env.VITE_API_BASE_URL as string) || 'http://localhost:3000/api/v1'; const serverBaseUrl = apiBaseUrl.replace('/api/v1', ''); return `${serverBaseUrl}${imageUrl}`; }