Files
WalletTracker/src/utils/helpers.ts
Arthur Lempereur 2f61e41d0d fix: resolve TypeScript type error in formatDate function
- Changed formatOptions typing to Record<string, Intl.DateTimeFormatOptions>
- Separated object creation from indexing to fix type inference
- Resolves incompatible type assignment for DateTimeFormatOptions
2025-10-23 15:01:17 +02:00

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;
};