"use client"; import Link from "next/link"; import { useMemo, useState } from "react"; import type { RidingListItem } from "@/lib/api"; import { cls, prob, ratingLabel } from "@/lib/format"; import { PARTY_IDS, partyVar, partyLabel } from "@/lib/parties"; import { PartyBadge } from "./party"; import { Badge } from "./ui"; function norm(s: string) { return s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]/g, ""); } export function RidingsGrid({ ridings, regions, initialSort = "nom" }: { ridings: RidingListItem[]; regions: { id: string; name: string }[]; initialSort?: string }) { const [q, setQ] = useState(""); const [region, setRegion] = useState("all"); const [fav, setFav] = useState("all"); const [sort, setSort] = useState(initialSort); const rows = useMemo(() => { const nq = norm(q); let r = ridings.filter((x) => (!nq || norm(x.name).includes(nq)) && (region === "all" || x.region === region) && (fav === "all" || x.forecast?.favorite === fav)); if (sort === "serre") r = r.slice().sort((a, b) => (a.forecast?.p ?? 1) - (b.forecast?.p ?? 1)); else if (sort === "sur") r = r.slice().sort((a, b) => (b.forecast?.p ?? 0) - (a.forecast?.p ?? 0)); else if (sort === "swing") r = r.slice().sort((a, b) => Math.abs(b.forecast?.swing ?? 0) - Math.abs(a.forecast?.swing ?? 0)); return r; }, [ridings, q, region, fav, sort]); return (
setQ(e.target.value)} placeholder="Rechercher une circonscription…" className="card px-3 py-2 w-full sm:w-[280px] outline-none focus:border-accent" aria-label="Rechercher" /> {rows.length} / {ridings.length}
{rows.map((r) => { const f = r.forecast; const pw = f ? Object.entries(f.pWin).filter(([k]) => k !== "aut").sort((a, b) => b[1] - a[1]) : []; return (
{r.name}
{r.regionName}{r.isNew ? " · nouvelle / renommée" : r.changed ? " · limites modifiées" : ""}
{f && {ratingLabel(f.rating)}}
{f ? ( <>
{pw.slice(0, 5).map(([id, p]) => (
{partyLabel(id)}
{prob(p)}
))}
favori{r.live && r.live.status !== "NOT_STARTED" && {r.live.bureaux[0]}/{r.live.bureaux[1]} bureaux}sortant·e {r.incumbent.party ? partyLabel(r.incumbent.party) : "—"}
) :
Pas de projection.
} ); })}

Probabilité de victoire = part des simulations où le parti obtient le plus de voix dans la circonscription.

); }