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%
2.4 KB · 37 lines tsx
Raw Blame History
1import type { Metadata } from "next";2import Link from "next/link";3import { apiSafe } from "@/lib/api";4import { AskBox } from "@/components/ask-box";5import { Card, Empty } from "@/components/ui";6import { PARTY_IDS, partyVar, partyLabel } from "@/lib/parties";78export const metadata: Metadata = { title: "Enjeux — ce que proposent les partis", description: "Positions et propositions des partis par enjeu (santé, logement, immigration, langue, environnement…), tirées de leurs documents officiels." };9export const revalidate = 300;1011interface Matrix { topics: { id: string; label: string; counts: Record<string, number> }[] }1213export default async function Page() {14  const m = await apiSafe<Matrix>("/api/matrix");15  if (!m) return <Empty title="Enjeux indisponibles" />;16  return (17    <div className="space-y-8">18      <div><div className="eyebrow eyebrow-accent">Issues tracker · {m.topics.length} enjeux documentés</div><h1 className="display text-[40px] md:text-[56px] mt-3">Enjeux</h1><p className="text-ink-2 mt-3 max-w-2xl text-[15px] leading-relaxed">Les principaux enjeux détectés dans les documents officiels des partis. Le nombre indique les propositions extraites (avec page et citation) par parti.</p></div>19      <AskBox />20      {m.topics.length === 0 ? <Empty title="Extraction en cours" /> : (21        <div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-3">22          {m.topics.sort((a, b) => Object.values(b.counts).reduce((x, y) => x + y, 0) - Object.values(a.counts).reduce((x, y) => x + y, 0)).map((t) => {23            const total = Object.values(t.counts).reduce((x, y) => x + y, 0);24            return (25              <Link key={t.id} href={`/enjeux/${t.id}`} className="card p-4 block hover:shadow-md">26                <div className="flex items-baseline justify-between"><div className="font-semibold text-[15px]">{t.label}</div><div className="num text-[12px] text-ink-3">{total} propositions</div></div>27                <div className="flex h-[8px] rounded-full overflow-hidden mt-3 bg-surface-3">{PARTY_IDS.map((id) => <div key={id} style={{ width: `${((t.counts[id] ?? 0) / Math.max(total, 1)) * 100}%`, background: partyVar(id) }} />)}</div>28                <div className="flex flex-wrap gap-x-3 mt-2 text-[12px] num">{PARTY_IDS.map((id) => <span key={id}><b style={{ color: partyVar(id) }}>{partyLabel(id)}</b> {t.counts[id] ?? 0}</span>)}</div>29              </Link>30            );31          })}32        </div>33      )}34    </div>35  );36}37