- Changed formatOptions typing to Record<string, Intl.DateTimeFormatOptions> - Separated object creation from indexing to fix type inference - Resolves incompatible type assignment for DateTimeFormatOptions
252 lines
6.2 KiB
TypeScript
252 lines
6.2 KiB
TypeScript
/**
|
|
* 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<string, Intl.DateTimeFormatOptions> = {
|
|
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 = <T extends { date: Date }>(items: T[]): Map<string, T[]> => {
|
|
const grouped = new Map<string, T[]>();
|
|
|
|
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 = <T extends { date: Date }>(items: T[]): Map<string, T[]> => {
|
|
const grouped = new Map<string, T[]>();
|
|
|
|
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 = <T extends { date: Date }>(items: T[]): T[] => {
|
|
return [...items].sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
};
|
|
|
|
/**
|
|
* Filtre les transactions par période
|
|
*/
|
|
export const filterByDateRange = <T extends { date: Date }>(
|
|
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 = <T extends { amount: number }>(items: T[]): number => {
|
|
return items.reduce((sum, item) => sum + item.amount, 0);
|
|
};
|
|
|
|
/**
|
|
* Attend X millisecondes (pour les animations)
|
|
*/
|
|
export const wait = (ms: number): Promise<void> => {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
};
|
|
|
|
/**
|
|
* Debounce une fonction
|
|
*/
|
|
export const debounce = <T extends (...args: any[]) => any>(
|
|
func: T,
|
|
delay: number
|
|
): ((...args: Parameters<T>) => void) => {
|
|
let timeoutId: NodeJS.Timeout;
|
|
|
|
return (...args: Parameters<T>) => {
|
|
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;
|
|
};
|