import { format, formatDistanceToNowStrict, parseISO } from "date-fns"; import { fr } from "date-fns/locale"; import { MINUTES_PER_RUBIS } from "@rubis/shared"; /** * Formateurs métier — centralisés pour cohérence d'affichage. * Cf. /docs/tech/frontend.md §9. */ /** "1 240,00 €" depuis un montant en centimes. */ export function formatEuros(cents: number): string { return new Intl.NumberFormat("fr-FR", { style: "currency", currency: "EUR", minimumFractionDigits: 2, }).format(cents / 100); } /** * Convertit un nombre de rubis en libellé "X h Y" (1 rubis = 10 min). * Ex : 124 rubis → "20 h 40". */ export function formatRubisToHours(rubis: number): string { const totalMinutes = rubis * MINUTES_PER_RUBIS; const hours = Math.floor(totalMinutes / 60); const minutes = totalMinutes % 60; if (hours === 0) return `${minutes} min`; if (minutes === 0) return `${hours} h`; return `${hours} h ${minutes.toString().padStart(2, "0")}`; } /** "5 mai 2026" depuis une date ISO. */ export function formatDate(iso: string): string { return format(parseISO(iso), "d MMMM yyyy", { locale: fr }); } /** "dans 3 jours" / "il y a 2 jours" depuis maintenant. */ export function formatRelativeDate(iso: string): string { return formatDistanceToNowStrict(parseISO(iso), { locale: fr, addSuffix: true }); } /** "5 mai" version courte. */ export function formatDateShort(iso: string): string { return format(parseISO(iso), "d MMM", { locale: fr }); } /** * Délai par rapport à aujourd'hui pour une échéance ISO : * - "J−3" si dépassée de 3 jours * - "J+10" si dans 10 jours * - "Aujourd'hui" si dans la journée * * Convention : le signe '−' (minus typographique, pas hyphen) marque le retard. * Pas d'espace autour de J — c'est un token visuel compact. */ export function formatDueDelta(iso: string, now: Date = new Date()): string { const due = parseISO(iso); // Compare en jours calendaires (pas en ms exactes). const oneDay = 24 * 60 * 60 * 1000; const dueDay = Math.floor(due.getTime() / oneDay); const today = Math.floor(now.getTime() / oneDay); const diff = dueDay - today; if (diff === 0) return "Aujourd'hui"; if (diff < 0) return `J−${Math.abs(diff)}`; return `J+${diff}`; } /** True si l'échéance est passée (strict, pas le jour même). */ export function isOverdue(iso: string, now: Date = new Date()): boolean { return parseISO(iso).getTime() < now.setHours(0, 0, 0, 0); }