"use client"; import { useState, useEffect } from "react"; import { Calendar, Clock, Download, Music2, Disc, ArrowRight, CheckCircle2, Loader2 } from "lucide-react"; import { cn } from "@/utils/cn"; import { GradientSpinner } from "@/components/ui/GradientSpinner"; import Link from "next/link"; interface ReleaseItem { id: number | string; title: string; artistName: string; artistMbid?: string; albumMbid: string; releaseDate: string; coverUrl: string | null; source: 'lidarr' | 'similar'; status: 'upcoming' | 'released' | 'available'; inLibrary: boolean; canDownload: boolean; } interface ReleaseRadarData { upcoming: ReleaseItem[]; recent: ReleaseItem[]; monitoredArtistCount: number; similarArtistCount: number; } export default function ReleasesPage() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [downloadingId, setDownloadingId] = useState(null); const [error, setError] = useState(null); const fetchReleases = async () => { try { setLoading(true); const res = await fetch("/api/releases/radar?daysBack=30&daysAhead=90"); if (!res.ok) throw new Error("Failed to fetch releases"); const json = await res.json(); setData(json); } catch (err: any) { setError(err.message); } finally { setLoading(false); } }; useEffect(() => { fetchReleases(); }, []); const handleDownload = async (albumMbid: string, releaseId: string | number) => { try { setDownloadingId(releaseId); const res = await fetch(`/api/releases/download/${albumMbid}`, { method: "POST", }); if (res.ok) { // Refresh to show updated status await fetchReleases(); } } catch (err) { console.error("Download failed:", err); } finally { setDownloadingId(null); } }; const formatDate = (dateStr: string) => { const date = new Date(dateStr); const now = new Date(); const diffDays = Math.ceil((date.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)); if (diffDays === 0) return "Today"; if (diffDays === 1) return "Tomorrow"; if (diffDays > 0 && diffDays <= 7) return `In ${diffDays} days`; if (diffDays < 0 && diffDays >= -7) return `${Math.abs(diffDays)} days ago`; return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: date.getFullYear() !== now.getFullYear() ? "numeric" : undefined, }); }; if (loading) { return (
); } if (error) { return (

Failed to load releases

{error}

); } return (
{/* Hero Section */}
Release Radar

New & Upcoming

{data?.monitoredArtistCount || 0} monitored artists • {data?.upcoming.length || 0} upcoming • {data?.recent.length || 0} recent releases

{/* Upcoming Releases */} {data?.upcoming && data.upcoming.length > 0 && (

Coming Soon

({data.upcoming.length})
{data.upcoming.map((release) => ( ))}
)} {/* Recently Released */} {data?.recent && data.recent.length > 0 && (

Just Dropped

({data.recent.length})
{data.recent.map((release) => ( ))}
)} {/* Empty State */} {(!data?.upcoming?.length && !data?.recent?.length) && (

No releases found

Add artists to Lidarr and enable monitoring to see their upcoming and recent releases here.

Configure Lidarr
)}
); } function ReleaseCard({ release, formatDate, onDownload, isDownloading, }: { release: ReleaseItem; formatDate: (date: string) => string; onDownload: (albumMbid: string, releaseId: string | number) => void; isDownloading: boolean; }) { const isUpcoming = release.status === 'upcoming'; const hasIt = release.inLibrary; return (
{/* Cover Art */}
{release.coverUrl ? ( {release.title} ) : (
)} {/* Status Badge */}
{isUpcoming ? formatDate(release.releaseDate) : hasIt ? "In Library" : "Available"}
{/* Download Button Overlay */} {release.canDownload && !hasIt && ( )} {/* In Library Indicator */} {hasIt && (
)}
{/* Info */}

{release.title}

{release.artistName}

{isUpcoming && (

{new Date(release.releaseDate).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", })}

)}
); }