TutorLoom
The learning platform for companies, colleges and training institutes. Free and open source, with an Enterprise edition.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗République du Bénin — Plateforme nationale de suivi scolaire
« Personne ne voit un décrochage venir avant qu'il soit acté. Notre plateforme le détecte automatiquement. »
🔗 En ligne : education-benin-platform.vercel.app — version anglaise : /en
Projet sélectionné dans le cadre du challenge du Ministère de la Transformation Digitale et de l’Innovation. EduTech Bénin est un projet indépendant : ce n'est pas un service officiel d'un ministère.
Le problème. Les enseignants et les directeurs n'ont pas d'outil simple pour repérer les élèves en difficulté à temps. Aujourd'hui, l'alerte arrive après le décrochage, pas avant.
La solution. EduTech Bénin détecte automatiquement les baisses de résultats et les absences répétées, dès la saisie d'une note ou d'une absence. Les alertes remontent en temps réel jusqu'au niveau national, pour que les établissements et le pilotage national puissent agir pendant qu'il en est encore temps.
Les règles s'appliquent à chaque saisie de note ou d'absence, et à l'import CSV (une fois par élève, après toutes ses lignes). Elles sont simples et explicables : chaque alerte peut être justifiée devant un directeur ou un inspecteur.
| Signal | Condition | Niveau |
|---|---|---|
| Notes | Baisse de la moyenne ≥ 20 % et nouvelle moyenne < 10/20 | Élevé |
| Notes | Baisse de la moyenne ≥ 15 % | Moyen |
| Absences | 10 absences injustifiées ou plus | Élevé |
| Absences | 5 absences injustifiées ou plus | Moyen |
Une cloche, dans l'en-tête de l'admin, du directeur et de l'enseignant, liste les alertes actives créées ou aggravées (« Aggravée : Moyen → Élevé ») depuis 30 jours au plus, et les suivis confiés à l'utilisateur par quelqu'un d'autre (« Suivi confié par … »). Les non lues sont celles arrivées depuis la dernière ouverture de la cloche, date enregistrée sur le serveur pour chaque utilisateur : ce qui est lu sur un appareil l'est sur les autres. Une aggravation redevient non lue, même si la création avait déjà été vue. Elle se met à jour toutes les 60 secondes tant que l'onglet est visible, et dès le retour sur l'onglet. Le directeur et l'enseignant ne voient que leur établissement.
Chaque export ne contient que ce que l'utilisateur a le droit de voir.
La plateforme s'installe comme une application (PWA). Les pages et les données déjà consultées restent lisibles sans connexion ; un bandeau l'indique, et les actions d'écriture sont désactivées jusqu'au retour du réseau. Les exports et l'authentification ne sont jamais mis en cache, et le cache est vidé quand un autre compte se connecte sur l'appareil.
/en, /en/login) : sélecteur FR/EN mémorisé, voix de lecture anglaise sur les pages anglaises. Après connexion, l'application est en français.Gestion des comptes (création, modification, suppression), demandes de mot de passe oublié traitées par l'admin, historique des actions (journal d'audit), assistant en langage naturel (Google Gemini, avec un mode de secours sans IA).
| Étudiant | Enseignant | Directeur | Admin (pilotage national) | |
|---|---|---|---|---|
| Périmètre | Sa propre fiche | Son établissement | Son établissement | National |
| Tableau de bord | Ses résultats | Statistiques de l'école | Tableau de bord de l'établissement | Tableau de bord national |
| Fiches et listes d'élèves | Sa fiche | ✓ | ✓ | ✓ |
| Saisir des notes et des absences | — | ✓ | ✓ | — (consultation) |
| Supprimer une absence | — | ✓ | — | — |
| Importer des notes (CSV) | — | ✓ | — | ✓ |
| Pages Alertes et Absences | — | ✓ | ✓ | ✓ |
| Suivre une alerte (actions, clôture, réouverture, responsable) | — | ✓ | ✓ | — (consultation et pilotage) |
| Être responsable d'un suivi, filtre « Mes suivis » | — | ✓ | ✓ | — |
| Cloche de notifications | — | ✓ | ✓ | ✓ |
| Exports PDF et Excel | Sa fiche (Excel) | ✓ | ✓ | ✓ |
| Assistant | — | ✓ | ✓ | ✓ |
| Comptes, historique, mots de passe oubliés | — | — | — | ✓ |
Le tableau décrit ce que chaque rôle peut faire dans l'interface ; chaque route de l'API vérifie elle-même ces droits, et l'interface n'affiche une action qu'aux rôles que l'API autorise. Les comptes sont créés par l'administration : il n'y a pas d'inscription libre, ce qui empêche un utilisateur de s'attribuer un rôle sensible.
npm audit signale encore 5 alertes, toutes dans les outils de vérification du code (eslint-config-next), qui ne font pas partie de l'application déployée.ASSISTANT_SANS_IA=true coupe complètement l'appel à Gemini.src/lib/demo/generer.ts.noindex) pendant la démonstration ; les balises hreflang et Open Graph sont prêtes.| Rôle | Nom | Mot de passe | |
|---|---|---|---|
| Admin (pilotage national) | Arsène Kpadonou | [email protected] | password123 |
| Enseignant, CEG Zopah | Fulbert Gbaguidi | [email protected] | password123 |
| Étudiant, CEG Zopah | Prudence Ahouansou (Terminale D) | [email protected] | password123 |
| Directeur, CEG Zopah | Bernadette Sossou | à créer depuis /admin/utilisateurs, puis relancer le seed | — |
| Élève | Classe | Situation |
|---|---|---|
| Rodrigue Agossou | Première D | moyenne de 12,75 à 7,5 : alerte notes Élevé, en cours de suivi, responsable l'enseignant (entretien par l'enseignant, parents convoqués par la directrice) |
| Adéola Akanni | Terminale C | moyenne de 14,5 à 12 : alerte notes Moyen, nouvelle (pour montrer une première action) |
| Inoussa Maïga | 3ème | 7 absences injustifiées ce trimestre, surtout le lundi : alerte absences Moyen, en cours de suivi : parents convoqués par la directrice, qui confie ensuite le suivi à l'enseignant. À la connexion de l'enseignant, la cloche affiche « Suivi confié par Bernadette Sossou », non lu |
| Sèna Dossou | Seconde D | 6 notes à 11 de moyenne contre 13,5 : alerte Moyen, qui s'aggrave pendant la démo |
| Nafissatou Gounou | 6ème | arrivée ce trimestre : « Pas encore de période de comparaison » |
Pour Sèna Dossou, saisir deux notes dans le trimestre proposé, par exemple 6 puis 6 (ou 5 puis 7). La première affiche « Note enregistrée » ; la seconde, « Alerte aggravée : Moyen → Élevé ». Règle : première note d'au moins 4, et les deux font moins de 14. Les autres élèves du CEG Zopah n'ont aucune alerte. Dans les autres établissements, quelques alertes sont en cours de suivi (responsable « à désigner » : aucun compte fictif n'y est créé) ou clôturées avec un motif, pour le pilotage de l'admin. Le seed calcule les périodes à partir du jour où il est lancé : le relancer la veille de la démo.
| Catégorie | Technologie |
|---|---|
| Framework | Next.js 16 (App Router), React 19 |
| Langage | TypeScript (ESM) |
| Base de données | PostgreSQL (Neon) |
| ORM | Prisma 7, driver adapter pg |
| Authentification | Auth.js v5 (sessions JWT de 7 jours) |
| Style | Tailwind CSS 4 |
| Graphiques | Recharts |
| Exports | @react-pdf/renderer (PDF), ExcelJS (Excel) |
| IA | Google Gemini API |
| Interface | SweetAlert2 |
| Monitoring | Sentry |
| Tests | Vitest (unitaires), Playwright (navigateur) |
| Déploiement | Vercel (fonctions en région cle1, Cleveland, à côté de la base Neon us-east-2), Node.js 24 |
src/generated/prisma et importé depuis @/generated/prisma/client. Il se connecte via src/lib/prisma.ts, avec le driver pg et le pooler Neon.attachDatabasePool (@vercel/functions) garde l'instance active le temps de les fermer, au lieu de la mettre en veille avec des connexions ouvertes.etudiantId (notes, absences, alertes, inscriptions), Etudiant.etablissementId, Etudiant (nom, prenom), Alerte (statut, createdAt), Alerte.niveauModifieLe, Absence.date.src/app/(fr) et src/app/en), et c'est le proxy qui envoie une personne connectée vers son espace ;us-east-2) et les fonctions Vercel à Cleveland (cle1). Une migration vers l'Europe a été étudiée puis reportée (gain d'environ 100 ms par requête, hébergement des données à décider) : mesures et procédure complète dans docs/migration-region.md.Alerte.niveauModifieLe (date de la dernière aggravation) et User.notificationsVuesLe (dernière ouverture de la cloche), nullables, ajoutées de façon purement additive : l'ancien code reste compatible.Prérequis : Node.js 24, un compte Neon avec une branche dev, une clé API Google Gemini, un compte Sentry (facultatif en local).
git clone https://github.com/Wildev10/education-benin-platform
cd education-benin-platform
npm install
Créez un fichier .env à partir de .env.example :
| Variable | Valeur |
|---|---|
DATABASE_URL | Connexion à la branche dev sur Neon (jamais la production). sslmode=verify-full est conseillé : pg traite déjà require comme verify-full, mais require deviendra moins strict avec pg v9 |
AUTH_SECRET | À générer avec openssl rand -base64 32 |
GEMINI_API_KEY | Depuis aistudio.google.com |
ASSISTANT_SANS_IA | true pour ne jamais appeler Gemini : l'assistant répond seulement avec l'analyseur local. Vide ou absente (par défaut) : Gemini d'abord, analyseur local en secours |
PROTECTED_DB_HOSTS | Hôte(s) de la base de production, séparés par des virgules (garde-fou) |
NEXT_PUBLIC_SENTRY_DSN | Depuis sentry.io (facultatif en local) |
npx prisma generate # client dans src/generated/prisma (aussi lancé par npm install)
npx prisma db push # crée les tables (ne régénère plus le client depuis Prisma 7)
npx tsx prisma/seed.ts # simulation : affiche ce qui sera supprimé et créé
npx tsx prisma/seed.ts --execute --hote=<hôte affiché> # données et comptes de démonstration
npm run dev
L'application est disponible sur http://localhost:3000.
⚠️ Utilisez toujours une branche Neon « dev », jamais la production. Le garde-fou
prisma/garde-prod.tsbloque les scripts d'écriture (seed, recalcul des alertes) siDATABASE_URLpointe vers un hôte listé dansPROTECTED_DB_HOSTS, ou si cette variable est vide. Les commandes Prisma (db push…) n'y passent pas : vérifiez l'hôte vous-même avant de les lancer.
npm run seed:prod
Réservé au développeur, depuis la racine du projet. Le script :
PROTECTED_DB_HOSTS du .env (même règle que le garde-fou) ;*** s'il apparaît ;OUI en toutes lettres, et s'arrête sur toute autre réponse, sans rien écrire ;--execute --hote=… --autoriser-prod, en une seule transaction), puis recalculer-alertes en simulation (lecture seule, attendu : 0 alerte à créer ni à mettre à jour) et affiche les volumes du suivi : alertes en cours de suivi, clôturées, nouvelles, nombre d'étapes.Le seed remplace toutes les données de démonstration de la base (élèves, notes, absences, alertes, historique). Le relancer la veille de la démo, puis reconnecter chaque compte de démonstration.
npm test # 685 tests unitaires
npm run lint # ESLint : 0 erreur, 0 avertissement attendus
npm run build # vérification TypeScript et build complet
npm run e2e # tests navigateur : connexions, excel, pagination, finitions, notifications, langues, droits, fuseaux, nombres, responsive, contraste
npm run e2e fait le build, lance next start sur le port 3100, exécute les scénarios de e2e/ avec Google Chrome, puis arrête le serveur. Il refuse de démarrer si la base du .env figure dans PROTECTED_DB_HOSTS.
notifications crée dans la base de dev deux élèves de test (nommés E2E-Alertes), leurs notes, absences et alertes, un compte élève pour l'import, et rétablit à la fin la date de consultation de la cloche des comptes de démonstration ; le scénario droits crée une absence temporaire. Les deux les suppriment à la fin, même en cas d'échec..env.e2e.example en .env.e2e.local (ignoré par git) et renseignez E2E_DIRECTEUR_EMAIL et E2E_DIRECTEUR_MOT_DE_PASSE avec un compte directeur de la base de dev ; sans eux, ces étapes sont ignorées.npm run e2e -- pagination pour un seul scénario, npm run e2e -- --sans-build pour réutiliser le build existant.Alerte.suiviDepuisAlerte.resolueLeSuiviAlertestatutsuiviDepuistraitee, colonne resolueLe, transition resoudre et action resolution de l'historique.Alerte.responsableId, Alerte.responsableDepuis, Alerte.motifCloture, SuiviAlerte.motifCloture, SuiviAlerte.responsableId et SuiviAlerte.responsableNom (nullables), 2 index, 2 clés étrangères vers User en SET NULL (un compte supprimé laisse l'alerte « à désigner », l'historique garde son nom). Purement additif. Même ordre : SQL en production, déploiement, puis seed.