# Plan d'Implémentation — ESTAIR Frontend Spec

**Date** : 2026-04-16
**Spec de référence** : `ESTAIR_FRONTEND_SPEC_COMPLET.md`
**État actuel** : ~65-70% de la spec implémenté

---

## État des lieux

### Déjà implémenté (118 fichiers)

| Catégorie | Fichiers | Composants clés |
|-----------|----------|-----------------|
| Pages | 22 | dashboard, login, CRUD générique `[domaine]/[module]`, admin (7 pages), monitoring (4 pages), SPC (2 pages), rapports, config |
| Composables | 15 | useEstair, useMetadata, useFilters, useDynamicColumns, useExport, useMessager, useSpcOperations |
| Stores | 6 | auth, app, metadata, navigation, spc, tenant |
| Components | 45 | EstairDataTable, DetailDrawer, CreateEditDrawer, FilterDrawer, FieldEditor, FieldRenderer, SPC tracker, admin matrices |
| Middleware | 3 | auth.global, acl.global, tenant.global |
| Plugins | 14 | Keycloak, Vuetify, CASL, i18n, Iconify |
| Layouts | 2 (+8 sous-composants) | default, blank |

### Manquant vs spec

| Priorité | Section spec | Composants manquants | Effort |
|----------|-------------|---------------------|--------|
| **P0** | §10-11 | Fix intégration données (plan existant : api.ts, navigation, metadata, useEstair) | 1 jour |
| **P0** | §10 | Fix 5 bugs CRUD (search, filters, picklists, CTA "Nouveau", autocomplete) | 1 jour |
| **P1** | §8 | 4 pages auth (forgot-password, reset-password, verify-email, two-steps) + auth/callback | 0.5 jour |
| **P1** | §9 | WidgetRenderer + 6 types widgets + AppPeriodSelector + dashboard dynamique | 2 jours |
| **P1** | §10-11 | KpiBand + AppSkeletonTable + AppSkeletonKpi | 0.5 jour |
| **P1** | §11 | RightPanel unifié (mode manager) + ContextSection + ContextListView + grow tabs | 2 jours |
| **P1** | §14 | FormWizard (stepper horizontal/vertical, validation par étape, champs conditionnels) | 1.5 jours |
| **P2** | §12 | Pages users/roles (RoleCards, UserTable, UserDetailView avec tabs) | 1.5 jours |
| **P2** | §13 | 4 AdvanceCards (A: icon+value+%, B: logo+progress, C: icon+badge, D: avatar+amount) | 1 jour |
| **P2** | §17 | AppToast, AppConfirmModal, error pages (404/403/500), skeleton loaders panel | 0.5 jour |
| **P2** | §18 | Stores ui.ts + period.ts (manquants) | 0.5 jour |
| **P3** | §6 | Menu dynamique depuis backend (type-based rendering, role filtering complet) | 0.5 jour |
| **P3** | §19 | i18n : fichier fr.json pour labels UI génériques | 0.5 jour |
| **P3** | §4 | Responsive : card view mobile, bottom sheet actions, form 1-col mobile | 1 jour |

**Effort total estimé** : ~14 jours de développement

---

## Phases d'implémentation

---

### PHASE 0 — Fix intégration données (prérequis)

> Plan existant dans `gleaming-painting-whale.md`. Doit être fait en premier car tout le reste en dépend.

**Objectif** : Les données backend s'affichent correctement dans le frontend.

| # | Tâche | Fichier | Description |
|---|-------|---------|-------------|
| 0.1 | Fix `$api` unwrapping | `utils/api.ts` | Préserver `pagination` + `indicators` dans la réponse ESTAIR au lieu de tout écraser par `data` |
| 0.2 | Fix navigation store | `stores/navigation.ts` | Parser `{ role, menus: [...] }` de CHAPS au lieu d'attendre un array direct |
| 0.3 | Fix useMetadata | `composables/useMetadata.ts` | Parser le format réel CHAPS : `ListeView.list[0].fields`, `CreateView.fields`, `DetailView.fields` (tuples → objets) |
| 0.4 | Fix useEstair | `composables/useEstair.ts` | Adapter au format post-fix : `{ data, pagination, indicators }` |
| 0.5 | Fix dashboard KPIs | `pages/app/dashboard.vue` | Vérifier extraction après fix unwrap |
| 0.6 | Test login flow | `pages/login.vue` | Vérifier end-to-end |

**Critère de succès** : Login → Dashboard → Clic module → DataTable avec données réelles + pagination.

---

### PHASE 1 — Fondations UI (stores + composants communs)

