Aller au contenu principal

Module JS.11 – Mini-projet

Objectif

Créer une application interactive JavaScript complète pour valider toutes les compétences JavaScript acquises.

Théorie

Application : Gestionnaire de tâches web

Application complète utilisant toutes les fonctionnalités JavaScript apprises.

Spécifications du projet

Fonctionnalités

1. CRUD complet (Create, Read, Update, Delete)

  • Créer une nouvelle tâche
  • Afficher toutes les tâches
  • Modifier une tâche existante
  • Supprimer une tâche

2. Persistance avec LocalStorage

  • Sauvegarder les tâches dans le navigateur
  • Charger les tâches au démarrage
  • Synchronisation automatique

3. Interface utilisateur moderne

  • Design responsive
  • Animations fluides
  • Feedback visuel

4. Fonctionnalités avancées

  • Filtrer par statut (toutes, actives, terminées)
  • Recherche de tâches
  • Tri par date, priorité
  • Statistiques (nombre total, terminées, actives)

Structure de données

{
id: 1,
titre: "Apprendre JavaScript",
description: "Maîtriser tous les concepts",
priorite: "haute",
date: "2026-01-26",
termine: false,
dateCreation: "2026-01-25T10:00:00Z"
}

Architecture recommandée

Structure de fichiers :

projet-todo/
├── index.html
├── css/
│ └── style.css
├── js/
│ ├── main.js # Point d'entrée
│ ├── models/
│ │ └── Task.js # Modèle de données
│ ├── services/
│ │ └── StorageService.js # Gestion LocalStorage
│ ├── controllers/
│ │ └── TaskController.js # Logique métier
│ └── views/
│ └── TaskView.js # Manipulation DOM
└── README.md

Guide de développement

Étape 1 : Modèle (Model)

// models/Task.js
class Task {
constructor(titre, description, priorite = "moyenne") {
this.id = Date.now();
this.titre = titre;
this.description = description;
this.priorite = priorite;
this.date = new Date().toISOString().split('T')[0];
this.termine = false;
this.dateCreation = new Date().toISOString();
}

toJSON() {
return {
id: this.id,
titre: this.titre,
description: this.description,
priorite: this.priorite,
date: this.date,
termine: this.termine,
dateCreation: this.dateCreation
};
}

static fromJSON(data) {
const task = new Task(data.titre, data.description, data.priorite);
task.id = data.id;
task.date = data.date;
task.termine = data.termine;
task.dateCreation = data.dateCreation;
return task;
}
}

Étape 2 : Service de stockage

// services/StorageService.js
class StorageService {
static STORAGE_KEY = 'todos';

static sauvegarder(tasks) {
const json = JSON.stringify(tasks.map(t => t.toJSON()));
localStorage.setItem(this.STORAGE_KEY, json);
}

static charger() {
const json = localStorage.getItem(this.STORAGE_KEY);
if (!json) return [];

const data = JSON.parse(json);
return data.map(d => Task.fromJSON(d));
}
}

Étape 3 : Controller

// controllers/TaskController.js
class TaskController {
constructor() {
this.tasks = StorageService.charger();
this.view = new TaskView(this);
}

ajouter(titre, description, priorite) {
const task = new Task(titre, description, priorite);
this.tasks.push(task);
this.sauvegarder();
this.view.afficher();
}

supprimer(id) {
this.tasks = this.tasks.filter(t => t.id !== id);
this.sauvegarder();
this.view.afficher();
}

modifier(id, donnees) {
const task = this.tasks.find(t => t.id === id);
if (task) {
Object.assign(task, donnees);
this.sauvegarder();
this.view.afficher();
}
}

marquerTermine(id) {
const task = this.tasks.find(t => t.id === id);
if (task) {
task.termine = !task.termine;
this.sauvegarder();
this.view.afficher();
}
}

sauvegarder() {
StorageService.sauvegarder(this.tasks);
}

filtrer(statut) {
switch(statut) {
case 'actives':
return this.tasks.filter(t => !t.termine);
case 'terminees':
return this.tasks.filter(t => t.termine);
default:
return this.tasks;
}
}
}

Étape 4 : View (Manipulation DOM)

