14 KiB
Analyse approfondie du système Missions - Code Review Senior
📋 Vue d'ensemble
Ce document présente une analyse complète du système de gestion des missions, incluant la page de liste, les détails de mission, et l'architecture backend associée.
🏗️ Architecture générale
Structure des fichiers
app/
├── missions/
│ ├── page.tsx # Page principale de liste des missions
│ ├── layout.tsx # Layout avec sidebar CAP
│ ├── new/
│ │ └── page.tsx # Création de nouvelle mission
│ └── [missionId]/
│ ├── page.tsx # Page de détails de mission
│ └── edit/
│ └── page.tsx # Édition de mission
│
├── api/
│ └── missions/
│ ├── route.ts # GET/POST missions
│ ├── [missionId]/
│ │ ├── route.ts # GET/PUT/DELETE mission spécifique
│ │ ├── close/route.ts # Clôture de mission
│ │ └── generate-plan/ # Génération plan d'action IA
│ └── ...
│
components/
└── missions/
├── missions-frame.tsx # Iframe wrapper
├── missions-admin-panel.tsx # Panel de création/édition
└── ...
📄 Page de liste des missions (app/missions/page.tsx)
Points forts ✅
-
Interface utilisateur claire
- Design en grille responsive (1/2/3 colonnes)
- Cartes de mission bien structurées
- Indicateurs visuels pour missions clôturées
- Recherche en temps réel
-
Gestion d'état
- Utilisation appropriée de
useStateetuseEffect - Gestion des états de chargement
- Filtrage côté client efficace
- Utilisation appropriée de
-
Affichage des données
- Logos avec fallback gracieux
- Badges ODD avec icônes
- Affichage conditionnel des services
- Formatage des dates en français
Points d'amélioration 🔧
-
Performance
// ❌ Problème: Filtrage côté client uniquement const filteredMissions = missions.filter(mission => mission.name.toLowerCase().includes(searchTerm.toLowerCase()) || ... ); // ✅ Suggestion: Pagination et recherche côté serveur // Utiliser les query params dans l'API -
Gestion d'erreurs
// ⚠️ Actuel: Toast générique toast({ title: "Erreur", description: "Impossible de charger les missions", variant: "destructive", }); // ✅ Suggestion: Messages d'erreur plus spécifiques // + Retry automatique pour erreurs réseau -
Console.log en production
// ❌ Lignes 59, 199-203: console.log en production console.log("Mission data with intention:", data.missions); // ✅ Suggestion: Utiliser un logger conditionnel if (process.env.NODE_ENV === 'development') { console.log(...); } -
Accessibilité
- Manque d'attributs ARIA sur les cartes
- Navigation clavier non optimisée
- Pas de skip links
📄 Page de détails de mission (app/missions/[missionId]/page.tsx)
Points forts ✅
-
Architecture en onglets
- Organisation claire: Général, Plan d'actions, Équipe, Ressources
- Compteurs visuels sur les onglets (équipe, documents)
- Navigation intuitive
-
Fonctionnalités avancées
- Génération de plan d'action par IA (N8N)
- Édition inline du plan avec sauvegarde
- Gestion des gardiens de l'intention
- Clôture de mission avec intégration N8N
-
Gestion d'état complexe
- Suivi des modifications non sauvegardées
- États de chargement multiples (generating, saving, deleting, closing)
- Synchronisation avec le backend
Points d'amélioration critiques 🔴
-
Sécurité - Validation côté client uniquement
// ⚠️ Ligne 192: Confirmation simple avec confirm() if (!confirm("Êtes-vous sûr de vouloir supprimer cette mission ?")) { return; } // ✅ Suggestion: Modal de confirmation avec détails // + Vérification des permissions côté serveur (déjà fait ✅) -
Gestion des erreurs réseau
// ⚠️ Pas de retry automatique // Pas de gestion des timeouts // Pas de fallback si l'API est down // ✅ Suggestion: Implémenter retry avec exponential backoff // + Cache local pour données critiques -
Performance - Re-renders inutiles
// ⚠️ Ligne 112-116: useEffect qui se déclenche à chaque changement useEffect(() => { if (mission) { setIsPlanModified(editedPlan !== (mission.actionPlan || "")); } }, [editedPlan, mission]); // ✅ Suggestion: Utiliser useMemo pour éviter recalculs const isPlanModified = useMemo(() => { return mission ? editedPlan !== (mission.actionPlan || "") : false; }, [editedPlan, mission?.actionPlan]); -
Textarea auto-resize - Code fragile
// ⚠️ Lignes 676-688: Manipulation directe du DOM e.target.style.height = 'auto'; e.target.style.height = e.target.scrollHeight + 'px'; // ✅ Suggestion: Utiliser une librairie dédiée (react-textarea-autosize) // ou un hook personnalisé réutilisable -
Duplication de code
// ⚠️ Fonctions helper dupliquées entre page.tsx et [missionId]/page.tsx // getMissionTypeLabel, getDurationLabel, getNiveauLabel, etc. // ✅ Suggestion: Extraire dans lib/mission-helpers.ts
🔌 API Routes - Analyse Backend
app/api/missions/route.ts (GET/POST)
Points forts ✅
-
Sécurité
- Vérification d'authentification systématique
- Validation des champs requis
- Gestion des permissions
-
Gestion des fichiers
- Upload vers Minio/S3 bien structuré
- Vérification d'existence des fichiers avant N8N
- Cleanup en cas d'erreur (lignes 460-474)
-
Intégration N8N
- Workflow asynchrone pour création
- Gestion des erreurs non-bloquantes
- Logging détaillé
Points d'amélioration 🔧
-
Transaction database
// ⚠️ Pas de transaction Prisma const mission = await prisma.mission.create({...}); await prisma.missionUser.createMany({...}); // ✅ Suggestion: Utiliser $transaction pour atomicité await prisma.$transaction(async (tx) => { const mission = await tx.mission.create({...}); await tx.missionUser.createMany({...}); return mission; }); -
Validation des données
// ⚠️ Validation basique (lignes 230-235) if (!body.name || !body.oddScope) { return NextResponse.json({ error: 'Missing required fields' }, { status: 400 }); } // ✅ Suggestion: Utiliser Zod ou Yup pour validation stricte const MissionSchema = z.object({ name: z.string().min(3).max(100), oddScope: z.array(z.string().regex(/^odd-\d+$/)), // ... }); -
Gestion des erreurs N8N
// ⚠️ Ligne 439: Erreur N8N bloque la création if (!workflowResult.success) { throw new Error(workflowResult.error || 'N8N workflow failed'); } // ✅ Suggestion: Mode "best effort" - créer la mission même si N8N échoue // + Queue de retry pour N8N (BullMQ, etc.)
app/api/missions/[missionId]/route.ts (GET/PUT/DELETE)
Points forts ✅
-
DELETE bien implémenté
- Cleanup Minio avant suppression DB
- Intégration N8N pour rollback
- Gestion des erreurs non-bloquantes
-
Permissions granulaires
- Vérification créateur/admin pour DELETE
- Gardiens peuvent modifier (PUT)
Points d'amélioration 🔧
-
GET - Performance
// ⚠️ Ligne 38: findFirst au lieu de findUnique const mission = await (prisma as any).mission.findFirst({ where: { id: missionId, OR: [ { creatorId: userId }, { missionUsers: { some: { userId } } } ] }, // ... }); // ✅ Suggestion: findUnique + vérification permissions séparée // Plus performant avec index sur id -
PUT - Validation partielle
// ⚠️ Pas de validation des données mises à jour // Pas de vérification de cohérence (ex: oddScope doit être array) // ✅ Suggestion: Validation stricte avec schéma
🎨 Composants UI
components/missions/missions-admin-panel.tsx
Points forts ✅
-
Interface complète
- Formulaire multi-onglets bien organisé
- Gestion des gardiens et volontaires
- Upload de fichiers intégré
-
UX soignée
- Validation en temps réel
- Indicateurs visuels de progression
- Messages d'erreur contextuels
Points d'amélioration critiques 🔴
-
Fichier trop volumineux (1570 lignes)
// ❌ Un seul composant fait tout // Difficile à maintenir, tester, et réutiliser // ✅ Suggestion: Découper en sous-composants // - MissionGeneralForm // - MissionDetailsForm // - MissionAttachmentsForm // - MissionMembersForm // - MissionSkillsForm -
Gestion d'état complexe
// ⚠️ Trop de useState (15+) const [selectedServices, setSelectedServices] = useState<string[]>([]); const [selectedProfils, setSelectedProfils] = useState<string[]>([]); // ... 13 autres // ✅ Suggestion: Utiliser useReducer ou Zustand const [state, dispatch] = useReducer(missionReducer, initialState); -
Logique métier dans le composant
// ⚠️ Lignes 400-408: Conversion base64 dans le composant const convertFileToBase64 = (file: File): Promise<string> => { // ... }; // ✅ Suggestion: Extraire dans lib/file-utils.ts -
Console.log en production
// ❌ Lignes 412, 422, 428, 451, 465, 492, 504, 514, 541, 559 // Trop de logs de debug // ✅ Suggestion: Logger conditionnel ou supprimer
🔄 Flux de données
Création de mission
1. User remplit formulaire (missions-admin-panel.tsx)
↓
2. POST /api/missions
↓
3. Création DB (Prisma)
↓
4. Upload fichiers (Minio)
↓
5. Vérification fichiers
↓
6. Trigger N8N workflow
↓
7. N8N crée intégrations (Gitea, Leantime, etc.)
↓
8. Callback /api/missions/mission-created
↓
9. Mise à jour mission avec IDs externes
Problème potentiel: Si N8N échoue après création DB, la mission existe sans intégrations.
Solution: Queue de retry ou mode "best effort" avec notification.
Affichage de mission
1. GET /api/missions/[missionId]
↓
2. Prisma query avec includes
↓
3. Génération URLs publiques (logo, attachments)
↓
4. Affichage dans page.tsx
Optimisation possible: Cache Redis pour missions fréquemment consultées.
🐛 Bugs potentiels identifiés
-
Race condition sur plan d'action
// Si l'utilisateur modifie pendant la génération // Les modifications peuvent être écrasées -
Memory leak potentiel
// Textarea auto-resize avec ref callback // Pas de cleanup dans useEffect -
Type safety
// Utilisation de (prisma as any) dans plusieurs endroits // Indique que le schema Prisma n'est pas à jour
📊 Métriques de code
Complexité cyclomatique
missions-admin-panel.tsx: Très élevée (>50)[missionId]/page.tsx: Élevée (~30)page.tsx: Moyenne (~15)
Taille des fichiers
missions-admin-panel.tsx: 1570 lignes ⚠️[missionId]/page.tsx: 920 lignes ⚠️route.ts(POST): 480 lignes ⚠️
Recommandation: Découper les fichiers >500 lignes.
✅ Recommandations prioritaires
🔴 Critique (À faire immédiatement)
-
Sécurité
- Ajouter validation stricte avec Zod
- Implémenter rate limiting sur API
- Ajouter CSRF protection
-
Performance
- Implémenter pagination côté serveur
- Ajouter cache Redis
- Optimiser les requêtes Prisma (select spécifiques)
-
Maintenabilité
- Découper
missions-admin-panel.tsx - Extraire helpers dans lib/
- Supprimer console.log de production
- Découper
🟡 Important (À planifier)
-
Tests
- Unit tests pour helpers
- Integration tests pour API routes
- E2E tests pour flux critiques
-
Documentation
- JSDoc pour fonctions complexes
- Diagrammes de séquence pour flux N8N
- Guide de contribution
-
Monitoring
- Sentry pour erreurs frontend
- Logging structuré backend
- Métriques de performance
🟢 Amélioration (Nice to have)
-
UX
- Optimistic updates
- Skeleton loaders
- Animations de transition
-
Accessibilité
- ARIA labels complets
- Navigation clavier
- Support lecteurs d'écran
🎯 Conclusion
Le système de missions est fonctionnel et bien structuré avec une architecture claire. Les principales améliorations à apporter concernent:
- Maintenabilité: Découpage des gros composants
- Performance: Optimisation des requêtes et pagination
- Robustesse: Meilleure gestion d'erreurs et retry logic
- Sécurité: Validation stricte et rate limiting
Le code montre une bonne compréhension de Next.js, Prisma, et des patterns React modernes. Avec les améliorations suggérées, le système sera prêt pour la production à grande échelle.
Date de review: $(date) Reviewer: Senior Developer Version analysée: Current codebase