# Analyse Complète : Pages Missions et Centrale - Workflow Complet ## 📋 Table des Matières 1. [Vue d'ensemble](#vue-densemble) 2. [Architecture des Pages](#architecture-des-pages) 3. [Workflow de Navigation](#workflow-de-navigation) 4. [Workflow de Création de Mission](#workflow-de-création-de-mission) 5. [Workflow de Consultation](#workflow-de-consultation) 6. [API Routes](#api-routes) 7. [Base de Données](#base-de-données) 8. [Intégrations Externes](#intégrations-externes) 9. [Stockage de Fichiers](#stockage-de-fichiers) 10. [Composants Réutilisables](#composants-réutilisables) --- ## 🎯 Vue d'ensemble ### Page "Centrale" - **Route**: `/missions` - **Nom dans le menu**: "Centrale" - **Accès**: Rôles `entrepreneurship` ou `admin` (défini dans `components/main-nav.tsx`) - **Description**: Centre d'Administration et de Pilotage (CAP) - Interface principale pour gérer les missions ### Page "Missions" - **Route principale**: `/missions` - **Sous-routes**: - `/missions` - Liste des missions de l'utilisateur - `/missions/new` - Création d'une nouvelle mission - `/missions/[missionId]` - Détails d'une mission - `/missions/[missionId]/edit` - Édition d'une mission ### Page "Mission Tab" (Tableau des Missions) - **Route**: `/mission-tab` - **Description**: Vue publique de toutes les missions disponibles - **Sous-routes**: - `/mission-tab` - Liste de toutes les missions - `/mission-tab/[missionId]` - Détails d'une mission (vue publique) --- ## 🏗️ Architecture des Pages ### 1. Layout Principal - Missions (`app/missions/layout.tsx`) **Structure**: ``` ┌─────────────────────────────────────────┐ │ Sidebar (CAP) - Fond rose clair │ │ ┌───────────────────────────────────┐ │ │ │ CAP │ │ │ │ Centre d'Administration et de │ │ │ │ Pilotage │ │ │ └───────────────────────────────────┘ │ │ • Mes Missions (/missions) │ │ • Nouvelle Mission (/missions/new) │ └─────────────────────────────────────────┘ │ Contenu Principal (children) │ └─────────────────────────────────────────┘ ``` **Fonctionnalités**: - Sidebar fixe avec navigation - Fond rose clair (`bg-pink-50`) pour la sidebar - Fond blanc pour le contenu principal - Navigation active highlightée ### 2. Page Liste des Missions (`app/missions/page.tsx`) **Fonctionnalités**: - Affichage en grille (responsive: 1/2/3 colonnes) - Recherche par nom, niveau, type, ODD scope - Filtrage en temps réel - Cartes de mission avec: - Logo (ou initiales si pas de logo) - Nom de la mission - Badge niveau (A/B/C/S) avec couleurs - Icône ODD (Objectifs de Développement Durable) - Services associés - Description (intention) tronquée - Date de création - Bouton "Voir détails" **API utilisée**: `GET /api/missions` - Retourne uniquement les missions où l'utilisateur est: - Créateur (`creatorId`) - Ou membre (`missionUsers`) ### 3. Page Création de Mission (`app/missions/new/page.tsx`) **Composant principal**: `MissionsAdminPanel` - Formulaire multi-onglets (5 onglets) - Navigation séquentielle avec boutons Précédent/Suivant ### 4. Page Détails Mission (`app/missions/[missionId]/page.tsx`) **Fonctionnalités**: - Affichage complet des informations - Logo de la mission - Grille d'informations (Type, Donneur d'ordre, Durée, Niveau, Participation, ODD) - Description complète - Liste des documents/attachments - Profils recherchés - Services - Bouton de suppression (si créateur ou admin) **API utilisée**: `GET /api/missions/[missionId]` ### 5. Page Mission Tab (`app/mission-tab/page.tsx`) **Différences avec `/missions`**: - Affiche **TOUTES** les missions (pas de filtre utilisateur) - API utilisée: `GET /api/missions/all` - Vue publique pour découvrir toutes les missions disponibles --- ## 🔄 Workflow de Navigation ### Accès à la Centrale ``` 1. Utilisateur connecté avec rôle "entrepreneurship" ou "admin" ↓ 2. Menu déroulant utilisateur (MainNav) ↓ 3. Clic sur "Centrale" (href: '/missions') ↓ 4. Redirection vers /missions ↓ 5. Layout Missions s'affiche avec sidebar CAP ↓ 6. Page Liste des Missions (/missions/page.tsx) ``` ### Navigation dans la Centrale ``` ┌─────────────────────────────────────────┐ │ Sidebar CAP │ │ ├─ Mes Missions (/missions) │ │ └─ Nouvelle Mission (/missions/new) │ └─────────────────────────────────────────┘ │ │ │ │ ▼ ▼ ┌─────────────────┐ ┌──────────────────┐ │ Liste Missions │ │ Création Mission │ │ │ │ │ │ [Carte Mission] │ │ [Formulaire] │ │ └─► Détails │ │ │ └─────────────────┘ └──────────────────┘ │ ▼ ┌─────────────────┐ │ Détails Mission │ │ │ │ [Éditer] │ │ [Supprimer] │ └─────────────────┘ ``` --- ## 🚀 Workflow de Création de Mission ### Étape 1: Accès au Formulaire ``` User → /missions/new → MissionsAdminPanel ``` ### Étape 2: Formulaire Multi-Onglets **Onglet 1: General** - Nom de la mission (requis) - Logo (upload) - ODD scope (requis) - Sélection parmi 17 ODD - Niveau (requis) - A/B/C/S - Intention (requis) - Description avec éditeur de texte **Onglet 2: Details** - Type de mission (requis) - Remote/Onsite/Hybrid - Donneur d'ordre (requis) - Individu/ONG/Start-ups - Projection (requis) - Short/Medium/Long term - Services - Checkboxes (Gite, ArtLab, Calcul) - Participation (requis) - Volontaire/Cooptation - Profils - Checkboxes (DataIntelligence, Expression, Mediation, Investigation, Coding, Lean) **Onglet 3: Attachments** - Upload de fichiers (PDF, DOC, DOCX, XLS, XLSX, JPG, JPEG, PNG) - Liste des fichiers sélectionnés - Upload immédiat vers Minio (bucket 'missions') **Onglet 4: Skills** - Liste de compétences (non fonctionnel actuellement - placeholders) **Onglet 5: Membres** - **Les Gardiens de l'Intention** (3 gardiens requis): - Gardien du Temps - Gardien de la Parole - Gardien de la Mémoire - **Volontaires** (optionnel) - Recherche d'utilisateurs ou groupes - Assignation de rôles ### Étape 3: Validation et Soumission **Validation**: ```typescript const requiredFields = { name: !!missionData.name, oddScope: Array.isArray(missionData.oddScope) && missionData.oddScope.length > 0, niveau: !!missionData.niveau, intention: !!missionData.intention, missionType: !!missionData.missionType, donneurDOrdre: !!missionData.donneurDOrdre, projection: !!missionData.projection, participation: !!missionData.participation, gardiens: gardienDuTemps !== null && gardienDeLaParole !== null && gardienDeLaMemoire !== null } ``` **Soumission**: ```typescript POST /api/missions Body: { name, oddScope, niveau, intention, missionType, donneurDOrdre, projection, services, profils, participation, guardians, volunteers, logo, attachments } ``` ### Étape 4: Traitement Backend **Séquence d'exécution**: 1. **Création de la mission en base de données** ```typescript prisma.mission.create({ data: { name, oddScope, niveau, intention, ... } }) ``` 2. **Création des MissionUsers (gardiens + volontaires)** ```typescript prisma.missionUser.createMany({ data: [ { missionId, userId, role: 'gardien-temps' }, { missionId, userId, role: 'gardien-parole' }, { missionId, userId, role: 'gardien-memoire' }, { missionId, userId, role: 'volontaire' }, // pour chaque volontaire ] }) ``` 3. **Upload du logo vers Minio** - Path: `missions/{missionId}/logo{extension}` - Bucket: `missions` - Mise à jour du champ `logo` dans la mission 4. **Upload des attachments vers Minio** - Path: `missions/{missionId}/attachments/{filename}` - Création des enregistrements `Attachment` en base 5. **Vérification des fichiers dans Minio** - Vérifie que tous les fichiers sont bien présents avant de continuer 6. **Déclenchement du workflow N8N** ```typescript n8nService.triggerMissionCreation({ ...missionData, creatorId, logoPath, config: { N8N_API_KEY, MISSION_API_URL } }) ``` 7. **Intégrations externes (via N8N)**: - Création projet Leantime (si applicable) - Création collection Outline (si applicable) - Création canal RocketChat (si applicable) - Création repository Gitea (si applicable) - Création projet Penpot (si applicable) 8. **Retour succès/erreur** - Si succès: Redirection vers `/missions` - Si erreur: Nettoyage des fichiers uploadés + message d'erreur --- ## 👀 Workflow de Consultation ### Consultation Liste des Missions **Route**: `/missions` ou `/mission-tab` **Flux**: ``` 1. Chargement de la page ↓ 2. useEffect → fetch('/api/missions') ou fetch('/api/missions/all') ↓ 3. Affichage du loader ↓ 4. Réception des données ↓ 5. Transformation des données: - Ajout des logoUrl (si logo existe) - Formatage des dates - Calcul des couleurs de badges - Extraction des infos ODD ↓ 6. Filtrage par terme de recherche (si présent) ↓ 7. Affichage en grille ``` **Recherche**: - Filtre en temps réel sur: nom, niveau, type, ODD scope - Pas de requête API supplémentaire (filtrage côté client) ### Consultation Détails Mission **Route**: `/missions/[missionId]` ou `/mission-tab/[missionId]` **Flux**: ``` 1. Chargement de la page ↓ 2. Récupération du missionId depuis les params ↓ 3. useEffect → fetch(`/api/missions/${missionId}`) ↓ 4. Affichage du loader ↓ 5. Réception des données complètes: - Mission avec tous les champs - Creator (id, email) - MissionUsers (avec user details) - Attachments (avec publicUrl) ↓ 6. Transformation: - Ajout des logoUrl - Formatage des dates - Labels pour les types/niveaux - URLs publiques pour les attachments ↓ 7. Affichage des sections: - Header avec nom et logo - Grille d'informations - Description - Documents - Profils recherchés - Services - Actions (Éditer/Supprimer) ``` --- ## 🔌 API Routes ### 1. `GET /api/missions` **Fichier**: `app/api/missions/route.ts` **Fonctionnalité**: Liste les missions de l'utilisateur connecté **Filtres**: - `limit` (default: 10) - `offset` (default: 0) - `search` (recherche dans name et intention) - `name` (filtre exact) **Where Clause**: ```typescript { OR: [ { creatorId: userId }, { missionUsers: { some: { userId } } } ] } ``` **Retour**: ```json { "missions": [ { "id": "...", "name": "...", "logo": "missions/{id}/logo.png", "logoUrl": "/api/missions/image/missions/{id}/logo.png", "oddScope": ["odd-3"], "niveau": "a", "missionType": "remote", "projection": "short", "services": ["Gite"], "intention": "...", "createdAt": "...", "creator": { "id": "...", "email": "..." }, "missionUsers": [...], "attachments": [...] } ], "pagination": { "total": 10, "offset": 0, "limit": 10 } } ``` ### 2. `POST /api/missions` **Fichier**: `app/api/missions/route.ts` **Fonctionnalité**: Crée une nouvelle mission **Body**: ```typescript { name: string; oddScope: string[]; niveau?: string; intention?: string; missionType?: string; donneurDOrdre?: string; projection?: string; services?: string[]; profils?: string[]; participation?: string; guardians?: { "gardien-temps": string; "gardien-parole": string; "gardien-memoire": string; }; volunteers?: string[]; logo?: { data: string; // base64 name?: string; type?: string; }; attachments?: Array<{ data: string; // base64 name?: string; type?: string; }>; } ``` **Retour**: ```json { "success": true, "mission": { ... }, "message": "Mission created successfully with all integrations" } ``` ### 3. `GET /api/missions/[missionId]` **Fichier**: `app/api/missions/[missionId]/route.ts` **Fonctionnalité**: Récupère les détails d'une mission **Contrôle d'accès**: - Utilisateur doit être créateur OU membre de la mission **Retour**: Mission complète avec relations ### 4. `PUT /api/missions/[missionId]` **Fichier**: `app/api/missions/[missionId]/route.ts` **Fonctionnalité**: Met à jour une mission **Contrôle d'accès**: - Créateur OU gardien-temps/gardien-parole **Body**: Même structure que POST (tous les champs optionnels) ### 5. `DELETE /api/missions/[missionId]` **Fichier**: `app/api/missions/[missionId]/route.ts` **Fonctionnalité**: Supprime une mission **Contrôle d'accès**: - Créateur OU admin uniquement **Actions**: - Suppression du logo dans Minio - Suppression de la mission en base (cascade sur MissionUsers et Attachments) - TODO: Rollback N8N (non implémenté) ### 6. `GET /api/missions/all` **Fichier**: `app/api/missions/all/route.ts` **Fonctionnalité**: Liste TOUTES les missions (pas de filtre utilisateur) **Différences avec `/api/missions`**: - Pas de filtre par utilisateur - Retourne toutes les missions publiques - Utilisé par `/mission-tab` ### 7. `GET /api/missions/image/[...path]` **Fichier**: `app/api/missions/image/[...path]/route.ts` **Fonctionnalité**: Sert les images (logos et attachments) depuis Minio **Path**: `missions/{missionId}/logo.png` ou `missions/{missionId}/attachments/{filename}` ### 8. `POST /api/missions/upload` **Fichier**: `app/api/missions/upload/route.ts` **Fonctionnalité**: Upload de fichiers (logo ou attachments) ### 9. `GET /api/missions/[missionId]/attachments` **Fichier**: `app/api/missions/[missionId]/attachments/route.ts` **Fonctionnalité**: Liste les attachments d'une mission ### 10. `POST /api/missions/[missionId]/attachments` **Fichier**: `app/api/missions/[missionId]/attachments/route.ts` **Fonctionnalité**: Ajoute un attachment à une mission existante ### 11. `DELETE /api/missions/[missionId]/attachments/[attachmentId]` **Fichier**: `app/api/missions/[missionId]/attachments/[attachmentId]/route.ts` **Fonctionnalité**: Supprime un attachment --- ## 🗄️ Base de Données ### Modèle Mission (`prisma/schema.prisma`) ```prisma model Mission { id String @id @default(uuid()) name String logo String? // Path dans Minio oddScope String[] // Catégories ODD niveau String // A/B/C/S intention String // Description missionType String // remote/onsite/hybrid donneurDOrdre String // individual/group/organization projection String // short/medium/long services String[] // ["Gite", "ArtLab", "Calcul"] participation String? // volontaire/cooptation profils String[] // ["DataIntelligence", ...] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt creator User @relation(fields: [creatorId], references: [id]) creatorId String attachments Attachment[] missionUsers MissionUser[] // Intégrations externes leantimeProjectId String? outlineCollectionId String? rocketChatChannelId String? giteaRepositoryUrl String? penpotProjectId String? @@index([creatorId]) } ``` ### Modèle MissionUser ```prisma model MissionUser { id String @id @default(uuid()) role String // 'gardien-temps', 'gardien-parole', 'gardien-memoire', 'volontaire' createdAt DateTime @default(now()) updatedAt DateTime @updatedAt mission Mission @relation(fields: [missionId], references: [id]) missionId String user User @relation(fields: [userId], references: [id]) userId String @@unique([missionId, userId, role]) @@index([missionId]) @@index([userId]) } ``` ### Modèle Attachment ```prisma model Attachment { id String @id @default(uuid()) filename String filePath String // Path dans Minio: missions/{missionId}/attachments/{filename} fileType String // MIME type fileSize Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt mission Mission @relation(fields: [missionId], references: [id]) missionId String uploader User @relation(fields: [uploaderId], references: [id]) uploaderId String @@index([missionId]) @@index([uploaderId]) } ``` --- ## 🔗 Intégrations Externes ### Service N8N (`lib/services/n8n-service.ts`) **Webhook URL**: `https://brain.slm-lab.net/webhook/mission-created` **Données envoyées**: ```typescript { name, oddScope, niveau, intention, missionType, donneurDOrdre, projection, services, participation, profils, guardians, volunteers, creatorId, config: { N8N_API_KEY, MISSION_API_URL } } ``` **Workflow N8N déclenche**: 1. Création projet Leantime (si applicable) 2. Création collection Outline (si applicable) 3. Création canal RocketChat (si applicable) 4. Création repository Gitea (si applicable) 5. Création projet Penpot (si applicable) **Retour**: ```typescript { success: boolean; results?: { leantimeProjectId?: string; outlineCollectionId?: string; rocketChatChannelId?: string; giteaRepositoryUrl?: string; penpotProjectId?: string; failedServices?: { gitRepo?: boolean; leantimeProject?: boolean; docCollection?: boolean; rocketChatChannel?: boolean; } }; error?: string; } ``` **Rollback** (non implémenté): - Webhook: `https://brain.slm-lab.net/webhook/mission-rollback` - Appelé lors de la suppression d'une mission --- ## 📦 Stockage de Fichiers ### Minio Configuration **Endpoint**: `https://dome-api.slm-lab.net` **Bucket**: `missions` **Credentials**: Hardcodés dans `lib/mission-uploads.ts` (⚠️ à sécuriser) ### Structure des Chemins **Logo**: ``` missions/{missionId}/logo{extension} Exemple: missions/abc-123/logo.png ``` **Attachments**: ``` missions/{missionId}/attachments/{filename} Exemple: missions/abc-123/attachments/document.pdf ``` ### URLs Publiques **Format**: `/api/missions/image/{path}` **Exemples**: - Logo: `/api/missions/image/missions/{missionId}/logo.png` - Attachment: `/api/missions/image/missions/{missionId}/attachments/document.pdf` ### Fonctions Utilitaires (`lib/mission-uploads.ts`) - `getMissionLogoPath()` - Génère le chemin du logo - `getMissionAttachmentPath()` - Génère le chemin d'un attachment - `uploadMissionLogo()` - Upload un logo vers Minio - `uploadMissionAttachment()` - Upload un attachment vers Minio - `deleteMissionLogo()` - Supprime un logo (TODO) - `deleteMissionAttachment()` - Supprime un attachment - `getMissionFileUrl()` - Construit l'URL publique - `ensureMissionsPrefix()` - Normalise le chemin --- ## 🧩 Composants Réutilisables ### 1. `MissionsAdminPanel` (`components/missions/missions-admin-panel.tsx`) **Fonctionnalités**: - Formulaire multi-onglets - Gestion des gardiens et volontaires - Upload de fichiers - Validation complète - Soumission vers API **Props**: Aucune (composant autonome) **State**: - `missionData` - Données de la mission - `selectedServices` - Services sélectionnés - `selectedProfils` - Profils sélectionnés - `gardienDuTemps`, `gardienDeLaParole`, `gardienDeLaMemoire` - IDs des gardiens - `volontaires` - Array d'IDs de volontaires - `activeTab` - Onglet actif - `isSubmitting` - État de soumission ### 2. `FileUpload` (`components/missions/file-upload.tsx`) **Fonctionnalités**: - Upload de logo ou attachment - Conversion en base64 - Preview pour les images **Props**: - `type`: 'logo' | 'attachment' - `isNewMission`: boolean - `onFileSelect`: (fileData) => void ### 3. `AttachmentsList` (`components/missions/attachments-list.tsx`) **Fonctionnalités**: - Liste des attachments d'une mission - Upload de nouveaux attachments - Suppression d'attachments **Props**: - `missionId`: string - `allowUpload`: boolean - `allowDelete`: boolean ### 4. `MissionsFrame` (`components/missions/missions-frame.tsx`) **Fonctionnalités**: Wrapper iframe (non utilisé actuellement) --- ## 🔐 Contrôles d'Accès ### Page Centrale (`/missions`) - **Rôles requis**: `entrepreneurship` ou `admin` - Vérifié dans `components/main-nav.tsx` via `hasRole()` ### API Routes - **Authentification**: Session NextAuth requise - **GET /api/missions**: Missions où user est créateur ou membre - **GET /api/missions/all**: Toutes les missions (authentifié) - **GET /api/missions/[id]**: Créateur ou membre - **PUT /api/missions/[id]**: Créateur ou gardien-temps/gardien-parole - **DELETE /api/missions/[id]**: Créateur ou admin uniquement --- ## 📊 Flux de Données Complet ### Création de Mission ``` [Frontend] MissionsAdminPanel ↓ (soumission) POST /api/missions ↓ [Backend] 1. Validation 2. prisma.mission.create() 3. prisma.missionUser.createMany() 4. uploadMissionLogo() → Minio 5. uploadMissionAttachment() → Minio (pour chaque attachment) 6. prisma.attachment.create() (pour chaque attachment) 7. verifyFileExists() (vérification Minio) 8. n8nService.triggerMissionCreation() ↓ [N8N Workflow] - Création Leantime - Création Outline - Création RocketChat - Création Gitea - Création Penpot ↓ [Backend] 9. Retour succès/erreur ↓ [Frontend] Redirection → /missions ``` ### Consultation de Mission ``` [Frontend] MissionsPage ou MissionDetailPage ↓ fetch('/api/missions') ou fetch('/api/missions/[id]') ↓ [Backend] 1. Vérification session 2. Query Prisma avec relations 3. Transformation des paths en URLs publiques 4. Retour JSON ↓ [Frontend] Affichage des données ``` ### Affichage d'Image ``` [Frontend] ↓ [Backend] GET /api/missions/image/[...path] ↓ Lecture depuis Minio ↓ Stream vers client ``` --- ## 🎨 Styles et UI ### Couleurs des Badges Niveau - **A (Apprentissage)**: `bg-green-100 text-green-800` - **B (Basique)**: `bg-blue-100 text-blue-800` - **C (Complexe)**: `bg-purple-100 text-purple-800` - **S (Spécial)**: `bg-amber-100 text-amber-800` ### Layout Sidebar CAP - **Fond**: `bg-pink-50` - **Bordure**: `border-pink-100` - **Largeur**: `234px` fixe ### Grille de Missions - **Mobile**: 1 colonne - **Tablet**: 2 colonnes (`md:grid-cols-2`) - **Desktop**: 3 colonnes (`lg:grid-cols-3`) --- ## 🐛 Points d'Attention 1. **Credentials Minio hardcodés** dans `lib/mission-uploads.ts` - À déplacer vers variables d'environnement 2. **Rollback N8N non implémenté** lors de la suppression 3. **Skills tab non fonctionnel** - Placeholders uniquement 4. **Presigned URLs non implémentées** - Upload direct uniquement 5. **Gestion d'erreurs N8N** - Partielle (continue même si certaines intégrations échouent) --- ## 📝 Notes Techniques ### Types TypeScript **Mission Interface** (utilisée dans les pages): ```typescript interface Mission { id: string; name: string; logo?: string; logoUrl?: string; oddScope: string[]; niveau: string; missionType: string; projection: string; participation?: string; services?: string[]; profils?: string[]; intention?: string; donneurDOrdre?: string; createdAt: string; creator: User; missionUsers: MissionUser[]; attachments?: Attachment[]; } ``` ### Validation **Côté Frontend**: Validation dans `MissionsAdminPanel.validateMission()` **Côté Backend**: Validation minimale (name et oddScope requis) ### Gestion d'Erreurs - **Frontend**: Toast notifications via `useToast()` - **Backend**: Retour JSON avec `error` et `details` - **N8N**: Retour avec `success` et `failedServices` pour erreurs partielles --- ## 🔄 Évolutions Possibles 1. **Pagination** côté client pour les listes 2. **Filtres avancés** (par niveau, type, ODD, etc.) 3. **Recherche full-text** dans l'intention 4. **Export** des missions (PDF, CSV) 5. **Notifications** lors de l'assignation à une mission 6. **Statistiques** des missions 7. **Timeline** des activités d'une mission 8. **Commentaires** sur les missions 9. **États** des missions (brouillon, publiée, terminée, etc.) 10. **Permissions granulaires** par rôle de gardien --- **Document généré le**: $(date) **Version**: 1.0 **Auteur**: Analyse automatique du codebase