feat(alerts): DS alignment + AGENT_LIFECYCLE + single-inbox redesign #146

Merged
hsiegeln merged 49 commits from feat/alerts-ds-alignment into main 2026-04-21 19:53:12 +02:00
Showing only changes of commit e8de8d88ad - Show all commits

View File

@@ -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>