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%
1.6 KB · 27 lines tsx
Raw Blame History
1import Link from "next/link";2import { apiSafe } from "@/lib/api";3import { cls, dateTimeFr } from "@/lib/format";45interface Status { components: Record<string, string>; latestRun: { id: number; completedAt: string | null } | null; serverTime: string }67const LABELS: Record<string, string> = { pollWatcher: "Poll watcher", forecastEngine: "Forecast engine", electionFeed: "Election feed", documentWatcher: "Document watcher", officialData: "Données officielles", signals: "Signaux" };89export async function StatusStrip() {10  const st = await apiSafe<Status>("/api/status", { revalidate: 60 });11  return (12    <div className="border-t border-border">13      <div className="mx-auto max-w-[1280px] px-4 md:px-6 py-3 flex flex-wrap items-center gap-x-5 gap-y-2 text-[11.5px] text-ink-3">14        <Link href="/statut" className="eyebrow !text-[10.5px] hover:text-ink">QC26 · statut des données</Link>15        {st ? Object.entries(st.components).map(([k, v]) => (16          <span key={k} className="inline-flex items-center gap-1.5">17            <span className={cls("w-1.5 h-1.5 rounded-full", v === "operational" ? "bg-ok" : v === "ready" ? "bg-accent" : "bg-warn")} aria-hidden />18            {LABELS[k] ?? k} <span className="text-ink-2">{v === "operational" ? "operational" : v === "ready" ? "ready" : "degraded"}</span>19          </span>20        )) : <span>moteur injoignable</span>}21        {st?.latestRun?.completedAt && <span className="ml-auto num">Modèle recalculé {dateTimeFr(st.latestRun.completedAt)}</span>}22        <span className="num">© QC26 {new Date().getFullYear()}</span>23      </div>24    </div>25  );26}27