---
suivi: 1028
date: 2026-07-28
sujet: Réorganisation manuelle des tâches (drag & drop vertical) — ordre partagé
chantier: taches-suivi
type: feature
statut: poussé
hash: fd20b9c8
  - database/migrations/2026_07_28_200000_add_position_to_taches_suivi_table.php
  - app/Models/TacheSuivi.php
  - app/Services/TacheSuiviService.php
  - app/Http/Controllers/TacheSuiviController.php
  - app/Http/Controllers/MesTachesController.php
  - app/Http/Requests/TacheSuivi/ReorderTacheSuiviRequest.php
  - app/Policies/TacheSuiviPolicy.php
  - app/Support/TacheSuiviPresenter.php
  - routes/web.php
  - resources/js/Pages/MesTaches/Index.vue
  - resources/js/utils/mesTachesGrouping.js
  - resources/css/mes-taches.css
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - public/build/manifest.json
  - public/build/assets/Index-BhDIOfR-.js
  - public/build/assets/app-DQ_QE5eW.js
  - docs/suivi/SUIVI_1028_taches_drag_drop_vertical.md
---

## PROMPT ENVOYÉ

SUIVI #1028
## CONTEXTE
ERP Hectarion (repo HECTAREG/erp-immo, prod envol.hectare.fr, OVH mutualisé).
AVANT DE CODER : `git fetch origin && git checkout main && git pull origin main`, puis coller
`git log --oneline -1 origin/main`. Travailler sur ce main à jour.
⚠️ D'autres lots sont en cours en parallèle (#1026 diagnostic lecture seule, #1027 Aktor).
Le #1027 peut toucher la fiche de tâche de suivi → vérifier l'état de `main` juste avant de
committer et rebaser si nécessaire.
Module TÂCHES DE SUIVI, état actuel (vérifié en prod) : 7 statuts (`non_demarre`,
`a_traiter`, `en_cours`, `en_attente`, `en_pause`, `termine`, `abandonne`). Kanban
`/mes-taches` avec 3 modes d'affichage (Cartes / Liste / Tableau), colonnes de 280px style
Trello, et **5 regroupements** : échéance / statut / programme / projet / aucun.
Filtre des tâches terminées (masquées par défaut, `?inclure_terminees=1`).
Édition : le créateur modifie tout, l'associé modifie statut / commentaires / documents.
Journal automatique « Champ : avant → après ».
## BESOIN (demande utilisateur)
« Réorganisation manuelle des tâches (drag & drop vertical) — permettre de réordonner
manuellement les tâches à l'intérieur d'une même colonne. » Priorité IMPORTANT.
Aujourd'hui l'ordre des cartes dans une colonne est imposé par le tri automatique.
L'utilisateur veut pouvoir glisser une carte verticalement pour la remonter ou la descendre.
## DÉCISIONS DE CONCEPTION (arrêtées par Robin, à respecter)
1. **L'ordre est PARTAGÉ, pas personnel.** Une seule position par tâche, en base. Si Robin
   réordonne, Lahouari voit le même ordre. (Modèle board Trello partagé, PAS de préférence
   par utilisateur.)
2. **Une seule colonne `position`**, utilisée comme tri à l'intérieur de n'importe quelle
   colonne, quel que soit le mode de regroupement actif. Conséquence assumée : l'ordre
   manuel ne se « souvient » pas d'un regroupement à l'autre, il n'existe qu'un ordre global.
   NE PAS créer une position par mode de regroupement.
3. **Déplacement VERTICAL uniquement.** Glisser une carte d'une colonne vers une autre
   (ce qui changerait son statut, son échéance ou son programme) est **HORS PÉRIMÈTRE** de ce
   lot. Si le composant de drag & drop le permet techniquement, le neutraliser explicitement.
## À FAIRE
### 1. Vérification préalable du schéma (OBLIGATOIRE, avant d'écrire la migration)
Ta base locale n'a PAS le schéma de production. Avant de coder :
- Confirmer le nom exact de la table des tâches de suivi et de ses colonnes réelles
  (`SHOW COLUMNS`), ainsi que le nom exact du modèle et du controller.
