# Atelier 05 — Projet Fil Rouge : Mini-Outil Déployable sur GitLab Pages

> **Prérequis :** Ateliers 00 à 04 complétés  
> **Durée estimée :** 1 à 2 semaines  
> **Ce que tu sauras faire :** Concevoir et déployer un outil réel en Vanilla Web, structuré avec des Web Components

---

## Objectif

Construire **un outil utilisable au travail**, déployé sur GitLab Pages, en appliquant tous les concepts des ateliers précédents.

Cet atelier ne donne pas de solution : il te fournit un cadre de conception, une architecture de référence, et des points de contrôle. Tu choisis ton outil.

---

## 1. Choisir ton outil

L'outil doit être :
- **Utile** : tu l'utiliseras réellement au travail
- **Réaliste** : faisable seul en 1-2 semaines
- **Client-side only** : pas de backend, pas de base de données serveur (GitLab Pages = fichiers statiques)

### Exemples de candidats

| Outil | Complexité | Concepts mobilisés |
|---|---|---|
| Journal de bord quotidien | Faible | Custom Elements, `localStorage`, template |
| Checklist configurable | Faible | Custom Events, attributs, slots |
| Suivi de temps par projet | Moyenne | Shadow DOM, events, `localStorage`, timers |
| Convertisseur d'unités | Faible | Custom Events, formulaires |
| Générateur de notes de réunion | Moyenne | Slots, template, Shadow DOM |
| Tableau Kanban simplifié | Élevée | Architecture, events, drag & drop |

### Critère de choix

Commence par la version la plus simple de l'outil que tu veux. Tu peux toujours l'enrichir. Mieux vaut finir une chose simple qu'abandonner quelque chose d'ambitieux.

---

## 2. Architecture de référence

Quelle que soit l'outil choisi, applique cette architecture :

### Structure de fichiers

```
mon-outil/
├── public/                    ← Requis par GitLab Pages
│   ├── index.html
│   ├── main.js                ← Point d'entrée, coordinateur
│   ├── css/
│   │   └── global.css
│   └── components/
│       ├── app-shell.js       ← Composant racine (optionnel)
│       ├── [composant-a].js
│       └── [composant-b].js
└── .gitlab-ci.yml
```

### `.gitlab-ci.yml` pour GitLab Pages

```yaml
pages:
  stage: deploy
  script:
    - echo "Static site — no build step required"
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
```

### Responsabilités

| Fichier | Rôle |
|---|---|
| `index.html` | Structure HTML initiale, imports, déclarations de templates |
| `main.js` | Import des composants, coordination des événements globaux |
| `global.css` | Reset, variables CSS globales, theming |
| `components/*.js` | Un fichier par composant, export de la classe |

---

## 3. Persistance : `localStorage`

GitLab Pages étant statique, la persistance côté client passe par `localStorage`.

```js
// Sauvegarder
localStorage.setItem('mon-outil:taches', JSON.stringify(taches));

// Charger
const raw = localStorage.getItem('mon-outil:taches');
const taches = raw ? JSON.parse(raw) : [];
```

**Conventions recommandées :**
- Préfixe toutes tes clés avec le nom de l'outil : `mon-outil:clé`
- Crée un module `storage.js` pour centraliser l'accès :

```js
// storage.js
const PREFIX = 'mon-outil';

export function save(key, value) {
  localStorage.setItem(`${PREFIX}:${key}`, JSON.stringify(value));
}

export function load(key, defaultValue = null) {
  const raw = localStorage.getItem(`${PREFIX}:${key}`);
  return raw !== null ? JSON.parse(raw) : defaultValue;
}
```

---

## 4. Points de contrôle

Avance dans cet ordre. Ne passe pas à l'étape suivante si la précédente n'est pas solide.

### Étape 1 — Mise en place du projet (Jour 1)

- [ ] Créer le dépôt GitLab
- [ ] Créer la structure de fichiers (`public/`, `.gitlab-ci.yml`)
- [ ] `index.html` minimal avec `main.js` chargé en `type="module"`
- [ ] Push et vérifier que GitLab Pages se déploie (même vide)
- [ ] URL de Pages accessible

