refactor(ui/alerts/all): state filter to ButtonGroup (topnavbar style)
Replace the SegmentedTabs with multi-select ButtonGroup, matching the topnavbar Completed/Warning/Failed/Running pattern. State dots use the same palette as AlertStateChip (FIRING=error, ACKNOWLEDGED=warning, PENDING=muted, RESOLVED=success). Default selection is the three "open" states — Resolved is off by default and a single click surfaces closed alerts without navigating to /history.
This commit is contained in:
@@ -2,9 +2,9 @@ import { useState } from 'react';
|
|||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import { Bell } from 'lucide-react';
|
import { Bell } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
DataTable, EmptyState, SegmentedTabs,
|
ButtonGroup, DataTable, EmptyState,
|
||||||
} from '@cameleer/design-system';
|
} from '@cameleer/design-system';
|
||||||
import type { Column } from '@cameleer/design-system';
|
import type { ButtonGroupItem, Column } from '@cameleer/design-system';
|
||||||
import { PageLoader } from '../../components/PageLoader';
|
import { PageLoader } from '../../components/PageLoader';
|
||||||
import { SeverityBadge } from '../../components/SeverityBadge';
|
import { SeverityBadge } from '../../components/SeverityBadge';
|
||||||
import { AlertStateChip } from '../../components/AlertStateChip';
|
import { AlertStateChip } from '../../components/AlertStateChip';
|
||||||
@@ -20,17 +20,22 @@ import tableStyles from '../../styles/table-section.module.css';
|
|||||||
|
|
||||||
type AlertState = NonNullable<AlertDto['state']>;
|
type AlertState = NonNullable<AlertDto['state']>;
|
||||||
|
|
||||||
const STATE_FILTERS: Record<string, { label: string; values: AlertState[] }> = {
|
const STATE_ITEMS: ButtonGroupItem[] = [
|
||||||
open: { label: 'Currently open', values: ['PENDING', 'FIRING', 'ACKNOWLEDGED'] },
|
{ value: 'FIRING', label: 'Firing', color: 'var(--error)' },
|
||||||
firing: { label: 'Firing now', values: ['FIRING'] },
|
{ value: 'ACKNOWLEDGED', label: 'Acknowledged', color: 'var(--warning)' },
|
||||||
acked: { label: 'Acknowledged', values: ['ACKNOWLEDGED'] },
|
{ value: 'PENDING', label: 'Pending', color: 'var(--text-muted)' },
|
||||||
all: { label: 'All states', values: ['PENDING', 'FIRING', 'ACKNOWLEDGED', 'RESOLVED'] },
|
{ value: 'RESOLVED', label: 'Resolved', color: 'var(--success)' },
|
||||||
};
|
];
|
||||||
|
|
||||||
|
const DEFAULT_OPEN_STATES = new Set<string>(['PENDING', 'FIRING', 'ACKNOWLEDGED']);
|
||||||
|
|
||||||
export default function AllAlertsPage() {
|
export default function AllAlertsPage() {
|
||||||
const [filterKey, setFilterKey] = useState<string>('open');
|
const [stateSel, setStateSel] = useState<Set<string>>(() => new Set(DEFAULT_OPEN_STATES));
|
||||||
const filter = STATE_FILTERS[filterKey];
|
const stateValues: AlertState[] | undefined = stateSel.size === 0
|
||||||
const { data, isLoading, error } = useAlerts({ state: filter.values, limit: 200 });
|
? undefined
|
||||||
|
: [...stateSel] as AlertState[];
|
||||||
|
|
||||||
|
const { data, isLoading, error } = useAlerts({ state: stateValues, limit: 200 });
|
||||||
const markRead = useMarkAlertRead();
|
const markRead = useMarkAlertRead();
|
||||||
|
|
||||||
const rows = data ?? [];
|
const rows = data ?? [];
|
||||||
@@ -77,10 +82,10 @@ export default function AllAlertsPage() {
|
|||||||
<span className={css.pageSubtitle}>{rows.length} matching your filter</span>
|
<span className={css.pageSubtitle}>{rows.length} matching your filter</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={css.pageActions}>
|
<div className={css.pageActions}>
|
||||||
<SegmentedTabs
|
<ButtonGroup
|
||||||
tabs={Object.entries(STATE_FILTERS).map(([value, f]) => ({ value, label: f.label }))}
|
items={STATE_ITEMS}
|
||||||
active={filterKey}
|
value={stateSel}
|
||||||
onChange={setFilterKey}
|
onChange={setStateSel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
Reference in New Issue
Block a user