diff --git a/ui/src/components/NotificationBell.module.css b/ui/src/components/NotificationBell.module.css
index af01b0ba..239ba3cb 100644
--- a/ui/src/components/NotificationBell.module.css
+++ b/ui/src/components/NotificationBell.module.css
@@ -5,11 +5,11 @@
justify-content: center;
width: 32px;
height: 32px;
- border-radius: 8px;
- color: var(--fg);
+ border-radius: var(--radius-md);
+ color: var(--text-primary);
text-decoration: none;
}
-.bell:hover { background: var(--hover-bg); }
+.bell:hover { background: var(--bg-hover); }
.badge {
position: absolute;
top: 2px;
@@ -18,7 +18,7 @@
height: 16px;
padding: 0 4px;
border-radius: 8px;
- color: var(--bg);
+ color: #fff;
font-size: 10px;
font-weight: 600;
line-height: 16px;
@@ -26,4 +26,4 @@
}
.badgeCritical { background: var(--error); }
.badgeWarning { background: var(--amber); }
-.badgeInfo { background: var(--muted); }
+.badgeInfo { background: var(--text-muted); }
diff --git a/ui/src/index.css b/ui/src/index.css
index dd5ad60e..7765690d 100644
--- a/ui/src/index.css
+++ b/ui/src/index.css
@@ -64,6 +64,14 @@
--text-muted: #766A5E;
/* White text on colored badge backgrounds (not in DS yet) */
--text-inverse: #fff;
+
+ /* Spacing scale — DS doesn't ship these, but many app modules reference them.
+ Keep local here until the DS grows a real spacing system. */
+ --space-xs: 4px;
+ --space-sm: 8px;
+ --space-md: 16px;
+ --space-lg: 24px;
+ --space-xl: 32px;
}
[data-theme="dark"] {
diff --git a/ui/src/pages/Alerts/AllAlertsPage.tsx b/ui/src/pages/Alerts/AllAlertsPage.tsx
index 4212e7b7..f607e1b1 100644
--- a/ui/src/pages/Alerts/AllAlertsPage.tsx
+++ b/ui/src/pages/Alerts/AllAlertsPage.tsx
@@ -2,7 +2,7 @@ import { useState } from 'react';
import { Link } from 'react-router';
import { Bell } from 'lucide-react';
import {
- SectionHeader, DataTable, EmptyState, SegmentedTabs,
+ DataTable, EmptyState, SegmentedTabs,
} from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
import { PageLoader } from '../../components/PageLoader';
@@ -71,17 +71,19 @@ export default function AllAlertsPage() {
return (
-
- All alerts
-
-
-
- ({ value, label: f.label }))}
- active={filterKey}
- onChange={setFilterKey}
- />
-
+
+
+
All alerts
+ {rows.length} shown
+
+
+ ({ value, label: f.label }))}
+ active={filterKey}
+ onChange={setFilterKey}
+ />
+
+
{rows.length === 0 ? (
) : (
-
+
columns={columns as Column[]}
data={rows as Array}
sortable
flush
+ fillHeight
+ pageSize={200}
rowAccent={(row) => row.severity ? severityToAccent(row.severity) : undefined}
expandedContent={renderAlertExpanded}
/>
diff --git a/ui/src/pages/Alerts/HistoryPage.tsx b/ui/src/pages/Alerts/HistoryPage.tsx
index d80bdb7c..7be14ccd 100644
--- a/ui/src/pages/Alerts/HistoryPage.tsx
+++ b/ui/src/pages/Alerts/HistoryPage.tsx
@@ -2,7 +2,7 @@ import { useState } from 'react';
import { Link } from 'react-router';
import { History } from 'lucide-react';
import {
- SectionHeader, DataTable, EmptyState, DateRangePicker,
+ DataTable, EmptyState, DateRangePicker,
} from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
import { PageLoader } from '../../components/PageLoader';
@@ -86,13 +86,15 @@ export default function HistoryPage() {
return (
-
- History
-
-
-
-
-
+
{filtered.length === 0 ? (
) : (
-
+
columns={columns as Column[]}
data={filtered as Array}
sortable
flush
+ fillHeight
+ pageSize={200}
rowAccent={(row) => row.severity ? severityToAccent(row.severity) : undefined}
expandedContent={renderAlertExpanded}
/>
diff --git a/ui/src/pages/Alerts/InboxPage.tsx b/ui/src/pages/Alerts/InboxPage.tsx
index 1c5213f8..4d55c0fa 100644
--- a/ui/src/pages/Alerts/InboxPage.tsx
+++ b/ui/src/pages/Alerts/InboxPage.tsx
@@ -2,7 +2,7 @@ import { useMemo, useState } from 'react';
import { Link } from 'react-router';
import { Inbox } from 'lucide-react';
import {
- Button, SectionHeader, DataTable, EmptyState, useToast,
+ Button, DataTable, EmptyState, useToast,
} from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
import { PageLoader } from '../../components/PageLoader';
@@ -19,7 +19,7 @@ import css from './alerts-page.module.css';
import tableStyles from '../../styles/table-section.module.css';
export default function InboxPage() {
- const { data, isLoading, error } = useAlerts({ state: ['FIRING', 'ACKNOWLEDGED'], limit: 100 });
+ const { data, isLoading, error } = useAlerts({ state: ['FIRING', 'ACKNOWLEDGED'], limit: 200 });
const bulkRead = useBulkReadAlerts();
const markRead = useMarkAlertRead();
const ack = useAckAlert();
@@ -123,19 +123,19 @@ export default function InboxPage() {
const selectedIds = Array.from(selected);
+ const subtitle =
+ selectedIds.length > 0
+ ? `${selectedIds.length} selected`
+ : `${unreadIds.length} firing · ${rows.length} total`;
+
return (
-
- Inbox
-
-
-
-
- {selectedIds.length > 0
- ? `${selectedIds.length} selected`
- : `${unreadIds.length} unread`}
-
-
+
+
+
Inbox
+ {subtitle}
+
+
-
+
{rows.length === 0 ? (
) : (
-
+
columns={columns as Column[]}
data={rows as Array}
sortable
flush
+ fillHeight
+ pageSize={200}
rowAccent={(row) => row.severity ? severityToAccent(row.severity) : undefined}
expandedContent={renderAlertExpanded}
/>
diff --git a/ui/src/pages/Alerts/RulesListPage.tsx b/ui/src/pages/Alerts/RulesListPage.tsx
index c4489bce..2f502ae6 100644
--- a/ui/src/pages/Alerts/RulesListPage.tsx
+++ b/ui/src/pages/Alerts/RulesListPage.tsx
@@ -2,7 +2,7 @@ import { useState } from 'react';
import { Link, useNavigate } from 'react-router';
import { FilePlus } from 'lucide-react';
import {
- Button, SectionHeader, Toggle, useToast, Badge, DataTable,
+ Button, Toggle, useToast, Badge, DataTable,
EmptyState, Dropdown, ConfirmDialog,
} from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
@@ -111,17 +111,17 @@ export default function RulesListPage() {
return (
-
-
-
-
- }
- >
- Alert rules
-
-
+
{rows.length === 0 ? (
) : (
-
+
columns={columns}
data={rows as (AlertRuleResponse & { id: string })[]}
flush
+ fillHeight
/>
)}
diff --git a/ui/src/pages/Alerts/SilencesPage.tsx b/ui/src/pages/Alerts/SilencesPage.tsx
index 77596eed..1294031f 100644
--- a/ui/src/pages/Alerts/SilencesPage.tsx
+++ b/ui/src/pages/Alerts/SilencesPage.tsx
@@ -1,7 +1,7 @@
import { useState } from 'react';
import { BellOff } from 'lucide-react';
import {
- Button, FormField, Input, SectionHeader, useToast, DataTable,
+ Button, FormField, Input, useToast, DataTable,
EmptyState, ConfirmDialog, MonoText,
} from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
@@ -92,9 +92,12 @@ export default function SilencesPage() {
return (
-
- Alert silences
-
+
) : (
-
+
columns={columns}
data={rows.map((s) => ({ ...s, id: s.id ?? '' }))}
flush
+ fillHeight
/>
)}
diff --git a/ui/src/pages/Alerts/alerts-page.module.css b/ui/src/pages/Alerts/alerts-page.module.css
index 3ed159ae..a45a7c45 100644
--- a/ui/src/pages/Alerts/alerts-page.module.css
+++ b/ui/src/pages/Alerts/alerts-page.module.css
@@ -3,16 +3,48 @@
display: flex;
flex-direction: column;
gap: var(--space-md);
+ height: 100%;
+ min-height: 0;
}
-.toolbar {
+.pageHeader {
display: flex;
justify-content: space-between;
align-items: center;
- gap: var(--space-sm);
+ gap: var(--space-md);
+ padding-bottom: var(--space-sm);
+ border-bottom: 1px solid var(--border-subtle);
flex-wrap: wrap;
}
+.pageTitleGroup {
+ display: flex;
+ align-items: baseline;
+ gap: var(--space-sm);
+ min-width: 0;
+}
+
+.pageTitle {
+ font-size: 20px;
+ font-weight: 600;
+ color: var(--text-primary);
+ margin: 0;
+ letter-spacing: -0.01em;
+}
+
+.pageSubtitle {
+ font-size: 13px;
+ color: var(--text-muted);
+ font-variant-numeric: tabular-nums;
+}
+
+.pageActions {
+ display: flex;
+ gap: var(--space-sm);
+ align-items: center;
+ flex-shrink: 0;
+}
+
.filterBar {
display: flex;
gap: var(--space-sm);
@@ -20,14 +52,11 @@
flex-wrap: wrap;
}
-.bulkBar {
+.tableWrap {
+ flex: 1 1 auto;
+ min-height: 0;
display: flex;
- gap: var(--space-sm);
- align-items: center;
- padding: var(--space-sm) var(--space-md);
- background: var(--bg-hover);
- border: 1px solid var(--border-subtle);
- border-radius: var(--radius-md);
+ flex-direction: column;
}
.titleCell {
@@ -54,10 +83,12 @@
.titlePreview {
font-size: 12px;
color: var(--text-muted);
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- max-width: 48ch;
+ word-break: break-word;
+ line-height: 1.4;
}
.expanded {