Add utilities, constants, sample data and quick start guide
- Add helper functions (formatCurrency, formatDate, validation, etc.) - Add constants (colors, spacing, error messages, etc.) - Add sample data generator for testing - Add QUICKSTART.md for quick setup - Update app.json with proper configuration
This commit is contained in:
251
src/utils/helpers.ts
Normal file
251
src/utils/helpers.ts
Normal file
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* 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 options: 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' }
|
||||
}[format];
|
||||
|
||||
return new Intl.DateTimeFormat('fr-FR', options).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;
|
||||
};
|
||||
Reference in New Issue
Block a user