À propos de ce cours
Projet final du cours JavaScript : réalisez une application web complète de gestion des stagiaires avec Vite et Bootstrap. Chargement des données avec fetch, classe Stagiaire, recherche avec debounce, filtres et tri, statistiques, formulaire dans une modale avec validation, notifications, persistance dans le localStorage, sécurité contre les failles XSS, ESLint, Prettier, tests Vitest, puis publication automatique sur GitHub Pages avec GitHub Actions. Avec cahier des charges, barème et soutenance.
Présentation du projet
- Chapitre : 24 – Vers l'écosystème JavaScript moderne. Ce projet clôture le cours et mobilise les compétences de tous les chapitres.
- Durée conseillée : 12 à 15 heures, en individuel ou en binôme, réparties sur plusieurs séances.
- Prérequis : Node.js 26 LTS, VS Code, Git et un compte GitHub.
- Sujet : le centre de formation souhaite une application web de gestion des stagiaires. Elle affiche la liste des stagiaires avec leurs notes, calcule leur moyenne et leur décision (Admis / Ajourné), permet d'ajouter, de modifier et de supprimer un stagiaire, de rechercher, filtrer et trier, et affiche des statistiques. L'application est publiée en ligne sur GitHub Pages.

Compétences mobilisées : modules ES, classes et getters, tableaux et méthodes filter / map / reduce / toSorted, DOM et délégation d'événements, formulaires et validation, fetch et async/await, localStorage et JSON, debounce, sécurité (pas d'innerHTML avec des données), Bootstrap 5.3 avec npm, Vite, ESLint, Prettier, Vitest, GitHub Actions.
Règles : le dépôt GitHub est créé dès la première séance et reçoit des commits réguliers (au minimum 15), avec des messages clairs. Un projet rendu en un seul commit la veille ne sera pas accepté. En binôme, chaque membre doit avoir des commits à son nom.
Cahier des charges
| N° | Fonctionnalité | Détails |
|---|---|---|
| F1 | Chargement des données | Au premier lancement, les stagiaires sont lus dans public/data/stagiaires.json avec fetch. Un indicateur de chargement (spinner) s'affiche ; en cas d'échec, un message d'erreur clair. |
| F2 | Liste des stagiaires | Tableau : nom complet, e-mail, groupe, notes, moyenne (2 décimales), décision avec un badge vert ou rouge, boutons Modifier et Supprimer. |
| F3 | Statistiques | Nombre de stagiaires affichés, moyenne générale, taux d'admis en %, meilleure moyenne. Elles suivent les filtres. |
| F4 | Recherche | Sur le prénom, le nom et l'e-mail, sans tenir compte des majuscules, avec un debounce de 300 ms. |
| F5 | Filtre et tri | Filtre par groupe (liste construite automatiquement à partir des données) ; tri par nom, moyenne décroissante ou croissante. |
| F6 | Ajout et modification | Formulaire dans une modale Bootstrap, prérempli pour la modification. Notes saisies séparées par des virgules. |
| F7 | Validation | Prénom et nom : 2 caractères minimum ; e-mail valide ; groupe au format DEV101; notes entre 0 et 20. Messages d'erreur sous chaque champ (is-invalid, invalid-feedback). |
| F8 | Suppression | Avec confirmation. |
| F9 | Persistance | Les données sont enregistrées en JSON dans le localStorage et retrouvées après rechargement. Un bouton « Réinitialiser » recharge les données d'origine. |
| F10 | Notifications | Un toast Bootstrap confirme chaque ajout, modification ou suppression. |
| F11 | Thème | Bouton mode clair / mode sombre (data-bs-theme). |
| F12 | Publication | Le site est publié automatiquement sur GitHub Pages à chaque push, après le lint et les tests. |
Exigences techniques :
- Projet créé avec Vite (modèle vanilla), Bootstrap installé avec npm (pas de CDN).
- Code découpé en modules; une classe
Stagiaire; la logique métier sans accès au DOM. - Aucun
innerHTMLavec des données :createElement,textContent,replaceChildren. - ESLint sans aucun problème et code formaté avec Prettier (configuration du chapitre 23).
- Au moins 25 tests Vitest qui passent.
Partie 1 Organisation et dépôt GitHub
- Créez sur GitHub un dépôt public
tp24-gestion-stagiaires(sans README, Vite en créera les fichiers). - Dans l'onglet Projects du dépôt, créez un tableau avec les colonnes « À faire », « En cours », « Terminé », et une carte par fonctionnalité F1 à F12. Déplacez les cartes au fil du projet.
- Rédigez dans un fichier
docs/maquette.md(ou sur papier, photographié) une maquette de l'écran principal et du formulaire.
Partie 2 Mise en place du projet
npm create vite@latest tp24-gestion-stagiaires -- --template vanilla
cd tp24-gestion-stagiaires
npm install
npm install bootstrap
npm install --save-dev eslint @eslint/js globals prettier eslint-config-prettier vitest
npm pkg set scripts.lint="eslint ." scripts.format="prettier --write ." scripts.test="vitest"
- Supprimez les fichiers de démonstration (
counter.js, images d'exemple) et videzmain.js. - Reprenez
eslint.config.js,.prettierrcet.prettierignoredu chapitre 23, en ajoutant"dist/"à la listeignores. - Créez
vite.config.js(le nom aprèsbasedoit être exactement celui de votre dépôt) :
import { defineConfig } from "vite";
export default defineConfig({
// nom du dépôt GitHub : le site est publié sur https://<compte>.github.io/tp24-gestion-stagiaires/
base: "/tp24-gestion-stagiaires/",
test: {
environment: "node",
},
});
- Créez le fichier de données
public/data/stagiaires.json:
[
{
"id": "s1",
"prenom": "Sami",
"nom": "Ben Ali",
"email": "sami.benali@exemple.tn",
"groupe": "DEV101",
"notes": [14, 15, 9]
},
{
"id": "s2",
"prenom": "Rim",
"nom": "Trabelsi",
"email": "rim.trabelsi@exemple.tn",
"groupe": "DEV101",
"notes": [17, 16, 18]
},
{
"id": "s3",
"prenom": "Yassine",
"nom": "Gharbi",
"email": "yassine.gharbi@exemple.tn",
"groupe": "DEV102",
"notes": [8, 12, 10]
},
{
"id": "s4",
"prenom": "Nour",
"nom": "Hammami",
"email": "nour.hammami@exemple.tn",
"groupe": "DEV102",
"notes": [11, 7, 9]
},
{
"id": "s5",
"prenom": "Amal",
"nom": "Jlassi",
"email": "amal.jlassi@exemple.tn",
"groupe": "DEV101",
"notes": [13, 14, 12]
},
{
"id": "s6",
"prenom": "Ahmed",
"nom": "Karray",
"email": "ahmed.karray@exemple.tn",
"groupe": "WEB201",
"notes": [6, 9, 8]
},
{
"id": "s7",
"prenom": "Ines",
"nom": "Mabrouk",
"email": "ines.mabrouk@exemple.tn",
"groupe": "WEB201",
"notes": [19, 17, 18]
},
{
"id": "s8",
"prenom": "Omar",
"nom": "Sassi",
"email": "omar.sassi@exemple.tn",
"groupe": "DEV102",
"notes": [10, 10, 10]
}
]
- Reliez le dossier au dépôt (
git init,git remote add origin …), faites le premier commit et le premierpush.
Partie 3 Architecture et logique métier
Organisez le code selon cette architecture :
src/
├── main.js ← démarrage, événements, coordination
├── outils.js ← debounce, genererIdentifiant, creerElement
├── modeles/
│ └── Stagiaire.js ← classe métier
├── logique/ ← fonctions pures, sans DOM, testées
│ ├── filtres.js
│ ├── statistiques.js
│ └── validation.js
├── services/
│ ├── api.js ← fetch du fichier JSON
│ └── stockage.js ← localStorage
└── ui/
├── affichage.js ← tableau, statistiques, liste des groupes
├── formulaire.js ← modale Bootstrap
└── notifications.js ← toasts Bootstrap
tests/ ← un fichier de test par module de logique
Écrivez d'abord le modèle et la logique, avec leurs tests, avant toute interface :
| Élément | Comportement attendu |
|---|---|
class Stagiaire | Constructeur à partir d'un objet { id, prenom, nom, email, groupe, notes } (prénom et nom nettoyés, e-mail en minuscules, copie du tableau de notes) ; getters nomComplet (« Sami BEN ALI »), moyenne (arrondie à 2 décimales, 0 sans note) et estAdmis (moyenne ≥ 10 et au moins une note) ; méthodes versObjet() et static depuisObjet(objet). |
filtrerStagiaires(liste, { texte, groupe }) | Recherche dans prénom, nom et e-mail sans tenir compte des majuscules ; filtre de groupe facultatif. |
trierStagiaires(liste, critere) | "nom", "moyenne-desc", "moyenne-asc"; renvoie une nouvelle liste (toSorted). |
listerGroupes(liste) | Groupes uniques triés (utilisez un Set). |
calculerStatistiques(liste) | { nombre, moyenneGenerale, tauxAdmis, meilleur }; gère la liste vide. |
analyserNotes(texte) | "12, 15; 9" → [12, 15, 9]; texte vide → []. |
validerStagiaire(donnees) | { valide, erreurs } où erreurs associe un message à chaque champ invalide. |
Méthode conseillée : pour chaque fonction, écrivez d'abord 3 ou 4 tests (cas normal, valeurs limites comme une moyenne d'exactement 10 ou les notes 0 et 20, cas d'erreur), puis le code qui les fait passer. Un commit par fonction terminée.
Partie 4 Données : fetch et localStorage
- Dans
services/api.js, chargez le JSON et transformez chaque objet enStagiaire. Attention au chemin : sur GitHub Pages, le site est dans un sous-dossier, il faut donc utiliserimport.meta.env.BASE_URL:
import { Stagiaire } from "../modeles/Stagiaire.js";
export async function chargerStagiairesInitiaux() {
const adresse = `${import.meta.env.BASE_URL}data/stagiaires.json`;
const reponse = await fetch(adresse);
if (!reponse.ok) {
throw new Error(`Chargement impossible (${reponse.status})`);
}
const donnees = await reponse.json();
return donnees.map(Stagiaire.depuisObjet);
}
- Dans
services/stockage.js, écrivezlireStagiairesEnregistres()(renvoienullsi rien n'est enregistré ou si les données sont corrompues, grâce àtry...catch),enregistrerStagiaires(liste)eteffacerStagiairesEnregistres(). - Au démarrage (fonction
async) : utilisez les données enregistrées si elles existent, sinon celles du JSON. Affichez le spinner pendant le chargement et un message d'erreur sifetchéchoue (testez en renommant temporairement le fichier JSON).
Partie 5 Interface : affichage, recherche, filtres et tri
- Construisez l'écran avec Bootstrap : barre de navigation, 4 cartes de statistiques, barre de recherche et de filtres, tableau responsive (
table-responsive). - Dans
ui/affichage.js, créez chaque ligne du tableau aveccreateElementettextContent. Enregistrez l'identifiant dansligne.dataset.id. - Écrivez une fonction
rafraichir()qui applique dans l'ordre : filtre → tri → affichage du tableau → statistiques → liste des groupes. - Recherche avec debounce, filtre de groupe et tri : chaque changement appelle
rafraichir(). - Pour les boutons Modifier et Supprimer, utilisez la délégation d'événements: un seul écouteur sur le
<tbody>, qui retrouve le bouton avecevenement.target.closest("button[data-action]").
Partie 6 Formulaire, validation et notifications

