151 lines
5.7 KiB
TypeScript
151 lines
5.7 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useEffect } from "react"
|
|
import { Button } from "@/components/ui/button"
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
} from "@/components/ui/dialog"
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select"
|
|
import { ListMusic, Loader2 } from "lucide-react"
|
|
import { useToast } from "@/components/ui/use-toast"
|
|
import { getApiUrl } from "@/lib/api-config"
|
|
import { Label } from "@/components/ui/label"
|
|
import { Input } from "@/components/ui/input"
|
|
|
|
import { Playlist } from "@/types/models"
|
|
|
|
interface AddToPlaylistDialogProps {
|
|
performanceId: number
|
|
songTitle: string
|
|
}
|
|
|
|
export function AddToPlaylistDialog({ performanceId, songTitle }: AddToPlaylistDialogProps) {
|
|
const [open, setOpen] = useState(false)
|
|
const [playlists, setPlaylists] = useState<Playlist[]>([])
|
|
const [loading, setLoading] = useState(false)
|
|
const [submitting, setSubmitting] = useState(false)
|
|
const [selectedPlaylistId, setSelectedPlaylistId] = useState<string>("")
|
|
const [notes, setNotes] = useState("")
|
|
const { toast } = useToast()
|
|
|
|
useEffect(() => {
|
|
if (open && playlists.length === 0) {
|
|
setLoading(true)
|
|
const token = localStorage.getItem("token")
|
|
if (!token) return
|
|
|
|
fetch(`${getApiUrl()}/playlists/mine`, {
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
})
|
|
.then(res => res.json())
|
|
.then((data: Playlist[]) => setPlaylists(data))
|
|
.catch(err => console.error(err))
|
|
.finally(() => setLoading(false))
|
|
}
|
|
}, [open, playlists.length])
|
|
|
|
const handleSubmit = async () => {
|
|
if (!selectedPlaylistId) return
|
|
|
|
setSubmitting(true)
|
|
const token = localStorage.getItem("token")
|
|
|
|
try {
|
|
const res = await fetch(`${getApiUrl()}/playlists/${selectedPlaylistId}/performances/${performanceId}`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${token}`
|
|
},
|
|
body: JSON.stringify({ notes: notes || undefined })
|
|
})
|
|
|
|
if (res.ok) {
|
|
toast({ title: "Added to playlist" })
|
|
setOpen(false)
|
|
} else if (res.status === 400) {
|
|
toast({ title: "Already in playlist", variant: "default" })
|
|
} else {
|
|
throw new Error("Failed to add")
|
|
}
|
|
} catch (error) {
|
|
console.error(error)
|
|
toast({ title: "Error", description: "Could not add to playlist", variant: "destructive" })
|
|
} finally {
|
|
setSubmitting(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<DialogTrigger asChild>
|
|
<Button variant="ghost" size="icon" className="h-6 w-6 text-muted-foreground hover:text-primary" title="Add to Playlist">
|
|
<ListMusic className="h-4 w-4" />
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent className="sm:max-w-[425px]">
|
|
<DialogHeader>
|
|
<DialogTitle>Add to Playlist</DialogTitle>
|
|
<DialogDescription>
|
|
Add "{songTitle}" to one of your collections.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<div className="grid gap-4 py-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="playlist">Select Playlist</Label>
|
|
<Select onValueChange={setSelectedPlaylistId} value={selectedPlaylistId}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select a playlist" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{loading ? (
|
|
<div className="flex items-center justify-center p-2">
|
|
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
|
|
</div>
|
|
) : playlists.length === 0 ? (
|
|
<div className="p-2 text-sm text-muted-foreground text-center">No playlists found</div>
|
|
) : (
|
|
playlists.map((p) => (
|
|
<SelectItem key={p.id} value={p.id.toString()}>
|
|
{p.name}
|
|
</SelectItem>
|
|
))
|
|
)}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="notes">Notes (Optional)</Label>
|
|
<Input
|
|
id="notes"
|
|
placeholder="Why did you pick this version?"
|
|
value={notes}
|
|
onChange={(e) => setNotes(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<DialogFooter>
|
|
<Button onClick={handleSubmit} disabled={submitting || !selectedPlaylistId}>
|
|
{submitting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
Add to Playlist
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|