Skip to content

Commit 7830445

Browse files
committed
Add TOML-based dashboard config for sidebar section/link visibility
Introduce dashboard.config.toml as a single extensible config file for the dashboard, parsed at build time via smol-toml and injected through Vite's define option. Sidebar sections and individual links can now be disabled by ID or route path without backend changes.
1 parent fa8b071 commit 7830445

File tree

7 files changed

+134
-48
lines changed

7 files changed

+134
-48
lines changed

dashboard/dashboard.config.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# FlinkReactor Dashboard Configuration
2+
# This file controls dashboard behavior and is parsed at build time by Vite.
3+
4+
[sidebar]
5+
# Hide entire sidebar sections by ID.
6+
# Valid IDs: overview, jobs, cluster, observe, data, tools, instruments
7+
disabled_sections = ["instruments", "tools"]
8+
9+
# Hide individual sidebar links by their route path.
10+
# Examples: "/jobs/submit", "/sandbox", "/admin/simulations"
11+
disabled_links = [
12+
"/insights/bottlenecks",
13+
"/insights/metrics",
14+
"/insights/health",
15+
"/materialized-tables",
16+
"/monitoring/alerts",
17+
"/monitoring/checkpoints",
18+
"/deployments",
19+
]

dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"@types/react": "^19.2.14",
9090
"@types/react-dom": "^19.2.3",
9191
"@vitejs/plugin-react": "^4.7.0",
92+
"smol-toml": "^1.6.1",
9293
"tailwindcss": "^4.2.1",
9394
"typescript": "^5.9.3",
9495
"vite": "^6.4.1",

dashboard/src/components/layout/sidebar.tsx

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
Upload,
3333
} from "lucide-react"
3434
import { cn } from "@/lib/cn"
35+
import { isLinkEnabled, isSectionEnabled } from "@/lib/dashboard-config"
3536
import { useUiStore } from "@/stores/ui-store"
3637