**Objectif** : Créer les briques réutilisables nécessaires aux phases suivantes.

| # | Tâche | Fichier(s) | Spec § |
|---|-------|-----------|--------|
| 1.1 | Créer store `ui.ts` | `stores/ui.ts` | §18 |
|     | État : rightPanel (open, mode, data), toasts[], activeDetailTab, activeContextTab | | |
| 1.2 | Créer store `period.ts` | `stores/period.ts` | §18 |
|     | État : start, end, preset. Actions : setPeriod, setPreset. Partagé dashboard + listes | | |
| 1.3 | Créer `AppToast.vue` | `components/common/AppToast.vue` | §17 |
|     | Position top-right, auto-dismiss 4s, types success/error/warning/info | | |
| 1.4 | Créer `useToast.ts` | `composables/useToast.ts` | §17 |
|     | `showToast({ type, message })` — écrit dans store ui.ts | | |
| 1.5 | Créer `AppConfirmModal.vue` | `components/common/AppConfirmModal.vue` | §17 |
|     | Message + Cancel/Confirm(danger). Utilisé par actions destructives | | |
| 1.6 | Créer `AppPeriodSelector.vue` | `components/common/AppPeriodSelector.vue` | §9 |
|     | Presets (Aujourd'hui, 7j, 30j, Ce mois, Custom) + date range picker. Écrit dans period.ts | | |
| 1.7 | Créer skeletons | `components/common/AppSkeleton{Table,Kpi,Panel}.vue` | §17 |
|     | 3 variantes : table (lignes grises animées), KPI (4 blocs), panel (sections) | | |
| 1.8 | Créer `usePermissions.ts` | `composables/usePermissions.ts` | §16 |
|     | `hasPermission(module, action)` — lit depuis auth store + metadata | | |
| 1.9 | Ajouter logo | `assets/images/logo-estair.png` | §7 |
|     | Copier depuis `frontend/logo_estair.png` vers assets + référencer dans sidebar | | |

**Critère de succès** : Toasts fonctionnent, period selector pilote les appels API, skeletons affichés pendant chargement.

---

### PHASE 2 — Pages Auth (§8)

**Objectif** : Compléter le flow d'authentification Keycloak.

| # | Tâche | Source template | Fichier cible | Description |
|---|-------|----------------|--------------|-------------|
| 2.1 | Page forgot-password | `login-v2/forgot-password-v2.vue` | `pages/forgot-password.vue` | Email input → POST `/api/auth/forgot-password`, layout blank split-screen |
| 2.2 | Page reset-password | `login-v2/reset-password-v2.vue` | `pages/reset-password.vue` | New password + confirm, token en query param |
| 2.3 | Page verify-email | `login-v2/verify-email-v2.vue` | `pages/verify-email.vue` | Texte informatif + "Renvoyer" + "Passer" |
| 2.4 | Page two-steps | `login-v2/two-steps-v2.vue` | `pages/two-steps.vue` | VOtpInput 6 digits, auto-submit onFinish |
| 2.5 | Auth callback | — | `pages/auth/callback.vue` | Échange code OAuth2 → tokens Keycloak → redirect dashboard |
| 2.6 | Layout auth | — | `layouts/auth.vue` | Split-screen Vuexy (md=8 illustration + md=4 formulaire), si non déjà couvert par blank.vue |

**Adaptations** :
- Labels en français
- Couleurs ESTAIR (#29ABE2 primary)
- Intégration Keycloak API (pas mock)
- Routes publiques dans middleware auth.global.ts

**Critère de succès** : Flow complet : Login → Keycloak → Callback → Dashboard. Forgot/Reset fonctionnel via Keycloak.

---

### PHASE 3 — Fix CRUD + KpiBand (§10)

**Objectif** : La vue liste générique fonctionne parfaitement avec les vraies données.

| # | Tâche | Fichier | Description |
|---|-------|---------|-------------|
| 3.1 | Fix search non transmis | `pages/app/[domaine]/[module]/index.vue` ou composable | Le paramètre `search` doit être passé à l'API `?search=...` |
| 3.2 | Fix filtres non appliqués | `composables/useFilters.ts` + FilterDrawer | Les critères du panneau filtre doivent construire les query params corrects |
| 3.3 | Fix bouton "Nouveau" | `pages/app/[domaine]/[module]/index.vue` | Afficher si `permissions.create === true` dans metadata |
| 3.4 | Fix picklists "objet" | `components/app/FieldRenderer.vue` | Afficher `option.label` au lieu de `[object Object]` dans les selects |
| 3.5 | Fix autocomplete | `components/app/FieldEditor.vue` | Intégrer `useReferenceSearch` pour les champs FK |
| 3.6 | Créer `KpiBand.vue` | `components/crud/KpiBand.vue` | Bande 2-4 KPIs au-dessus du tableau. Icône + valeur + trend. Skeleton pendant chargement |
| 3.7 | Intégrer KpiBand | `pages/app/[domaine]/[module]/index.vue` | Appeler `getIndicators()` + afficher KpiBand. Conditionnel sur `hasPeriod` avec AppPeriodSelector |
| 3.8 | Intégrer period store | `composables/useEstair.ts` | Si module `hasPeriod`, ajouter `start`/`end` du period store aux requêtes API |

**Critère de succès** : Module `/app/vente/etatsdeventes` → KPIs affichés + recherche fonctionne + filtres appliqués + bouton Nouveau visible + picklists lisibles.

---

### PHASE 4 — Panneau Droit unifié (§11)

**Objectif** : Un seul composant `RightPanel` gère les 3 modes (détail, filtre, create/update).

| # | Tâche | Fichier | Description |
|---|-------|---------|-------------|
| 4.1 | Créer `RightPanel.vue` | `components/crud/RightPanel.vue` | Mode manager : `mode = 'detail' | 'filter' | 'create' | 'update' | null`. Animation slide-in/out. 480px desktop, 100% mobile |
| 4.2 | Refactorer DetailDrawer | `components/crud/DetailPanel.vue` | Extraire en sous-composant du RightPanel. Ajouter grow tabs (VTabs grow) pour blocs détail |
| 4.3 | Refactorer FilterDrawer | `components/crud/FilterPanel.vue` | Extraire en sous-composant. Ajouter opérateur global AND/OR |
| 4.4 | Créer WizardPanel | `components/crud/WizardPanel.vue` | FormWizard intégré au panel. Chaque BlockDef = 1 step. Validation par étape |
| 4.5 | Créer ContextSection | `components/crud/ContextSection.vue` | Grow tabs pour objets contextuels liés (via `contextObjects` de metadata) |
| 4.6 | Créer ContextListView | `components/crud/ContextListView.vue` | Mini DataTable dans un tab contextuel (5 records, pagination, filtre inline) |
| 4.7 | Connecter au store ui.ts | — | `openPanel(mode, data)` / `closePanel()` via store actions. Escape key ferme le panel |
| 4.8 | Intégrer dans page CRUD | `pages/app/[domaine]/[module]/index.vue` | Remplacer les 3 drawers par le RightPanel unifié |

**Critère de succès** : Clic ligne → détail avec grow tabs + objets contextuels. Clic "Nouveau" → wizard steps. Clic "Filtres" → panneau filtre avec AND/OR.

---

### PHASE 5 — FormWizard (§14)

**Objectif** : Formulaires multi-étapes pour création/édition d'enregistrements.

| # | Tâche | Fichier | Description |
|---|-------|---------|-------------|
| 5.1 | Créer `FormWizard.vue` | `components/crud/FormWizard.vue` | Stepper horizontal (md+) / vertical (sm-). Steps = BlockDefs ordonnés |
| 5.2 | Créer `FormStepContent.vue` | `components/crud/FormStepContent.vue` | Renderer de champs par type. 2 colonnes (colSpan 1) ou pleine largeur (colSpan 2). Mobile : 1 colonne |
| 5.3 | Champs conditionnels | — | `dependsOn: { field, value }` → afficher/cacher dynamiquement |
| 5.4 | Steps optionnels | — | `required: false` → stepper grisé + bouton "Passer" |
| 5.5 | Validation par étape | — | Valider tous les champs `required` du step avant autoriser "Suivant". Afficher erreurs inline |
| 5.6 | Types de champs | `components/app/FieldEditor.vue` | Vérifier support complet : text, email, tel, number, textarea, select, multiselect, autocomplete, date, datetime, radio, checkbox, switch, file, currency, readonly |
| 5.7 | Intégrer dans WizardPanel | `components/crud/WizardPanel.vue` | FormWizard encapsulé dans le panneau droit mode create/update |

**Critère de succès** : Clic "Nouveau" sur module accounts → wizard 3 steps → champs dynamiques → validation → sauvegarde → toast succès → rechargement liste.

---

### PHASE 6 — Dashboard dynamique (§9)

**Objectif** : Dashboard piloté par widgets configurables depuis le backend.

| # | Tâche | Fichier | Description |
|---|-------|---------|-------------|
| 6.1 | Créer `WidgetRenderer.vue` | `components/dashboard/WidgetRenderer.vue` | Switch sur `widgetType` : HERO, KPI, BAR_CHART, GAUGE, LIST, TABLE |
| 6.2 | Widget Hero | `components/dashboard/widgets/WidgetHero.vue` | Carousel 3 slides (VCarousel). Titre + valeur + CTA |
| 6.3 | Widget KPI | `components/dashboard/widgets/WidgetKpi.vue` | Valeur + tendance + sparkline (ApexCharts). Réutilise AdvanceCardA |
| 6.4 | Widget BarChart | `components/dashboard/widgets/WidgetBarChart.vue` | ApexCharts bar comparatif (ex: émissions vs remboursements) |
| 6.5 | Widget Gauge | `components/dashboard/widgets/WidgetGauge.vue` | ApexCharts radialBar (ex: taux conversion, utilisation RHC) |
| 6.6 | Widget List | `components/dashboard/widgets/WidgetList.vue` | Liste triée avec avatars/icônes (top compagnies, top agents) |
| 6.7 | Widget Table | `components/dashboard/widgets/WidgetTable.vue` | Mini DataTable (5 lignes) avec colonnes configurables |
| 6.8 | Refactorer dashboard | `pages/app/dashboard.vue` | Charger widgets via API `GET /dashboard/{slug}?start=...&end=...`. Render via WidgetRenderer. Grid responsive (3 cols lg, 2 md, 1 sm) |
| 6.9 | Intégrer AppPeriodSelector | `pages/app/dashboard.vue` | En haut du dashboard, contrôle la période pour tous les widgets |

**Dépendances** : Les endpoints API dashboard doivent exister côté ESTAIR. En attendant, on peut utiliser les endpoints existants (`/badges/dashboard`, `/mouvements/stats`, etc.) comme sources de données.

**Critère de succès** : Dashboard affiche 10 widgets (W1-W10 du spec), période modifiable, données réelles.

---

### PHASE 7 — Users & Rôles (§12)

**Objectif** : Interface d'administration des utilisateurs et des rôles RBAC.

| # | Tâche | Fichier | Description |
|---|-------|---------|-------------|
| 7.1 | Page users list | `pages/app/admin/users/index.vue` | Haut : 6 RoleCards (grid 3 cols). Bas : UserTable avec filtres role/status |
| 7.2 | Créer RoleCard | `components/users/RoleCard.vue` | Total users + avatars empilés + nom rôle + "Edit role" link. Basé sur template `RoleCards.vue` |
| 7.3 | Créer UserTable | `components/users/UserTable.vue` | VDataTableServer avec avatar+name, role badge, status chip, actions. Basé sur template `user/list` |
| 7.4 | Page user detail | `pages/app/admin/users/[id].vue` | 2 colonnes : gauche (UserBioPanel 400px) + droite (VTabs : Compte, Sécurité, Agence, Permissions, Historique) |
| 7.5 | Créer UserBioPanel | `components/users/UserBioPanel.vue` | Photo + nom + rôle badge + KPIs (billets émis, EMDs actifs) + boutons Modifier/Suspendre |
| 7.6 | Créer permission matrix | `components/users/PermissionMatrix.vue` | Matrice modules × permissions (read/create/update/delete) par rôle |

**Critère de succès** : `/app/admin/users` → cartes rôles + table users. Clic user → détail avec tabs. Modification rôle sauvegardée.

---

### PHASE 8 — AdvanceCards (§13)

**Objectif** : 4 types de cartes réutilisables dans les blocs détail et le dashboard.

| # | Tâche | Fichier | Description |
|---|-------|---------|-------------|
| 8.1 | AdvanceCardA | `components/cards/AdvanceCardA.vue` | Icône arrondie colorée + label + valeur + % variation (↑ vert / ↓ rouge) |
| 8.2 | AdvanceCardB | `components/cards/AdvanceCardB.vue` | Logo carré + titre + sous-titre + progress bar + % |
| 8.3 | AdvanceCardC | `components/cards/AdvanceCardC.vue` | Icône grise + label + sub-label + valeur + badge % coloré |
| 8.4 | AdvanceCardD | `components/cards/AdvanceCardD.vue` | Avatar circulaire + montant + label + flèche ↑↓ + % |
| 8.5 | Intégrer dans KpiBand | `components/crud/KpiBand.vue` | Utiliser AdvanceCardA pour les KPIs de la bande |
| 8.6 | Intégrer dans DetailPanel | `components/crud/DetailPanel.vue` | Utiliser le type de card défini par `BlockDef.advanceCard` dans les blocs détail |

**Critère de succès** : KpiBand utilise AdvanceCardA. Blocs détail accounts utilisent CardA (balance, crédit).

---

### PHASE 9 — Menu dynamique + i18n + responsive (§6, §19, §4)

**Objectif** : Finitions et polish.

| # | Tâche | Fichier | Description |
|---|-------|---------|-------------|
| 9.1 | Menu dynamique | `stores/navigation.ts` + layout sidebar | Render menu selon `type` (crud → route /app/{domaine}/{module}, dashboard → route /app/dashboard, setup → route /app/setup, group → collapsible). Filtrer par rôle |
| 9.2 | Fichier i18n fr.json | `locales/fr.json` | Labels UI : Enregistrer, Annuler, Supprimer, Nouveau, Modifier, Rechercher, Filtres, Exporter, Précédent, Suivant, etc. |
| 9.3 | Error pages | `pages/[...error].vue` ou `error.vue` | 404 "Page introuvable", 403 "Accès refusé", 500 "Erreur serveur" + bouton retour |
| 9.4 | Responsive table mobile | `components/app/EstairDataTable.vue` | `sm-` : basculer en vue cartes (1 ligne = 1 card) |
| 9.5 | Responsive form mobile | `components/crud/FormStepContent.vue` | Forcer 1 colonne sur `sm-`, ignorer `colSpan` |
| 9.6 | Responsive panel mobile | `components/crud/RightPanel.vue` | `sm-` : plein écran (width 100%, height 100%) + bouton retour |

**Critère de succès** : Menu sidebar reflète les droits du user connecté. App utilisable sur mobile. Labels en français.

---

## Dépendances entre phases

```
Phase 0 (Fix data)
    ↓
Phase 1 (Stores + composants communs)
    ↓
    ├── Phase 2 (Auth pages) ← indépendant
    ├── Phase 3 (Fix CRUD + KpiBand) ← dépend de Phase 0 + 1
    │       ↓
    │   Phase 4 (Panneau Droit) ← dépend de Phase 3
    │       ↓
    │   Phase 5 (FormWizard) ← dépend de Phase 4
    │
    ├── Phase 6 (Dashboard) ← dépend de Phase 1 (period store, widgets)
    ├── Phase 7 (Users/Roles) ← indépendant mais utilise Phase 1
    └── Phase 8 (AdvanceCards) ← indépendant mais utilisé par Phase 3+4

Phase 9 (Polish) ← après toutes les autres
```

**Parallélisation possible** :
- Phase 2 (auth) peut démarrer en parallèle de Phase 3 (CRUD)
- Phase 6 (dashboard) peut démarrer en parallèle de Phase 4 (panel)
- Phase 7 (users) et Phase 8 (cards) peuvent se faire en parallèle

---

## Récapitulatif effort

| Phase | Effort | Priorité | Bloque |
|-------|--------|----------|--------|
| Phase 0 — Fix data | 1 jour | P0 | Tout |
| Phase 1 — Fondations UI | 1.5 jours | P0 | Phases 2-9 |
| Phase 2 — Auth pages | 0.5 jour | P1 | Rien |
| Phase 3 — Fix CRUD + KpiBand | 1.5 jours | P0 | Phase 4, 5 |
| Phase 4 — Panneau Droit | 2 jours | P1 | Phase 5 |
| Phase 5 — FormWizard | 1.5 jours | P1 | — |
| Phase 6 — Dashboard | 2 jours | P1 | — |
| Phase 7 — Users/Rôles | 1.5 jours | P2 | — |
| Phase 8 — AdvanceCards | 1 jour | P2 | — |
| Phase 9 — Polish | 2 jours | P3 | — |
| **TOTAL** | **~14.5 jours** | | |

---

## Convention de fichiers

Conformément au spec §3 :

```
components/
├── common/           ← Composants réutilisables (Toast, Modal, Period, Skeletons)
├── crud/             ← Vue liste + panneau droit (RightPanel, KpiBand, FormWizard, etc.)
├── dashboard/        ← WidgetRenderer + widgets/
├── users/            ← RoleCard, UserTable, UserBioPanel, PermissionMatrix
└── cards/            ← AdvanceCard A/B/C/D

stores/
├── auth.ts           ← existant
├── app.ts            ← existant (à enrichir si besoin)
├── metadata.ts       ← existant
├── navigation.ts     ← existant (fix Phase 0)
├── ui.ts             ← NOUVEAU (Phase 1)
├── period.ts         ← NOUVEAU (Phase 1)
├── spc.ts            ← existant
└── tenant.ts         ← existant
```
