diff --git a/apps/fabro-web/app/router.tsx b/apps/fabro-web/app/router.tsx index 7b8332dc8..e9382e032 100644 --- a/apps/fabro-web/app/router.tsx +++ b/apps/fabro-web/app/router.tsx @@ -28,6 +28,8 @@ import * as Insights from "./routes/insights"; import * as InsightsEditor from "./routes/insights-editor"; import * as InsightsNew from "./routes/insights-new"; import * as Settings from "./routes/settings"; +import * as SettingsOverview from "./routes/settings-overview"; +import * as SettingsLiveEvents from "./routes/settings-live-events"; import * as Profile from "./routes/profile"; import AppShellModule from "./layouts/app-shell"; @@ -113,7 +115,12 @@ export const routes: RouteObject[] = [ route("new", InsightsNew), ], }), - route("settings", Settings), + route("settings", Settings, { + children: [ + indexRoute(SettingsOverview), + route("live-events", SettingsLiveEvents), + ], + }), route("profile", Profile), ], }, diff --git a/apps/fabro-web/app/routes/settings-live-events.tsx b/apps/fabro-web/app/routes/settings-live-events.tsx new file mode 100644 index 000000000..ed77f700f --- /dev/null +++ b/apps/fabro-web/app/routes/settings-live-events.tsx @@ -0,0 +1,3 @@ +export default function SettingsLiveEvents() { + return
; +} diff --git a/apps/fabro-web/app/routes/settings-overview.tsx b/apps/fabro-web/app/routes/settings-overview.tsx new file mode 100644 index 000000000..cdab04d34 --- /dev/null +++ b/apps/fabro-web/app/routes/settings-overview.tsx @@ -0,0 +1,221 @@ +import { useState } from "react"; +import type { + ServerListenSettings, + ServerSettings, +} from "@qltysh/fabro-api-client"; +import { useServerSettings } from "../lib/queries"; +import { CollapsibleFile } from "../components/collapsible-file"; +import { + Badge, + Count, + Mono, + Muted, + NumberValue, + ObjectStoreValue, + Panel, + PanelSkeleton, + Row, + type SettingsView, + Toggle, + UrlValue, + UsernameList, + ViewToggle, + plural, +} from "../components/settings-panel"; + +export default function SettingsOverview() { + const settingsQuery = useServerSettings(); + const settings = settingsQuery.data; + const [view, setView] = useState("settings"); + + if (!settings) { + return ( +
+ + + + + +
+ ); + } + + return ( +
+ + {view === "settings" ? ( + <> + + + + + + ) : ( + + )} +
+ ); +} + +function PageIntro({ + view, + setView, +}: { + view: SettingsView; + setView: (v: SettingsView) => void; +}) { + return ( +
+

+ Snapshot of the server configuration. Edit via{" "} + settings.toml; changes take + effect on the next server restart. +

+ +
+ ); +} + +function ServerPanel({ settings }: { settings: ServerSettings }) { + const { listen, web, api, scheduler } = settings.server; + return ( + + + {web.enabled ? : } + + + {api.url ? : Same origin} + + + + + + + + + ); +} + +function DataPanel({ settings }: { settings: ServerSettings }) { + const { storage, slatedb, artifacts } = settings.server; + return ( + + + {storage.root} + + + + + + + + + ); +} + +function SecurityPanel({ settings }: { settings: ServerSettings }) { + const { auth, ip_allowlist } = settings.server; + const githubUsers = auth.github.allowed_usernames; + return ( + + + {auth.methods.length === 0 ? ( + None configured + ) : ( +
+ {auth.methods.map((m) => ( + {m} + ))} +
+ )} +
+ + {githubUsers.length === 0 ? ( + Anyone + ) : ( + + )} + + + 0 + ? `· ${ip_allowlist.trusted_proxy_count} trusted ${plural(ip_allowlist.trusted_proxy_count, "proxy", "proxies")}` + : undefined + } + /> + +
+ ); +} + +function IntegrationsPanel({ settings }: { settings: ServerSettings }) { + const { integrations } = settings.server; + return ( + + + + + + ); +} + +function ListenValue({ listen }: { listen: ServerListenSettings }) { + if (listen.type === "tcp") { + return ( + + tcp + + {listen.address} + + + ); + } + return ( + + unix + + {listen.path} + + + ); +} + +function IntegrationValue({ + enabled, + detail, +}: { + enabled: boolean; + detail?: string; +}) { + if (!enabled) return ; + return ( + + + {detail ? ( + {detail} + ) : null} + + ); +} diff --git a/apps/fabro-web/app/routes/settings.tsx b/apps/fabro-web/app/routes/settings.tsx index 939ccce48..1d765dd4d 100644 --- a/apps/fabro-web/app/routes/settings.tsx +++ b/apps/fabro-web/app/routes/settings.tsx @@ -1,226 +1,77 @@ -import { useState } from "react"; -import type { - ServerListenSettings, - ServerSettings, -} from "@qltysh/fabro-api-client"; -import { useServerSettings } from "../lib/queries"; -import { CollapsibleFile } from "../components/collapsible-file"; -import { - Badge, - Count, - Mono, - Muted, - NumberValue, - ObjectStoreValue, - Panel, - PanelSkeleton, - Row, - type SettingsView, - Toggle, - UrlValue, - UsernameList, - ViewToggle, - plural, -} from "../components/settings-panel"; +import { BoltIcon, Cog6ToothIcon } from "@heroicons/react/24/outline"; +import { Link, Outlet, useLocation } from "react-router"; export function meta({}: any) { return [{ title: "Settings — Fabro" }]; } -export default function Settings() { - const settingsQuery = useServerSettings(); - const settings = settingsQuery.data; - const [view, setView] = useState("settings"); +export const handle = { hideHeader: true }; - if (!settings) { - return ( -
- - - - - +type NavItem = { + name: string; + href: string; + icon: typeof Cog6ToothIcon; + match: (pathname: string) => boolean; +}; + +const navItems: NavItem[] = [ + { + name: "Settings", + href: "/settings", + icon: Cog6ToothIcon, + match: (p) => p === "/settings", + }, + { + name: "Live Events", + href: "/settings/live-events", + icon: BoltIcon, + match: (p) => p.startsWith("/settings/live-events"), + }, +]; + +function classNames(...classes: Array) { + return classes.filter(Boolean).join(" "); +} + +export default function SettingsLayout() { + const { pathname } = useLocation(); + const currentName = navItems.find((item) => item.match(pathname))?.name ?? "Settings"; + + return ( +
+ + +
+

+ {currentName} +

+
- ); - } - - return ( -
- - {view === "settings" ? ( - <> - - - - - - ) : ( - - )}
); } - -function PageIntro({ - view, - setView, -}: { - view: SettingsView; - setView: (v: SettingsView) => void; -}) { - return ( -
-

- Snapshot of the server configuration. Edit via{" "} - settings.toml; changes take - effect on the next server restart. -

- -
- ); -} - -function ServerPanel({ settings }: { settings: ServerSettings }) { - const { listen, web, api, scheduler } = settings.server; - return ( - - - {web.enabled ? : } - - - {api.url ? : Same origin} - - - - - - - - - ); -} - -function DataPanel({ settings }: { settings: ServerSettings }) { - const { storage, slatedb, artifacts } = settings.server; - return ( - - - {storage.root} - - - - - - - - - ); -} - -function SecurityPanel({ settings }: { settings: ServerSettings }) { - const { auth, ip_allowlist } = settings.server; - const githubUsers = auth.github.allowed_usernames; - return ( - - - {auth.methods.length === 0 ? ( - None configured - ) : ( -
- {auth.methods.map((m) => ( - {m} - ))} -
- )} -
- - {githubUsers.length === 0 ? ( - Anyone - ) : ( - - )} - - - 0 - ? `· ${ip_allowlist.trusted_proxy_count} trusted ${plural(ip_allowlist.trusted_proxy_count, "proxy", "proxies")}` - : undefined - } - /> - -
- ); -} - -function IntegrationsPanel({ settings }: { settings: ServerSettings }) { - const { integrations } = settings.server; - return ( - - - - - - ); -} - -function ListenValue({ listen }: { listen: ServerListenSettings }) { - if (listen.type === "tcp") { - return ( - - tcp - - {listen.address} - - - ); - } - return ( - - unix - - {listen.path} - - - ); -} - -function IntegrationValue({ - enabled, - detail, -}: { - enabled: boolean; - detail?: string; -}) { - if (!enabled) return ; - return ( - - - {detail ? ( - {detail} - ) : null} - - ); -} -