import { useEffect, useState } from 'react' import { useNavigate } from 'react-router' import { AppLayout } from '@/components/AppLayout' import { playlistsService, type Playlist, type PlaylistSortField, type SortOrder } from '@/lib/api/services/playlists' import { Skeleton } from '@/components/ui/skeleton' import { Input } from '@/components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Button } from '@/components/ui/button' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { Badge } from '@/components/ui/badge' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { AlertCircle, Search, SortAsc, SortDesc, X, RefreshCw, Music, User, Calendar, Clock, Plus, Play, Edit } from 'lucide-react' import { toast } from 'sonner' import { formatDuration } from '@/utils/format-duration' export function PlaylistsPage() { const navigate = useNavigate() const [playlists, setPlaylists] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) // Search and sorting state const [searchQuery, setSearchQuery] = useState('') const [sortBy, setSortBy] = useState('name') const [sortOrder, setSortOrder] = useState('asc') // Create playlist dialog state const [showCreateDialog, setShowCreateDialog] = useState(false) const [createLoading, setCreateLoading] = useState(false) const [newPlaylist, setNewPlaylist] = useState({ name: '', description: '', genre: '' }) // Debounce search query const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(searchQuery) useEffect(() => { const handler = setTimeout(() => { setDebouncedSearchQuery(searchQuery) }, 300) return () => clearTimeout(handler) }, [searchQuery]) const fetchPlaylists = async () => { try { setLoading(true) setError(null) const playlistData = await playlistsService.getPlaylists({ search: debouncedSearchQuery.trim() || undefined, sort_by: sortBy, sort_order: sortOrder, }) setPlaylists(playlistData) } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to fetch playlists' setError(errorMessage) toast.error(errorMessage) } finally { setLoading(false) } } useEffect(() => { fetchPlaylists() }, [debouncedSearchQuery, sortBy, sortOrder]) const handleCreatePlaylist = async () => { if (!newPlaylist.name.trim()) { toast.error('Playlist name is required') return } try { setCreateLoading(true) await playlistsService.createPlaylist({ name: newPlaylist.name.trim(), description: newPlaylist.description.trim() || undefined, genre: newPlaylist.genre.trim() || undefined, }) toast.success(`Playlist "${newPlaylist.name}" created successfully`) // Reset form and close dialog setNewPlaylist({ name: '', description: '', genre: '' }) setShowCreateDialog(false) // Refresh the playlists list fetchPlaylists() } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to create playlist' toast.error(errorMessage) } finally { setCreateLoading(false) } } const handleCancelCreate = () => { setNewPlaylist({ name: '', description: '', genre: '' }) setShowCreateDialog(false) } const handleSetCurrent = async (playlist: Playlist) => { try { await playlistsService.setCurrentPlaylist(playlist.id) toast.success(`"${playlist.name}" is now the current playlist`) // Refresh the playlists list to update the current status fetchPlaylists() } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to set current playlist' toast.error(errorMessage) } } const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString() } const renderContent = () => { if (loading) { return (
{Array.from({ length: 5 }).map((_, i) => ( ))}
) } if (error) { return (

Failed to load playlists

{error}

) } if (playlists.length === 0) { return (

No playlists found

{searchQuery ? 'No playlists match your search criteria.' : 'No playlists are available.'}

) } return (
Name Genre User Tracks Duration Created Status Actions {playlists.map((playlist) => (
{playlist.name}
{playlist.description && (
{playlist.description}
)}
{playlist.genre ? ( {playlist.genre} ) : ( - )} {playlist.user_name ? (
{playlist.user_name}
) : ( System )}
{playlist.sound_count}
{formatDuration(playlist.total_duration || 0)}
{formatDate(playlist.created_at)}
{playlist.is_current && ( Current )} {playlist.is_main && ( Main )} {!playlist.is_current && !playlist.is_main && ( - )}
{!playlist.is_current && ( )}
))}
) } return (

Playlists

Manage and browse your soundboard playlists

Create New Playlist Add a new playlist to organize your sounds. Give it a name and optionally add a description and genre.
setNewPlaylist(prev => ({ ...prev, name: e.target.value }))} onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleCreatePlaylist() } }} />