/** * Fonctions utilitaires pour l'application */ /** * Formate un montant en euros */ export const formatCurrency = (amount: number): string => { return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(amount); }; /** * Formate une date */ export const formatDate = ( date: Date, format: 'short' | 'medium' | 'long' = 'medium' ): string => { const formatOptions: Record = { short: { day: '2-digit', month: '2-digit', year: 'numeric' }, medium: { day: '2-digit', month: 'short', year: 'numeric' }, long: { day: '2-digit', month: 'long', year: 'numeric' } }; return new Intl.DateTimeFormat('fr-FR', formatOptions[format]).format(date); }; /** * Formate une date relative (il y a X jours) */ export const formatRelativeDate = (date: Date): string => { const now = new Date(); const diffInMs = now.getTime() - date.getTime(); const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24)); if (diffInDays === 0) return "Aujourd'hui"; if (diffInDays === 1) return 'Hier'; if (diffInDays < 7) return `Il y a ${diffInDays} jours`; if (diffInDays < 30) { const weeks = Math.floor(diffInDays / 7); return `Il y a ${weeks} semaine${weeks > 1 ? 's' : ''}`; } if (diffInDays < 365) { const months = Math.floor(diffInDays / 30); return `Il y a ${months} mois`; } const years = Math.floor(diffInDays / 365); return `Il y a ${years} an${years > 1 ? 's' : ''}`; }; /** * Obtient le nom du mois en français */ export const getMonthName = (date: Date, format: 'long' | 'short' = 'long'): string => { return new Intl.DateTimeFormat('fr-FR', { month: format, year: 'numeric' }).format(date); }; /** * Calcule le nombre de jours entre deux dates */ export const daysBetween = (date1: Date, date2: Date): number => { const diffInMs = Math.abs(date2.getTime() - date1.getTime()); return Math.ceil(diffInMs / (1000 * 60 * 60 * 24)); }; /** * Vérifie si une date est dans le mois en cours */ export const isCurrentMonth = (date: Date): boolean => { const now = new Date(); return ( date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear() ); }; /** * Obtient le premier jour du mois */ export const getFirstDayOfMonth = (date: Date): Date => { return new Date(date.getFullYear(), date.getMonth(), 1); }; /** * Obtient le dernier jour du mois */ export const getLastDayOfMonth = (date: Date): Date => { return new Date(date.getFullYear(), date.getMonth() + 1, 0, 23, 59, 59); }; /** * Valide une adresse email */ export const isValidEmail = (email: string): boolean => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }; /** * Valide un montant */ export const isValidAmount = (amount: string | number): boolean => { const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount; return !isNaN(numAmount) && numAmount > 0; }; /** * Tronque un texte */ export const truncate = (text: string, maxLength: number): string => { if (text.length <= maxLength) return text; return text.substring(0, maxLength) + '...'; }; /** * Capitalise la première lettre */ export const capitalize = (text: string): string => { if (!text) return ''; return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase(); }; /** * Génère une couleur aléatoire */ export const generateRandomColor = (): string => { const colors = [ '#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E2', '#F8B739', '#52C41A', '#13C2C2', '#1890FF', '#EB2F96' ]; return colors[Math.floor(Math.random() * colors.length)]; }; /** * Calcule le pourcentage */ export const calculatePercentage = (value: number, total: number): number => { if (total === 0) return 0; return (value / total) * 100; }; /** * Arrondit un nombre à N décimales */ export const roundTo = (num: number, decimals: number = 2): number => { return Math.round(num * Math.pow(10, decimals)) / Math.pow(10, decimals); }; /** * Groupe les transactions par date */ export const groupByDate = (items: T[]): Map => { const grouped = new Map(); items.forEach((item) => { const dateKey = formatDate(item.date, 'short'); const existing = grouped.get(dateKey) || []; grouped.set(dateKey, [...existing, item]); }); return grouped; }; /** * Groupe les transactions par mois */ export const groupByMonth = (items: T[]): Map => { const grouped = new Map(); items.forEach((item) => { const monthKey = getMonthName(item.date); const existing = grouped.get(monthKey) || []; grouped.set(monthKey, [...existing, item]); }); return grouped; }; /** * Trie les transactions par date (plus récentes en premier) */ export const sortByDateDesc = (items: T[]): T[] => { return [...items].sort((a, b) => b.date.getTime() - a.date.getTime()); }; /** * Filtre les transactions par période */ export const filterByDateRange = ( items: T[], startDate: Date, endDate: Date ): T[] => { return items.filter((item) => item.date >= startDate && item.date <= endDate); }; /** * Calcule la somme des montants */ export const sumAmounts = (items: T[]): number => { return items.reduce((sum, item) => sum + item.amount, 0); }; /** * Attend X millisecondes (pour les animations) */ export const wait = (ms: number): Promise => { return new Promise((resolve) => setTimeout(resolve, ms)); }; /** * Debounce une fonction */ export const debounce = any>( func: T, delay: number ): ((...args: Parameters) => void) => { let timeoutId: NodeJS.Timeout; return (...args: Parameters) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; }; /** * Vérifie si l'objet est vide */ export const isEmpty = (obj: any): boolean => { if (obj === null || obj === undefined) return true; if (typeof obj === 'string') return obj.trim().length === 0; if (Array.isArray(obj)) return obj.length === 0; if (typeof obj === 'object') return Object.keys(obj).length === 0; return false; };