"use client"; import { useEffect, useState, type ReactNode } from "react"; import { Modal } from "./modal"; import { CoverArt } from "./cover-art"; type Track = { position: number; title: string; lengthMs: number | null }; function fmt(ms: number | null): string { if (ms == null) return ""; const s = Math.round(ms / 1000); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`; } export type AlbumInfo = { rgMbid: string | null; album: string; artist: string; year?: string | null; type?: string | null; }; /** Reusable album modal: cover art + tracklist (fetched from MB by release-group MBID) with * slots for a status chip and context-specific action buttons. */ export function AlbumModal({ open, onClose, album, status, actions, }: { open: boolean; onClose: () => void; album: AlbumInfo; status?: ReactNode; actions?: ReactNode; }) { const [tracks, setTracks] = useState("loading"); useEffect(() => { if (!open) return; if (!album.rgMbid) { setTracks("error"); return; } setTracks("loading"); let cancelled = false; fetch(`/api/mb/release-groups/${album.rgMbid}/tracks`) .then((r) => (r.ok ? r.json() : Promise.reject())) .then((d) => !cancelled && setTracks(d.tracks)) .catch(() => !cancelled && setTracks("error")); return () => { cancelled = true; }; }, [open, album.rgMbid]); return (

{album.album}

{album.type ? {album.type} : null} {album.year ? ( <> · {album.year} ) : null}
{status ?
{status}
: null} {actions ?
{actions}
: null}
{tracks === "loading" ?

Loading tracks…

: null} {tracks === "error" ?

No tracklist available.

: null} {Array.isArray(tracks) ? (
    {tracks.map((t) => (
  1. {t.position} {t.title} {t.lengthMs != null ? {fmt(t.lengthMs)} : null}
  2. ))}
) : null}
); }