// ----------------------------------------------------------------------------- // Forma-Ka — Agrégateur de formations (province de Québec) // Auteur : Simon-Pierre Boucher — contact@spboucher.ai // App.tsx : layout global (header + ticker en direct + footer encre) et routage // ----------------------------------------------------------------------------- import { useEffect, useState } from "react"; import { NavLink, Route, Routes, useLocation } from "react-router-dom"; import { fetchFacets, fetchSources, fetchStats, registerSourceNames, sourceName } from "./api"; import CookieConsent from "./components/CookieConsent"; import Home from "./pages/Home"; import FormationPage from "./pages/Formation"; import PrivacyPage from "./pages/Privacy"; import SourcesPage from "./pages/Sources"; import StatsPage from "./pages/Stats"; function Ticker() { const [items, setItems] = useState([]); useEffect(() => { Promise.all([fetchStats(), fetchFacets(), fetchSources()]) .then(([stats, facets, src]) => { registerSourceNames(src.sources); const parts: string[] = [`${stats.total} formations actives`]; if ((stats.gratuites ?? 0) > 0) parts.push(`${stats.gratuites} gratuites`); if ((stats.en_ligne ?? 0) > 0) parts.push(`${stats.en_ligne} en ligne`); for (const t of (stats.par_type ?? []).slice(0, 5)) parts.push(`${t.t} · ${t.n}`); for (const s of facets.sources.slice(0, 8)) parts.push(`${sourceName(s.source)} · ${s.n}`); parts.push("Mise à jour automatique"); setItems(parts); }) .catch(() => setItems(["Forma-Ka — toutes les formations du Québec"])); }, []); if (items.length === 0) return null; // contenu doublé pour une boucle de défilement continue return ( ); } const NAV_LINKS = [ { to: "/", label: "Formations", icon: "🎓", end: true }, { to: "/stats", label: "Stats", icon: "📊", end: false }, { to: "/sources", label: "Sources", icon: "🗂", end: false }, { to: "/confidentialite", label: "Confidentialité", icon: "🔒", end: false }, ]; function Header() { const [open, setOpen] = useState(false); const location = useLocation(); // fermer le menu à chaque navigation + verrouiller le défilement en dessous useEffect(() => { setOpen(false); }, [location]); useEffect(() => { document.body.style.overflow = open ? "hidden" : ""; return () => { document.body.style.overflow = ""; }; }, [open]); return ( <>
FormaKa Cours · séminaires · ateliers — tout le Québec
{/* menu déroulant mobile */}
{NAV_LINKS.map((l, i) => ( `mm-link ${isActive ? "active" : ""}`} onClick={() => setOpen(false)} > {l.label} ))}
Agrégateur indépendant — mis à jour automatiquement, chaque fiche renvoie à la formation originale.
{open &&
setOpen(false)} aria-hidden="true" />} ); } function Footer() { return (
FormaKa
Agrégateur indépendant de formations dans la province de Québec — cours en ligne, cours universitaires et collégiaux, séminaires, ateliers, certifications. Les fiches proviennent des sites publics des établissements et sont rafraîchies automatiquement — chaque fiche renvoie vers la page originale de la formation.
© {new Date().getFullYear()} Simon-Pierre Boucher — contact@spboucher.ai {" · "} Confidentialité {" · "}
); } export default function App() { return ( <>
} /> } /> } /> } /> } />
🧭

Page introuvable

Le lien demandé n'existe pas.

} />