SPB Git forge

spb/qc26

Public
10commits 1branches 0releases
2.8 MBsize
maindefault branch
17 days agolast push
Python 51.6% TypeScript 46.7% CSS 1.7%
3.4 KB · 66 lines typescript
Raw Blame History
1const fmtPct1 = new Intl.NumberFormat("fr-CA", { minimumFractionDigits: 1, maximumFractionDigits: 1 });2const fmtPct0 = new Intl.NumberFormat("fr-CA", { maximumFractionDigits: 0 });3const fmtInt = new Intl.NumberFormat("fr-CA");45export function pct(v: number | null | undefined, digits: 0 | 1 = 1, suffix = " %"): string {6  if (v === null || v === undefined || Number.isNaN(v)) return "—";7  return (digits === 1 ? fmtPct1 : fmtPct0).format(v) + suffix;8}9export function prob(p: number | null | undefined): string {10  if (p === null || p === undefined) return "—";11  if (p > 0.995) return "> 99 %";12  if (p < 0.005) return "< 1 %";13  return fmtPct0.format(p * 100) + " %";14}15export function delta(v: number | null | undefined, digits: 0 | 1 = 1, suffix = ""): string {16  if (v === null || v === undefined || Number.isNaN(v)) return "—";17  const f = (digits === 1 ? fmtPct1 : fmtPct0).format(Math.abs(v));18  if (Math.abs(v) < (digits === 1 ? 0.05 : 0.5)) return "0" + suffix;19  return (v > 0 ? "+" : "−") + f + suffix;20}21export function int(v: number | null | undefined): string {22  if (v === null || v === undefined) return "—";23  return fmtInt.format(Math.round(v));24}25export function dateFr(iso: string | null | undefined, opts: Intl.DateTimeFormatOptions = { day: "numeric", month: "long", year: "numeric" }): string {26  if (!iso) return "—";27  const d = iso.length === 10 ? new Date(iso + "T12:00:00") : new Date(iso);28  return new Intl.DateTimeFormat("fr-CA", { timeZone: "America/Toronto", ...opts }).format(d);29}30export function dateShort(iso: string | null | undefined): string {31  return dateFr(iso, { day: "numeric", month: "short" });32}33export function dateTimeFr(iso: string | null | undefined): string {34  if (!iso) return "—";35  return new Intl.DateTimeFormat("fr-CA", { timeZone: "America/Toronto", day: "numeric", month: "long", hour: "2-digit", minute: "2-digit" }).format(new Date(iso));36}37export function timeFr(iso: string | null | undefined): string {38  if (!iso) return "—";39  return new Intl.DateTimeFormat("fr-CA", { timeZone: "America/Toronto", hour: "2-digit", minute: "2-digit", second: "2-digit" }).format(new Date(iso));40}41export function relTime(iso: string | null | undefined): string {42  if (!iso) return "—";43  const s = Math.round((Date.now() - new Date(iso).getTime()) / 1000);44  if (s < 60) return `il y a ${s} s`;45  if (s < 3600) return `il y a ${Math.round(s / 60)} min`;46  if (s < 86400) return `il y a ${Math.round(s / 3600)} h`;47  return `il y a ${Math.round(s / 86400)} j`;48}49export function daysLabel(d: number): string {50  if (d > 0) return `J-${d}`;51  if (d === 0) return "JOUR J";52  return `J+${-d}`;53}54export function ratingLabel(r: string | null | undefined): string {55  return ({ safe: "Acquis", likely: "Probable", lean: "Favori fragile", tossup: "Serré" } as Record<string, string>)[r ?? ""] ?? "—";56}57export function statusLabel(s: string | null | undefined): string {58  return ({ NOT_STARTED: "En attente", REPORTING: "Dépouillement", LEADING: "En avance", PROJECTED: "Projeté QC26", ELECTED: "Élu·e", FINAL: "Final" } as Record<string, string>)[s ?? ""] ?? "—";59}60export function modeLabel(m: string | null | undefined): string {61  return ({ web: "Web", phone: "Téléphone", ivr: "IVR (automatisé)", mixed: "Mixte", sms: "SMS", unknown: "Non précisé" } as Record<string, string>)[m ?? ""] ?? (m ?? "—");62}63export function cls(...xs: (string | false | null | undefined)[]): string {64  return xs.filter(Boolean).join(" ");65}66