spb/forma-ka Public
Python 65.1%
TypeScript 17.9%
CSS 16.4%
HTML 0.5%
1// -----------------------------------------------------------------------------2// Forma-Ka — Agrégateur de formations (province de Québec)3// Auteur : Simon-Pierre Boucher — contact@spboucher.ai4// pages/Sources.tsx : registre des établissements de formation agrégés5// -----------------------------------------------------------------------------6import { useEffect, useState } from "react";7import { Source, fetchSources } from "../api";89export default function SourcesPage() {10 const [sources, setSources] = useState<Source[] | null>(null);11 const [error, setError] = useState<string | null>(null);1213 useEffect(() => {14 fetchSources()15 .then((r) => setSources(r.sources))16 .catch((e) => setError(String(e)));17 }, []);1819 return (20 <div className="container sources">21 <span className="kicker">Registre — établissements de formation</span>22 <h1>Sources agrégées</h1>23 <p className="sub">24 Universités, cégeps, firmes de formation et organisateurs d'événements du25 Québec recensés par Forma-Ka. Chaque source « active » est synchronisée26 périodiquement par un connecteur dédié; les autres sont en attente de27 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>Établissement</th>39 <th>Type d'offre</th>40 <th>Statut</th>41 <th style={{ textAlign: "right" }}>Formations actives</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 <a href={s.url} target="_blank" rel="noopener noreferrer">{s.name}</a>50 </td>51 <td style={{ color: "var(--ink-2)", maxWidth: 380 }}>{s.type_offre}</td>52 <td>53 {s.connector ? (54 <span className="pill ok">connecté</span>55 ) : (56 <span className="pill todo">{s.status}</span>57 )}58 </td>59 <td style={{ textAlign: "right" }}>60 <span className="count-pill">{s.active_formations || "—"}</span>61 </td>62 <td style={{ color: "var(--ink-3)" }}>63 {s.last_sync64 ? new Date(s.last_sync * 1000).toLocaleString("fr-CA")65 : "—"}66 </td>67 </tr>68 ))}69 </tbody>70 </table>71 </div>72 )}73 </div>74 );75}76