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) <noreply@anthropic.com>
This commit is contained in:
8
ui/package-lock.json
generated
8
ui/package-lock.json
generated
@@ -9,7 +9,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cameleer/design-system": "^0.1.39",
|
"@cameleer/design-system": "^0.1.40",
|
||||||
"@tanstack/react-query": "^5.90.21",
|
"@tanstack/react-query": "^5.90.21",
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^1.7.0",
|
||||||
"openapi-fetch": "^0.17.0",
|
"openapi-fetch": "^0.17.0",
|
||||||
@@ -279,9 +279,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cameleer/design-system": {
|
"node_modules/@cameleer/design-system": {
|
||||||
"version": "0.1.39",
|
"version": "0.1.40",
|
||||||
"resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.39/design-system-0.1.39.tgz",
|
"resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.40/design-system-0.1.40.tgz",
|
||||||
"integrity": "sha512-fKJp00KR/KLe/8WeG1yj0NXb+6eJCQL79CJ3j2vRXFWSAhFDgTucASHd2NJRQSWBH1mQXE88AuyUonHvRKZRZA==",
|
"integrity": "sha512-RfSomx02Aj52r5Gco2hK1vCcbqSGl05+PUk1wUB+BnvNJRs0Heb1TQ8f9mxAjC017vbbnPlYdOhAphHTI23XSQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^1.7.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
|||||||
@@ -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')\""
|
"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": {
|
"dependencies": {
|
||||||
"@cameleer/design-system": "^0.1.39",
|
"@cameleer/design-system": "^0.1.40",
|
||||||
"@tanstack/react-query": "^5.90.21",
|
"@tanstack/react-query": "^5.90.21",
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^1.7.0",
|
||||||
"openapi-fetch": "^0.17.0",
|
"openapi-fetch": "^0.17.0",
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import {
|
|||||||
SidebarTree,
|
SidebarTree,
|
||||||
StatusDot,
|
StatusDot,
|
||||||
TopBar,
|
TopBar,
|
||||||
|
SearchTrigger,
|
||||||
|
AutoRefreshToggle,
|
||||||
|
ButtonGroup,
|
||||||
|
TimeRangeDropdown,
|
||||||
CommandPalette,
|
CommandPalette,
|
||||||
CommandPaletteProvider,
|
CommandPaletteProvider,
|
||||||
GlobalFilterProvider,
|
GlobalFilterProvider,
|
||||||
@@ -15,7 +19,7 @@ import {
|
|||||||
useGlobalFilters,
|
useGlobalFilters,
|
||||||
useStarred,
|
useStarred,
|
||||||
} from '@cameleer/design-system';
|
} 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 sidebarLogo from '@cameleer/design-system/assets/cameleer3-logo.svg';
|
||||||
import { Box, Settings, FileText, ChevronRight, Square, Pause, Star, X, User } from 'lucide-react';
|
import { Box, Settings, FileText, ChevronRight, Square, Pause, Star, X, User } from 'lucide-react';
|
||||||
import { AboutMeDialog } from './AboutMeDialog';
|
import { AboutMeDialog } from './AboutMeDialog';
|
||||||
@@ -264,6 +268,13 @@ function StarredList({ items, onNavigate, onRemove }: { items: StarredItem[]; on
|
|||||||
/* Section state keys */
|
/* 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_APPS = 'sidebar:section:apps';
|
||||||
const SK_ADMIN = 'sidebar:section:admin';
|
const SK_ADMIN = 'sidebar:section:admin';
|
||||||
const SK_COLLAPSED = 'sidebar:collapsed';
|
const SK_COLLAPSED = 'sidebar:collapsed';
|
||||||
@@ -276,7 +287,8 @@ function LayoutContent() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { timeRange, autoRefresh, refreshTimeRange } = useGlobalFilters();
|
const globalFilters = useGlobalFilters();
|
||||||
|
const { timeRange, autoRefresh, refreshTimeRange } = globalFilters;
|
||||||
|
|
||||||
// --- Role checks ----------------------------------------------------
|
// --- Role checks ----------------------------------------------------
|
||||||
const isAdmin = useIsAdmin();
|
const isAdmin = useIsAdmin();
|
||||||
@@ -754,7 +766,30 @@ function LayoutContent() {
|
|||||||
user={username ? { name: username } : undefined}
|
user={username ? { name: username } : undefined}
|
||||||
userMenuItems={userMenuItems}
|
userMenuItems={userMenuItems}
|
||||||
onLogout={handleLogout}
|
onLogout={handleLogout}
|
||||||
|
>
|
||||||
|
<SearchTrigger onClick={() => setPaletteOpen(true)} />
|
||||||
|
<ButtonGroup
|
||||||
|
items={STATUS_ITEMS}
|
||||||
|
value={globalFilters.statusFilters}
|
||||||
|
onChange={(selected) => {
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
<TimeRangeDropdown
|
||||||
|
value={globalFilters.timeRange}
|
||||||
|
onChange={globalFilters.setTimeRange}
|
||||||
|
/>
|
||||||
|
<AutoRefreshToggle
|
||||||
|
active={globalFilters.autoRefresh}
|
||||||
|
onChange={globalFilters.setAutoRefresh}
|
||||||
|
/>
|
||||||
|
</TopBar>
|
||||||
<AboutMeDialog open={aboutMeOpen} onClose={() => setAboutMeOpen(false)} />
|
<AboutMeDialog open={aboutMeOpen} onClose={() => setAboutMeOpen(false)} />
|
||||||
<CommandPalette
|
<CommandPalette
|
||||||
key={isAdminPage ? 'admin' : 'ops'}
|
key={isAdminPage ? 'admin' : 'ops'}
|
||||||
|
|||||||
Reference in New Issue
Block a user