- Créez la modale avec un formulaire (
novalidatepour gérer vous-même la validation). ImportezModaletToastdepuis"bootstrap". - Le bouton « + Ajouter » ouvre la modale vide ; « Modifier » l'ouvre préremplie (un champ caché contient l'identifiant).
- À l'envoi (
submit+preventDefault()) : lisez les champs, validez avecvaliderStagiaire, affichez les erreurs sous les champs ou enregistrez (nouvel identifiant aveccrypto.randomUUID()), fermez la modale, sauvegardez, rafraîchissez et affichez un toast. - Suppression avec confirmation, puis toast rouge (
text-bg-danger). - Ajoutez le bouton de thème clair / sombre.
Test de sécurité obligatoire : ajoutez un stagiaire dont le prénom est
<img src=x onerror="alert('XSS')">. Le texte doit s'afficher tel quel dans le tableau, sans aucune alerte. Faites une capture pour le README.Partie 7 Qualité : lint, format et tests
npm run lint: aucune ligne affichée.npm run format: tout le code est formaté.npx vitest run: au moins 25 tests, tous réussis. Bonus : mesurez la couverture des dossiersmodelesetlogique(objectif 100 %).- Vérifiez dans la console du navigateur qu'aucune erreur n'apparaît pendant l'utilisation.
- Faites tester l'application par un autre stagiaire et corrigez ses remarques (notez-les dans le README).
Partie 8 Build et publication sur GitHub Pages
- Testez la version de production :
npm run buildpuisnpm run preview. Vérifiez que les données se chargent bien à l'adressehttp://localhost:4173/tp24-gestion-stagiaires/. - Créez le fichier
.github/workflows/deploy.yml: à chaquepushsurmain, GitHub installe le projet, lance le lint et les tests, construit le site et le publie.
# Construit le site avec Vite et le publie sur GitHub Pages à chaque push sur main
name: Déploiement sur GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 26
cache: npm
- run: npm ci
- run: npm run lint
- run: npx vitest run
- run: npm run build
- uses: actions/configure-pages@v6
- uses: actions/upload-pages-artifact@v5
with:
path: dist
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v5
- Sur GitHub : Settings → Pages → Source : GitHub Actions.
- Faites un
pushet suivez l'exécution dans l'onglet Actions. Si une étape échoue (lint, tests), le site n'est pas publié : lisez le journal, corrigez, recommencez. - Le site est disponible à l'adresse
https://<votre-compte>.github.io/tp24-gestion-stagiaires/. Ajoutez ce lien en haut du README et dans la description du dépôt.
Erreurs fréquentes : page blanche en ligne →
base ne correspond pas au nom du dépôt ; données introuvables en ligne → chemin du JSON sans import.meta.env.BASE_URL; npm ci échoue → package-lock.json n'a pas été commité.Partie 9 README et soutenance
- Rédigez un
README.mdcomplet : présentation, lien du site, captures d'écran, fonctionnalités, technologies, installation (npm install,npm run dev), commandes de test, architecture, difficultés rencontrées et solutions. - Préparez une soutenance de 10 minutes: démonstration en ligne (5 minutes), présentation d'un extrait de code dont vous êtes fier (2 minutes), lancement des tests et du lint en direct (1 minute), questions (2 minutes).
Bonus Pour aller plus loin
- TypeScript : convertissez les modules
modelesetlogiqueen.tsavec des interfaces, et ajouteznpx tsc --noEmitau workflow. - Export CSV : bouton qui télécharge la liste filtrée au format CSV (
BlobetURL.createObjectURL). - Graphique : répartition des moyennes par tranche dessinée avec l'API Canvas.
- Accessibilité : audit Lighthouse (score d'accessibilité supérieur à 95) et navigation complète au clavier.
- Framework : refaites l'écran de liste avec Angular, React ou Vue et comparez la quantité de code.
Livrables et barème
- Le lien du dépôt GitHub et le lien du site publié.
- Le
README.mdcomplet, le tableau de suivi du projet (Projects) et l'historique des commits. - La soutenance.
| Critère | Points |
|---|---|
| Fonctionnalités F1 à F11 opérationnelles | 6 |
Architecture en modules, classe Stagiaire, logique séparée du DOM | 3 |
Sécurité (aucun innerHTML avec des données, test XSS) et validation | 2 |
| Qualité : 0 problème ESLint, code formaté, au moins 25 tests qui passent | 3 |
| Publication automatique sur GitHub Pages (F12) | 2 |
| Gestion de projet : commits réguliers, tableau Projects, README | 2 |
| Soutenance : démonstration, explication du code, réponses aux questions | 2 |
| Total | 20 |
| Bonus | +3 |
Bravo : ce projet réunit tout le cours JavaScript. Il constitue une excellente première pièce de votre portfolio de développeur : soignez-le et partagez le lien !