spb/food-ka Public
Food-Ka — agrégateur de produits d'épicerie du Québec — www.food-ka.com
Python 57.7%
TypeScript 24.9%
CSS 16.7%
HTML 0.6%
1// -----------------------------------------------------------------------------2// Food-Ka — Agrégateur de produits d'épicerie (province de Québec)3// Auteur : Simon-Pierre Boucher — contact@spboucher.ai4// pages/Sources.tsx : registre des bannières d'épicerie agrégées5// -----------------------------------------------------------------------------6import { useEffect, useState } from "react";7import { Source, fetchSources } from "../api";8import SourceLogo from "../components/SourceLogo";910export default function SourcesPage() {11 const [sources, setSources] = useState<Source[] | null>(null);12 const [error, setError] = useState<string | null>(null);1314 useEffect(() => {15 fetchSources()16 .then((r) => setSources(r.sources))17 .catch((e) => setError(String(e)));18 }, []);1920 return (21 <div className="container sources">22 <span className="kicker">Registre — bannières d'épicerie</span>23 <h1>Sources agrégées</h1>24 <p className="sub">25 Bannières d'épicerie du Québec recensées par Food·Ka. Chaque source «26 active » est synchronisée périodiquement par un connecteur dédié; les27 autres sont en attente de connecteur.28 </p>2930 {error && <div className="notice">⚠️ {error}</div>}31 {!sources && !error && <div className="notice">Chargement…</div>}3233 {sources && (34 <div className="src-wrap">35 <table className="src-table">36 <thead>37 <tr>38 <th>Bannière</th>39 <th>Région</th>40 <th>Statut</th>41 <th style={{ textAlign: "right" }}>Produits actifs</th>42 <th>Dernière synchro</th>43 </tr>44 </thead>45 <tbody>46 {sources.map((s) => (47 <tr key={s.id}>48 <td>49 <div className="src-cell">50 <SourceLogo source={s.id} size={32} fallback="hide" />51 <div>52 <a href={s.url} target="_blank" rel="noopener noreferrer">{s.name}</a>53 {s.notes && (54 <div style={{ color: "var(--ink-3)", fontSize: 12, marginTop: 2 }}>{s.notes}</div>55 )}56 </div>57 </div>58 </td>59 <td style={{ color: "var(--ink-2)", maxWidth: 380 }}>{s.region}</td>60 <td>61 {s.connector ? (62 <span className="pill ok">connecté</span>63 ) : (64 <span className="pill todo">{s.status}</span>65 )}66 </td>67 <td style={{ textAlign: "right" }}>68 <span className="count-pill">{s.active_products || "—"}</span>69 </td>70 <td style={{ color: "var(--ink-3)" }}>71 {s.last_sync72 ? new Date(s.last_sync * 1000).toLocaleString("fr-CA")73 : "—"}74 </td>75 </tr>76 ))}77 </tbody>78 </table>79 </div>80 )}81 </div>82 );83}84