481 lines
13 KiB
Markdown
481 lines
13 KiB
Markdown
# Gestion des Productions - Documentation Staff
|
|
|
|
## 📋 Vue d'ensemble
|
|
|
|
La page **Gestion des Productions** (`/staff/gestion-productions`) est une interface complète permettant aux membres du staff de gérer l'ensemble des productions et spectacles de toutes les organisations.
|
|
|
|
## ✨ Fonctionnalités
|
|
|
|
### 🎬 CRUD Complet
|
|
|
|
- **Créer** : Ajouter de nouvelles productions avec nom, référence, date de déclaration et organisation
|
|
- **Lire** : Visualiser toutes les productions dans un tableau moderne avec filtres
|
|
- **Mettre à jour** : Modifier les informations d'une production existante
|
|
- **Supprimer** : Supprimer une production (avec vérification d'utilisation dans les contrats)
|
|
|
|
### 🔍 Recherche et Filtrage
|
|
|
|
- **Recherche textuelle** : Recherche en temps réel par nom ou référence
|
|
- **Filtre par organisation** : Filtrer les productions d'une organisation spécifique
|
|
- **Effacement rapide** : Bouton pour réinitialiser tous les filtres
|
|
|
|
### 📊 Interface Moderne
|
|
|
|
- **Design ultra-moderne** : Interface avec gradients, ombres et animations fluides
|
|
- **Modales élégantes** : Modales avec backdrop blur et animations d'entrée
|
|
- **Badges et icônes** : Affichage visuel clair avec icônes Lucide
|
|
- **Responsive** : Adaptation automatique mobile/tablette/desktop
|
|
- **États de chargement** : Spinners et messages informatifs
|
|
|
|
## 🎨 Interface Utilisateur
|
|
|
|
### Header
|
|
```
|
|
┌────────────────────────────────────────────────────┐
|
|
│ Gestion des productions [+ Nouvelle] │
|
|
│ Gérez les productions et spectacles... │
|
|
└────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### Barre de Filtres
|
|
- **Recherche** : Input avec icône de recherche et bouton de réinitialisation
|
|
- **Filtres** : Bouton avec badge indiquant le nombre de filtres actifs
|
|
- **Organisation** : Dropdown pour filtrer par organisation
|
|
|
|
### Tableau des Productions
|
|
|
|
| Colonne | Description | Type |
|
|
|---------|-------------|------|
|
|
| Nom | Nom de la production | Texte |
|
|
| Référence | Numéro d'objet/référence | Badge avec icône Hash |
|
|
| Date déclaration | Date de déclaration | Date avec icône Calendar |
|
|
| Organisation | Organisation liée | Texte |
|
|
| Actions | Éditer / Supprimer | Boutons icônes |
|
|
|
|
### Modales
|
|
|
|
#### Modal Création/Édition
|
|
```
|
|
┌──────────────────────────────────────┐
|
|
│ 🏢 Nouvelle production ✕ │
|
|
├──────────────────────────────────────┤
|
|
│ │
|
|
│ 📄 Nom de la production * │
|
|
│ [Ex: Festival d'Avignon 2025] │
|
|
│ │
|
|
│ # Numéro d'objet / Référence │
|
|
│ [Ex: PROD-2025-001] │
|
|
│ │
|
|
│ 📅 Date de déclaration │
|
|
│ [Date picker] │
|
|
│ │
|
|
│ 🏢 Organisation * │
|
|
│ [Dropdown] │
|
|
│ │
|
|
│ [Annuler] [Créer ⟳] │
|
|
└──────────────────────────────────────┘
|
|
```
|
|
|
|
#### Modal Suppression
|
|
```
|
|
┌──────────────────────────────────────┐
|
|
│ 🗑️ Confirmer la suppression │
|
|
│ Cette action est irréversible │
|
|
├──────────────────────────────────────┤
|
|
│ │
|
|
│ Voulez-vous vraiment supprimer │
|
|
│ la production "Festival 2025" ? │
|
|
│ Référence : PROD-001 │
|
|
│ │
|
|
│ [Annuler] [Supprimer ⟳] │
|
|
└──────────────────────────────────────┘
|
|
```
|
|
|
|
## 🔧 Implémentation Technique
|
|
|
|
### Architecture
|
|
|
|
```
|
|
app/(app)/staff/gestion-productions/
|
|
└── page.tsx (Client Component)
|
|
|
|
app/api/staff/productions/
|
|
├── route.ts (GET, POST)
|
|
└── [id]/
|
|
└── route.ts (GET, PATCH, DELETE)
|
|
|
|
components/
|
|
└── Sidebar.tsx (+ lien navigation)
|
|
```
|
|
|
|
### Hooks React Query
|
|
|
|
#### `useStaffCheck()`
|
|
Vérifie si l'utilisateur connecté est staff.
|
|
|
|
```typescript
|
|
const { data: staffCheck, isLoading } = useStaffCheck();
|
|
// Returns: { isStaff: boolean }
|
|
```
|
|
|
|
#### `useOrganizations()`
|
|
Récupère la liste de toutes les organisations.
|
|
|
|
```typescript
|
|
const { data: organizations } = useOrganizations();
|
|
// Returns: Organization[]
|
|
```
|
|
|
|
#### `useProductions(orgId?, searchQuery?)`
|
|
Récupère les productions avec filtres optionnels.
|
|
|
|
```typescript
|
|
const { data: productions, isLoading, error } = useProductions(orgId, searchQuery);
|
|
// Returns: Production[]
|
|
```
|
|
|
|
### Types TypeScript
|
|
|
|
```typescript
|
|
type Production = {
|
|
id: string;
|
|
name: string;
|
|
reference: string | null;
|
|
declaration_date: string | null;
|
|
org_id: string;
|
|
created_at?: string;
|
|
updated_at?: string;
|
|
};
|
|
|
|
type Organization = {
|
|
id: string;
|
|
name: string;
|
|
structure_api: string;
|
|
};
|
|
```
|
|
|
|
## 🌐 Routes API
|
|
|
|
### GET `/api/staff/productions`
|
|
|
|
**Description** : Liste toutes les productions avec filtres optionnels
|
|
|
|
**Query Parameters** :
|
|
- `org_id` (optional) : Filtrer par organisation
|
|
- `q` (optional) : Recherche textuelle (nom ou référence)
|
|
|
|
**Response** :
|
|
```json
|
|
{
|
|
"productions": [
|
|
{
|
|
"id": "uuid",
|
|
"name": "Festival d'Avignon 2025",
|
|
"reference": "PROD-2025-001",
|
|
"declaration_date": "2025-01-15",
|
|
"org_id": "org-uuid",
|
|
"created_at": "2025-01-10T10:00:00Z",
|
|
"updated_at": "2025-01-10T10:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Codes d'erreur** :
|
|
- `401` : Non authentifié
|
|
- `403` : Accès refusé (non-staff)
|
|
- `500` : Erreur serveur
|
|
|
|
### POST `/api/staff/productions`
|
|
|
|
**Description** : Créer une nouvelle production
|
|
|
|
**Body** :
|
|
```json
|
|
{
|
|
"name": "Festival d'Avignon 2025",
|
|
"reference": "PROD-2025-001",
|
|
"declaration_date": "2025-01-15",
|
|
"org_id": "org-uuid"
|
|
}
|
|
```
|
|
|
|
**Validation** :
|
|
- `name` : Requis, non vide
|
|
- `org_id` : Requis, doit exister dans `organizations`
|
|
- `reference` : Optionnel
|
|
- `declaration_date` : Optionnel
|
|
|
|
**Response** :
|
|
```json
|
|
{
|
|
"production": { /* Production créée */ }
|
|
}
|
|
```
|
|
|
|
**Codes d'erreur** :
|
|
- `400` : Validation échouée
|
|
- `401` : Non authentifié
|
|
- `403` : Accès refusé (non-staff)
|
|
- `500` : Erreur serveur
|
|
|
|
### GET `/api/staff/productions/[id]`
|
|
|
|
**Description** : Récupérer une production spécifique
|
|
|
|
**Response** :
|
|
```json
|
|
{
|
|
"production": { /* Production */ }
|
|
}
|
|
```
|
|
|
|
**Codes d'erreur** :
|
|
- `401` : Non authentifié
|
|
- `403` : Accès refusé (non-staff)
|
|
- `404` : Production introuvable
|
|
- `500` : Erreur serveur
|
|
|
|
### PATCH `/api/staff/productions/[id]`
|
|
|
|
**Description** : Mettre à jour une production
|
|
|
|
**Body** (tous les champs optionnels) :
|
|
```json
|
|
{
|
|
"name": "Nouveau nom",
|
|
"reference": "NEW-REF",
|
|
"declaration_date": "2025-02-01",
|
|
"org_id": "autre-org-uuid"
|
|
}
|
|
```
|
|
|
|
**Response** :
|
|
```json
|
|
{
|
|
"production": { /* Production mise à jour */ }
|
|
}
|
|
```
|
|
|
|
**Codes d'erreur** :
|
|
- `400` : Validation échouée
|
|
- `401` : Non authentifié
|
|
- `403` : Accès refusé (non-staff)
|
|
- `404` : Production introuvable
|
|
- `500` : Erreur serveur
|
|
|
|
### DELETE `/api/staff/productions/[id]`
|
|
|
|
**Description** : Supprimer une production
|
|
|
|
**Vérifications** :
|
|
- La production existe
|
|
- La production n'est pas utilisée dans des contrats CDDU
|
|
|
|
**Response** :
|
|
```json
|
|
{
|
|
"message": "Production supprimée avec succès",
|
|
"deleted_id": "uuid"
|
|
}
|
|
```
|
|
|
|
**Codes d'erreur** :
|
|
- `400` : Production utilisée dans des contrats
|
|
- `401` : Non authentifié
|
|
- `403` : Accès refusé (non-staff)
|
|
- `404` : Production introuvable
|
|
- `500` : Erreur serveur
|
|
|
|
## 🔐 Sécurité
|
|
|
|
### Vérifications
|
|
|
|
- **Authentification** : Session Supabase requise
|
|
- **Autorisation** : Vérification `staff_users.is_staff = true`
|
|
- **Validation** : Toutes les entrées utilisateur sont validées
|
|
- **Protection suppression** : Vérification des dépendances (contrats)
|
|
|
|
### Permissions
|
|
|
|
- **Staff uniquement** : Toutes les opérations réservées au staff
|
|
- **Aucune restriction par organisation** : Le staff peut gérer toutes les organisations
|
|
|
|
## 📱 Responsive Design
|
|
|
|
### Desktop (≥1024px)
|
|
- Tableau pleine largeur
|
|
- Filtres sur une ligne
|
|
- Modales centrées (max-width: 640px)
|
|
|
|
### Tablet (768px - 1023px)
|
|
- Tableau scrollable horizontal
|
|
- Filtres sur deux lignes
|
|
- Modales adaptées
|
|
|
|
### Mobile (<768px)
|
|
- Tableau scrollable
|
|
- Filtres empilés verticalement
|
|
- Modales plein écran avec padding
|
|
|
|
## 🎨 Design System
|
|
|
|
### Couleurs
|
|
|
|
- **Primary Gradient** : `from-indigo-600 to-purple-600`
|
|
- **Hover Gradient** : `from-indigo-700 to-purple-700`
|
|
- **Background** : `bg-white`, `bg-slate-50`
|
|
- **Border** : `border-slate-200`
|
|
- **Text** : `text-slate-900`, `text-slate-600`
|
|
|
|
### Animations
|
|
|
|
- **Modal entrée** : `animate-in fade-in zoom-in-95 duration-200`
|
|
- **Backdrop** : `backdrop-blur-sm`
|
|
- **Transitions** : `transition-all`, `transition-colors`
|
|
|
|
### Icônes (Lucide React)
|
|
|
|
- `Building2` : Organisations
|
|
- `FileText` : Nom de production
|
|
- `Hash` : Référence
|
|
- `Calendar` : Date de déclaration
|
|
- `Plus` : Créer
|
|
- `Edit2` : Éditer
|
|
- `Trash2` : Supprimer
|
|
- `Filter` : Filtres
|
|
- `Search` : Recherche
|
|
- `Loader2` : Chargement
|
|
- `Clapperboard` : Icône sidebar
|
|
|
|
## 🚀 Utilisation
|
|
|
|
### Créer une production
|
|
|
|
1. Cliquer sur **"+ Nouvelle production"**
|
|
2. Remplir le formulaire :
|
|
- Nom (requis)
|
|
- Référence (optionnel)
|
|
- Date de déclaration (optionnel)
|
|
- Organisation (requis)
|
|
3. Cliquer sur **"Créer"**
|
|
|
|
### Modifier une production
|
|
|
|
1. Cliquer sur l'icône ✏️ dans la colonne Actions
|
|
2. Modifier les champs souhaités
|
|
3. Cliquer sur **"Enregistrer"**
|
|
|
|
### Supprimer une production
|
|
|
|
1. Cliquer sur l'icône 🗑️ dans la colonne Actions
|
|
2. Confirmer la suppression dans le modal
|
|
3. La production est supprimée si elle n'est pas utilisée
|
|
|
|
### Filtrer les productions
|
|
|
|
1. Cliquer sur **"Filtres"**
|
|
2. Sélectionner une organisation
|
|
3. Les résultats sont filtrés automatiquement
|
|
|
|
### Rechercher une production
|
|
|
|
1. Saisir du texte dans la barre de recherche
|
|
2. Les résultats sont filtrés en temps réel
|
|
3. La recherche porte sur le nom et la référence
|
|
|
|
## 🐛 Gestion des Erreurs
|
|
|
|
### Messages utilisateur
|
|
|
|
- **Champ requis** : "Le nom est requis"
|
|
- **Organisation requise** : "L'organisation est requise"
|
|
- **Production utilisée** : "Cette production est utilisée dans des contrats..."
|
|
- **Erreur réseau** : "Erreur lors du chargement des productions"
|
|
|
|
### États
|
|
|
|
- **Loading** : Spinner avec message "Chargement..."
|
|
- **Empty** : Message "Aucune production trouvée"
|
|
- **Error** : Message d'erreur en rouge avec détails
|
|
|
|
## 📊 Performances
|
|
|
|
### Optimisations
|
|
|
|
- **React Query** : Cache des requêtes API (15s)
|
|
- **useMemo** : Mémoïsation du filtrage local
|
|
- **Invalidation sélective** : Rafraîchissement uniquement des données modifiées
|
|
- **Debouncing** : Pas de debounce nécessaire (filtrage instant)
|
|
|
|
### Stale Time
|
|
|
|
- `useStaffCheck()` : 30 secondes
|
|
- `useOrganizations()` : 60 secondes
|
|
- `useProductions()` : 15 secondes
|
|
|
|
## 🔗 Intégration
|
|
|
|
### Sidebar
|
|
|
|
La page est accessible via la sidebar staff :
|
|
|
|
```tsx
|
|
<Link href="/staff/gestion-productions">
|
|
<Clapperboard className="w-4 h-4" />
|
|
Gestion des productions
|
|
</Link>
|
|
```
|
|
|
|
### Navigation
|
|
|
|
- URL : `/staff/gestion-productions`
|
|
- Position : Entre "Virements salaires" et "Gestion des utilisateurs"
|
|
- Icône : 🎬 Clapperboard
|
|
|
|
## 🧪 Tests
|
|
|
|
### Scénarios de test
|
|
|
|
1. **Accès non-staff** : Vérifier refus d'accès
|
|
2. **Création** : Créer une production valide
|
|
3. **Création invalide** : Vérifier validation (nom vide, org manquante)
|
|
4. **Modification** : Éditer une production existante
|
|
5. **Suppression** : Supprimer une production non utilisée
|
|
6. **Suppression protection** : Tenter de supprimer une production utilisée
|
|
7. **Filtres** : Vérifier filtrage par organisation
|
|
8. **Recherche** : Vérifier recherche textuelle
|
|
9. **Responsive** : Tester sur mobile/tablet/desktop
|
|
|
|
### Console logs
|
|
|
|
```javascript
|
|
// Vérifier les requêtes API
|
|
// Réseau > Fetch/XHR
|
|
// GET /api/staff/productions
|
|
// POST /api/staff/productions
|
|
// PATCH /api/staff/productions/[id]
|
|
// DELETE /api/staff/productions/[id]
|
|
```
|
|
|
|
## 📝 Todo / Améliorations futures
|
|
|
|
- [ ] Export CSV/Excel des productions
|
|
- [ ] Tri des colonnes du tableau
|
|
- [ ] Pagination (si + de 100 productions)
|
|
- [ ] Filtre par date de déclaration (plage)
|
|
- [ ] Import CSV de productions en masse
|
|
- [ ] Historique des modifications
|
|
- [ ] Duplication de production
|
|
- [ ] Statistiques (nb productions par org, par période)
|
|
- [ ] Champ description/notes
|
|
- [ ] Tags/catégories de productions
|
|
- [ ] Lien direct vers les contrats associés
|
|
|
|
## 📚 Références
|
|
|
|
- [Lucide Icons](https://lucide.dev/)
|
|
- [TanStack Query (React Query)](https://tanstack.com/query/latest)
|
|
- [Tailwind CSS](https://tailwindcss.com/)
|
|
- [Next.js App Router](https://nextjs.org/docs/app)
|
|
- [Supabase Client](https://supabase.com/docs/reference/javascript)
|