diff --git a/docs/superpowers/specs/2026-05-20-workflow-ui-fixes-design.md b/docs/superpowers/specs/2026-05-20-workflow-ui-fixes-design.md new file mode 100644 index 0000000..41b0874 --- /dev/null +++ b/docs/superpowers/specs/2026-05-20-workflow-ui-fixes-design.md @@ -0,0 +1,182 @@ +# Specs — Correctifs UI suite au système de workflow + UX mail/modales + +> Date : 2026-05-20 +> Contexte : suite à l'introduction des **workflows** (`docs/superpowers/specs/2026-05-19-project-workflows-design.md`), +> plusieurs régressions UI et points UX sont apparus. Reviews faites par Lucile Schnödt et Tristan Schnödtin. +> Ce document liste les 7 chantiers à traiter, avec problème, fichiers concernés, solution validée et points ouverts. + +## Vue d'ensemble + +| # | Chantier | Type | Décision | +|---|----------|------|----------| +| 1 | Drag & drop cassé dans « mes tâches » | régression workflow | Drop = changer de statut (gérer ambiguïté multi-statuts) | +| 2 | Sélecteur de statut mélange les workflows | régression workflow | Filtrer par le workflow du projet de la tâche | +| 3 | Cartes de tâche non responsive (tags) | UI | Refonte responsive + troncature | +| 4 | Couleurs de statut du workflow de base | data/UI | Remettre la palette classique | +| 5 | Bouton « lier un mail » dans l'onglet mail d'un ticket | UX mail | Supprimer le bouton | +| 6 | Création de ticket depuis un mail | UX mail | + sélecteur user, + sélecteur statut (remplace priorité), modale agrandie | +| 7 | Footer collant des modales centrées | UX global | Composant modale réutilisable (header / body scrollable / footer sticky) | + +--- + +## 1. Drag & drop cassé dans « mes tâches » + +**Problème.** Le drag & drop des cartes entre colonnes ne fonctionne plus depuis l'arrivée des workflows. + +**Fichiers.** +- `frontend/pages/my-tasks.vue` — colonnes kanban construites sur les **catégories canoniques** fixes (`CATEGORIES = ['todo','in_progress','blocked','review','done']`, ~l.118-127), template kanban ~l.396-424. +- `frontend/components/task/TaskCard.vue` — `@dragstart` / `@dragend` HTML5 natif (~l.154-162), `dataTransfer` = `task.id`. +- Pas de librairie externe (HTML5 natif). + +**Cause racine.** Les colonnes sont des **catégories canoniques** (la vue agrège plusieurs projets/workflows, donc on ne peut pas afficher une colonne par statut d'un workflow précis). Or un workflow peut désormais mapper **plusieurs statuts sur une même catégorie** (ex. deux statuts « in_progress »). Au drop dans une colonne, le statut cible devient ambigu — ce qui a cassé / rendu indéterminé le changement de statut. + +**Solution retenue.** Drop dans une colonne ⇒ change le statut de la tâche vers un statut de cette catégorie **dans le workflow du projet de la tâche**. Gestion de l'ambiguïté : +- Récupérer les statuts du workflow du projet de la tâche déposée, filtrés par la catégorie de la colonne cible. +- **0 statut** dans cette catégorie pour ce workflow → drop refusé (feedback visuel, pas de changement). +- **1 statut** → appliquer directement. +- **≥ 2 statuts** → afficher un mini-sélecteur (popover/menu) au point de drop pour choisir le statut exact. + +**Points ouverts.** +- ⚠️ **À trancher demain** : confirmer la stratégie de désambiguïsation (popover au drop vs. choisir le statut de plus petite `position` dans la catégorie). Le popover est plus sûr mais plus de travail ; le « plus petite position » est transparent mais peut surprendre. +- Ajouter les **handlers de drop** sur les colonnes (`@dragover.prevent`, `@drop`) — actuellement absents dans `my-tasks.vue`. +- Comme la vue est multi-projets, la résolution du statut cible doit se faire **par tâche** (selon son projet → son workflow), pas globalement. + +--- + +## 2. Sélecteur de statut mélange les statuts de tous les workflows + +**Problème.** En ouvrant une tâche (modale), le sélecteur « Statut » liste **tous les statuts globaux**, donc ceux du workflow de base **et** ceux des autres workflows. + +**Fichiers.** +- `frontend/components/task/TaskModal.vue` — `` (~l.103-109) ; `statusOptions = props.statuses.map(...)` (~l.674-676). +- `frontend/pages/my-tasks.vue` — `:statuses="statuses"` (~l.489), chargés via `statusService.getAll()` (~l.132). +- `frontend/services/task-statuses.ts` — `getAll()` → `GET /task_statuses` (tous les statuts). +- DTO : `frontend/services/dto/task-status.ts` — le champ `workflow` existe déjà (`{ '@id', id } | string`). + +**Solution retenue.** Le sélecteur ne doit proposer que les statuts du **workflow du projet de la tâche éditée**. +- Filtrer `statusOptions` sur `status.workflow` correspondant au workflow du projet de la tâche. +- Source du filtre : soit filtrer côté front la liste déjà chargée par `workflow.id`, soit charger les statuts du workflow via le service workflow (`useWorkflowService()` existe mais n'est pas utilisé ici). +- S'assurer que le statut courant de la tâche reste affiché même si édge case (ex. statut hérité d'un ancien workflow). + +**Points ouverts.** +- Vérifier que la tâche/le projet expose bien l'`@id`/`id` du workflow côté payload pour faire le filtre sans appel supplémentaire. + +--- + +## 3. Cartes de tâche non responsive (tags mal placés / trop gros) + +**Problème.** Depuis l'ajout d'éléments dans la carte (statut, priorité, effort, tags, deadline, avatars…), les tags débordent ou se chevauchent quand les textes sont longs ; la carte n'est plus responsive. + +**Fichiers.** +- `frontend/components/task/TaskCard.vue` — badges statut (~l.43-49) et tags (~l.58-64) : `rounded-full px-2 py-0.5 text-xs font-semibold text-white`, **sans `truncate`, sans `max-w`, sans `line-clamp`** ; conteneur `flex` sans contrainte de largeur (~l.42-106). + +**Solution retenue.** Refonte du layout de la carte pour rester contenue quelle que soit la longueur des textes : +- Conteneur des badges en `flex flex-wrap gap-1` (retour à la ligne propre). +- Tags : `max-w-[...] truncate` (ou `line-clamp-1`) + `title`/tooltip pour le texte complet au survol. +- Hiérarchiser l'info : titre prioritaire (`line-clamp-2`), badges secondaires qui passent à la ligne ou se condensent. +- Option : limiter le nombre de tags affichés (ex. 2-3 + « +N »). + +**Points ouverts.** +- ⚠️ Choix d'UX à valider : `flex-wrap` (tous les tags visibles, carte plus haute) vs. troncature « +N » (hauteur fixe). Décision visuelle à prendre demain (éventuellement via mockup). + +--- + +## 4. Couleurs de statut du workflow de base à remettre + +**Problème.** Les couleurs « classiques » des statuts du workflow de base ont changé ; il faut remettre les couleurs d'origine. + +**Investigation faite.** Le commit `4775cbf` (palette élargie 9→18 teintes + couleur perso) ne touche **que** `ColorPicker.vue` et `ProjectDrawer.vue` — il n'a pas modifié les couleurs des statuts. Les couleurs d'origine du **workflow Standard** sont dans les fixtures : + +| Catégorie | Statut | Hex classique | +|---|---|---| +| todo | À faire | `#222783` | +| in_progress | En cours | `#4A90D9` | +| blocked | Bloqué | `#C62828` | +| review | En attente de validation | `#FF8F00` | +| done | Terminé | `#26A69A` | + +Source : `src/DataFixtures/AppFixtures.php:101-105` (statuts rattachés au `$standardWorkflow`, ~l.93-116). +Migration de rattachement : `migrations/Version20260519175114.php` (attache les statuts existants au workflow Standard). + +**Solution retenue.** Remettre ces 5 couleurs sur les statuts du **workflow Standard/de base**. +- Vérifier en prod (et en base de dev si dérive) que les statuts du workflow Standard portent bien ces hex ; corriger ceux qui ont dérivé (via l'UI d'admin des statuts, ou un script/migration de correction des couleurs). +- Ne **pas** toucher aux couleurs des statuts des autres workflows. + +**Points ouverts.** +- Confirmer où la dérive a eu lieu (prod vs. nouveaux workflows créés via l'UI avec d'autres couleurs). Si c'est un workflow Standard en prod avec des couleurs erronées → correction data ; si c'est par défaut à la création d'un workflow → ajuster les couleurs proposées par défaut. + +--- + +## 5. Supprimer le bouton « lier un mail » dans l'onglet mail d'un ticket + +**Problème.** En ouvrant un ticket, l'onglet mail propose un bouton « lier un mail » qui n'a pas d'utilité de ce côté. + +**Fichiers.** +- `frontend/components/task/TaskModal.vue` — bouton « lier un mail » (~l.486-494, `mail.taskTab.linkButton`, ouvre `showMailPickerModal`) ; `` (~l.498-503), visible si `isEditing && isMailUser`, onglet `mails`. +- Composant lié : `frontend/components/mail/MailPickerModal.vue`. + +**Solution retenue.** Supprimer le bouton « lier un mail » de l'onglet mail du ticket. +- Retirer le bouton et, si plus aucun usage, le `MailPickerModal` associé + l'état `showMailPickerModal` + `handleMailLinked`. +- Nettoyer la clé i18n `mail.taskTab.linkButton` si elle n'est plus utilisée ailleurs. + +**Points ouverts.** +- Vérifier que `MailPickerModal` n'est pas réutilisé ailleurs avant de le supprimer (sinon ne retirer que le bouton). + +--- + +## 6. Création d'un ticket depuis un mail + +**Problème.** Le formulaire de création depuis un mail est incomplet et la modale dépasse de l'écran. + +**Fichiers.** +- `frontend/components/mail/MailCreateTaskModal.vue` — champs actuels (~l.119-224) : info source (lecture seule), Projet (requis), Groupe (optionnel), **Priorité** (optionnelle). Footer non sticky (~l.210-224). +- `frontend/services/mail.ts` — `createTaskFromMail()` (~l.184-192) → `POST /mail/messages/{id}/create-task` avec `{ projectId, taskGroupId, priority }`. + +**Solution retenue.** +1. **Sélecteur de user (assigné).** Ajouter un sélecteur d'utilisateur. Un user par défaut est déjà appliqué ; le sélecteur permet d'en choisir un autre. (Charger la liste via le service users.) +2. **Statut à la place de la priorité.** **Retirer** le sélecteur de priorité, le **remplacer** par un sélecteur de statut. Le statut proposé doit respecter le workflow du projet sélectionné (cf. chantier #2 — réutiliser la même logique de filtrage par workflow). Au changement de projet, recharger les statuts du workflow correspondant. +3. **Agrandir la modale.** Élargir la largeur (et gérer la hauteur via body scrollable + footer sticky, cf. chantier #7) pour qu'elle ne dépasse plus. +4. **Backend.** Adapter le payload / l'endpoint `create-task` : accepter `assigneeId` (ou IRI user) et `statusId` (ou IRI statut) ; retirer/garder `priority` selon décision (ici : remplacé par statut côté UI — décider si on garde la priorité par défaut côté backend ou non). + +**Points ouverts.** +- Confirmer le statut **par défaut** présélectionné (statut initial du workflow du projet). +- Décider si la priorité disparaît totalement du payload ou reste à une valeur par défaut côté backend. + +--- + +## 7. Footer collant pour les modales centrées + +**Problème.** Les modales qui s'ouvrent au milieu de l'écran ont leurs boutons d'action en bas, qui défilent avec le contenu et finissent hors écran. On veut un footer **toujours visible**. + +**Constat.** Il n'existe **aucun composant modale réutilisable** (`MalioModal`/`AppModal`). Chaque modale réimplémente `Teleport` + `Transition` + header/body/footer. Footers actuels en `border-t` mais **non sticky** : +- `frontend/components/task/TaskModal.vue` (footer ~l.507-549) +- `frontend/components/mail/MailCreateTaskModal.vue` (~l.210-224) +- `frontend/components/mail/MailLinkTaskModal.vue` (~l.226-239) +- `frontend/components/mail/MailPickerModal.vue` (~l.187-201) +- `frontend/components/ui/ConfirmDeleteTaskModal.vue` (~l.11-24) +- `frontend/components/project/ProjectWorkflowSwitchModal.vue` (~l.63-76) + +**Solution retenue (décision : composant réutilisable).** Créer un composant modale réutilisable dans `frontend/components/ui/` (ex. `AppModal.vue` / `MalioModal.vue` local) : +- Structure : `Teleport to="body"` + `Transition`, overlay `fixed inset-0`, conteneur avec **hauteur max** (`max-h-[90vh]`), en flex-col : + - **header** (titre + fermeture) fixe en haut, + - **body** `flex-1 overflow-y-auto`, + - **footer** sticky en bas (`shrink-0 border-t`), via slot `#footer`. +- Props : largeur (`sm`/`md`/`lg`/`xl`), titre, `v-model` ouverture ; slots `default` (body) et `footer`. +- **Migration progressive** des modales existantes vers ce composant (commencer par celles citées par les reviews : TaskModal, MailCreateTaskModal). Ne pas tout migrer d'un coup. + +**Points ouverts.** +- Nom et emplacement définitifs du composant. +- Ordre de migration (au minimum : MailCreateTaskModal #6 et TaskModal #2/#5, qui sont déjà touchées par les autres chantiers). + +--- + +## Découpage suggéré pour l'implémentation + +Regrouper par zone pour limiter les conflits : + +- **Lot A — Workflow / statuts** : #2 (filtrage statut) → réutilisé par #1 (D&D) et #6 (statut à la création mail). +- **Lot B — Cartes** : #3 (responsive) + #4 (couleurs classiques). +- **Lot C — Mail** : #5 (suppr. bouton) + #6 (form création) — dépend de #2 et #7. +- **Lot D — Modale réutilisable** : #7 (composant) puis migration de TaskModal et MailCreateTaskModal. + +Ordre conseillé : **#7 (composant modale)** et **#2 (filtrage statut)** d'abord (briques réutilisées), puis #1, #6, #5, puis #3/#4.