3738
/** A single navigation link in the sidebar. */
@@ -46,6 +47,8 @@ type NavItem = {
4647

4748
/** A labeled group of related {@link NavItem} entries in the sidebar. */
4849
type NavGroup = {
50+
/** Stable identifier used in dashboard.config.toml to disable this section. */
51+
id: string
4952
/** Section heading displayed above the group's links. */
5053
label: string
5154
/** Ordered list of navigation links within this group. */
@@ -55,10 +58,12 @@ type NavGroup = {
5558
/** Static navigation structure defining all sidebar groups and their routes. */
5659
const NAV_GROUPS: NavGroup[] = [
5760
{
61+
id: "overview",
5862
label: "Overview",
5963
items: [{ href: "/overview", label: "Overview", icon: LayoutDashboard }],
6064
},
6165
{
66+
id: "jobs",
6267
label: "Jobs",
6368
items: [
6469
{ href: "/jobs/running", label: "Running Jobs", icon: Play },
@@ -72,6 +77,7 @@ const NAV_GROUPS: NavGroup[] = [
7277
],
7378
},
7479
{
80+
id: "cluster",
7581
label: "Cluster",
7682
items: [
7783
{ href: "/task-managers", label: "Task Managers", icon: Server },
@@ -80,6 +86,7 @@ const NAV_GROUPS: NavGroup[] = [
8086
],
8187
},
8288
{
89+
id: "observe",
8390
label: "Observe",
8491
items: [
8592
{ href: "/insights/metrics", label: "Metrics Explorer", icon: LineChart },
@@ -99,6 +106,7 @@ const NAV_GROUPS: NavGroup[] = [
99106
],
100107
},
101108
{
109+
id: "data",
102110
label: "Data",
103111
items: [
104112
{
@@ -119,6 +127,7 @@ const NAV_GROUPS: NavGroup[] = [
119127
],
120128
},
121129
{
130+
id: "tools",
122131
label: "Tools",
123132
items: [
124133
{
@@ -164,42 +173,48 @@ export function Sidebar() {
164173

165174
{/* Navigation */}
166175
<nav className="flex-1 overflow-y-auto p-1.5">
167-
{NAV_GROUPS.map((group) => (
168-
<div key={group.label} className="mb-1">
169-
{!collapsed && (
170-
<div className="px-2.5 pb-1 pt-2.5 text-[10px] font-medium uppercase tracking-wider text-zinc-600">
171-
{group.label}
176+
{NAV_GROUPS.filter((g) => isSectionEnabled(g.id)).map((group) => {
177+
const items = group.items.filter((i) => isLinkEnabled(i.href))
178+
if (items.length === 0) return null
179+
return (
180+
<div key={group.id} className="mb-1">
181+
{!collapsed && (
182+
<div className="px-2.5 pb-1 pt-2.5 text-[10px] font-medium uppercase tracking-wider text-zinc-600">
183+
{group.label}
184+
</div>
185+
)}
186+
<div className="space-y-0.5">
187+
{items.map((item) => {
188+
const active = pathname.startsWith(item.href)
189+
return (
190+
<Link
191+
key={item.href}
192+
to={item.href}
193+
className={cn(
194+
"flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-xs transition-colors",
195+
active
196+
? "bg-white/[0.08] text-white"
197+
: "text-zinc-500 hover:bg-white/[0.04] hover:text-zinc-300",
198+
)}
199+
>
200+
<item.icon className="size-3.5 shrink-0" />
201+
{!collapsed && <span>{item.label}</span>}
202+
</Link>
203+
)
204+
})}
172205
</div>
173-
)}
174-
<div className="space-y-0.5">
175-
{group.items.map((item) => {
176-
const active = pathname.startsWith(item.href)
177-
return (
178-
<Link
179-
key={item.href}
180-
to={item.href}
181-
className={cn(
182-
"flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-xs transition-colors",
183-
active
184-
? "bg-white/[0.08] text-white"
185-
: "text-zinc-500 hover:bg-white/[0.04] hover:text-zinc-300",
186-
)}
187-
>
188-
<item.icon className="size-3.5 shrink-0" />
189-
{!collapsed && <span>{item.label}</span>}
190-
</Link>
191-
)
192-
})}
193206
</div>
194-
</div>
195-
))}
207+
)
208+
})}
196209

197210
{/* Dynamic instruments group — rendered by the instruments UI package */}
198-
<InstrumentSidebarSection
199-
collapsed={collapsed}
200-
activePath={pathname}
201-
LinkComponent={Link}
202-
/>
211+
{isSectionEnabled("instruments") && (
212+
<InstrumentSidebarSection
213+
collapsed={collapsed}
214+
activePath={pathname}
215+
LinkComponent={Link}
216+
/>
217+
)}
203218
</nav>
204219

205220
{/* Collapse toggle */}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @module dashboard-config
3+
* Typed access to the build-time dashboard configuration injected by Vite's
4+
* `define` option from `dashboard.config.toml`.
5+
*/
6+
7+
interface SidebarConfig {
8+
disabled_sections: string[]
9+
disabled_links: string[]
10+
}
11+
12+
interface DashboardTomlConfig {
13+
sidebar: SidebarConfig
14+
}
15+
16+
declare const __DASHBOARD_CONFIG__: DashboardTomlConfig
17+
18+
const cfg: DashboardTomlConfig = __DASHBOARD_CONFIG__
19+
20+
const disabledSections = new Set(
21+
cfg.sidebar.disabled_sections.map((s) => s.toLowerCase()),
22+
)
23+
const disabledLinks = new Set(cfg.sidebar.disabled_links)
24+
25+
/** Returns true when a sidebar section should be rendered. */
26+
export function isSectionEnabled(id: string): boolean {
27+
return !disabledSections.has(id.toLowerCase())
28+
}
29+
30+
/** Returns true when a sidebar link should be rendered. */
31+
export function isLinkEnabled(href: string): boolean {
32+
return !disabledLinks.has(href)
33+
}

dashboard/vite.config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
/** Vite configuration for the FlinkReactor dashboard — TanStack Router plugin, dev server proxy. */
2+
import { readFileSync } from "node:fs"
23
import tailwindcss from "@tailwindcss/vite"
34
import { TanStackRouterVite } from "@tanstack/router-plugin/vite"
45
import react from "@vitejs/plugin-react"
6+
import { parse } from "smol-toml"
57
import { defineConfig } from "vite"
68
import tsconfigPaths from "vite-tsconfig-paths"
79

10+
const dashboardConfig = parse(
11+
readFileSync("./dashboard.config.toml", "utf-8"),
12+
)
13+
814
export default defineConfig({
15+
define: {
16+
__DASHBOARD_CONFIG__: JSON.stringify(dashboardConfig),
17+
},
918
plugins: [
1019
TanStackRouterVite({
1120
routesDirectory: "./src/routes",

pnpm-lock.yaml

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/config/development.yml

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,19 @@ storage:
2020
enabled: true
2121
dsn: "postgres://reactor:reactor@localhost:5433/reactor"
2222

23-
instruments:
24-
- name: "pagila"
25-
type: "database"
26-
config:
27-
driver: "postgres"
28-
dsn: "postgres://reactor:reactor@localhost:5433/pagila"
29-
- name: "chinook"
30-
type: "database"
31-
config:
32-
driver: "postgres"
33-
dsn: "postgres://reactor:reactor@localhost:5433/chinook"
34-
- name: "employees"
35-
type: "database"
36-
config:
37-
driver: "postgres"
38-
dsn: "postgres://reactor:reactor@localhost:5433/employees"
23+
# instruments:
24+
# - name: "pagila"
25+
# type: "database"
26+
# config:
27+
# driver: "postgres"
28+
# dsn: "postgres://reactor:reactor@localhost:5433/pagila"
29+
# - name: "chinook"
30+
# type: "database"
31+
# config:
32+
# driver: "postgres"
33+
# dsn: "postgres://reactor:reactor@localhost:5433/chinook"
34+
# - name: "employees"
35+
# type: "database"
36+
# config:
37+
# driver: "postgres"
38+
# dsn: "postgres://reactor:reactor@localhost:5433/employees"

0 commit comments

Comments
 (0)