From 2863ceef122ca438cfaabd57a7df68c3cd426568 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Fri, 10 Apr 2026 17:06:03 +0200 Subject: [PATCH] refactor: compose TopBar center slot with server-specific controls Update to @cameleer/design-system@0.1.40 which decomposes TopBar into a composable shell. Move status filters, time range, search trigger, and auto-refresh toggle from the DS TopBar into LayoutShell as composed children. Fixes cameleer/cameleer-saas#53. Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/package-lock.json | 8 +++--- ui/package.json | 2 +- ui/src/components/LayoutShell.tsx | 41 ++++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/ui/package-lock.json b/ui/package-lock.json index da886b3b..40936b57 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "hasInstallScript": true, "dependencies": { - "@cameleer/design-system": "^0.1.39", + "@cameleer/design-system": "^0.1.40", "@tanstack/react-query": "^5.90.21", "lucide-react": "^1.7.0", "openapi-fetch": "^0.17.0", @@ -279,9 +279,9 @@ } }, "node_modules/@cameleer/design-system": { - "version": "0.1.39", - "resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.39/design-system-0.1.39.tgz", - "integrity": "sha512-fKJp00KR/KLe/8WeG1yj0NXb+6eJCQL79CJ3j2vRXFWSAhFDgTucASHd2NJRQSWBH1mQXE88AuyUonHvRKZRZA==", + "version": "0.1.40", + "resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.40/design-system-0.1.40.tgz", + "integrity": "sha512-RfSomx02Aj52r5Gco2hK1vCcbqSGl05+PUk1wUB+BnvNJRs0Heb1TQ8f9mxAjC017vbbnPlYdOhAphHTI23XSQ==", "dependencies": { "lucide-react": "^1.7.0", "react": "^19.0.0", diff --git a/ui/package.json b/ui/package.json index d9311dbe..1e6d3738 100644 --- a/ui/package.json +++ b/ui/package.json @@ -15,7 +15,7 @@ "postinstall": "node -e \"const fs=require('fs');fs.mkdirSync('public',{recursive:true});fs.copyFileSync('node_modules/@cameleer/design-system/assets/cameleer3-logo.svg','public/favicon.svg')\"" }, "dependencies": { - "@cameleer/design-system": "^0.1.39", + "@cameleer/design-system": "^0.1.40", "@tanstack/react-query": "^5.90.21", "lucide-react": "^1.7.0", "openapi-fetch": "^0.17.0", diff --git a/ui/src/components/LayoutShell.tsx b/ui/src/components/LayoutShell.tsx index f8305daa..fd5e0164 100644 --- a/ui/src/components/LayoutShell.tsx +++ b/ui/src/components/LayoutShell.tsx @@ -6,6 +6,10 @@ import { SidebarTree, StatusDot, TopBar, + SearchTrigger, + AutoRefreshToggle, + ButtonGroup, + TimeRangeDropdown, CommandPalette, CommandPaletteProvider, GlobalFilterProvider, @@ -15,7 +19,7 @@ import { useGlobalFilters, useStarred, } from '@cameleer/design-system'; -import type { SearchResult, SidebarTreeNode, DropdownItem } from '@cameleer/design-system'; +import type { SearchResult, SidebarTreeNode, DropdownItem, ButtonGroupItem } from '@cameleer/design-system'; import sidebarLogo from '@cameleer/design-system/assets/cameleer3-logo.svg'; import { Box, Settings, FileText, ChevronRight, Square, Pause, Star, X, User } from 'lucide-react'; import { AboutMeDialog } from './AboutMeDialog'; @@ -264,6 +268,13 @@ function StarredList({ items, onNavigate, onRemove }: { items: StarredItem[]; on /* Section state keys */ /* ------------------------------------------------------------------ */ +const STATUS_ITEMS: ButtonGroupItem[] = [ + { value: 'completed', label: 'OK', color: 'var(--success)' }, + { value: 'warning', label: 'Warn', color: 'var(--warning)' }, + { value: 'failed', label: 'Error', color: 'var(--error)' }, + { value: 'running', label: 'Running', color: 'var(--running)' }, +] + const SK_APPS = 'sidebar:section:apps'; const SK_ADMIN = 'sidebar:section:admin'; const SK_COLLAPSED = 'sidebar:collapsed'; @@ -276,7 +287,8 @@ function LayoutContent() { const navigate = useNavigate(); const location = useLocation(); const queryClient = useQueryClient(); - const { timeRange, autoRefresh, refreshTimeRange } = useGlobalFilters(); + const globalFilters = useGlobalFilters(); + const { timeRange, autoRefresh, refreshTimeRange } = globalFilters; // --- Role checks ---------------------------------------------------- const isAdmin = useIsAdmin(); @@ -754,7 +766,30 @@ function LayoutContent() { user={username ? { name: username } : undefined} userMenuItems={userMenuItems} onLogout={handleLogout} - /> + > + setPaletteOpen(true)} /> + { + const current = globalFilters.statusFilters + for (const v of selected) { + if (!current.has(v)) globalFilters.toggleStatus(v as 'completed' | 'warning' | 'failed' | 'running') + } + for (const v of current) { + if (!selected.has(v)) globalFilters.toggleStatus(v as 'completed' | 'warning' | 'failed' | 'running') + } + }} + /> + + + setAboutMeOpen(false)} />