Resto·Ka — tous les restaurants du Québec, menus complets et prix réels (famille ·Ka)
Python 69.3%
TypeScript 16.7%
CSS 7.9%
JavaScript 4.7%
HTML 1.4%
1// ==============================================================================2// Author: Simon-Pierre Boucher <contact@spboucher.ai>3// File: pages/Sources.tsx4// Desc: Registre public des sources connectées (même patron que la page5// Sources de Lou·Ka) : plateforme, contexte de prix produit, volumes,6// dernière synchronisation.7// ==============================================================================8import { useEffect, useState } from "react";9import { CONTEXT_SHORT, Source, fetchSources, fmtTs } from "../api";1011export default function SourcesPage() {12 const [sources, setSources] = useState<Source[]>([]);13 const [error, setError] = useState(false);1415 useEffect(() => {16 fetchSources().then((r) => setSources(r.sources)).catch(() => setError(true));17 }, []);1819 return (20 <div className="container sources">21 <span className="kicker">Transparence</span>22 <h1>Les sources de Resto-Ka</h1>23 <p className="sub">24 Chaque menu est capté à sa source et étiqueté selon son contexte de25 prix. Les plateformes de commande des restos eux-mêmes (prix réels,26 non majorés) passent toujours avant les plateformes de livraison.27 </p>28 {error && <div className="notice"><h2>Impossible de charger les sources</h2></div>}29 <div className="src-wrap">30 <table className="src-table">31 <thead>32 <tr>33 <th>Source</th>34 <th>Contexte de prix</th>35 <th>Cadence</th>36 <th>Restos actifs</th>37 <th>Plats</th>38 <th>Dernière synchro</th>39 <th>Statut</th>40 </tr>41 </thead>42 <tbody>43 {sources.map((s) => (44 <tr key={s.id}>45 <td>46 <a href={s.url} target="_blank" rel="noopener noreferrer">{s.name}</a>47 {s.integrations && (48 <div className="src-integrations">49 {s.integrations.filter((i) => i.status !== "inactif").length} intégrations :{" "}50 {s.integrations.filter((i) => i.status !== "inactif")51 .map((i) => i.label).join(" · ")}52 </div>53 )}54 </td>55 <td>56 <span className={`pill ctx-pill ctx-${s.price_context}`}>57 {CONTEXT_SHORT[s.price_context ?? ""] ?? s.price_context}58 </span>59 </td>60 <td>{s.cadence ?? "—"}</td>61 <td><span className="count-pill">{s.active_restaurants}</span></td>62 <td><span className="count-pill">{s.menu_items ?? 0}</span></td>63 <td>{fmtTs(s.last_sync)}</td>64 <td>65 <span className={`pill ${s.status === "actif" ? "ok" : "todo"}`}>66 {s.status}67 </span>68 </td>69 </tr>70 ))}71 </tbody>72 </table>73 </div>74 </div>75 );76}77