78 lines
3.6 KiB
TypeScript
78 lines
3.6 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import { useRouter } from 'next/navigation'
|
|
import { authFetch, getApiBase, getToken } from '../lib/auth'
|
|
import AdminShell from '../ui/AdminShell'
|
|
import { CONFIG_GROUPS, serviceStatusLabel } from './configNavigation'
|
|
|
|
type ServiceState = { name: string; status: string }
|
|
|
|
export default function AdminLandingPage() {
|
|
const router = useRouter()
|
|
const [services, setServices] = useState<ServiceState[]>([])
|
|
const [ready, setReady] = useState(false)
|
|
const [error, setError] = useState('')
|
|
|
|
useEffect(() => {
|
|
let active = true
|
|
const load = async () => {
|
|
if (!getToken()) { router.replace('/login'); return }
|
|
try {
|
|
const response = await authFetch(`${getApiBase()}/auth/me`)
|
|
if (!response.ok) { router.replace('/login'); return }
|
|
if ((await response.json())?.role !== 'admin') { router.replace('/'); return }
|
|
if (!active) return
|
|
setReady(true)
|
|
const status = await authFetch(`${getApiBase()}/status/services`)
|
|
if (!status.ok) throw new Error('Status unavailable')
|
|
const data = await status.json()
|
|
if (active) setServices(Array.isArray(data.services) ? data.services : [])
|
|
} catch {
|
|
if (active) setError('Connection status is unavailable. Refresh the page to try again.')
|
|
}
|
|
}
|
|
void load()
|
|
return () => { active = false }
|
|
}, [router])
|
|
|
|
return (
|
|
<AdminShell title="Settings" subtitle="Connections, access and the way Magent works.">
|
|
{!ready ? error ? <p className="error-banner" role="alert">{error}</p> : <p role="status">Loading settings…</p> : (
|
|
<div className="config-directory">
|
|
{error && <p className="error-banner" role="alert">{error}</p>}
|
|
{CONFIG_GROUPS.filter((group) => !group.advanced).map((group) => (
|
|
<section className="config-directory-region" key={group.title}>
|
|
<header><h2>{group.title}</h2><p>{group.description}</p></header>
|
|
<div className="config-directory-links">
|
|
{group.items.map((item) => {
|
|
const service = services.find((entry) => entry.name.toLowerCase() === item.service?.toLowerCase())
|
|
return (
|
|
<a href={item.href} key={item.href} className="config-directory-link">
|
|
{item.symbol && <span className="config-link-icon" aria-hidden="true">{item.symbol}</span>}
|
|
<span className="config-link-copy"><strong>{item.label}</strong><small>{item.description}</small></span>
|
|
{item.service && <span className={`config-connection-badge is-${service?.status ?? 'unknown'}`}>{serviceStatusLabel(service?.status)}</span>}
|
|
<span className="config-link-arrow" aria-hidden="true">→</span>
|
|
</a>
|
|
)
|
|
})}
|
|
</div>
|
|
</section>
|
|
))}
|
|
<details className="config-advanced-directory">
|
|
<summary><strong>Advanced tools</strong><span>Hosting, logs, caches and recovery</span></summary>
|
|
<div className="config-directory-links">
|
|
{CONFIG_GROUPS.filter((group) => group.advanced).flatMap((group) => group.items).map((item) => (
|
|
<a href={item.href} key={item.href} className="config-directory-link">
|
|
<span className="config-link-copy"><strong>{item.label}</strong><small>{item.description}</small></span>
|
|
<span className="config-link-arrow" aria-hidden="true">→</span>
|
|
</a>
|
|
))}
|
|
</div>
|
|
</details>
|
|
</div>
|
|
)}
|
|
</AdminShell>
|
|
)
|
|
}
|