# Mail Integration — Master Plan

> **Master plan** : ce document décrit le découpage en phases. Chaque phase aura son propre plan détaillé (rédigé par un subagent rédacteur) puis sera implémentée par un subagent codeur, en cycle.

**Spec source** : `docs/superpowers/specs/2026-05-19-mail-integration-design.md`

**Goal** : Ajouter à Lesstime un client mail intégré pour une boîte partagée OVH (IMAP/SMTP), avec lecture inbox/dossiers et création/lien tâche depuis un mail.

**Stratégie** : 7 phases séquentielles, dépendances claires, chaque phase = working software testable. Cycle par phase : rédacteur → codeur → review humaine → phase suivante.

---

## Cartographie des phases

```
Phase 1 (Backend foundations)   ──┐
                                  ├─→ Phase 2 (IMAP provider + sync)  ──┐
                                  │                                     ├─→ Phase 3 (API backend)  ──┐
                                  │                                     │                            │
                                  └─→─────────────────────────────────────────────────────────────────┤
                                                                                                     │
Phase 4 (Frontend services + store)  ←──────────────────────────────────────────────────────────────┘
        │
        ├─→ Phase 5 (UI principale 3 colonnes)
        │
        ├─→ Phase 6 (Intégration tâches : modals, onglet TaskDrawer)
        │
        └─→ Phase 7 (Admin config + sidebar + polish)
```

Chaque phase produit du logiciel fonctionnel (testable, mergeable) sans casser les précédentes.

---

## Phase 1 — Backend Foundations

**Plan détaillé attendu** : `docs/superpowers/plans/2026-05-19-mail-phase1-foundations.md`

