import React, { useState, useEffect, useRef } 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 { Badge } from '@/components/ui/badge'; import { UploadModal } from './UploadModal'; import { TrackEditDialog } from '@/features/tracks/components/TrackEditDialog'; import { WaveformDisplay } from '@/features/tracks/components/WaveformDisplay'; import { VirtualLibraryGrid } from './VirtualLibraryGrid'; import { apiService, type Track } from '@/services/api'; import { useToast } from '@/hooks/use-toast'; import { Loader2, Plus, Music, Play, Pause, Trash2, Upload, Search, Filter, Grid, List, MoreVertical, Clock, FileAudio, Volume2, Download, Edit, Activity } from 'lucide-react'; interface LibraryManagerProps { onTrackSelect?: (track: Track) => void; } export function LibraryManager({ onTrackSelect }: LibraryManagerProps) { const [tracks, setTracks] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [isDeletingTrack, setIsDeletingTrack] = 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 [selectedTrack, setSelectedTrack] = useState(null); const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); const [expandedWaveform, setExpandedWaveform] = useState(null); const [playingTrackId, setPlayingTrackId] = useState(null); const parentRef = useRef(null); const { toast } = useToast(); const fetchTracks = async () => { try { setIsLoading(true); setError(null); const response = await apiService.getTracks({ page: pagination.page, limit: pagination.limit, search: searchQuery || undefined, artist: filterType !== 'all' ? filterType : undefined, }); setTracks(response.tracks); setPagination(prev => ({ ...prev, total: response.total, })); } catch (err: any) { setError(err.response?.data?.error || err.message || 'Failed to fetch tracks'); console.error('Error fetching tracks:', err); } finally { setIsLoading(false); } }; useEffect(() => { fetchTracks(); }, [pagination.page, searchQuery, filterType]); const handleUploadComplete = () => { fetchTracks(); // Refresh tracks after upload }; const handleDeleteTrack = async (trackId: string) => { if (!confirm('Are you sure you want to delete this track?')) return; try { setIsDeletingTrack(trackId); await apiService.deleteTrack(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', }); } finally { setIsDeletingTrack(null); } }; const handleEditTrack = (track: Track) => { setSelectedTrack(track); setIsEditDialogOpen(true); }; const handleTrackUpdated = (updated: Track) => { setTracks(prev => prev.map(t => t.id === updated.id ? updated : t)); }; const toggleWaveform = (trackId: string) => { setExpandedWaveform(prev => prev === trackId ? null : trackId); }; const handlePlayTrack = (track: Track) => { setPlayingTrackId(track.id); onTrackSelect?.(track); // TODO: Implement actual playback toast({ title: 'Playing track', description: `Now playing: ${track.title} by ${track.artist}`, }); }; const formatDuration = (durationMs?: number): string => { if (!durationMs) return '0:00'; const minutes = Math.floor(durationMs / 60000); const seconds = Math.floor((durationMs % 60000) / 1000); return `${minutes}:${seconds.toString().padStart(2, '0')}`; }; const formatFileSize = (bytes?: number): string => { if (!bytes) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2)) } ${ sizes[i]}`; }; 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.'}

{!searchQuery && ( )}
) : (
playingTrackId === trackId} parentRef={parentRef} />
)} {/* Track Edit Dialog */} {selectedTrack && ( )} {/* Statistics */}
{pagination.total}
Total Tracks
{tracks.filter(track => track.isPublic).length}
Public Tracks
{tracks.reduce((total, track) => total + (track.playCount || 0), 0)}
Total Plays
{Math.round(tracks.reduce((total, track) => total + (track.durationMs || 0), 0) / 60000)}m
Total Duration
{/* Upload Modal - Centralisé (une seule instance) */} setIsUploadModalOpen(false)} onUploadComplete={() => { handleUploadComplete(); setIsUploadModalOpen(false); }} /> {/* Edit Dialog */} {selectedTrack && ( { setIsEditDialogOpen(false); setSelectedTrack(null); }} onTrackUpdated={handleTrackUpdated} /> )}
); }