"use client"; import type { FeatureCollection, Geometry } from "geojson"; import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; import { useMeasure } from "@/lib/use-measure"; import { cls } from "@/lib/format"; import { PARTY_IDS, partyVar, partyLabel } from "@/lib/parties"; import { QuebecMap, type MapMode, type MapRiding } from "./map"; const MODES: { id: MapMode; label: string; hint: string }[] = [ { id: "projection", label: "Projection", hint: "Favori par circonscription ; intensité = certitude" }, { id: "probabilite", label: "Probabilité", hint: "Opacité proportionnelle à la probabilité de victoire" }, { id: "marge", label: "Marge", hint: "Écart projeté entre le favori et le 2e" }, { id: "variation", label: "Variation", hint: "Circonscriptions qui changeraient de parti vs 2022" }, { id: "2022", label: "2022", hint: "Résultat 2022 transposé sur la carte 2026" }, ]; interface RidingsPayload { ridings: (MapRiding & { incumbent?: { party: string | null } })[]; regions: { id: string; name: string }[] } export function MapSection({ height = 560, compact = false, initialMode = "projection", showModes = true, live = false, liveState }: { height?: number; compact?: boolean; initialMode?: MapMode; showModes?: boolean; live?: boolean; liveState?: Record | null; }) { const [geo, setGeo] = useState | null>(null); const [data, setData] = useState(null); const [mode, setMode] = useState(live ? "live" : initialMode); const { ref, width } = useMeasure(1000); const mobile = width < 640; useEffect(() => { fetch("/api/geo").then((r) => r.json()).then(setGeo).catch(() => {}); fetch("/api/ridings").then((r) => r.json()).then(setData).catch(() => {}); }, []); // « 2022 » = parti du député sortant de la circonscription contributrice principale (résultat 2022 transposé) const ridings = useMemo(() => { const out: Record = {}; data?.ridings.forEach((r) => { out[r.code] = { ...r, baseline2022: r.incumbent?.party ?? null, live: liveState?.[String(r.code)] ?? r.live ?? null }; }); return out; }, [data, liveState]); const counts = useMemo(() => { const c: Record = {}; Object.values(ridings).forEach((r) => { const id = mode === "live" ? r.live?.leader : mode === "2022" ? r.baseline2022 : r.forecast?.favorite; if (id) c[id] = (c[id] ?? 0) + 1; }); return c; }, [ridings, mode]); if (!geo || !data) return
; return (
{showModes && (
{(live ? [{ id: "live" as MapMode, label: "2026 Live", hint: "Résultats officiels en cours" }, ...MODES.filter((m) => m.id === "projection" || m.id === "2022")] : MODES).map((m) => ( ))} {(live ? [{ id: "live", hint: "Résultats officiels en cours" }, ...MODES] : MODES).find((m) => m.id === mode)?.hint}
)}
{PARTY_IDS.filter((id) => counts[id]).sort((a, b) => (counts[b] ?? 0) - (counts[a] ?? 0)).map((id) => ( {partyLabel(id)} {counts[id]} ))} {mode === "live" ? "En tête / élu" : mode === "2022" ? "Sièges 2022 transposés" : "Favoris (probabilité, pas certitude)"} Carte complète →
); }