Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
JavaScript

TP 24 : Projet final — application web complète en JavaScript

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,...

0 chapitres 0 QCM Accès gratuit

À 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.

Application de gestion des stagiaires : statistiques, recherche, filtres et tableau

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
F1Chargement des donnéesAu 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.
F2Liste des stagiairesTableau : nom complet, e-mail, groupe, notes, moyenne (2 décimales), décision avec un badge vert ou rouge, boutons Modifier et Supprimer.
F3StatistiquesNombre de stagiaires affichés, moyenne générale, taux d'admis en %, meilleure moyenne. Elles suivent les filtres.
F4RechercheSur le prénom, le nom et l'e-mail, sans tenir compte des majuscules, avec un debounce de 300 ms.
F5Filtre et triFiltre par groupe (liste construite automatiquement à partir des données) ; tri par nom, moyenne décroissante ou croissante.
F6Ajout et modificationFormulaire dans une modale Bootstrap, prérempli pour la modification. Notes saisies séparées par des virgules.
F7ValidationPré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).
F8SuppressionAvec confirmation.
F9PersistanceLes 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.
F10NotificationsUn toast Bootstrap confirme chaque ajout, modification ou suppression.
F11ThèmeBouton mode clair / mode sombre (data-bs-theme).
F12PublicationLe 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 innerHTML avec 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
  1. Créez sur GitHub un dépôt public tp24-gestion-stagiaires (sans README, Vite en créera les fichiers).
  2. 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.
  3. 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"
  1. Supprimez les fichiers de démonstration (counter.js, images d'exemple) et videz main.js.
  2. Reprenez eslint.config.js, .prettierrc et .prettierignore du chapitre 23, en ajoutant "dist/" à la liste ignores.
  3. Créez vite.config.js (le nom après base doit ê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",
  },
});
  1. 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]
  }
]
  1. Reliez le dossier au dépôt (git init, git remote add origin …), faites le premier commit et le premier push.
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émentComportement attendu
class StagiaireConstructeur à 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
  1. Dans services/api.js, chargez le JSON et transformez chaque objet en Stagiaire. Attention au chemin : sur GitHub Pages, le site est dans un sous-dossier, il faut donc utiliser import.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);
}
  1. Dans services/stockage.js, écrivez lireStagiairesEnregistres() (renvoie null si rien n'est enregistré ou si les données sont corrompues, grâce à try...catch), enregistrerStagiaires(liste) et effacerStagiairesEnregistres().
  2. 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 si fetch échoue (testez en renommant temporairement le fichier JSON).
Partie 5 Interface : affichage, recherche, filtres et tri
  1. Construisez l'écran avec Bootstrap : barre de navigation, 4 cartes de statistiques, barre de recherche et de filtres, tableau responsive (table-responsive).
  2. Dans ui/affichage.js, créez chaque ligne du tableau avec createElement et textContent. Enregistrez l'identifiant dans ligne.dataset.id.
  3. Écrivez une fonction rafraichir() qui applique dans l'ordre : filtre → tri → affichage du tableau → statistiques → liste des groupes.
  4. Recherche avec debounce, filtre de groupe et tri : chaque changement appelle rafraichir().
  5. Pour les boutons Modifier et Supprimer, utilisez la délégation d'événements: un seul écouteur sur le <tbody>, qui retrouve le bouton avec evenement.target.closest("button[data-action]").
Partie 6 Formulaire, validation et notifications

Formulaire d'ajout dans une modale Bootstrap avec messages de validation

  1. Créez la modale avec un formulaire (novalidate pour gérer vous-même la validation). Importez Modal et Toast depuis "bootstrap".
  2. Le bouton « + Ajouter » ouvre la modale vide ; « Modifier » l'ouvre préremplie (un champ caché contient l'identifiant).
  3. À l'envoi (submit + preventDefault()) : lisez les champs, validez avec validerStagiaire, affichez les erreurs sous les champs ou enregistrez (nouvel identifiant avec crypto.randomUUID()), fermez la modale, sauvegardez, rafraîchissez et affichez un toast.
  4. Suppression avec confirmation, puis toast rouge (text-bg-danger).
  5. 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
  1. npm run lint: aucune ligne affichée.
  2. npm run format: tout le code est formaté.
  3. npx vitest run: au moins 25 tests, tous réussis. Bonus : mesurez la couverture des dossiers modeles et logique (objectif 100 %).
  4. Vérifiez dans la console du navigateur qu'aucune erreur n'apparaît pendant l'utilisation.
  5. 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
  1. Testez la version de production : npm run build puis npm run preview. Vérifiez que les données se chargent bien à l'adresse http://localhost:4173/tp24-gestion-stagiaires/.
  2. Créez le fichier .github/workflows/deploy.yml: à chaque push sur main, 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
  1. Sur GitHub : Settings → Pages → Source : GitHub Actions.
  2. Faites un push et 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.
  3. 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
  1. Rédigez un README.md complet : 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.
  2. 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 modeles et logique en .ts avec des interfaces, et ajoutez npx tsc --noEmit au workflow.
  • Export CSV : bouton qui télécharge la liste filtrée au format CSV (Blob et URL.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.md complet, le tableau de suivi du projet (Projects) et l'historique des commits.
  • La soutenance.
CritèrePoints
Fonctionnalités F1 à F11 opérationnelles6
Architecture en modules, classe Stagiaire, logique séparée du DOM3
Sécurité (aucun innerHTML avec des données, test XSS) et validation2
Qualité : 0 problème ESLint, code formaté, au moins 25 tests qui passent3
Publication automatique sur GitHub Pages (F12)2
Gestion de projet : commits réguliers, tableau Projects, README2
Soutenance : démonstration, explication du code, réponses aux questions2
Total20
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 !