**Scope** :
- Entité `MailConfiguration` (singleton, fields complets de la spec, `encryptedPassword` via `TokenEncryptor`)
- Entité `MailFolder`
- Entité `MailMessage`
- Entité `TaskMailLink` (avec unique constraint)
- Repositories : `MailConfigurationRepository::findSingleton()`, `MailFolderRepository`, `MailMessageRepository`, `TaskMailLinkRepository`
- Migration Doctrine unique créant les 4 tables (raw SQL)
- DTOs sous `src/Mail/Dto/` : `MailFolderDto`, `MailMessageHeaderDto`, `MailMessageDetailDto`, `MailAttachmentDto`
- Interface `App\Mail\MailProviderInterface` (signatures uniquement, pas d'impl)
- Exception `App\Mail\Exception\MailProviderException`
- Tests unitaires repositories (au moins le pattern singleton)

**Critère d'acceptation** :
- `make migration-migrate` passe sans erreur
- `php bin/console doctrine:schema:validate` OK
- `make test` vert (au moins les tests créés)
- Fixture `MailConfiguration` désactivée (OVH defaults) ajoutée

**Dépendances** : aucune (point d'entrée).

---

## Phase 2 — IMAP Provider + Sync

**Plan détaillé attendu** : `docs/superpowers/plans/2026-05-19-mail-phase2-imap-sync.md`

**Scope** :
- Ajout dépendance Composer `webklex/php-imap` (vérifier compat PHP 8.4)
- Implémentation `App\Mail\ImapMailProvider implements MailProviderInterface`
  - Lecture config via `MailConfigurationRepository::findSingleton()`
  - Déchiffrement password via `TokenEncryptor`
  - `listFolders`, `listMessages`, `fetchMessage`, `markRead`, `markFlagged`, `moveMessage`, `fetchAttachment`
  - Wrapping erreurs en `MailProviderException`
- `App\Service\MailSyncService`
  - `syncAll(): MailSyncReport`
  - `syncFolder(string $folderPath): MailSyncReport`
  - `syncFolderStructure(): void`
  - Algorithme exact de la spec (UID FETCH lastUid+1:*, resync flags N=200 derniers, detect suppressions avec garde 50%)
- DTO `MailSyncReport` (count créés / mis à jour / supprimés / errors)
- Symfony Lock (`mail.sync`, TTL 10 min)
- Commande console `app:mail:sync` (avec option `--folder=...`)
- Documentation cron OS + cible Makefile `make mail-sync`
- Tests : ImapMailProvider mocké via fixture serveur ou interface, MailSyncService avec provider mocké

**Critère d'acceptation** :
- `php bin/console app:mail:sync --dry-run` fonctionne contre une fake config
- Tests `make test` verts
- `make mail-sync` documentée dans Makefile

**Dépendances** : Phase 1.

---

## Phase 3 — API Backend

**Plan détaillé attendu** : `docs/superpowers/plans/2026-05-19-mail-phase3-api.md`

**Scope** :
- API Platform ressources :
  - `GET /api/mail/configuration` (ROLE_ADMIN) — singleton provider
  - `PATCH /api/mail/configuration` (ROLE_ADMIN) — processor (jamais retourner password en clair, accepter nouveau password à chiffrer)
- Custom controllers (priority: 1) :
  - `POST /api/mail/configuration/test` (ROLE_ADMIN) — test connexion
  - `GET /api/mail/folders` (ROLE_USER, refus ROLE_CLIENT explicite) — arbre + unreadCount depuis BDD
  - `GET /api/mail/folders/{path}/messages?page&limit` — pagination cursor `sentAt DESC, id DESC`
  - `GET /api/mail/messages/{id}` — fetch live IMAP + cache Symfony `mail_body_{messageId}` TTL 5 min
  - `POST /api/mail/messages/{id}/read` (body `{ read: bool }`)
  - `POST /api/mail/messages/{id}/flag`
  - `POST /api/mail/messages/{id}/create-task` (body `{ projectId, taskGroupId?, priority? }`)
  - `POST /api/mail/messages/{id}/link-task` (body `{ taskId }`)
  - `DELETE /api/mail/messages/{id}/link-task/{taskId}`
  - `GET /api/tasks/{id}/mails`
  - `GET /api/mail/attachments/{id}` — stream, `Content-Disposition: attachment`, jamais inline
  - `POST /api/mail/sync` — async via Messenger
- Message + Handler Symfony Messenger `MailSyncRequested`
- Sécurité : `#[IsGranted('IS_AUTHENTICATED_FULLY')]` + check `ROLE_USER && !ROLE_CLIENT` explicite
- Tests fonctionnels endpoints (auth, format réponses, ROLE_CLIENT refusé)

**Critère d'acceptation** :
- Tous endpoints répondent corrects status/format
- Tests `make test` verts
- ROLE_CLIENT refusé sur 100% des endpoints mail
- Password jamais leak dans les réponses

**Dépendances** : Phase 1, Phase 2.

---

## Phase 4 — Frontend Services + Store

**Plan détaillé attendu** : `docs/superpowers/plans/2026-05-19-mail-phase4-frontend-services.md`

**Scope** :
- Install npm `dompurify` + types
- `frontend/services/dto/mail.ts` : tous les types TS
- `frontend/services/mail.ts` : méthodes API (suivre pattern `tasks.ts`)
  - `listFolders`, `listMessages`, `getMessage`, `markRead`, `markFlagged`
  - `createTaskFromMail`, `linkTask`, `unlinkTask`, `listMailsForTask`
  - `triggerSync`
  - `getConfiguration`, `updateConfiguration`, `testConfiguration`
  - `downloadAttachment` (retourne Blob)
- Store Pinia `frontend/stores/useMailStore.ts`
  - State : `folders`, `selectedFolderPath`, `messages[]`, `selectedMessageId`, `selectedMessageDetail`, `loading`, `syncing`, `globalUnreadCount`
  - Actions correspondantes
  - Polling `pollUnreadCount()` toutes les 30s (start/stop)
- Sanitization helper `frontend/utils/sanitizeMailHtml.ts` (DOMPurify avec config bloquante : script/iframe/object/embed/on*/javascript:, strip ou placeholder pour `<img src="http(s)://...">` distants)

**Critère d'acceptation** :
- `cd frontend && npx tsc --noEmit` OK
- Test manuel d'un appel `mail.listFolders()` depuis devtools renvoie 401 si pas authentifié, 200 sinon

**Dépendances** : Phase 3 (les endpoints doivent exister).

---

## Phase 5 — UI principale (page /mail)

**Plan détaillé attendu** : `docs/superpowers/plans/2026-05-19-mail-phase5-ui-main.md`

**Scope** :
- Page `frontend/pages/mail.vue` — layout 3 colonnes (dossiers / liste / lecteur), responsive
- Composants `frontend/components/mail/` :
  - `MailFolderTree.vue` — arbre récursif avec badges unread, sélection
  - `MailMessageList.vue` — liste paginée (infinite scroll), indicateurs lu/étoilé/PJ, formatage relatif des dates
  - `MailMessageViewer.vue` — header (de/à/cc/date) + body sanitizé via DOMPurify + liste PJ téléchargeables + actions (Créer tâche / Lier / Marquer lu/non-lu / Étoiler)
  - `MailRefreshButton.vue` — bouton sync manuel, désactivé pendant `syncing`
- i18n clés `mail.*` dans `frontend/i18n/locales/fr.json` (et `en.json` si présent) : titres, vides, actions, erreurs
- Mapping noms dossiers système (`INBOX`, `Sent`, `Drafts`, `Archive`, `Trash`, `Junk`) → labels traduits
- Gestion query param `?messageId=X` pour deep-link vers un mail (selection auto à l'ouverture)
- Refus visuel pour ROLE_CLIENT (le middleware backend bloque déjà, mais ajouter check côté router/middleware Nuxt)

**Critère d'acceptation** :
- Page accessible à `/mail` pour ROLE_USER/ROLE_ADMIN
- ROLE_CLIENT redirigé vers `/portal`
- Pas d'XSS via body mail (test manuel avec un mail contenant `<script>alert(1)</script>`)
- Pixels tracking distants remplacés par placeholder

**Dépendances** : Phase 4.

---

## Phase 6 — Intégration Tâches

**Plan détaillé attendu** : `docs/superpowers/plans/2026-05-19-mail-phase6-task-integration.md`

**Scope** :
- `frontend/components/mail/MailCreateTaskModal.vue` — wrapper du `TaskDrawer` existant pré-rempli :
  - Titre = subject
  - Description = body plain text
  - Picker projet + groupe + priorité
  - À la création : appelle `POST /api/mail/messages/{id}/create-task`, ferme modal, redirige ou affiche succès
- `frontend/components/mail/MailLinkTaskModal.vue` — autocomplete sur tâches existantes (filter par projet, statut non-archivé)
- Onglet **"Mails"** sur `TaskDrawer.vue` :
  - Nouvelle section affichée à côté Documents / Time tracking / etc.
  - Liste `MailMessage` liés à la tâche (via `GET /api/tasks/{id}/mails`)
  - Item cliquable → `router.push('/mail?messageId=' + id)`
  - Bouton "Lier un mail" → ouvre un picker mail (TBD selon ergonomie : modal recherche ou redirige vers /mail)
- Tests manuels : créer tâche depuis mail, lier mail à tâche existante, voir mail depuis onglet tâche

**Critère d'acceptation** :
- Workflow complet : mail → "Créer tâche" → tâche créée et liée → visible dans onglet "Mails" du TaskDrawer
- Workflow : tâche existante → "Lier mail" → mail apparaît dans onglet

**Dépendances** : Phase 5.

---

## Phase 7 — Admin Config + Sidebar + Polish

**Plan détaillé attendu** : `docs/superpowers/plans/2026-05-19-mail-phase7-admin-polish.md`

**Scope** :
- `frontend/components/admin/AdminMailTab.vue` (calqué sur `AdminZimbraTab.vue`) :
  - Form : protocol (imap pour MVP), imapHost/Port/Encryption, smtpHost/Port/Encryption, username, password (write-only, `hasPassword: true` côté GET), sentFolderPath, enabled toggle
  - Bouton "Tester la connexion" → `POST /api/mail/configuration/test`
  - Indicateur OVH defaults pré-remplis (`ssl0.ovh.net:993/465`)
- Ajout onglet `AdminMailTab` dans la page admin (selon pattern existant)
- Lien sidebar dans le layout default :
  - Icône `material-symbols:mail-outline`
  - Label traduit
  - Badge unread (count `useMailStore.globalUnreadCount`)
  - Visible uniquement pour `ROLE_USER && !ROLE_CLIENT`
- Lifecycle polling 30s : start dans `app.vue` ou layout default, stop au logout
- Documentation finale :
  - README ou `docs/` : section "Mail integration" (cron OS, variables config, sécurité)
  - Makefile : `make mail-sync` documentée
- Vérification finale tracking pixels (relire `sanitizeMailHtml.ts` + tester)
- QA passe : workflow end-to-end depuis vraie boîte OVH (si dispo) ou IMAP test (greenmail/dovecot local)

**Critère d'acceptation** :
- Admin peut configurer la boîte, tester, activer
- Sidebar affiche badge unread temps réel (30s polling)
- Doc d'install à jour
- Aucun warning console front, aucun ERROR PHP dans `make logs-dev`

**Dépendances** : Phase 5 (sidebar utilise le store), Phase 3 (admin API).

---

## Conventions communes à toutes les phases

- **TDD** : test rouge → code → test vert → commit
- **Strict types** PHP (`declare(strict_types=1)`) en tête de chaque fichier
- **PHP CS Fixer** : `make php-cs-fixer-allow-risky` avant chaque commit
- **Commits** : format `<type>(mail) : <message>` (espace avant `:`)
- **Branche** : `feat/mail-integration` (créée au début de Phase 1)
- **Pas de jamais logger** : bodies, password, attachments
- **Review humaine entre chaque phase** : le user valide avant lancement phase suivante

---

## Cycle d'exécution

Pour chaque phase N :

1. **Spawn subagent rédacteur** (`feature-dev:code-architect`)
   - Input : ce master plan + spec + scope phase N
   - Output : `docs/superpowers/plans/2026-05-19-mail-phaseN-*.md` au format `writing-plans` (tasks bite-sized, fichiers exacts, code complet, commandes test)

2. **Spawn subagent codeur** (`ruflo-core:coder`)
   - Input : plan détaillé phase N
   - Output : code + tests + commits (TDD strict)

3. **Review humaine** : user valide ou demande corrections

4. **Phase suivante** uniquement si OK
