From 40fa088410940be843d4ac9df406f882eaa6a4d4 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sat, 25 Apr 2026 09:37:55 -0400 Subject: [PATCH] refactor(web): redesign settings page as titled panels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the raw JSON dump with three panels (Server, Access & Capacity, Integrations & Artifacts), each rendering a small set of curated rows. Each row uses an aligned two-column layout — title and help on the left, a typed value renderer on the right (toggle dot, mono path, URL link, badge, tabular-nums count, listen/object-store summaries). Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/fabro-web/app/routes/settings.tsx | 352 +++++++++++++++++- lib/crates/fabro-spa/assets/assets/app.css | 2 +- .../{entry-6kgat4hc.js => entry-89wr1mf8.js} | 334 ++++++++--------- lib/crates/fabro-spa/assets/index.html | 2 +- 4 files changed, 508 insertions(+), 182 deletions(-) rename lib/crates/fabro-spa/assets/assets/{entry-6kgat4hc.js => entry-89wr1mf8.js} (55%) diff --git a/apps/fabro-web/app/routes/settings.tsx b/apps/fabro-web/app/routes/settings.tsx index d9df32c68..cf4b1f1fb 100644 --- a/apps/fabro-web/app/routes/settings.tsx +++ b/apps/fabro-web/app/routes/settings.tsx @@ -1,5 +1,9 @@ -import type { ServerSettings } from "@qltysh/fabro-api-client"; -import { CollapsibleFile } from "../components/collapsible-file"; +import type { ReactNode } from "react"; +import type { + ObjectStoreSettings, + ServerListenSettings, + ServerSettings, +} from "@qltysh/fabro-api-client"; import { useServerSettings } from "../lib/queries"; export function meta({}: any) { @@ -8,18 +12,340 @@ export function meta({}: any) { export default function Settings() { const settingsQuery = useServerSettings(); - const settings = (settingsQuery.data ?? {}) as ServerSettings; + const settings = settingsQuery.data; + + if (!settings) { + return ( +
+ + + + +
+ ); + } return ( - <> -

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

- - +
+ + + + +
); } + +function PageIntro() { + 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, storage } = settings.server; + return ( + + + + + + {web.enabled ? : } + + + {api.url ? : Same origin} + + + {storage.root} + + + ); +} + +function AccessPanel({ settings }: { settings: ServerSettings }) { + const { auth, ip_allowlist, scheduler } = 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, artifacts } = settings.server; + return ( + + + + + + + + + ); +} + +function Panel({ title, children }: { title: string; children: ReactNode }) { + return ( +
+
+

+ {title} +

+
+
{children}
+
+ ); +} + +function PanelSkeleton() { + return ( +
+
+
+
+
+
+
+
+ ); +} + +function Row({ + title, + help, + children, +}: { + title: string; + help?: string; + children: ReactNode; +}) { + return ( +
+
+
{title}
+ {help ? ( +
{help}
+ ) : null} +
+
{children}
+
+ ); +} + +function Mono({ children }: { children: ReactNode }) { + return ( +
+ {children} +
+ ); +} + +function Muted({ children }: { children: ReactNode }) { + return {children}; +} + +function Badge({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} + +function Number({ value }: { value: number }) { + return {value}; +} + +function Dot({ on }: { on: boolean }) { + return ( +