import { useEffect, useState } from "react";
import { Link, Route, useLocation } from "wouter";
import { Activity, AlertTriangle, Globe, LayoutGrid, Megaphone, Moon, Plus, Search, Server, Settings, Sun } from "lucide-react";
import { BoardProvider, useBoard } from "@/board/store";
import { BoardSwitcher } from "@/components/BoardSwitcher";
import { Board } from "@/board/Board";
import { ServiceDialog } from "@/components/ServiceDialog";
import { WidgetDialog } from "@/components/WidgetDialog";
import { SettingsDialog } from "@/components/SettingsDialog";
import { CommandPalette } from "@/components/CommandPalette";
import { MonitorsPage } from "@/pages/MonitorsPage";
import { MonitorDetailPage } from "@/pages/MonitorDetailPage";
import { DomainsPage } from "@/pages/DomainsPage";
import { DomainDetailPage } from "@/pages/DomainDetailPage";
import { SystemsPage } from "@/pages/SystemsPage";
import { SystemDetailPage } from "@/pages/SystemDetailPage";
import { IncidentsPage } from "@/pages/IncidentsPage";
import { StatusPagesPage } from "@/pages/StatusPagesPage";
import { StatusPublicPage } from "@/pages/StatusPublicPage";
import { LoginPage } from "@/pages/LoginPage";
import { api } from "@/api";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { t, type Key } from "@/i18n";
import type { Item } from "@/types";
export default function App() {
return (
);
}
const NAV: { href: string; labelKey: Key; icon: typeof LayoutGrid }[] = [
{ href: "/", labelKey: "nav.board", icon: LayoutGrid },
{ href: "/monitors", labelKey: "nav.monitors", icon: Activity },
{ href: "/domains", labelKey: "nav.domains", icon: Globe },
{ href: "/systems", labelKey: "nav.systems", icon: Server },
{ href: "/incidents", labelKey: "nav.incidents", icon: AlertTriangle },
{ href: "/status", labelKey: "nav.status", icon: Megaphone },
];
function Shell() {
const board = useBoard();
const [loc] = useLocation();
// null = still checking; gate blocks everything except public status pages.
const [auth, setAuth] = useState<{ enabled?: boolean; setup?: boolean; authed?: boolean } | null>(null);
const [settingsOpen, setSettingsOpen] = useState(false);
const [paletteOpen, setPaletteOpen] = useState(false);
const [svcOpen, setSvcOpen] = useState(false);
const [wdgOpen, setWdgOpen] = useState(false);
const [editing, setEditing] = useState- ();
// ⌘K / Ctrl+K opens the palette; / does too when not typing in a field.
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
const typing = e.target instanceof HTMLElement
&& (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA" || e.target.isContentEditable);
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
e.preventDefault();
setPaletteOpen((o) => !o);
} else if (e.key === "/" && !typing && !e.metaKey && !e.ctrlKey) {
e.preventDefault();
setPaletteOpen(true);
}
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, []);
// Auth gate: check the session once, and again whenever the client sees a
// 401 (middleware in api.ts dispatches dash:unauthorized).
useEffect(() => {
const load = () => api.GET("/api/auth/session").then(({ data }) => setAuth(data ?? {}));
load();
window.addEventListener("dash:unauthorized", load);
return () => window.removeEventListener("dash:unauthorized", load);
}, []);
// URL is the board source of truth: / -> default, /b/ -> that board.
const setActiveSlug = board.setActiveSlug;
useEffect(() => {
setActiveSlug(loc.startsWith("/b/") ? decodeURIComponent(loc.slice(3)) : "");
}, [loc, setActiveSlug]);
function openEditor(it: Item) {
setEditing(it);
if (it.kind === "widget") setWdgOpen(true);
else setSvcOpen(true);
}
// Public status pages stay reachable when auth is on — the backend serves
// their API unauthenticated, so the SPA must let the route through too.
const publicPage = loc.startsWith("/status/");
if (auth?.enabled && !auth.authed && !publicPage) {
return location.reload()} />;
}
return (
Dash
{(p) => }
{(p) => }
{(p) => }
{(p) => }
{ setEditing(undefined); setSvcOpen(true); }}
onAddWidget={() => { setEditing(undefined); setWdgOpen(true); }}
onSettings={() => setSettingsOpen(true)}
/>
);
}