"use client"; import { useState, useEffect } from "react"; import { FolderOpen, RefreshCw, Disc, Music, ArrowLeft, Trash2, Search, Waves } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { ScrollArea } from "@/components/ui/scroll-area"; import { AlbumSelector } from "@/components/album-selector"; import { AlbumView } from "@/components/album-view"; import { StatusLog } from "@/components/status-log"; import { AudioWave } from "@/components/ui/audio-wave"; import { TidalSearch } from "@/components/tidal-search"; import { motion, AnimatePresence } from "motion/react"; import { Album } from "@/types"; export default function Dashboard() { const [albums, setAlbums] = useState([]); const [selectedAlbum, setSelectedAlbum] = useState(null); const [loading, setLoading] = useState(false); const [logs, setLogs] = useState([]); const [view, setView] = useState<"idle" | "selection" | "details" | "tidal">("idle"); const addLog = (message: string) => { setLogs((prev) => { const newLogs = [...prev, `[${new Date().toLocaleTimeString()}] ${message}`]; if (newLogs.length > 50) return newLogs.slice(newLogs.length - 50); return newLogs; }); }; const scanWatchFolder = async (preserveView = false) => { setLoading(true); addLog("Scanning watch folder..."); try { const res = await fetch("/api/scan"); const data = await res.json(); if (data.albums) { setAlbums(data.albums); addLog(`Found ${data.albums.length} albums.`); if (!preserveView) { if (data.albums.length > 0) { setView("selection"); } else { setView("idle"); } } } } catch (error) { addLog("Error scanning watch folder."); console.error(error); } finally { setLoading(false); } }; useEffect(() => { scanWatchFolder(); }, []); // Auto-refresh watch folder useEffect(() => { const interval = setInterval(() => { // Silent scan - don't show loading spinner for auto-refresh fetch("/api/scan") .then(res => res.json()) .then((data: { albums: Album[] }) => { if (data.albums) { // Only update if count changed or deep comparison (simplified here by length/names check) setAlbums(prev => { const prevNames = prev.map(a => a.path).sort().join(','); const newNames = data.albums.map((a) => a.path).sort().join(','); if (prevNames !== newNames) { addLog(`Watch folder updated: ${data.albums.length} albums found.`); return data.albums; } // Even if list didn't change structure, update to get new cover paths if they appeared // But we need to be careful not to cause re-renders if nothing changed. // Let's check if any coverPath changed for the selected album return data.albums; }); // Sync selectedAlbum with new data to ensure cover art updates if (selectedAlbum) { const updatedSelected = data.albums.find((a) => a.path === selectedAlbum.path); if (updatedSelected && updatedSelected.coverPath !== selectedAlbum.coverPath) { setSelectedAlbum(updatedSelected); } } // If we are in idle view and albums appear, switch to selection if (view === "idle" && data.albums.length > 0) { setView("selection"); } // If we are in selection view and albums disappear, switch to idle. // However, if we are in TIDAL view, we shouldn't force switch back. if (view === "selection" && data.albums.length === 0) { setView("idle"); } } }) .catch(err => console.error("Auto-scan failed", err)); }, 1000); return () => clearInterval(interval); }, [view, selectedAlbum]); const handleAlbumUpdate = async (newPath?: string) => { await scanWatchFolder(true); if (newPath) { const name = newPath.split('/').pop() || ""; if (selectedAlbum) { setSelectedAlbum({ ...selectedAlbum, path: newPath, name }); } } }; const handleSelectAlbum = (album: Album) => { setSelectedAlbum(album); setView("details"); }; const handleBack = () => { setSelectedAlbum(null); setView(albums.length > 0 ? "selection" : "idle"); }; return (
{/* Header */}

crossfade

{view === "details" && ( )} {view === "tidal" ? ( ) : ( )}
{/* Main Content Area */}
{view === "idle" && (

Waiting for Music

Place albums in the watch folder to begin.

)} {view === "selection" && (

Select Album

Found {albums.length} albums ready for processing

)} {view === "tidal" && (

TIDAL Search

Search and copy links from TIDAL

)} {view === "details" && selectedAlbum && (
)}
{/* Status Log */} {process.env.NEXT_PUBLIC_SHOW_SYSTEM_LOG === "true" && (
)}
); }