import { useState, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { UploadModal } from './UploadModal'; // import { TrackEditDialog } from '@/features/tracks/components/TrackEditDialog'; import { TrackGrid } from '@/features/tracks/components/TrackGrid'; import { TrackList } from '@/features/tracks/components/TrackList'; import { apiClient } from '@/services/api/client'; import type { Track as ApiTrack } from '@/features/tracks/types/track'; import type { Track as PlayerTrack } from '@/features/player/types'; import { useToast } from '@/hooks/useToast'; import { Loader2, Music, Upload, Search, Filter, Grid, List, FileAudio, } from 'lucide-react'; import { logger } from '@/utils/logger'; import { parseApiError } from '@/utils/apiErrorHandler'; interface LibraryManagerProps { onTrackSelect?: (track: ApiTrack) => void; } export function LibraryManager({ onTrackSelect }: LibraryManagerProps) { const [tracks, setTracks] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); const [filterType, setFilterType] = useState('all'); const [pagination, setPagination] = useState({ page: 1, limit: 20, total: 0, }); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); const [playingTrackId, setPlayingTrackId] = useState(null); const { info: showInfo } = useToast(); const fetchTracks = async () => { try { setIsLoading(true); setError(null); const response = await apiClient.get<{ data: ApiTrack[]; total: number; page: number; limit: number; }>('/tracks', { params: { page: pagination.page, limit: pagination.limit, search: searchQuery || undefined, artist: filterType !== 'all' ? filterType : undefined, }, }); // apiClient unwrap déjà le format { success, data } const data = response.data; setTracks(data.data || []); setPagination((prev) => ({ ...prev, total: data.total || 0, })); } catch (err: unknown) { const apiError = parseApiError(err); setError(apiError.message); logger.error('Error fetching tracks:', { message: apiError.message }); } finally { setIsLoading(false); } }; useEffect(() => { fetchTracks(); }, [pagination.page, searchQuery, filterType]); const handleUploadComplete = () => { fetchTracks(); // Refresh tracks after upload }; /* Unused for now const handleDeleteTrack = async (trackId: string) => { if (!confirm('Are you sure you want to delete this track?')) return; try { await apiClient.delete(`/tracks/${trackId}`); toast({ title: 'Track deleted', description: 'The track has been deleted from your library.', }); fetchTracks(); // Refresh tracks } catch (error: any) { toast({ title: 'Error', description: error.response?.data?.error || 'Failed to delete track', variant: 'destructive', }); } }; */ const handleEditTrack = (track: PlayerTrack) => { // Need mapping back to ApiTrack if needed, or just find it const originalTrack = tracks.find((t) => t.id === track.id); if (originalTrack) { // setSelectedTrack(originalTrack); // setIsEditDialogOpen(true); // TODO: Implement edit track functionality } }; /* const handleTrackUpdated = (updated: ApiTrack) => { setTracks(prev => prev.map(t => t.id === updated.id ? updated : t)); }; */ const handlePlayTrack = (track: PlayerTrack) => { setPlayingTrackId(track.id); // onTrackSelect expected ApiTrack const originalTrack = tracks.find((t) => t.id === track.id); if (originalTrack) { onTrackSelect?.(originalTrack); showInfo(`Now playing: ${track.title} by ${track.artist}`); } }; const mapToPlayerTrack = (track: ApiTrack): PlayerTrack => ({ id: track.id, title: track.title, artist: track.artist, album: track.album, duration: track.duration, url: track.stream_manifest_url || track.file_path, // Fallback cover: track.cover_art_path, genre: track.genre, }); const playerTracks = tracks.map(mapToPlayerTrack); if (isLoading) { return (
); } return (
{/* En-tête avec contrôles */}
Ma Bibliothèque Gérez votre collection audio personnelle
{/* Barre de recherche et filtres */}
setSearchQuery(e.target.value)} className="pl-10" />
{/* Tracks List */} {error && (
Error loading library: {error}
)} {tracks.length === 0 ? (

Empty Library

{searchQuery ? 'No tracks match your search.' : 'Start by uploading your first track.'}

) : (
{viewMode === 'grid' ? ( false} // Todo isPlaying={(id) => id === playingTrackId} onTrackMore={handleEditTrack} /> ) : ( false} currentPlayingId={playingTrackId} /> )}
)} {/* Statistics */}
{pagination.total}
Total Tracks
{tracks.filter((track) => track.is_public).length}
Public Tracks
{tracks.reduce( (total, track) => total + (track.play_count || 0), 0, )}
Total Plays
{Math.round( tracks.reduce( (total, track) => total + (track.duration || 0), 0, ) / 60, )} m
Total Duration
{/* Upload Modal - Centralisé (une seule instance) */} setIsUploadModalOpen(false)} onUploadComplete={() => { handleUploadComplete(); setIsUploadModalOpen(false); }} /> {/* Edit Dialog - Commented out as missing */} {/* {selectedTrack && ( { setIsEditDialogOpen(false); setSelectedTrack(null); }} onTrackUpdated={handleTrackUpdated} /> )} */}
); }