import React, { useState, useEffect } from 'react'; import { Song, Playlist, playlistsApi, songsApi, getAudioUrl } from '../services/api'; import { useAuth } from '../context/AuthContext'; import { useI18n } from '../context/I18nContext'; import { ArrowLeft, Play, MoreHorizontal, Clock, Calendar, Shuffle, Trash2, Mic2, Music } from 'lucide-react'; interface PlaylistDetailProps { playlistId: string; onBack: () => void; onPlaySong: (song: Song, list?: Song[]) => void; onSelect: (song: Song) => void; onNavigateToProfile: (username: string) => void; } export const PlaylistDetail: React.FC = ({ playlistId, onBack, onPlaySong, onSelect, onNavigateToProfile }) => { const { user: currentUser, token } = useAuth(); const { t } = useI18n(); const [playlist, setPlaylist] = useState(null); const [songs, setSongs] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { loadPlaylist(); }, [playlistId]); const loadPlaylist = async () => { setLoading(true); try { const res = await playlistsApi.getPlaylist(playlistId, token); // res.playlist comes from DB row, which now includes creator_avatar setPlaylist(res.playlist as any); const mappedSongs: Song[] = res.songs.map((s: any) => ({ id: s.id, title: s.title, lyrics: s.lyrics, style: s.style, coverUrl: s.cover_url || s.coverUrl || `https://picsum.photos/seed/${s.id}/400/400`, audioUrl: getAudioUrl(s.audio_url || s.audioUrl, s.id), duration: s.duration, bpm: s.bpm, tags: s.tags || [], is_public: s.is_public || false, likeCount: s.like_count || 0, viewCount: s.view_count || 0, creator: s.creator, created_at: s.created_at, addedAt: s.added_at })); setSongs(mappedSongs); } catch (error) { console.error('Failed to load playlist:', error); } finally { setLoading(false); } }; // ... (retaining methods handleRemove, handleDelete) ... const handleRemoveSong = async (songId: string) => { if (!token || !playlist) return; try { await playlistsApi.removeSong(playlist.id, songId, token); setSongs(prev => prev.filter(s => s.id !== songId)); } catch (error) { console.error('Failed to remove song:', error); } }; const handleDeletePlaylist = async () => { if (!token || !playlist) return; if (!confirm(t('deletePlaylistConfirm'))) return; try { await playlistsApi.delete(playlist.id, token); onBack(); } catch (error) { console.error('Failed to delete playlist:', error); } }; if (loading) return (
{t('loadingPlaylist')}
); if (!playlist) return (
{t('playlistNotFound')}
); const isOwner = currentUser?.id === playlist.user_id; // Gradient based on ID/Name const gradients = [ 'from-purple-900 to-black', 'from-blue-900 to-black', 'from-indigo-900 to-black', 'from-rose-900 to-black', ]; const bgGradient = gradients[playlist.name.length % gradients.length]; return (
{/* Header */}
{/* Cover */}
{playlist.cover_url ? ( {playlist.name} ) : (
{playlist.name[0].toUpperCase()}
)}
{/* Info */}
{t('playlist')}

{playlist.name}

{playlist.description && (

{playlist.description}

)}
{playlist.creator && (
onNavigateToProfile(playlist.creator!)} > {playlist.creator_avatar ? ( {playlist.creator} ) : (
)} {playlist.creator}
)} {songs.length} {t('songs')} {songs.reduce((acc, s) => acc + (s.duration ? (typeof s.duration === 'string' ? 0 : s.duration) : 0), 0) > 0 ? Math.floor(songs.reduce((acc, s) => acc + (s.duration as number || 0), 0) / 60) + " " + t('min') : ""}
{/* Actions Bar */}
{isOwner && ( )}
{playlist.is_public ? t('public') : t('private')}
{/* Song List */}
{/* Desktop Header */}
# {t('title')} {t('artist')} {t('dateAdded')}
{songs.map((song, index) => (
{ onSelect(song); onPlaySong(song, songs); }} > {/* Index - hidden on mobile */} {index + 1} {/* Cover + Title */}
{song.title} {song.creator || t('unknown')} • {song.duration ? `${Math.floor(song.duration / 60)}:${String(Math.floor(song.duration % 60)).padStart(2, '0')}` : '0:00'}
{/* Artist - hidden on mobile */} { e.stopPropagation(); song.creator && onNavigateToProfile(song.creator); }}> {song.creator || t('unknown')} {/* Date Added - hidden on mobile */} {song.addedAt ? new Date(song.addedAt).toLocaleDateString() : t('justNow')} {/* Duration + Actions */}
{song.duration ? `${Math.floor(song.duration / 60)}:${String(Math.floor(song.duration % 60)).padStart(2, '0')}` : '0:00'} {isOwner && ( )}
{/* Mobile delete button */} {isOwner && ( )}
))}
{/* Back button absolute */}
); };