# Correctifs UI workflow — Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommandé) ou superpowers:executing-plans pour exécuter ce plan tâche par tâche. Les étapes utilisent la syntaxe checkbox (`- [ ]`). **Goal:** Corriger les régressions UI introduites par les workflows (D&D, sélecteur de statut, cartes, couleurs) et améliorer l'UX mail/modales, sur la base de `docs/superpowers/specs/2026-05-20-workflow-ui-fixes-design.md`. **Architecture:** Une brique partagée (filtrage des statuts par workflow + palette de catégories + composant modale réutilisable) consommée par les autres chantiers. Backend modifié uniquement pour l'endpoint `create-task` (#6). Correction de données prod (#4) via migration Doctrine. **Tech Stack:** Symfony 8 / API Platform 4 / Doctrine (backend, PHPUnit) ; Nuxt 4 / Vue 3 / Pinia / Tailwind / `@malio/layer-ui` (frontend). > **Note testing (importante).** Lesstime **n'a pas de test runner frontend** (vérifié : pas de vitest/jest dans `frontend/package.json`). La discipline TDD ne s'applique donc qu'au **backend** (PHPUnit via `make test`). Pour le **frontend**, chaque tâche se vérifie par : (1) `npm run build:dist` qui doit réussir (exit 0), puis (2) contrôle navigateur via Chrome DevTools MCP sur `http://localhost:8082` (DOM/visuel). **Toujours hard-reload sans cache** après build (le navigateur cache les chunks JS hashés). Login dev avec données prod importées : `Matthieu` / `admin`. > **Branche.** Créer une branche d'implémentation depuis `develop` (ex. `fix/workflow-ui-fixes`) avant de commencer. Commits fréquents, format `() : `. --- ## Ordre d'exécution (dépendances) 1. **Task 1** — Brique front : palette de catégories + helper contraste (`#4b`, réutilisé par #1) 2. **Task 2** — Composant `AppModal` réutilisable (`#7`) 3. **Task 3** — Filtrage du sélecteur de statut par workflow dans TaskModal (`#2`) 4. **Task 4** — Drag & drop dans « Mes tâches » + entêtes teintées (`#1` + `#4b`) 5. **Task 5** — Backend : endpoint `create-task` (statut + assigné, sans priorité) (`#6` back) 6. **Task 6** — Frontend : modale de création depuis mail (`#6` front, sur AppModal) 7. **Task 7** — Suppression du bouton « Lier un mail » (`#5`) 8. **Task 8** — Cartes responsive (`#3`) 9. **Task 9** — Couleurs par défaut par catégorie + migration data prod (`#4a` + `#4c`) 10. **Task 10** — Migration de TaskModal vers AppModal (`#7`) --- ## Task 1 : Palette de catégories + helper de contraste **Files:** - Modify: `frontend/services/dto/workflow.ts` - [ ] **Step 1 : Ajouter la palette et le helper de contraste** Dans `frontend/services/dto/workflow.ts`, après `STATUS_CATEGORY_LABEL` (l.5-11), ajouter : ```ts /** Palette canonique des catégories (couleurs « classiques »), indépendante des workflows. */ export const STATUS_CATEGORY_COLOR: Record = { todo: '#222783', in_progress: '#4A90D9', blocked: '#C62828', review: '#FF8F00', done: '#26A69A', } /** Renvoie '#1f2937' (foncé) ou '#ffffff' (blanc) selon la luminance du fond, pour rester lisible. */ export function contrastText(hex: string): string { const c = hex.replace('#', '') const r = parseInt(c.slice(0, 2), 16) const g = parseInt(c.slice(2, 4), 16) const b = parseInt(c.slice(4, 6), 16) const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255 return lum > 0.6 ? '#1f2937' : '#ffffff' } ``` - [ ] **Step 2 : Vérifier le build** Run: `cd frontend && npm run build:dist` Expected: exit 0, aucune erreur TypeScript. - [ ] **Step 3 : Commit** ```bash git add frontend/services/dto/workflow.ts git commit -m "feat(workflow) : palette de catégories canonique + helper de contraste" ``` --- ## Task 2 : Composant modale réutilisable `AppModal` (#7) **Files:** - Create: `frontend/components/ui/AppModal.vue` - [ ] **Step 1 : Créer `AppModal.vue`** ```vue ``` - [ ] **Step 2 : Build** Run: `cd frontend && npm run build:dist` Expected: exit 0. - [ ] **Step 3 : Commit** ```bash git add frontend/components/ui/AppModal.vue git commit -m "feat(ui) : composant AppModal réutilisable (header fixe / body scrollable / footer sticky)" ``` > AppModal sera consommé par MailCreateTaskModal (Task 6) et TaskModal (Task 10). --- ## Task 3 : Filtrer le sélecteur de statut par workflow dans TaskModal (#2) **Files:** - Modify: `frontend/components/task/TaskModal.vue` (statusOptions ~l.674-676) **Contexte vérifié :** TaskModal reçoit déjà `:projects` (`Project[]` avec `workflow.statuses`). Le projet effectif est `showProjectSelect ? form.projectId : props.projectId` (cf. l.717). `props.statuses` (global) devient un fallback. - [ ] **Step 1 : Remplacer `statusOptions`** Remplacer (l.674-676) : ```ts const statusOptions = computed(() => props.statuses.map(s => ({ label: s.label, value: s.id })) ) ``` par : ```ts const effectiveProjectId = computed(() => showProjectSelect.value ? form.projectId : props.projectId, ) const statusOptions = computed(() => { const project = props.projects?.find(p => p.id === effectiveProjectId.value) const wfStatuses = project?.workflow?.statuses ?? props.statuses const opts = wfStatuses.map(s => ({ label: s.label, value: s.id })) // Garder le statut courant s'il n'appartient pas (plus) au workflow, pour ne pas le perdre. const current = props.task?.status if (current && !wfStatuses.some(s => s.id === current.id)) { opts.unshift({ label: current.label, value: current.id }) } return opts }) ``` > Si une variable `effectiveProjectId`/`activeProjectId` existe déjà (vérifier autour de l.717), réutiliser celle-ci au lieu d'en redéclarer une. - [ ] **Step 2 : Build** Run: `cd frontend && npm run build:dist` Expected: exit 0. - [ ] **Step 3 : Vérification navigateur (Chrome MCP)** 1. Hard-reload `http://localhost:8082` (cache ignoré), login `Matthieu`/`admin`. 2. Ouvrir une tâche d'un projet **Standard** (ex. `LST-49` via « Mes tâches »). 3. Ouvrir le sélecteur « Statut ». Expected : **5 options** (les statuts du workflow Standard) — plus aucun statut ERP (« Prêt à dev », « En dev », « Mergé », « Validation client », « Validé prod », « Abandonné »). 4. Ouvrir une tâche du projet **STARSEED** (workflow ERP, code `ERP-…`). Expected : uniquement les statuts ERP. - [ ] **Step 4 : Commit** ```bash git add frontend/components/task/TaskModal.vue git commit -m "fix(task) : sélecteur de statut filtré par le workflow du projet" ``` --- ## Task 4 : Drag & drop « Mes tâches » + entêtes teintées (#1 + #4b) **Files:** - Create: `frontend/components/task/StatusPickerPopover.vue` - Modify: `frontend/pages/my-tasks.vue` (template kanban ~l.394-424 ; script ~l.118-140) - Modify: `frontend/services/tasks.ts` (réutiliser `update()` existant) **Contexte vérifié :** `TaskCard.vue` pose déjà `dataTransfer.setData('text/plain', task.id)` au `dragstart`. `my-tasks.vue` n'a **aucun** handler `@drop`/`@dragover`. Les colonnes itèrent sur `CATEGORIES` (l.119). `tasks.value` contient les tâches affichées. `tasks.ts` expose `update(id, payload: Partial)` → `PATCH /tasks/{id}` ; le statut s'écrit en IRI (`status: '/api/task_statuses/{id}'`, cf. TaskModal l.1070). - [ ] **Step 1 : Créer le popover de désambiguïsation** `frontend/components/task/StatusPickerPopover.vue` : ```vue ``` - [ ] **Step 2 : Ajouter la logique de drop dans `my-tasks.vue` (script)** Dans `