Python 51.6%
TypeScript 46.7%
CSS 1.7%
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