'use client'; import { useCallback, useEffect, useState } from 'react'; import { formatDate } from '@/lib/format'; type Reserved = { name: string; created_at: string }; export default function AdminReservedPage() { const [reserved, setReserved] = useState([]); const [hardcoded, setHardcoded] = useState([]); const [name, setName] = useState(''); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [notice, setNotice] = useState(null); const [busy, setBusy] = useState(false); const load = useCallback(async () => { setLoading(true); setError(null); try { const res = await fetch('/api/admin/reserved'); if (!res.ok) { const b = (await res.json().catch(() => ({}))) as { error?: string }; throw new Error(b.error ?? `Request failed (${res.status})`); } const data = (await res.json()) as { reserved: Reserved[]; hardcoded: string[]; }; setReserved(data.reserved); setHardcoded(data.hardcoded); } catch (e) { setError((e as Error).message); } finally { setLoading(false); } }, []); useEffect(() => { load(); }, [load]); async function add(e: React.FormEvent) { e.preventDefault(); const value = name.trim().toLowerCase(); if (!value) return; setBusy(true); setError(null); setNotice(null); try { const res = await fetch('/api/admin/reserved', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: value }), }); if (!res.ok) { const b = (await res.json().catch(() => ({}))) as { error?: string }; throw new Error(b.error ?? `Request failed (${res.status})`); } setName(''); setNotice(`Reserved '${value}'`); await load(); } catch (e) { setError((e as Error).message); } finally { setBusy(false); } } async function remove(n: string) { if (!window.confirm(`Remove reserved subdomain '${n}'?`)) return; setBusy(true); setError(null); setNotice(null); try { const res = await fetch( `/api/admin/reserved?name=${encodeURIComponent(n)}`, { method: 'DELETE' }, ); if (!res.ok) { const b = (await res.json().catch(() => ({}))) as { error?: string }; throw new Error(b.error ?? `Request failed (${res.status})`); } setNotice(`Removed '${n}'`); await load(); } catch (e) { setError((e as Error).message); } finally { setBusy(false); } } return (

Reserved subdomains

Add reserved subdomain

setName(e.target.value.toLowerCase())} placeholder="e.g. status" autoCapitalize="none" autoCorrect="off" spellCheck={false} style={{ maxWidth: 280 }} />
{error &&

{error}

} {notice &&

{notice}

}

Database reserved

{loading ? (

Loading…

) : reserved.length === 0 ? (

None reserved in the database.

) : (
{reserved.map((r) => ( ))}
Name Added
{r.name} {formatDate(r.created_at)}
)}

Built-in reserved

These are hardcoded in the app and always reserved (cannot be removed here).

{hardcoded.map((h) => ( {h} ))}
); }