feat(alerting): Plan 03 — UI + backfills (SSRF guard, metrics caching, docker stack) #144

Merged
hsiegeln merged 39 commits from feat/alerting-03-ui into main 2026-04-20 16:27:49 +02:00
2 changed files with 6 additions and 20 deletions
Showing only changes of commit 38083d7c3f - Show all commits

View File

@@ -60,14 +60,11 @@ describe('useUnreadCount', () => {
it('returns the server payload unmodified', async () => {
(apiClient.GET as any).mockResolvedValue({
data: { total: 3, bySeverity: { CRITICAL: 1, WARNING: 2, INFO: 0 } },
data: { count: 3 },
error: null,
});
const { result } = renderHook(() => useUnreadCount(), { wrapper });
await waitFor(() => expect(result.current.isSuccess).toBe(true));
expect(result.current.data).toEqual({
total: 3,
bySeverity: { CRITICAL: 1, WARNING: 2, INFO: 0 },
});
expect(result.current.data).toEqual({ count: 3 });
});
});

View File

@@ -2,7 +2,6 @@ import { Link } from 'react-router';
import { Bell } from 'lucide-react';
import { useUnreadCount } from '../api/queries/alerts';
import { useSelectedEnv } from '../api/queries/alertMeta';
import { usePageVisible } from '../hooks/usePageVisible';
import css from './NotificationBell.module.css';
/**
@@ -10,23 +9,13 @@ import css from './NotificationBell.module.css';
* inbox and renders a badge with the unread-alert count for the currently
* selected environment.
*
* Polling is driven by `useUnreadCount` (30s interval, paused in background
* via TanStack Query's `refetchIntervalInBackground: false`). The
* `usePageVisible` hook is retained as a defense-in-depth signal so future
* UI behavior (e.g. animations, live-region updates) can key off visibility
* without re-wiring the polling logic.
*
* TODO (spec §13): per-severity badge coloring — the backend
* `UnreadCountResponse` currently exposes only a scalar `count` field. To
* colour the badge by max unread severity (CRITICAL → error, WARNING →
* amber, INFO → muted) the DTO must grow a `bySeverity` map; deferred to a
* future task. Until then the badge uses a single `var(--error)` tint.
* Polling pause when the tab is hidden is handled by `useUnreadCount`'s
* `refetchIntervalInBackground: false`; no separate visibility subscription
* is needed. If per-severity coloring (spec §13) is re-introduced, the
* backend `UnreadCountResponse` must grow a `bySeverity` map.
*/
export function NotificationBell() {
const env = useSelectedEnv();
// Subscribe to visibility so the component re-renders on tab focus, even
// though the polling pause itself is handled inside useUnreadCount.
usePageVisible();
const { data } = useUnreadCount();
const count = data?.count ?? 0;