2025-12-03 21:56:50 +00:00
|
|
|
# Feature: Playlists
|
|
|
|
|
|
|
|
|
|
Cette feature fournit tous les composants, hooks et services nécessaires pour gérer les playlists dans l'application VEZA.
|
|
|
|
|
|
|
|
|
|
## Architecture
|
|
|
|
|
|
|
|
|
|
### Structure des fichiers
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
features/playlists/
|
|
|
|
|
├── components/ # Composants UI
|
|
|
|
|
│ ├── PlaylistCard.tsx # Carte d'affichage d'une playlist
|
|
|
|
|
│ ├── PlaylistList.tsx # Liste de playlists avec pagination
|
|
|
|
|
│ ├── PlaylistHeader.tsx # En-tête de détail de playlist
|
|
|
|
|
│ ├── PlaylistActions.tsx # Actions (edit/delete) pour une playlist
|
|
|
|
|
│ ├── PlaylistForm.tsx # Formulaire création/édition
|
|
|
|
|
│ ├── CreatePlaylistDialog.tsx # Dialog de création
|
|
|
|
|
│ ├── PlaylistAnalytics.tsx # Analytics d'une playlist
|
|
|
|
|
│ ├── PlaylistEditor.tsx # Éditeur de playlist
|
|
|
|
|
│ └── PlaylistManager.tsx # Gestionnaire de playlists
|
|
|
|
|
├── pages/ # Pages
|
|
|
|
|
│ ├── PlaylistListPage.tsx # Page liste des playlists
|
|
|
|
|
│ ├── PlaylistDetailPage.tsx # Page détail d'une playlist
|
|
|
|
|
│ ├── PlaylistCreatePage.tsx # Page création
|
|
|
|
|
│ └── PlaylistEditPage.tsx # Page édition
|
|
|
|
|
├── hooks/ # Hooks React
|
|
|
|
|
│ ├── usePlaylist.ts # Hooks pour CRUD playlists et collaboration
|
|
|
|
|
│ └── usePlaylistPermissions.ts # Hooks pour les permissions
|
|
|
|
|
├── services/ # Services
|
|
|
|
|
│ └── playlistService.ts # Service API pour playlists
|
|
|
|
|
├── routes.tsx # Routes React Router
|
|
|
|
|
├── types.ts # Types TypeScript
|
|
|
|
|
├── __tests__/ # Tests d'intégration
|
|
|
|
|
│ ├── playlist.integration.test.tsx
|
|
|
|
|
│ └── collaboration.integration.test.tsx
|
|
|
|
|
└── index.ts # Exports publics
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Utilisation
|
|
|
|
|
|
|
|
|
|
### Routes
|
|
|
|
|
|
|
|
|
|
Les routes sont configurées dans `routes.tsx` et intégrées dans le router principal :
|
|
|
|
|
|
|
|
|
|
- `/playlists` - Liste des playlists
|
|
|
|
|
- `/playlists/new` - Créer une nouvelle playlist
|
|
|
|
|
- `/playlists/:id` - Détails d'une playlist
|
|
|
|
|
- `/playlists/:id/edit` - Éditer une playlist
|
|
|
|
|
|
|
|
|
|
### Composants principaux
|
|
|
|
|
|
|
|
|
|
#### Gestion des Tracks dans une Playlist
|
|
|
|
|
|
|
|
|
|
La page `PlaylistDetailPage` intègre tous les composants de gestion des tracks :
|
|
|
|
|
|
|
|
|
|
- **PlaylistTrackList** : Affiche la liste des tracks avec numérotation, actions (play, remove) et drag-and-drop pour réorganiser
|
|
|
|
|
- **AddTrackToPlaylistModal** : Modal pour rechercher et ajouter des tracks à la playlist
|
|
|
|
|
- **RemoveTrackButton** : Bouton pour retirer un track de la playlist (intégré dans PlaylistTrackItem)
|
|
|
|
|
|
|
|
|
|
**Fonctionnalités :**
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
- Affichage ordonné des tracks par position
|
|
|
|
|
- Drag-and-drop pour réorganiser les tracks
|
|
|
|
|
- Ajout de tracks via modal de recherche
|
|
|
|
|
- Suppression de tracks avec confirmation
|
|
|
|
|
- Lecture des tracks directement depuis la playlist
|
|
|
|
|
- Synchronisation avec le player global
|
|
|
|
|
|
|
|
|
|
**Exemple d'utilisation :**
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
<PlaylistTrackList
|
|
|
|
|
playlistTracks={playlistTracks}
|
|
|
|
|
tracks={tracks}
|
|
|
|
|
playlistId={playlistId}
|
|
|
|
|
onTrackClick={handleTrackClick}
|
|
|
|
|
onTrackPlay={handleTrackPlay}
|
|
|
|
|
onTrackRemoved={handleTrackRemoved}
|
|
|
|
|
onTracksReordered={handleTracksReordered}
|
|
|
|
|
isPlaying={checkIsPlaying}
|
|
|
|
|
currentPlayingId={currentTrack?.id}
|
|
|
|
|
enableDragAndDrop={true}
|
|
|
|
|
/>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### PlaylistList
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
Affiche une liste de playlists avec pagination et vue grille/liste.
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { PlaylistList } from '@/features/playlists';
|
|
|
|
|
|
2025-12-13 02:34:34 +00:00
|
|
|
<PlaylistList view="grid" limit={20} />;
|
2025-12-03 21:56:50 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### PlaylistCard
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
Carte d'affichage d'une playlist individuelle.
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { PlaylistCard } from '@/features/playlists';
|
|
|
|
|
|
2025-12-13 02:34:34 +00:00
|
|
|
<PlaylistCard playlist={playlist} />;
|
2025-12-03 21:56:50 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### PlaylistForm
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
Formulaire réutilisable pour créer ou éditer une playlist.
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { PlaylistForm } from '@/features/playlists';
|
|
|
|
|
|
|
|
|
|
// Création
|
|
|
|
|
<PlaylistForm onSubmit={handleCreate} onCancel={handleCancel} />
|
|
|
|
|
|
|
|
|
|
// Édition
|
|
|
|
|
<PlaylistForm playlist={playlist} onSubmit={handleUpdate} onCancel={handleCancel} />
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### PlaylistDetailPage
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
Page complète de détail d'une playlist avec header, actions et liste des tracks.
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { PlaylistDetailPage } from '@/features/playlists';
|
|
|
|
|
|
|
|
|
|
// Utilisé via les routes : /playlists/:id
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Hooks
|
|
|
|
|
|
|
|
|
|
#### usePlaylist
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
Récupère une playlist par son ID.
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { usePlaylist } from '@/features/playlists';
|
|
|
|
|
|
|
|
|
|
const { data: playlist, isLoading, error } = usePlaylist(playlistId);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### usePlaylists
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
Récupère la liste des playlists avec pagination.
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { usePlaylists } from '@/features/playlists';
|
|
|
|
|
|
|
|
|
|
const { data, isLoading, error } = usePlaylists(limit, offset);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### useCreatePlaylist
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
Hook de mutation pour créer une playlist.
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { useCreatePlaylist } from '@/features/playlists';
|
|
|
|
|
|
|
|
|
|
const createMutation = useCreatePlaylist();
|
|
|
|
|
|
|
|
|
|
await createMutation.mutateAsync({
|
|
|
|
|
title: 'My Playlist',
|
|
|
|
|
description: 'Description',
|
|
|
|
|
is_public: true,
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### useUpdatePlaylist
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
Hook de mutation pour mettre à jour une playlist.
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { useUpdatePlaylist } from '@/features/playlists';
|
|
|
|
|
|
|
|
|
|
const updateMutation = useUpdatePlaylist();
|
|
|
|
|
|
|
|
|
|
await updateMutation.mutateAsync({
|
|
|
|
|
id: 1,
|
|
|
|
|
data: {
|
|
|
|
|
title: 'Updated Title',
|
|
|
|
|
description: 'Updated description',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### useDeletePlaylist
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
Hook de mutation pour supprimer une playlist.
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import { useDeletePlaylist } from '@/features/playlists';
|
|
|
|
|
|
|
|
|
|
const deleteMutation = useDeletePlaylist();
|
|
|
|
|
|
|
|
|
|
await deleteMutation.mutateAsync(playlistId);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Services
|
|
|
|
|
|
|
|
|
|
#### playlistService
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
Service API pour toutes les opérations CRUD.
|
|
|
|
|
|
|
|
|
|
```tsx
|
2025-12-13 02:34:34 +00:00
|
|
|
import {
|
|
|
|
|
createPlaylist,
|
|
|
|
|
getPlaylist,
|
|
|
|
|
updatePlaylist,
|
|
|
|
|
deletePlaylist,
|
|
|
|
|
listPlaylists,
|
|
|
|
|
} from '@/features/playlists';
|
2025-12-03 21:56:50 +00:00
|
|
|
|
|
|
|
|
// Créer
|
|
|
|
|
const playlist = await createPlaylist({
|
|
|
|
|
title: 'My Playlist',
|
|
|
|
|
description: 'Description',
|
|
|
|
|
is_public: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Lire
|
|
|
|
|
const playlist = await getPlaylist(1);
|
|
|
|
|
const playlists = await listPlaylists(20, 0);
|
|
|
|
|
|
|
|
|
|
// Mettre à jour
|
|
|
|
|
const updated = await updatePlaylist(1, {
|
|
|
|
|
title: 'Updated Title',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Supprimer
|
|
|
|
|
await deletePlaylist(1);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Types
|
|
|
|
|
|
|
|
|
|
```tsx
|
|
|
|
|
import type {
|
|
|
|
|
Playlist,
|
|
|
|
|
PlaylistTrack,
|
|
|
|
|
CreatePlaylistRequest,
|
|
|
|
|
UpdatePlaylistRequest,
|
|
|
|
|
PlaylistListResponse,
|
|
|
|
|
} from '@/features/playlists';
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Fonctionnalités
|
|
|
|
|
|
|
|
|
|
### CRUD complet
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
- ✅ Création de playlists
|
|
|
|
|
- ✅ Lecture (liste et détail)
|
|
|
|
|
- ✅ Mise à jour
|
|
|
|
|
- ✅ Suppression
|
|
|
|
|
|
|
|
|
|
### Interface utilisateur
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
- ✅ Vue grille et liste
|
|
|
|
|
- ✅ Pagination
|
|
|
|
|
- ✅ Filtres et recherche
|
|
|
|
|
- ✅ Actions (edit/delete) avec vérification d'ownership
|
|
|
|
|
- ✅ Modals de confirmation
|
|
|
|
|
|
|
|
|
|
### Validation
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
- ✅ Validation des formulaires avec zod
|
|
|
|
|
- ✅ Messages d'erreur en français
|
|
|
|
|
- ✅ Validation côté client et serveur
|
|
|
|
|
|
|
|
|
|
### Tests
|
2025-12-13 02:34:34 +00:00
|
|
|
|
2025-12-03 21:56:50 +00:00
|
|
|
- ✅ Tests unitaires pour tous les composants
|
|
|
|
|
- ✅ Tests d'intégration CRUD
|
|
|
|
|
- ✅ Coverage ≥ 80%
|
|
|
|
|
|
|
|
|
|
## Dépendances
|
|
|
|
|
|
|
|
|
|
- React Router pour la navigation
|
|
|
|
|
- React Query pour la gestion des données
|
|
|
|
|
- Zustand pour l'état d'authentification
|
|
|
|
|
- Zod pour la validation
|
|
|
|
|
- React Hook Form pour les formulaires
|
|
|
|
|
|
|
|
|
|
## Notes
|
|
|
|
|
|
|
|
|
|
- Les playlists sont liées à un utilisateur (user_id)
|
|
|
|
|
- Seul le propriétaire peut modifier/supprimer sa playlist
|
|
|
|
|
- Les playlists peuvent être publiques ou privées
|
|
|
|
|
- Les playlists peuvent contenir des tracks (PlaylistTrack)
|