'use client' import { useRouter } from 'next/navigation' import { useEffect, useState } from 'react' import { authFetch, getApiBase, getToken, clearToken } from './lib/auth' export default function HomePage() { const router = useRouter() const [query, setQuery] = useState('') const [recent, setRecent] = useState< { id: number title: string year?: number statusLabel?: string artwork?: { poster_url?: string } createdAt?: string | null }[] >([]) const [recentError, setRecentError] = useState(null) const [recentLoading, setRecentLoading] = useState(false) const [searchResults, setSearchResults] = useState< { title: string; year?: number; type?: string; requestId?: number; statusLabel?: string }[] >([]) const [searchError, setSearchError] = useState(null) const [role, setRole] = useState(null) const [recentDays, setRecentDays] = useState(90) const [authReady, setAuthReady] = useState(false) const [servicesStatus, setServicesStatus] = useState< { overall: string; services: { name: string; status: string; message?: string }[] } | null >(null) const [servicesLoading, setServicesLoading] = useState(false) const [servicesError, setServicesError] = useState(null) const [serviceTesting, setServiceTesting] = useState>({}) const [serviceTestResults, setServiceTestResults] = useState>({}) 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) } const toServiceSlug = (name: string) => name.toLowerCase().replace(/[^a-z0-9]/g, '') const updateServiceStatus = (name: string, status: string, message?: string) => { setServicesStatus((prev) => { if (!prev) return prev return { ...prev, services: prev.services.map((service) => service.name === name ? { ...service, status, message } : service ), } }) } const testService = async (name: string) => { const slug = toServiceSlug(name) setServiceTesting((prev) => ({ ...prev, [name]: true })) setServiceTestResults((prev) => ({ ...prev, [name]: null })) try { const baseUrl = getApiBase() const response = await authFetch(`${baseUrl}/status/services/${slug}/test`, { method: 'POST', }) if (!response.ok) { if (response.status === 401) { clearToken() router.push('/login') return } const text = await response.text() throw new Error(text || `Service test failed: ${response.status}`) } const data = await response.json() const status = data?.status ?? 'unknown' const message = data?.message || (status === 'up' ? 'API OK' : status === 'down' ? 'API unreachable' : status === 'degraded' ? 'Health warnings' : status === 'not_configured' ? 'Not configured' : 'Unknown') setServiceTestResults((prev) => ({ ...prev, [name]: message })) updateServiceStatus(name, status, data?.message) } catch (error) { console.error(error) setServiceTestResults((prev) => ({ ...prev, [name]: 'Test failed' })) } finally { setServiceTesting((prev) => ({ ...prev, [name]: false })) } } useEffect(() => { if (!getToken()) { router.push('/login') return } 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() const userRole = me?.role ?? null setRole(userRole) setAuthReady(true) const take = userRole === 'admin' ? 50 : 6 const response = await authFetch( `${baseUrl}/requests/recent?take=${take}&days=${recentDays}` ) 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 (Array.isArray(data?.results)) { setRecent( data.results .filter((item: any) => item?.id) .map((item: any) => { const id = item.id const rawTitle = item.title const placeholder = typeof rawTitle === 'string' && rawTitle.trim().toLowerCase() === `request ${id}` return { id, title: !rawTitle || placeholder ? `Request #${id}` : rawTitle, year: item.year, statusLabel: item.statusLabel, artwork: item.artwork, createdAt: item.createdAt ?? null, } }) ) } } catch (error) { console.error(error) setRecentError('Recent requests are not available right now.') } finally { setRecentLoading(false) } } load() }, [recentDays]) useEffect(() => { if (!authReady) { return } const load = async () => { setServicesLoading(true) setServicesError(null) try { const baseUrl = getApiBase() const response = await authFetch(`${baseUrl}/status/services`) if (!response.ok) { if (response.status === 401) { clearToken() router.push('/login') return } throw new Error(`Service status failed: ${response.status}`) } const data = await response.json() setServicesStatus(data) } catch (error) { console.error(error) setServicesError('Service status is not available right now.') } finally { setServicesLoading(false) } } load() const timer = setInterval(load, 30000) return () => clearInterval(timer) }, [authReady, router]) 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( data.results.map((item: any) => ({ title: item.title, year: item.year, type: item.type, requestId: item.requestId, statusLabel: item.statusLabel, })) ) 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() } return (

System status

{servicesLoading ? 'Checking services...' : servicesError ? 'Status not available yet' : servicesStatus?.overall === 'up' ? 'Services are up and running' : servicesStatus?.overall === 'down' ? 'Something is down' : 'Some services need attention'}
{(() => { const order = [ 'Jellyseerr', 'Sonarr', 'Radarr', 'Prowlarr', 'qBittorrent', 'Jellyfin', ] const items = servicesStatus?.services ?? [] return order.map((name) => { const item = items.find((entry) => entry.name === name) const status = item?.status ?? 'unknown' const testing = serviceTesting[name] ?? false return (
{name} {serviceTestResults[name] && ( {serviceTestResults[name]} )}
{status === 'up' ? 'Up' : status === 'down' ? 'Down' : status === 'degraded' ? 'Needs attention' : status === 'not_configured' ? 'Not configured' : 'Unknown'}
) }) })()}

{role === 'admin' ? 'All requests' : 'My recent requests'}

{authReady && ( )}
{recentLoading ? (
) : recentError ? ( ) : recent.length === 0 ? ( ) : ( recent.map((item) => ( )) )}
) }