"use client"; import PageHeading from "./ui/PageHeading"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { authFetch, getApiBase, getToken, clearToken, getEventStreamToken } from "./lib/auth"; import { normalizeRecentResults, normalizeSearchResults, type RecentRequest, type RequestSearchResult, } from "./lib/request-results"; import RequestStageFilter, { type RequestStage } from "./ui/RequestStageFilter"; export default function HomePage() { const router = useRouter(); const [query, setQuery] = useState(""); const [recent, setRecent] = useState([]); const [recentError, setRecentError] = useState(null); const [recentLoading, setRecentLoading] = useState(false); const [searchResults, setSearchResults] = useState([]); const [searchError, setSearchError] = useState(null); const [role, setRole] = useState(null); const [recentDays, setRecentDays] = useState(90); const [recentStage, setRecentStage] = useState("all"); const [authReady, setAuthReady] = useState(false); const submit = (event: React.FormEvent) => { event.preventDefault(); const trimmed = query.trim(); if (!trimmed) return; if (/^\d+$/.test(trimmed)) { router.push(`/requests/${encodeURIComponent(trimmed)}`); return; } void runSearch(trimmed); }; useEffect(() => { if (!getToken()) { router.push("/login"); return; } let cancelled = false; const load = async () => { setRecentLoading(true); setRecentError(null); try { const baseUrl = getApiBase(); const meResponse = await authFetch(`${baseUrl}/auth/me`); if (!meResponse.ok) { if (meResponse.status === 401) { clearToken(); router.push("/login"); return; } throw new Error(`Auth failed: ${meResponse.status}`); } const me = await meResponse.json(); if (cancelled) return; const userRole = me?.role ?? null; setRole(userRole); setAuthReady(true); const take = userRole === "admin" ? 50 : 6; const params = new URLSearchParams({ take: String(take), days: String(recentDays), }); if (recentStage !== "all") { params.set("stage", recentStage); } const response = await authFetch(`${baseUrl}/requests/recent?${params.toString()}`); if (!response.ok) { if (response.status === 401) { clearToken(); router.push("/login"); return; } throw new Error(`Recent requests failed: ${response.status}`); } const data = await response.json(); if (cancelled) return; if (Array.isArray(data?.results)) { setRecent(normalizeRecentResults(data.results)); } } catch (error) { console.error(error); if (!cancelled) setRecentError("Recent requests are not available right now."); } finally { if (!cancelled) setRecentLoading(false); } }; void load(); return () => { cancelled = true; }; }, [recentDays, recentStage, router]); useEffect(() => { if (!authReady) { return; } if (!getToken()) { return; } const baseUrl = getApiBase(); let closed = false; let source: EventSource | null = null; const connect = async () => { try { const streamToken = await getEventStreamToken(); if (closed) return; const params = new URLSearchParams({ stream_token: streamToken, recent_days: String(recentDays), }); if (recentStage !== "all") { params.set("recent_stage", recentStage); } const streamUrl = `${baseUrl}/events/stream?${params.toString()}`; source = new EventSource(streamUrl); source.onmessage = (event) => { if (closed) return; try { const payload = JSON.parse(event.data); if (!payload || typeof payload !== "object") { return; } if (payload.type === "home_recent") { if (Array.isArray(payload.results)) { setRecent(normalizeRecentResults(payload.results)); setRecentError(null); setRecentLoading(false); } else if (typeof payload.error === "string" && payload.error.trim()) { setRecentError("Recent requests are not available right now."); setRecentLoading(false); } return; } } catch (error) { console.error(error); } }; } catch (error) { if (closed) return; console.error(error); } }; void connect(); return () => { closed = true; source?.close(); }; }, [authReady, recentDays, recentStage]); const runSearch = async (term: string) => { try { const baseUrl = getApiBase(); const response = await authFetch(`${baseUrl}/requests/search?query=${encodeURIComponent(term)}`); if (!response.ok) { if (response.status === 401) { clearToken(); router.push("/login"); return; } throw new Error(`Search failed: ${response.status}`); } const data = await response.json(); if (Array.isArray(data?.results)) { setSearchResults(normalizeSearchResults(data.results)); setSearchError(null); } } catch (error) { console.error(error); setSearchError("Search failed. Try a request ID instead."); setSearchResults([]); } }; const resolveArtworkUrl = (url?: string | null) => { if (!url) return null; return url.startsWith("http") ? url : `${getApiBase()}${url}`; }; const formatRequestTime = (value?: string | null) => { if (!value) return null; const date = new Date(value); if (Number.isNaN(date.valueOf())) return value; return date.toLocaleString(); }; const activeRecentCount = recent.filter((item) => { const label = String(item.statusLabel ?? "").toLowerCase(); return !label.includes("ready") && !label.includes("available") && !label.includes("declined"); }).length; const readyRecentCount = recent.filter((item) => { const label = String(item.statusLabel ?? "").toLowerCase(); return label.includes("ready") || label.includes("available"); }).length; const requestCardState = (value?: string) => { const label = String(value ?? "").toLowerCase(); if (label.includes("partial")) return { key: "attention", label: value || "Partially ready", progress: 65 }; if (!/not |unavailable|waiting/.test(label) && (label.includes("ready") || label.includes("available"))) return { key: "ready", label: value || "Ready", progress: 100 }; if (label.includes("declined") || label.includes("failed") || label.includes("error")) return { key: "attention", label: value || "Needs attention", progress: 12 }; if (label.includes("working") || label.includes("progress") || label.includes("download")) return { key: "processing", label: value || "In progress", progress: 58 }; return { key: "waiting", label: value || "Waiting", progress: 4 }; }; return (
setQuery(event.target.value)} placeholder="Dune 2021 or 1289" />
} /> {(searchError || searchResults.length > 0) && (
Search results

{searchError ? "Search unavailable" : `${searchResults.length} match${searchResults.length === 1 ? "" : "es"} found`}

{searchError ? (
{searchError}
) : (
{searchResults.map((item, index) => ( ))}
)}
)}
In view {recent.length}
In progress {activeRecentCount}
Ready {readyRecentCount}
Request activity

{role === "admin" ? "Recent requests" : "My recent requests"}

{authReady && (
)}
{authReady && }
{recentLoading ? (
) : recentError ? (
{recentError}
) : recent.length === 0 ? (
No requests match these filters Try a wider period or a different stage.
) : ( recent.map((item) => ( )) )}
); }