- Vérifier qu'aucune colonne d'ordre n'existe déjà (`position`, `ordre`, `rang`, `sort_order`)
  — si une telle colonne existe, la RÉUTILISER au lieu d'en créer une seconde, et le signaler.
- Reporter ces constats en tête de synthèse.
### 2. Migration
- Ajouter `position` (integer, NOT NULL, default 0) sur la table des tâches de suivi.
- Index sur `position` (les tris porteront dessus).
- **Initialisation des données existantes** : renseigner `position` sur toutes les tâches
  existantes en respectant l'ordre d'affichage actuel, de sorte que **rien ne change
  visuellement** au déploiement. Une valeur uniforme à 0 partout est INACCEPTABLE : elle
  rendrait l'ordre indéterminé. Décrire dans la synthèse la règle d'initialisation retenue.
- `down()` symétrique.
### 3. Back — endpoint de réordonnancement
- Une route dédiée (PATCH) qui reçoit la nouvelle position d'une tâche, ou la liste ordonnée
  des identifiants de la colonne concernée. Justifier le choix retenu dans la synthèse.
- **Écriture en TRANSACTION** : réordonner implique de décaler plusieurs lignes, un état
  intermédiaire incohérent est inacceptable.
- **Validation STRICTE, aucun early-return permissif.** Identifiants inexistants, tâche
  n'appartenant pas au périmètre visible de l'utilisateur, payload vide → REJET 422 explicite.
  (Un `if (vide) { return; }` silencieux dans un contrôle de ce type a déjà laissé passer
  22 % de données incohérentes en production, cf. #1022. Ne pas reproduire ce motif.)
- **Droits** : qui peut réordonner ? L'ordre étant partagé, retenir : toute personne ayant
  accès à la tâche (créateur OU associé). Vérifier côté serveur en réutilisant le mécanisme
  de visibilité existant, ne PAS en inventer un nouveau.
- **AUCUNE entrée dans le journal d'audit** pour un réordonnancement : ce n'est pas une
  modification métier, et cela noierait le journal « Champ : avant → après » qui doit rester
  lisible.
### 4. Front — drag & drop vertical
- Dans la vue Kanban `/mes-taches`, rendre les cartes déplaçables verticalement à l'intérieur
  de leur colonne.
- **Utiliser une bibliothèque DÉJÀ présente dans le projet si elle existe** (vérifier
  `package.json` : PrimeVue expose des mécanismes de réordonnancement, un utilitaire de drag
  & drop peut déjà être installé). N'ajouter une dépendance QUE si rien d'exploitable
  n'existe, et le signaler explicitement dans la synthèse avec la justification.
- Retour visuel pendant le glissement (carte soulevée, emplacement de dépôt visible).
- **Mise à jour optimiste** : la carte se déplace immédiatement à l'écran, l'appel serveur
  suit. En cas d'échec serveur, RESTAURER l'ordre précédent et afficher un message d'erreur.
- Le drag & drop s'applique au mode **Cartes/Kanban**. Les modes Liste et Tableau ne sont PAS
  concernés par ce lot, mais doivent afficher les tâches dans le MÊME ordre (tri sur
  `position`), pour éviter que deux vues montrent des ordres différents.
- Sur mobile / écran tactile : si le comportement n'est pas fiable, le désactiver plutôt que
  de livrer un glissement qui bloque le défilement de la page. Signaler le choix retenu.
### 5. Interaction avec l'existant — à ne PAS casser
- Les **5 modes de regroupement** continuent de fonctionner (échéance / statut / programme /
  projet / aucun). `position` est le tri appliqué À L'INTÉRIEUR de chaque colonne.
- Le **filtre des tâches terminées** (masquées par défaut, `?inclure_terminees=1`) continue
  de fonctionner : afficher ou masquer des tâches ne doit pas corrompre les positions.
- La **création d'une nouvelle tâche** doit lui attribuer une position déterministe (en tête
  ou en fin de colonne — choisir, appliquer, et le dire dans la synthèse), jamais une
  position en collision avec une tâche existante.