// views/TaskView.js
class TaskView {
constructor(controller) {
this.controller = controller;
this.container = document.getElementById('tasks');
this.init();
}

init() {
// Écouteurs d'événements
document.getElementById('addTask').addEventListener('click', () => {
this.afficherFormulaire();
});

// Filtres
document.querySelectorAll('.filter').forEach(btn => {
btn.addEventListener('click', (e) => {
const statut = e.target.dataset.statut;
this.afficher(this.controller.filtrer(statut));
});
});
}

afficher(tasks = null) {
const tasksAAfficher = tasks || this.controller.tasks;
this.container.innerHTML = tasksAAfficher.map(task => this.renderTask(task)).join('');
this.attacherEventListeners();
}

renderTask(task) {
return `
<div class="task ${task.termine ? 'termine' : ''}" data-id="${task.id}">
<h3>${task.titre}</h3>
<p>${task.description}</p>
<span class="priorite ${task.priorite}">${task.priorite}</span>
<button class="toggle" data-id="${task.id}">
${task.termine ? 'Réactiver' : 'Terminer'}
</button>
<button class="delete" data-id="${task.id}">Supprimer</button>
</div>
`;
}

attacherEventListeners() {
document.querySelectorAll('.toggle').forEach(btn => {
btn.addEventListener('click', (e) => {
const id = parseInt(e.target.dataset.id);
this.controller.marquerTermine(id);
});
});

document.querySelectorAll('.delete').forEach(btn => {
btn.addEventListener('click', (e) => {
const id = parseInt(e.target.dataset.id);
if (confirm('Supprimer cette tâche ?')) {
this.controller.supprimer(id);
}
});
});
}
}

Étape 5 : Point d'entrée

// main.js
document.addEventListener('DOMContentLoaded', () => {
const controller = new TaskController();
controller.view.afficher();
});

Critères d'évaluation

Fonctionnalité (40%) :

  • CRUD complet fonctionnel
  • LocalStorage opérationnel
  • Filtres et recherche
  • Interface réactive

Code (30%) :

  • Architecture modulaire (classes, modules)
  • Code organisé et commenté
  • Respect des bonnes pratiques

JavaScript (20%) :

  • Utilisation appropriée des concepts
  • POO avec classes
  • Async/await si nécessaire
  • Gestion d'erreurs

Design (10%) :

  • Interface claire et moderne
  • Responsive
  • Animations fluides

Exercice - Développement

Suivez ces étapes :

  1. Setup (30 min)

    • Créez la structure de fichiers
    • Configurez HTML de base
    • Ajoutez le CSS minimal
  2. Modèle (1h)

    • Créez la classe Task
    • Implémentez toJSON/fromJSON
    • Testez avec console
  3. Service (1h)

    • Créez StorageService
    • Testez sauvegarde/chargement
    • Gèrez les erreurs
  4. Controller (1h30)

    • Créez TaskController
    • Implémentez toutes les méthodes
    • Testez la logique
  5. View (2h)

    • Créez TaskView
    • Manipulez le DOM
    • Attachez les événements
  6. Intégration (1h)

    • Connectez tout
    • Testez toutes les fonctionnalités
    • Corrigez les bugs
  7. Améliorations (1h)

    • Ajoutez les fonctionnalités bonus
    • Améliorez l'UX
    • Finalisez le design

Quiz de révision

Avant de commencer, vérifiez vos connaissances :

  1. Comment créer une classe en JavaScript ?
  2. Comment sauvegarder dans LocalStorage ?
  3. Comment manipuler le DOM ?
  4. Comment gérer les événements ?
  5. Comment organiser le code en modules ?

Ressources

Si vous êtes bloqué :

  • Revisitez les modules précédents
  • MDN Web Docs : developer.mozilla.org
  • Stack Overflow pour problèmes spécifiques

Bonnes pratiques à respecter :

  • Architecture modulaire
  • Code propre et commenté
  • Gestion d'erreurs appropriée
  • Interface utilisateur claire
  • Performance optimisée

Objectif final

Créer une application web interactive complète qui démontre :

  • Maîtrise de la syntaxe JavaScript
  • Compréhension de la POO
  • Manipulation du DOM
  • Gestion d'événements
  • Persistance de données
  • Architecture modulaire
  • Bonnes pratiques

Validation : Vous avez terminé JavaScript quand votre application est fonctionnelle, bien structurée et utilise toutes les fonctionnalités apprises.


Félicitations ! Vous avez terminé l'apprentissage complet de JavaScript. Vous êtes maintenant capable de créer des applications web interactives professionnelles.