// Auteur : Simon-Pierre Boucher — contact@spboucher.ai "use client"; import { useState } from "react"; import { CountUp } from "./MetricViz"; import { useLang } from "./LangContext"; import type { TKey } from "@/lib/i18n"; export interface ProvStats { generated: string; unites: number; valeur_totale_2026: number; valeur_role_totale: number; valeur_mediane_2026: number; croissance_2021_2026_pct: number; logements: number; aire_etages_km2: number; terrain_km2: number; municipalites: number; totaux_annee: { year: number; total: number; n: number }[]; par_type: { type: string; n: number; total: number; mediane: number | null }[]; par_ville: { ville: string; n: number; total: number; mediane: number | null }[]; } const POP_QC = 9_111_000; // population du Québec (2026, ISQ) const BUDGET_QC = 165_800_000_000; // dépenses budget QC 2026-2027 (approx.) const PIB_QC = 640_000_000_000; // PIB nominal QC (approx. 2025) const money = (v: number, lang: string, frac = 0) => new Intl.NumberFormat(lang === "fr" ? "fr-CA" : "en-CA", { style: "currency", currency: "CAD", maximumFractionDigits: frac, }).format(v); const compact = (v: number, lang: string) => { const fr = lang === "fr"; if (v >= 1e12) return `${(v / 1e12).toLocaleString(fr ? "fr-CA" : "en-CA", { maximumFractionDigits: 2 })} ${fr ? "billions $" : "T$"}`; if (v >= 1e9) return `${(v / 1e9).toLocaleString(fr ? "fr-CA" : "en-CA", { maximumFractionDigits: 1 })} G$`; if (v >= 1e6) return `${(v / 1e6).toLocaleString(fr ? "fr-CA" : "en-CA", { maximumFractionDigits: 1 })} M$`; return money(v, lang); }; const num = (v: number, lang: string) => v.toLocaleString(lang === "fr" ? "fr-CA" : "en-CA"); export default function StatsView({ s }: { s: ProvStats }) { const { lang, t } = useLang(); const fr = lang === "fr"; const [q, setQ] = useState(""); const maxYear = Math.max(...s.totaux_annee.map((x) => x.total)); const maxType = Math.max(...s.par_type.map((x) => x.total)); const top10 = s.par_ville.slice(0, 10); const maxVille = Math.max(...top10.map((x) => x.total)); const villes = s.par_ville.filter((v) => v.ville.toLowerCase().includes(q.trim().toLowerCase()) ); return (
{fr ? `≈ ${compact(s.valeur_totale_2026, lang)} · ${num(s.unites, lang)} propriétés · ${num(s.municipalites, lang)} municipalités · millésime 2026` : `≈ ${compact(s.valeur_totale_2026, lang)} · ${num(s.unites, lang)} properties · ${num(s.municipalites, lang)} municipalities · 2026 vintage`}
{fr ? "Somme des 3 747 008 estimations Vrai-Prix (modèle hédonique, rôle 2026). Évaluation municipale totale correspondante : " : "Sum of 3,747,008 Vrai-Prix estimates (hedonic model, 2026 roll). Corresponding total municipal assessment: "} {compact(s.valeur_role_totale, lang)}.
{fr ? "Télécharger le rapport statistique provincial (PDF)" : "Download the provincial statistics report (PDF)"}{" "} ↓{v}
{k}
{fr ? "Sommes des unités estimées chaque millésime — le parc s'agrandit d'année en année (nouvelles constructions incluses)." : "Sums of units estimated each vintage — the stock grows every year (new construction included)."}
| # | {fr ? "Municipalité" : "Municipality"} | {fr ? "Propriétés" : "Properties"} | {fr ? "Valeur totale" : "Total value"} | {fr ? "Valeur médiane" : "Median value"} |
|---|---|---|---|---|
| {rank} | {v.ville} | {num(v.n, lang)} | {compact(v.total, lang)} | {v.mediane ? money(v.mediane, lang) : "—"} |
{fr ? `Top 200 des ${num(s.municipalites, lang)} municipalités, par valeur totale estimée (millésime 2026). Généré le ${s.generated}.` : `Top 200 of ${num(s.municipalites, lang)} municipalities by total estimated value (2026 vintage). Generated ${s.generated}.`}