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

History

+ {filtered.length} resolved +
+
+ +
+
{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 - -
+
+
+

Alert rules

+ {rows.length} total +
+
+ + + +
+
{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 -
+
+
+

Alert silences

+ {rows.length} active +
+
) : ( -
+
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 {