Compare commits

...

4 Commits

Author SHA1 Message Date
hsiegeln
5cb51e65be style(topbar): make .env badge match neutral button style of liveToggle/themeToggle
All checks were successful
Build & Publish / publish (push) Successful in 1m42s
SonarQube Analysis / sonarqube (push) Successful in 2m26s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 15:59:00 +02:00
hsiegeln
4dcd4aaa27 feat(topbar): change environment prop from string to ReactNode
All checks were successful
Build & Publish / publish (push) Successful in 1m21s
Allows consuming apps to pass a custom dropdown or any interactive
element instead of a static string label. Rendering changed from
<span> to <div> to support block-level children.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 15:25:20 +02:00
hsiegeln
58320b9762 fix(topbar): rename refresh toggle labels from LIVE/PAUSED to AUTO/MANUAL
All checks were successful
Build & Publish / publish (push) Successful in 54s
SonarQube Analysis / sonarqube (push) Successful in 3m3s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:30:17 +02:00
hsiegeln
c48dffaef2 feat(global-filter): add refreshTimeRange() for manual refresh in paused mode
All checks were successful
Build & Publish / publish (push) Successful in 55s
Revert auto-sliding when paused — time range only advances with
auto-refresh on. Add refreshTimeRange() to useGlobalFilters for
on-demand recomputation from the active preset.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:01:47 +02:00
4 changed files with 25 additions and 14 deletions

View File

@@ -306,7 +306,7 @@ import {
| Sidebar | Composable compound sidebar shell with icon-rail collapse mode. Sub-components: `Sidebar.Header`, `Sidebar.Section`, `Sidebar.Footer`, `Sidebar.FooterLink`. The app controls all content via children — the DS provides the frame. |
| SidebarTree | Data-driven tree for sidebar sections. Accepts `nodes: SidebarTreeNode[]` with expand/collapse, starring, keyboard nav, search filter, and path-based selection highlighting. |
| useStarred | Hook for localStorage-backed starred item IDs. Returns `{ starredIds, isStarred, toggleStar }`. |
| TopBar | Header bar with breadcrumb, search trigger, ButtonGroup status filters, time range selector, theme toggle, environment badge, user avatar |
| TopBar | Header bar with breadcrumb, search trigger, ButtonGroup status filters, time range selector, theme toggle, environment slot (`ReactNode` — pass a string for a static label or a custom dropdown for interactive selection), user avatar |
## Import Paths

View File

@@ -153,14 +153,17 @@
}
.env {
display: flex;
align-items: center;
height: 30px;
padding: 4px 10px;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--bg-raised);
color: var(--text-muted);
font-family: var(--font-mono);
font-size: 10px;
font-weight: 600;
padding: 3px 10px;
border-radius: 10px;
background: var(--success-bg);
color: var(--success);
border: 1px solid var(--success-border);
text-transform: uppercase;
letter-spacing: 0.5px;
}

View File

@@ -1,3 +1,4 @@
import { type ReactNode } from 'react'
import { Search, Moon, Sun, Power } from 'lucide-react'
import styles from './TopBar.module.css'
import { Breadcrumb } from '../../composites/Breadcrumb/Breadcrumb'
@@ -14,7 +15,7 @@ import type { BreadcrumbItem } from '../../providers/BreadcrumbProvider'
interface TopBarProps {
breadcrumb: BreadcrumbItem[]
environment?: string
environment?: ReactNode
user?: { name: string }
onLogout?: () => void
className?: string
@@ -90,7 +91,7 @@ export function TopBar({
title={globalFilters.autoRefresh ? 'Auto-refresh is on — click to pause' : 'Auto-refresh is paused — click to resume'}
>
<span className={styles.liveDot} />
{globalFilters.autoRefresh ? 'LIVE' : 'PAUSED'}
{globalFilters.autoRefresh ? 'AUTO' : 'MANUAL'}
</button>
<button
className={styles.themeToggle}
@@ -102,7 +103,7 @@ export function TopBar({
{theme === 'light' ? <Moon size={16} /> : <Sun size={16} />}
</button>
{environment && (
<span className={styles.env}>{environment}</span>
<div className={styles.env}>{environment}</div>
)}
{user && (
<Dropdown

View File

@@ -12,6 +12,7 @@ export type ExchangeStatus = 'completed' | 'failed' | 'running' | 'warning'
interface GlobalFilterContextValue {
timeRange: TimeRange
setTimeRange: (range: TimeRange) => void
refreshTimeRange: () => void
statusFilters: Set<ExchangeStatus>
toggleStatus: (status: ExchangeStatus) => void
clearStatusFilters: () => void
@@ -66,16 +67,22 @@ export function GlobalFilterProvider({ children }: { children: ReactNode }) {
try { localStorage.setItem('cameleer:auto-refresh', String(enabled)) } catch {}
}, [])
// Keep the time range sliding forward whenever a preset is active.
// PAUSED mode only stops query polling — the time window still advances
// so that manual refreshes and sidebar-triggered queries see current data.
// Keep the time range sliding forward when a preset is active and live
useEffect(() => {
if (!timeRange.preset) return
if (!autoRefresh || !timeRange.preset) return
const id = setInterval(() => {
const { start, end } = computePresetRange(timeRange.preset!)
setTimeRangeState({ start, end, preset: timeRange.preset })
}, 10_000)
return () => clearInterval(id)
}, [autoRefresh, timeRange.preset])
// Recompute time range from preset on demand (for manual refresh in PAUSED mode)
const refreshTimeRange = useCallback(() => {
if (timeRange.preset) {
const { start, end } = computePresetRange(timeRange.preset)
setTimeRangeState({ start, end, preset: timeRange.preset })
}
}, [timeRange.preset])
const isInTimeRange = useCallback(
@@ -92,7 +99,7 @@ export function GlobalFilterProvider({ children }: { children: ReactNode }) {
return (
<GlobalFilterContext.Provider
value={{ timeRange, setTimeRange, statusFilters, toggleStatus, clearStatusFilters, isInTimeRange, autoRefresh, setAutoRefresh }}
value={{ timeRange, setTimeRange, refreshTimeRange, statusFilters, toggleStatus, clearStatusFilters, isInTimeRange, autoRefresh, setAutoRefresh }}
>
{children}
</GlobalFilterContext.Provider>