**Critère :** L'URL GitLab Pages affiche quelque chose (même juste "Bonjour").

---

### Étape 2 — Composants de base (Jours 2-3)

- [ ] Identifier les 3-5 composants nécessaires pour la version minimale
- [ ] Créer chaque composant en fichier séparé, exporté
- [ ] Tester chaque composant en isolation dans `index.html` avant de les connecter

**Question à te poser pour chaque composant :**
- Quels attributs reçoit-il ?
- Quelles propriétés JS expose-t-il ?
- Quels événements émet-il ?
- Doit-il avoir un Shadow DOM ? (Besoin d'isolation de styles ?)

---

### Étape 3 — Communication (Jours 4-5)

- [ ] Définir les événements personnalisés (noms, payload `detail`)
- [ ] Implémenter le coordinateur dans `main.js`
- [ ] Vérifier que les composants ne se référencent pas directement

**Document les événements** dans un commentaire en tête de `main.js` :

```js
// Événements de l'application
// ─────────────────────────────────────────────────────────
// task-created      detail: { id, title, createdAt }
// task-deleted      detail: { id }
// task-status-changed  detail: { id, newStatus }
```

---

### Étape 4 — Persistance (Jour 6)

- [ ] Charger les données depuis `localStorage` au démarrage
- [ ] Sauvegarder après chaque modification
- [ ] Tester : fermer/rouvrir le navigateur conserve les données

**Question :** Où déclenches-tu la sauvegarde ? Dans le coordinateur (`main.js`) ou dans les composants ? Justifie ton choix.

---

### Étape 5 — Styles et UX (Jours 7-8)

- [ ] CSS global (reset, typographie, couleurs via Custom Properties)
- [ ] Styles encapsulés dans les Shadow DOM des composants
- [ ] Variables CSS exposées pour le theming (`--outil-primary`, etc.)
- [ ] Responsive minimal (l'outil fonctionne sur un écran de laptop)

---

### Étape 6 — Révision et déploiement final (Jour 9-10)

- [ ] Revoir chaque composant : cycle de vie correct ? Fuites mémoire ?
- [ ] Tester en navigation privée (localStorage vide, données initiales correctes ?)
- [ ] Vérifier dans la console Chrome : aucune erreur, aucun warning
- [ ] Déployer la version finale et tester sur l'URL GitLab Pages

---

## 5. Exercices intégrés

Ces exercices sont valables quelle que soit l'outil choisi.

### Exercice 5.1 — Schéma d'architecture

**Avant d'écrire une ligne de code**, dessine (sur papier ou dans un fichier Markdown) :
- La liste des composants
- Pour chaque composant : ses attributs, ses propriétés, ses événements émis
- Les flèches de communication (qui écoute qui)
- Où la persistance intervient

**Critère :** Le schéma doit te permettre d'estimer si l'architecture tient la route avant de coder.

---

> **Indice 1** — Si deux composants ont une flèche directe entre eux (un composant référence l'autre), c'est un signal d'alarme. Passe-t-il par un événement ? Si non, pourquoi ?

> **Indice 2** — Si un composant fait trop de choses (reçoit des données, émet des événements, et gère la persistance), c'est peut-être deux composants différents.

---

### Exercice 5.2 — Composant le plus complexe en isolation

**Identifie le composant le plus complexe** de ton outil. Développe-le **seul**, sans le contexte de l'application :

1. Crée un `test.html` dédié à ce composant
2. Fournis-lui des données statiques (hardcodées dans `test.html`)
3. Vérifie tous ses comportements (attributs, événements, styles, cycle de vie)
4. Intègre-le dans l'application seulement une fois qu'il est validé

---

> **Indice 1** — Développer en isolation révèle si ton composant a des dépendances cachées sur son environnement. Un bon composant fonctionne seul.

> **Indice 2** — Pour tester les événements en isolation : `document.addEventListener('ton-event', e => console.log(e.detail))` dans `test.html`.

---

### Exercice 5.3 — Revue du cycle de vie

Pour chaque composant de ton outil, réponds par écrit (dans un commentaire ou un README) :

1. Que fait `constructor()` ?
2. Que fait `connectedCallback()` ?
3. Y a-t-il un `disconnectedCallback()` ? Pourquoi ou pourquoi pas ?
4. Quels attributs sont dans `observedAttributes()` ? Pourquoi ceux-là et pas d'autres ?

---

> **Indice 1** — Si `disconnectedCallback` est vide ou absent, vérifie : as-tu des `setInterval`, des listeners sur `document` ou `window`, ou des observeurs (`MutationObserver`, `IntersectionObserver`) ? Si oui, il manque peut-être un nettoyage.

---

### Exercice 5.4 — Audit des styles

Ouvre les DevTools Chrome et, pour chaque composant avec Shadow DOM :

1. Vérifie que `:host { display: block; }` est présent (les Custom Elements sont `inline` par défaut)
2. Confirme qu'aucun style externe ne s'applique à l'intérieur du shadow root (test avec une règle agressive dans `global.css`)
3. Liste les CSS Custom Properties que ton composant expose — sont-elles documentées ?

---

### Exercice 5.5 — Test de robustesse

Teste ces scénarios qui révèlent des bugs courants :

- **Données vides :** L'application démarre sans aucune donnée dans `localStorage` (navigation privée). Tout s'affiche correctement ?
- **Données corrompues :** Mets manuellement une valeur invalide dans `localStorage` (`localStorage.setItem('mon-outil:taches', 'INVALIDE')`). L'application crashe-t-elle ou gère-t-elle l'erreur ?
- **Composant déplacé :** Dans la console, `document.body.appendChild(document.querySelector('ton-composant'))`. `connectedCallback` se redéclenche-t-il ? Est-ce un problème ?

---

## 6. Ce que tu ne dois PAS faire

Ces patterns sont tentants mais contre-productifs dans l'approche Vanilla Web :

**Évite les références directes entre composants :**
```js
// ❌ Couplage fort
class FilterBar extends HTMLElement {
  connectedCallback() {
    this._taskList = document.querySelector('task-list'); // couplage direct
  }
}

// ✅ Loose coupling via events
this.dispatchEvent(new CustomEvent('filter-changed', { bubbles: true, detail: {...} }));
```

**Évite l'état global en dehors du coordinateur :**
```js
// ❌ Variable globale accessible partout
window.appState = { tasks: [] };

// ✅ État géré dans main.js, distribué via propriétés
```

**Évite les `querySelector` trop larges depuis un composant :**
```js
// ❌ Un composant ne devrait pas chercher des éléments hors de lui-même
document.querySelector('autre-composant').doSomething();

// ✅ Émettre un événement et laisser le coordinateur réagir
```

---

## 7. Checklist de déploiement finale

- [ ] `public/index.html` est la page principale
- [ ] Aucun lien absolu (tous les chemins sont relatifs)
- [ ] Pas de `console.log` de debug restants
- [ ] `localStorage` géré avec `try/catch` pour les erreurs de parsing
- [ ] Le `.gitlab-ci.yml` déploie bien le dossier `public/`
- [ ] L'outil est testé depuis l'URL GitLab Pages (pas seulement en local)
- [ ] Chrome DevTools Console : zéro erreur

---

## Références

| Sujet | URL |
|---|---|
| GitLab Pages — Scratch | https://docs.gitlab.com/ee/user/project/pages/getting_started/pages_from_scratch.html |
| localStorage — MDN | https://developer.mozilla.org/fr/docs/Web/API/Window/localStorage |
| Web Components — MDN | https://developer.mozilla.org/fr/docs/Web/API/Web_components |
| Baseline — web.dev | https://web.dev/baseline |
| Open Web Components (patterns) | https://open-wc.org/guides/ |