931 lines
25 KiB
Markdown
931 lines
25 KiB
Markdown
# 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]
|
|
<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**: `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
|
|
|