Python 51.6%
TypeScript 46.7%
CSS 1.7%
1"use client";23import Link from "next/link";4import { useState } from "react";5import { PARTY_IDS, partyVar, partyLabel, partyName } from "@/lib/parties";6import { Card } from "./ui";78interface Ask { query: string; terms: string[]; parties: Record<string, { id: number; proposal: string; quote: string; page: number | null; topic: string; confidence: number; document: { id: number; title: string | null; url: string | null } }[]>; note: string }910export function AskBox() {11 const [q, setQ] = useState("");12 const [res, setRes] = useState<Ask | null>(null);13 const [busy, setBusy] = useState(false);14 const run = async (query: string) => {15 if (query.trim().length < 3) return;16 setBusy(true);17 try { setRes(await fetch(`/api/ask?q=${encodeURIComponent(query)}`).then((r) => r.json())); } finally { setBusy(false); }18 };19 return (20 <Card className="card-lg">21 <div className="eyebrow mb-2">Recherche documentaire</div>22 <form onSubmit={(e) => { e.preventDefault(); run(q); }} className="flex gap-2">23 <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Que proposent les partis pour le logement ?" className="flex-1 card px-3.5 py-2.5 text-[15px] outline-none focus:border-accent" aria-label="Question" />24 <button className="rounded-[8px] bg-brand text-white px-4 py-2.5 text-[14px] font-semibold disabled:opacity-40" disabled={busy}>{busy ? "…" : "Chercher"}</button>25 </form>26 <div className="flex flex-wrap gap-1.5 mt-2 text-[12px]">{["logement", "immigration", "santé", "troisième lien", "français", "impôts", "souveraineté", "Hydro-Québec"].map((s) => <button key={s} onClick={() => { setQ(s); run(s); }} className="rounded-full border border-border px-2.5 py-1 hover:bg-surface-2">{s}</button>)}</div>27 {res && (28 <div className="mt-5 grid md:grid-cols-2 xl:grid-cols-5 gap-3">29 {PARTY_IDS.map((id) => (30 <div key={id} className="surface-2 rounded-[8px] p-3" style={{ borderTop: `3px solid ${partyVar(id)}` }}>31 <div className="font-semibold text-[13px] mb-2">{partyName(id)}</div>32 {(res.parties[id] ?? []).length === 0 ? <div className="text-[12px] text-ink-3">Aucune proposition trouvée dans les documents ingérés.</div> : (res.parties[id] ?? []).map((p) => (33 <div key={p.id} className="mb-2.5 text-[12.5px] leading-snug"><div>{p.proposal}</div><div className="text-[11.5px] text-ink-3 mt-0.5"><Link href={`/documents/${p.document.id}#p${p.id}`} className="link">{p.document.title ?? "Document"}</Link>{p.page ? ` · p. ${p.page}` : ""}</div></div>34 ))}35 </div>36 ))}37 <p className="md:col-span-2 xl:col-span-5 text-[11.5px] text-ink-3">{res.note} {PARTY_IDS.map(partyLabel).join(", ")} — classement lexical sur {res.terms.join(", ")}.</p>38 </div>39 )}40 </Card>41 );42}43