SPB Git forge

spb/toveli

Public
14commits 1branches 0releases
1.1 MBsize
maindefault branch
2 h agolast push
TypeScript 50.6% JavaScript 46.1% SQL 2.1% Shell 0.6% CSS 0.5%
2.6 KB · 44 lines typescript
Raw Blame History
1// Formats fr-CA : prix CAD, pi²/m², dates, pourcentages.2const cad0 = new Intl.NumberFormat("fr-CA", { style: "currency", currency: "CAD", maximumFractionDigits: 0 });3const int = new Intl.NumberFormat("fr-CA", { maximumFractionDigits: 0 });4const dec1 = new Intl.NumberFormat("fr-CA", { maximumFractionDigits: 1, minimumFractionDigits: 0 });56export function fmtPrice(v: number | null | undefined, opts?: { rent?: boolean; compact?: boolean }): string {7  if (v == null || !Number.isFinite(v)) return "—";8  if (opts?.compact) {9    if (v >= 1_000_000) return `${dec1.format(v / 1_000_000).replace(",0", "")} M$`;10    if (v >= 1_000) return `${int.format(Math.round(v / 1000))} k$`;11  }12  const s = cad0.format(v);13  return opts?.rent ? `${s}/mois` : s;14}15export function fmtInt(v: number | null | undefined): string { return v == null || !Number.isFinite(v) ? "—" : int.format(v); }16export function fmtPct(v: number | null | undefined, digits = 1, signed = true): string {17  if (v == null || !Number.isFinite(v)) return "—";18  const s = new Intl.NumberFormat("fr-CA", { maximumFractionDigits: digits, minimumFractionDigits: 0 }).format(Math.abs(v) * 100);19  return `${signed ? (v > 0 ? "+" : v < 0 ? "−" : "") : ""}${s} %`;20}21export function fmtArea(sqft: number | null | undefined, unit: "sqft" | "m2" = "sqft"): string {22  if (sqft == null || !Number.isFinite(sqft) || sqft <= 0) return "—";23  return unit === "m2" ? `${int.format(Math.round(sqft * 0.09290304))} m²` : `${int.format(Math.round(sqft))} pi²`;24}25export function fmtDate(iso: string | null | undefined, style: "short" | "long" = "short"): string {26  if (!iso) return "—";27  const d = new Date(iso);28  if (Number.isNaN(d.getTime())) return "—";29  return d.toLocaleDateString("fr-CA", style === "long" ? { day: "numeric", month: "long", year: "numeric" } : { day: "numeric", month: "short", year: "numeric" });30}31export function daysSince(iso: string | null | undefined): number | null {32  if (!iso) return null; const t = Date.parse(iso); return Number.isNaN(t) ? null : Math.max(0, Math.floor((Date.now() - t) / 86400000));33}34export function fmtRooms(bed: number | null | undefined, bath: number | null | undefined): string {35  const p: string[] = [];36  if (bed != null) p.push(`${dec1.format(bed)} ch.`);37  if (bath != null) p.push(`${dec1.format(bath)} sdb`);38  return p.join(" · ");39}40export function slugify(s: string): string {41  return s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");42}43export function pluralize(n: number, one: string, many: string): string { return `${fmtInt(n)} ${n > 1 ? many : one}`; }44