// ----------------------------------------------------------------------------- // Forma-Ka — Agrégateur de formations (province de Québec) // Auteur : Simon-Pierre Boucher — contact@spboucher.ai // pages/Stats.tsx : portrait de l'offre de formation — totaux, répartition par // type (barres), santé des synchronisations récentes. // ----------------------------------------------------------------------------- import { useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { Stats, fetchSources, fetchStats, registerSourceNames, sourceName } from "../api"; export default function StatsPage() { const [stats, setStats] = useState(null); const [error, setError] = useState(null); useEffect(() => { fetchSources().then((r) => registerSourceNames(r.sources)).catch(() => {}); fetchStats().then(setStats).catch((e) => setError(String(e))); }, []); if (error) return (
⚠️

Impossible de charger les statistiques

{error}

); if (!stats) return (
Chargement…
); const maxType = Math.max(1, ...stats.par_type.map((t) => t.n)); return (
Portrait — l'offre de formation au Québec

Statistiques

Portrait en direct des formations actives agrégées par Forma-Ka, tous établissements confondus.

{stats.total} formations actives {stats.sources} établissements {(stats.gratuites ?? 0) > 0 && ( {stats.gratuites} gratuites )} {(stats.en_ligne ?? 0) > 0 && ( {stats.en_ligne} en ligne )} {stats.avg_price != null && ( prix moyen (affiché) {Math.round(stats.avg_price).toLocaleString("fr-CA")} $ )} {stats.avg_hours != null && ( durée moyenne {Math.round(stats.avg_hours)} h )}

Par type de formation

{stats.par_type.map((t) => ( {t.t || "Autre"} {t.n} ))}

Synchronisations récentes

{stats.recent_syncs.map((s, i) => ( ))}
Source Quand Trouvées + / ~ / − État
{sourceName(s.source)} {new Date(s.ts * 1000).toLocaleString("fr-CA")} {s.found} {s.added} / {s.updated} / {s.removed} {s.ok ? ( s.message === "ok" ? ok : alerte ) : ( échec )}
); }