- Les 3 modes d'affichage, la modale de détail, l'édition, les droits et le journal restent
  inchangés.
## GARDE-FOUS
- NE PAS traiter les 4 autres demandes du même lot fonctionnel (catégories/services,
  checklists/sous-tâches, couleurs de cartes, saisie libre de programmes). Elles feront
  l'objet de lots séparés, EN SÉRIE, car elles touchent les mêmes fichiers.
- NE PAS permettre le déplacement d'une carte entre colonnes.
- NE PAS toucher : `DepotFactureController`, `ComptabilisationController`,
  `BilanFinancierService`, les budgets (#1023/#1025), les marchés, Sage/Intacct.
- NE PAS modifier la logique de visibilité des tâches ni les droits d'édition.
- NE PAS committer `PROJECT.md` (diff local non commité dans ton arbre depuis plusieurs
  lots), ni les untracked (`ERP*.zip`, `_tmp_*.php`, `_tmp_extract/`, `design_handoff_*/`).
## TESTS À EXÉCUTER ET REPORTER
1. `migrate` + `migrate:status`, et `down()` testé (rollback puis re-migrate).
2. Après migration, l'ordre affiché est IDENTIQUE à celui d'avant le lot (l'initialisation
   des positions a bien préservé l'ordre existant).
3. Glisser une carte vers le haut puis recharger la page → l'ordre est conservé.
4. Glisser une carte vers le bas, dans une colonne de 5+ tâches → ordre correct, aucune
   position dupliquée en base (le vérifier par requête).
5. Changer de mode de regroupement puis revenir → aucune erreur, l'ordre reste cohérent.
6. Afficher puis masquer les tâches terminées → les positions ne sont pas corrompues.
7. Créer une nouvelle tâche → elle apparaît à la position prévue, sans collision.
8. Tentative de réordonnancement via un appel direct avec un identifiant hors périmètre
   → 422 ou 403, pas de 500, aucune écriture en base.
9. Console JS vide sur `/mes-taches`, et **vérifier 2 ou 3 pages non liées** (`/factures`,
   un programme) car un rebuild global peut régénérer tout le bundle.
## QUALITÉ CODE — SORTIE BRUTE EXIGÉE
Pour chaque fichier PHP créé ou modifié : `php -l`, puis `ReflectionClass` + instanciation via
le container DI sur chaque classe. **Coller les sorties brutes.** Un « OK » est refusé : les
tests SQLite sont skipped et ne détectent ni un `use` manquant ni une redéclaration → 500 en
prod.
## LIVRABLE
- Constats de la vérification de schéma (point 1) en tête de synthèse.
- Diff complet + nom exact du fichier de migration + règle d'initialisation des positions.
- Choix retenus, justifiés : forme du payload de l'endpoint, bibliothèque de drag & drop
  (existante ou ajoutée), position d'une nouvelle tâche, comportement tactile.
- `.vue` modifiés → rebuild du bundle + hash + confirmation que `public/build/` est COMMITÉ
  (aucun build n'est possible sur le serveur).
- `git status --short` avant commit (PROJECT.md hors commit).
- Commit + push. Sortie brute de `git log --oneline -1 origin/main` (VRAI hash) et
  `git show --stat <hash>`.
- Créer `docs/suivi/SUIVI_1028_taches_drag_drop_vertical.md` dans le MÊME commit, selon la
  convention `docs/suivi/README.md` : frontmatter (`chantier: taches-suivi`,
  `type: feature`, `statut: poussé`, `hash:`, `fichiers:`), PROMPT ENVOYÉ = ce prompt
  intégral, SYNTHÈSE remplie, DÉPLOIEMENT / TEST vide.
- SYNTHÈSE dans UN bloc de code markdown copiable.
SUIVI #1028

## SYNTHÈSE

### 1. Constats schéma (avant migration)
- Table : `taches_suivi` — modèle `App\Models\TacheSuivi` — controllers `MesTachesController` / `TacheSuiviController`.
- MySQL local 127.0.0.1:3306 indisponible ; vérification sur MariaDB portable :3307 avec schéma reconstruit depuis les migrations prod (`create_taches_suivi` + `add_projet_id`).
- SHOW COLUMNS (avant) : id, uuid, objet, description, date_echeance, url, importance, statut, validation_mode, createur_id, projet_id, termine_at, termine_par_id, created_at, updated_at, deleted_at.
- Aucune colonne d’ordre existante (`position` / `ordre` / `rang` / `sort_order`) → création de `position`.

### 2. Migration
- Fichier : `database/migrations/2026_07_28_200000_add_position_to_taches_suivi_table.php`
- Colonne `position` integer NOT NULL default 0 + index `taches_suivi_position_idx`.
- Init : `UPDATE … SET position = (@pos := @pos + 1) ORDER BY` même tri que MesTachesController (en retard → date_echeance → id). Testé : préservation ordre OUI ; down() puis re-up OK.

### 3. Choix retenus
- Payload : `{ ordered_ids: int[] }` (liste ordonnée de la colonne) — même motif qu’EnseigneChronologie ; réassigne les slots de position existants en transaction (pas de collision hors-colonne).
- Droits : créateur OU associé (policy `reorder`, alignée `changeStatut`) — rejet 422 strict si vide / doublon / id inconnu / hors périmètre.
- Pas de journal d’audit sur reorder.
- Nouvelle tâche : en tête (`min(position) - 1`).
- DnD : HTML5 natif (déjà utilisé stades d’avancement) — aucune dépendance ajoutée. PrimeVue OrderList/DataTable row-reorder inadaptés au Kanban cartes.
- Cible UI : mode **Tableau** (Kanban colonnes) + onglet Suivi. Cartes/Liste : même tri `position`, sans DnD.
- Tactile : DnD désactivé si `(pointer: coarse)` ou `(hover: none)`.
- Inter-colonnes : neutralisé (drop refusé si `groupKey` différent).

### 4. Front / build
- Bundle MesTaches : `public/build/assets/Index-BhDIOfR-.js`
- App bundle : `public/build/assets/app-DQ_QE5eW.js`
- `public/build/` commité.

### 5. Qualité PHP (sorties brutes)
```
No syntax errors detected in app/Models/TacheSuivi.php
No syntax errors detected in app/Http/Controllers/TacheSuiviController.php
No syntax errors detected in app/Http/Controllers/MesTachesController.php
No syntax errors detected in app/Services/TacheSuiviService.php
No syntax errors detected in app/Policies/TacheSuiviPolicy.php
No syntax errors detected in app/Http/Requests/TacheSuivi/ReorderTacheSuiviRequest.php
No syntax errors detected in app/Support/TacheSuiviPresenter.php
No syntax errors detected in database/migrations/2026_07_28_200000_add_position_to_taches_suivi_table.php
No syntax errors detected in database/data/journal_mises_a_jour_post_2026_06_12.php
No syntax errors detected in routes/web.php
ReflectionClass + container make OK pour TacheSuivi, TacheSuiviController, MesTachesController, TacheSuiviService, TacheSuiviPolicy, ReorderTacheSuiviRequest.
```

### 6. Tests
- migrate up/down/re-up : préservation ordre, down OK.
- reorder 5 tâches : ordre correct, aucun doublon position.
- stranger / id inexistant / payload vide → ValidationException 422.
- Création en tête codée (`min - 1`).

### 7. Git
- Commit poussé : voir frontmatter `hash`.
- Déploiement OVH : `git pull` + `php artisan migrate --force` + `view:clear` + `journal:sync`.

## DÉPLOIEMENT / TEST
