94 lines
4.2 KiB
TypeScript
94 lines
4.2 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter } from "@/components/ui/card"
|
|
import { ListMusic, Plus, PlayCircle, MoreHorizontal } from "lucide-react"
|
|
import Link from "next/link"
|
|
import { Button } from "@/components/ui/button"
|
|
import { getApiUrl } from "@/lib/api-config"
|
|
import { Skeleton } from "@/components/ui/skeleton"
|
|
import { Playlist } from "@/types/models"
|
|
|
|
export function UserPlaylistsList({ userId, isOwner = false }: { userId: number, isOwner?: boolean }) {
|
|
const [playlists, setPlaylists] = useState<Playlist[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
|
|
useEffect(() => {
|
|
const token = localStorage.getItem("token")
|
|
if (!token) return
|
|
|
|
// If isOwner, use /mine endpoint to see private playlists too
|
|
const endpoint = isOwner ? `${getApiUrl()}/playlists/mine` : `${getApiUrl()}/playlists?user_id=${userId}`
|
|
|
|
fetch(endpoint, {
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => setPlaylists(data))
|
|
.catch(err => console.error(err))
|
|
.finally(() => setLoading(false))
|
|
}, [userId, isOwner])
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
{[1, 2, 3].map(i => (
|
|
<Skeleton key={i} className="h-40 w-full rounded-lg" />
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (playlists.length === 0) {
|
|
return (
|
|
<div className="text-center py-12 border-2 border-dashed rounded-lg">
|
|
<ListMusic className="h-12 w-12 text-muted-foreground/30 mx-auto mb-4" />
|
|
<p className="text-muted-foreground font-medium">No playlists created yet</p>
|
|
{isOwner && (
|
|
<Button variant="outline" className="mt-4 gap-2">
|
|
<Plus className="h-4 w-4" /> Create Playlist
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{isOwner && (
|
|
<div className="flex justify-end">
|
|
<Button size="sm" className="gap-2">
|
|
<Plus className="h-4 w-4" /> New Playlist
|
|
</Button>
|
|
</div>
|
|
)}
|
|
|
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
{playlists.map((playlist) => (
|
|
<Link key={playlist.id} href={`/playlists/${playlist.id}`} className="group">
|
|
<Card className="h-full hover:border-primary/50 transition-colors cursor-pointer flex flex-col">
|
|
<CardHeader>
|
|
<div className="flex justify-between items-start gap-2">
|
|
<div className="bg-primary/10 p-2 rounded-md group-hover:bg-primary/20 transition-colors">
|
|
<ListMusic className="h-5 w-5 text-primary" />
|
|
</div>
|
|
{!playlist.is_public && (
|
|
<span className="text-[10px] uppercase font-bold tracking-wider bg-muted text-muted-foreground px-1.5 py-0.5 rounded">Private</span>
|
|
)}
|
|
</div>
|
|
<CardTitle className="mt-2 line-clamp-1">{playlist.name}</CardTitle>
|
|
<CardDescription className="line-clamp-2 min-h-[2.5em]">
|
|
{playlist.description || "No description"}
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardFooter className="mt-auto pt-0 text-xs text-muted-foreground flex justify-between items-center">
|
|
<span>{playlist.performance_count} tracks</span>
|
|
<span>Updated {new Date(playlist.created_at).toLocaleDateString()}</span>
|
|
</CardFooter>
|
|
</Card>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|