import { useCallback, useEffect, useState } from "react"; import { Link } from "wouter"; import { ExternalLink, Plus, Trash2 } from "lucide-react"; import { api } from "@/api"; import type { MaintenanceWindow, MonitorView, StatusPage } from "@/types"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { cn } from "@/lib/utils"; import { t } from "@/i18n"; // Admin for public status pages + maintenance windows. The public view // itself lives at /status/:slug. export function StatusPagesPage() { const [pages, setPages] = useState([]); const [windows, setWindows] = useState([]); const [monitors, setMonitors] = useState([]); const [loaded, setLoaded] = useState(false); const [pageOpen, setPageOpen] = useState(false); const [pTitle, setPTitle] = useState(""); const [pSlug, setPSlug] = useState(""); const [pDesc, setPDesc] = useState(""); const [pMonitors, setPMonitors] = useState([]); const [winOpen, setWinOpen] = useState(false); const [wTitle, setWTitle] = useState(""); const [wStart, setWStart] = useState(""); const [wEnd, setWEnd] = useState(""); const [busy, setBusy] = useState(false); const [err, setErr] = useState(""); const load = useCallback(async () => { const [{ data: p }, { data: w }, { data: m }] = await Promise.all([ api.GET("/api/status-pages"), api.GET("/api/maintenance"), api.GET("/api/monitors"), ]); if (p) setPages(p); if (w) setWindows(w); if (m) setMonitors(m); setLoaded(true); }, []); useEffect(() => { void load(); const t = setInterval(() => void load(), 30_000); return () => clearInterval(t); }, [load]); async function createPage() { if (!pTitle.trim()) return; setBusy(true); setErr(""); const { error } = await api.POST("/api/status-pages", { body: { title: pTitle.trim(), ...(pSlug.trim() ? { slug: pSlug.trim() } : {}), description: pDesc.trim(), monitorIds: pMonitors, }, }); setBusy(false); if (error) { setErr(t("statusPages.createError")); return; } setPageOpen(false); setPTitle(""); setPSlug(""); setPDesc(""); setPMonitors([]); void load(); } async function createWindow() { if (!wTitle.trim() || !wStart || !wEnd) return; setBusy(true); setErr(""); const { error } = await api.POST("/api/maintenance", { body: { title: wTitle.trim(), startsAt: new Date(wStart).toISOString(), endsAt: new Date(wEnd).toISOString(), }, }); setBusy(false); if (error) { setErr(t("maintenance.createError")); return; } setWinOpen(false); setWTitle(""); setWStart(""); setWEnd(""); void load(); } return (

{t("statusPages.title")}

{t("statusPages.publicAt")} /status/<slug>.

{loaded && pages.length === 0 ? (

{t("statusPages.empty")}

) : (
{pages.map((p) => (
{p.title} /status/{p.slug}
))}
)}

{t("maintenance.title")}

{t("maintenance.subtitle")}

{loaded && windows.length === 0 ? (

{t("maintenance.empty")}

) : (
{windows.map((w) => (
{w.title} {new Date(w.startsAt ?? "").toLocaleString()} → {new Date(w.endsAt ?? "").toLocaleString()} {w.active ? t("maintenance.active") : t("maintenance.scheduled")}
))}
)} {t("statusPages.newTitle")} {t("statusPages.newDesc")}
setPTitle(e.target.value)} placeholder={t("statusPages.titlePlaceholder")} autoFocus />
setPSlug(e.target.value)} placeholder={t("statusPages.slugPlaceholder")} className="font-mono text-[12px]" />
setPDesc(e.target.value)} placeholder={t("common.optional")} />
{monitors.map((m) => ( ))} {monitors.length === 0 &&

{t("statusPages.noMonitors")}

}

{t("statusPages.allHint")}

{err ?

{err}

: null}
{t("maintenance.dialogTitle")} {t("maintenance.dialogDesc")}
setWTitle(e.target.value)} placeholder={t("maintenance.titlePlaceholder")} autoFocus />
setWStart(e.target.value)} />
setWEnd(e.target.value)} />
{err ?

{err}

: null}
); }