25 KiB
Analyse Complète : Pages Missions et Centrale - Workflow Complet
📋 Table des Matières
- Vue d'ensemble
- Architecture des Pages
- Workflow de Navigation
- Workflow de Création de Mission
- Workflow de Consultation
- API Routes
- Base de Données
- Intégrations Externes
- Stockage de Fichiers
- Composants Réutilisables
🎯 Vue d'ensemble
Page "Centrale"
- Route:
/missions - Nom dans le menu: "Centrale"
- Accès: Rôles
entrepreneurshipouadmin(défini danscomponents/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)
- Créateur (
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:
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:
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:
-
Création de la mission en base de données
prisma.mission.create({ data: { name, oddScope, niveau, intention, ... } }) -
Création des MissionUsers (gardiens + volontaires)
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 ] }) -
Upload du logo vers Minio
- Path:
missions/{missionId}/logo{extension} - Bucket:
missions - Mise à jour du champ
logodans la mission
- Path:
-
Upload des attachments vers Minio
- Path:
missions/{missionId}/attachments/{filename} - Création des enregistrements
Attachmenten base
- Path:
-
Vérification des fichiers dans Minio
- Vérifie que tous les fichiers sont bien présents avant de continuer
-
Déclenchement du workflow N8N
n8nService.triggerMissionCreation({ ...missionData, creatorId, logoPath, config: { N8N_API_KEY, MISSION_API_URL } }) -
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)
-
Retour succès/erreur
- Si succès: Redirection vers
/missions - Si erreur: Nettoyage des fichiers uploadés + message d'erreur
- Si succès: Redirection vers
👀 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:
{
OR: [
{ creatorId: userId },
{ missionUsers: { some: { userId } } }
]
}
Retour:
{
"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:
{
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:
{
"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)
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
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
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:
{
name, oddScope, niveau, intention, missionType,
donneurDOrdre, projection, services, participation,
profils, guardians, volunteers, creatorId,
config: {
N8N_API_KEY,
MISSION_API_URL
}
}
Workflow N8N déclenche:
- 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)
Retour:
{
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 logogetMissionAttachmentPath()- Génère le chemin d'un attachmentuploadMissionLogo()- Upload un logo vers MiniouploadMissionAttachment()- Upload un attachment vers MiniodeleteMissionLogo()- Supprime un logo (TODO)deleteMissionAttachment()- Supprime un attachmentgetMissionFileUrl()- Construit l'URL publiqueensureMissionsPrefix()- 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 missionselectedServices- Services sélectionnésselectedProfils- Profils sélectionnésgardienDuTemps,gardienDeLaParole,gardienDeLaMemoire- IDs des gardiensvolontaires- Array d'IDs de volontairesactiveTab- Onglet actifisSubmitting- É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: booleanonFileSelect: (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: stringallowUpload: booleanallowDelete: 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:
entrepreneurshipouadmin - Vérifié dans
components/main-nav.tsxviahasRole()
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]
<img src="/api/missions/image/missions/{id}/logo.png" />
↓
[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:
234pxfixe
Grille de Missions
- Mobile: 1 colonne
- Tablet: 2 colonnes (
md:grid-cols-2) - Desktop: 3 colonnes (
lg:grid-cols-3)
🐛 Points d'Attention
- Credentials Minio hardcodés dans
lib/mission-uploads.ts- À déplacer vers variables d'environnement - Rollback N8N non implémenté lors de la suppression
- Skills tab non fonctionnel - Placeholders uniquement
- Presigned URLs non implémentées - Upload direct uniquement
- Gestion d'erreurs N8N - Partielle (continue même si certaines intégrations échouent)
📝 Notes Techniques
Types TypeScript
Mission Interface (utilisée dans les pages):
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
erroretdetails - N8N: Retour avec
successetfailedServicespour erreurs partielles
🔄 Évolutions Possibles
- Pagination côté client pour les listes
- Filtres avancés (par niveau, type, ODD, etc.)
- Recherche full-text dans l'intention
- Export des missions (PDF, CSV)
- Notifications lors de l'assignation à une mission
- Statistiques des missions
- Timeline des activités d'une mission
- Commentaires sur les missions
- États des missions (brouillon, publiée, terminée, etc.)
- Permissions granulaires par rôle de gardien
Document généré le: $(date) Version: 1.0 Auteur: Analyse automatique du codebase