diff --git a/dashboard/src/__tests__/wallet-integration.test.tsx b/dashboard/src/__tests__/wallet-integration.test.tsx index dcccf6fc..0413e6df 100644 --- a/dashboard/src/__tests__/wallet-integration.test.tsx +++ b/dashboard/src/__tests__/wallet-integration.test.tsx @@ -48,8 +48,8 @@ function writeReport(results: { wallet: string; passed: boolean }[]) { wallets: results, }, null, - 2 - ) + 2, + ), ); } @@ -94,19 +94,22 @@ describe('Wallet connection flows', () => { }); describe('Wallet switching', () => { - it.each(SUPPORTED_WALLETS)('switches to $label and updates persisted wallet id', async ({ id, address }) => { - const { wallet, store, kit } = await load(); + it.each(SUPPORTED_WALLETS)( + 'switches to $label and updates persisted wallet id', + async ({ id, address }) => { + const { wallet, store, kit } = await load(); - kit.__control.authModalImpl = async () => { - kit.__emit('WALLET_SELECTED', { id }); - kit.__emit('STATE_UPDATED', { address }); - }; + kit.__control.authModalImpl = async () => { + kit.__emit('WALLET_SELECTED', { id }); + kit.__emit('STATE_UPDATED', { address }); + }; - await wallet.connectWallet(); + await wallet.connectWallet(); - expect(localStorage.getItem(WALLET_ID_KEY)).toBe(id); - expect(store.useWalletStore.getState().address).toBe(address); - }); + expect(localStorage.getItem(WALLET_ID_KEY)).toBe(id); + expect(store.useWalletStore.getState().address).toBe(address); + }, + ); it('switches wallets without leaving a stale session', async () => { const { wallet, store, kit } = await load(); @@ -130,25 +133,23 @@ describe('Wallet switching', () => { describe('Notification workflow with wallet connected', () => { it('loads events while wallet session is active', async () => { - const fetchMock = jest - .fn<() => Promise>() - .mockResolvedValue({ - ok: true, - json: async () => ({ - events: [ - { - eventId: 'evt-wallet-1', - contractAddress: 'CTEST', - eventName: 'task_created', - type: 'contract', - topic: [], - value: '', - ledger: 100, - receivedAt: Date.parse('2026-06-24T12:00:00Z'), - }, - ], - }), - } as unknown as Response); + const fetchMock = jest.fn<() => Promise>().mockResolvedValue({ + ok: true, + json: async () => ({ + events: [ + { + eventId: 'evt-wallet-1', + contractAddress: 'CTEST', + eventName: 'task_created', + type: 'contract', + topic: [], + value: '', + ledger: 100, + receivedAt: Date.parse('2026-06-24T12:00:00Z'), + }, + ], + }), + } as unknown as Response); global.fetch = fetchMock as unknown as typeof fetch; const { fetchEvents } = await import('../services/eventsApi'); @@ -258,7 +259,9 @@ describe('Notification feed clears on wallet switch (issue #175)', () => { // localStorage must reflect the new account only expect(localStorage.getItem('notify-chain:wallet-address')).toBe(SUPPORTED_WALLETS[2].address); - expect(localStorage.getItem('notify-chain:wallet-address')).not.toBe(SUPPORTED_WALLETS[0].address); + expect(localStorage.getItem('notify-chain:wallet-address')).not.toBe( + SUPPORTED_WALLETS[0].address, + ); expect(store.useWalletStore.getState().address).toBe(SUPPORTED_WALLETS[2].address); }); diff --git a/dashboard/src/benchmark/render-benchmark.test.tsx b/dashboard/src/benchmark/render-benchmark.test.tsx index cc852be4..bf60e96a 100644 --- a/dashboard/src/benchmark/render-benchmark.test.tsx +++ b/dashboard/src/benchmark/render-benchmark.test.tsx @@ -17,7 +17,7 @@ describe('event list render performance', () => { const { container } = render(
-
+ , ); const renderedRows = countRenderedRows(container); @@ -38,7 +38,7 @@ describe('event list render performance', () => { const virtualRender = render(
-
+ , ); const virtualDuration = performance.now() - virtualStart; const virtualRows = countRenderedRows(virtualRender.container); diff --git a/dashboard/src/components/ActivityFeed.tsx b/dashboard/src/components/ActivityFeed.tsx index 259de616..9c73ae24 100644 --- a/dashboard/src/components/ActivityFeed.tsx +++ b/dashboard/src/components/ActivityFeed.tsx @@ -9,14 +9,14 @@ import { EmptyState } from './EmptyState'; // Helper to get icon/color based on activity type const getActivityTypeStyle = (type: ActivityType) => { const styles: Record = { - 'notification_sent': { color: '#34d399', icon: 'βœ“', bg: 'rgba(52, 211, 153, 0.12)' }, - 'notification_failed': { color: '#f87171', icon: 'βœ•', bg: 'rgba(248, 113, 113, 0.12)' }, - 'notification_retried': { color: '#f4b400', icon: '↻', bg: 'rgba(244, 180, 0, 0.12)' }, - 'contract_event_received': { color: '#60a5fa', icon: 'πŸ“‘', bg: 'rgba(96, 165, 250, 0.12)' }, - 'preference_updated': { color: '#a78bfa', icon: 'βš™', bg: 'rgba(167, 139, 250, 0.12)' }, - 'template_created': { color: '#38bdf8', icon: 'πŸ“„', bg: 'rgba(56, 189, 248, 0.12)' }, - 'template_updated': { color: '#22d3ee', icon: 'πŸ“', bg: 'rgba(34, 211, 238, 0.12)' }, - 'webhook_received': { color: '#fb923c', icon: 'πŸ”—', bg: 'rgba(251, 146, 60, 0.12)' }, + notification_sent: { color: '#34d399', icon: 'βœ“', bg: 'rgba(52, 211, 153, 0.12)' }, + notification_failed: { color: '#f87171', icon: 'βœ•', bg: 'rgba(248, 113, 113, 0.12)' }, + notification_retried: { color: '#f4b400', icon: '↻', bg: 'rgba(244, 180, 0, 0.12)' }, + contract_event_received: { color: '#60a5fa', icon: 'πŸ“‘', bg: 'rgba(96, 165, 250, 0.12)' }, + preference_updated: { color: '#a78bfa', icon: 'βš™', bg: 'rgba(167, 139, 250, 0.12)' }, + template_created: { color: '#38bdf8', icon: 'πŸ“„', bg: 'rgba(56, 189, 248, 0.12)' }, + template_updated: { color: '#22d3ee', icon: 'πŸ“', bg: 'rgba(34, 211, 238, 0.12)' }, + webhook_received: { color: '#fb923c', icon: 'πŸ”—', bg: 'rgba(251, 146, 60, 0.12)' }, }; return styles[type] || styles['contract_event_received']; }; @@ -49,14 +49,16 @@ const ActivityEventCard = ({ event }: { event: ActivityEvent }) => {

{event.message}

{Object.keys(event.metadata).length > 0 && (
- {Object.entries(event.metadata).map(([key, value]) => ( - value !== undefined && value !== null && ( - - {key}: - {String(value)} - - ) - ))} + {Object.entries(event.metadata).map( + ([key, value]) => + value !== undefined && + value !== null && ( + + {key}: + {String(value)} + + ), + )}
)} @@ -131,8 +133,8 @@ export function ActivityFeed() { const interval = setInterval(() => { const [newEvent] = generateMockActivityEvents(1); - setLiveEvents(prev => [newEvent, ...prev]); - setTotal(prev => prev + 1); + setLiveEvents((prev) => [newEvent, ...prev]); + setTotal((prev) => prev + 1); }, 15000); return () => clearInterval(interval); @@ -179,7 +181,9 @@ export function ActivityFeed() { className={`activity-feed__toggle-realtime ${realTimeEnabled ? 'activity-feed__toggle-realtime--active' : ''}`} onClick={() => setRealTimeEnabled(!realTimeEnabled)} aria-pressed={realTimeEnabled} - aria-label={realTimeEnabled ? 'Pause live activity updates' : 'Resume live activity updates'} + aria-label={ + realTimeEnabled ? 'Pause live activity updates' : 'Resume live activity updates' + } > {realTimeEnabled ? 'πŸ”΄ Live' : '⏸ Paused'} @@ -209,9 +213,7 @@ export function ActivityFeed() { className="empty-state--compact" /> ) : ( - displayedEvents.map(event => ( - - )) + displayedEvents.map((event) => ) )} diff --git a/dashboard/src/components/CopyButton.tsx b/dashboard/src/components/CopyButton.tsx index d5d0a653..fb7abd2f 100644 --- a/dashboard/src/components/CopyButton.tsx +++ b/dashboard/src/components/CopyButton.tsx @@ -23,9 +23,13 @@ export function CopyButton({ value, label, size = 'sm' }: CopyButtonProps) { title={copied ? 'Copied!' : `Copy ${label}`} > {copied ? ( - + ) : ( - + )} {copied ? 'Copied' : 'Copy'} diff --git a/dashboard/src/components/DeliveryHeatmap.tsx b/dashboard/src/components/DeliveryHeatmap.tsx index 3f6efad0..f7ebdd88 100644 --- a/dashboard/src/components/DeliveryHeatmap.tsx +++ b/dashboard/src/components/DeliveryHeatmap.tsx @@ -64,12 +64,12 @@ function DeliveryHeatmapInner({ events }: DeliveryHeatmapProps) { const handleStartChange = useCallback( (e: React.ChangeEvent) => setDateRange((prev) => ({ ...prev, start: e.target.value })), - [] + [], ); const handleEndChange = useCallback( (e: React.ChangeEvent) => setDateRange((prev) => ({ ...prev, end: e.target.value })), - [] + [], ); const handlePreset = useCallback((days: number) => { @@ -92,7 +92,7 @@ function DeliveryHeatmapInner({ events }: DeliveryHeatmapProps) { /* ── Heatmap data (memoised) ── */ const heatmap = useMemo( () => aggregateHeatmapData(events, startDate, endDate), - [events, startDate, endDate] + [events, startDate, endDate], ); /* ── Tooltip handlers ── */ @@ -109,7 +109,7 @@ function DeliveryHeatmapInner({ events }: DeliveryHeatmapProps) { y: cellRect.top - gridRect.top, }); }, - [] + [], ); const handleCellLeave = useCallback(() => setTooltip(null), []); @@ -133,17 +133,11 @@ function DeliveryHeatmapInner({ events }: DeliveryHeatmapProps) {
- - {heatmap.totalCount.toLocaleString()} - - - {hasFilters ? 'Filtered' : 'Total'} Events - + {heatmap.totalCount.toLocaleString()} + {hasFilters ? 'Filtered' : 'Total'} Events
- - {heatmap.maxCount.toLocaleString()} - + {heatmap.maxCount.toLocaleString()} Peak (per slot)
@@ -153,10 +147,7 @@ function DeliveryHeatmapInner({ events }: DeliveryHeatmapProps) {
{PRESETS.map((preset) => { - const isActive = - preset.days === 0 - ? !dateRange.start && !dateRange.end - : false; // full active-state logic lives in CSS via data attribute + const isActive = preset.days === 0 ? !dateRange.start && !dateRange.end : false; // full active-state logic lives in CSS via data attribute return ( )} diff --git a/dashboard/src/components/EventCard.stories.tsx b/dashboard/src/components/EventCard.stories.tsx index d14a9304..dee67813 100644 --- a/dashboard/src/components/EventCard.stories.tsx +++ b/dashboard/src/components/EventCard.stories.tsx @@ -1,10 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react'; import { EventCard } from './EventCard'; -import { - sampleEvent, - sampleSystemEvent, - sampleWithdrawalEvent, -} from '../stories/fixtures/events'; +import { sampleEvent, sampleSystemEvent, sampleWithdrawalEvent } from '../stories/fixtures/events'; const meta: Meta = { title: 'Components/EventCard', diff --git a/dashboard/src/components/EventCard.test.tsx b/dashboard/src/components/EventCard.test.tsx index 5a1793b1..3ad38a4d 100644 --- a/dashboard/src/components/EventCard.test.tsx +++ b/dashboard/src/components/EventCard.test.tsx @@ -18,9 +18,7 @@ const mockEvent: BlockchainEvent = { } as BlockchainEvent; test('clickable EventCard has no accessibility violations', async () => { - const { container } = render( - {}} /> - ); + const { container } = render( {}} />); const results = await axe(container); expect(results).toHaveNoViolations(); }); @@ -35,4 +33,4 @@ test('activates on Space key, not just Enter', () => { fireEvent.keyDown(card, { key: 'Enter' }); expect(onClick).toHaveBeenCalledTimes(2); -}); \ No newline at end of file +}); diff --git a/dashboard/src/components/EventCard.tsx b/dashboard/src/components/EventCard.tsx index c48a6138..6a840fa2 100644 --- a/dashboard/src/components/EventCard.tsx +++ b/dashboard/src/components/EventCard.tsx @@ -34,13 +34,7 @@ function getEventBadgeClass(name: string | null): string { } function SkeletonLine({ width = '100%', height = '14px' }: { width?: string; height?: string }) { - return ( -
Value: {event.value} - {event.txHash && ( - Tx: {shortenAddress(event.txHash)} - )} + {event.txHash && Tx: {shortenAddress(event.txHash)}}
); } -function ExpandedCard({ event, onClick }: { event: BlockchainEvent; onClick?: (e: BlockchainEvent) => void }) { +function ExpandedCard({ + event, + onClick, +}: { + event: BlockchainEvent; + onClick?: (e: BlockchainEvent) => void; +}) { const displayName = event.eventName ?? event.type; const badgeClass = getEventBadgeClass(event.eventName); const Wrapper = onClick ? 'div' : 'article'; @@ -191,7 +195,9 @@ function ExpandedCard({ event, onClick }: { event: BlockchainEvent; onClick?: (e
    {event.topic.map((t, i) => ( -
  • {t}
  • +
  • + {t} +
  • ))}
@@ -235,4 +241,4 @@ export const EventCard = memo(function EventCard({ } return ; -}); \ No newline at end of file +}); diff --git a/dashboard/src/components/EventExplorerTable.test.tsx b/dashboard/src/components/EventExplorerTable.test.tsx index e60c6517..f2167ccd 100644 --- a/dashboard/src/components/EventExplorerTable.test.tsx +++ b/dashboard/src/components/EventExplorerTable.test.tsx @@ -48,10 +48,7 @@ describe('EventExplorerTable column resizing helpers', () => { }); it('clamps widths below the minimum when loading', () => { - localStorage.setItem( - STORAGE_KEY, - JSON.stringify([40, 180, 120, 200, 110, 170]) - ); + localStorage.setItem(STORAGE_KEY, JSON.stringify([40, 180, 120, 200, 110, 170])); const loaded = loadColumnWidths(); expect(loaded[0]).toBe(DEFAULT_COLUMN_WIDTHS[0]); expect(loaded[0]).toBeGreaterThanOrEqual(MIN_COLUMN_WIDTH); @@ -68,9 +65,7 @@ describe('EventExplorerTable resizing UI', () => { }); it('renders resize handles and keeps layout stable while resizing', () => { - const { getByLabelText, container } = render( - - ); + const { getByLabelText, container } = render(); const handle = getByLabelText('Resize Contract column'); expect(handle).toBeInTheDocument(); diff --git a/dashboard/src/components/EventExplorerTable.tsx b/dashboard/src/components/EventExplorerTable.tsx index 07d57fa9..3fa9e97e 100644 --- a/dashboard/src/components/EventExplorerTable.tsx +++ b/dashboard/src/components/EventExplorerTable.tsx @@ -87,14 +87,17 @@ export function EventExplorerTable({ }; }, []); - const startResize = useCallback((index: number, clientX: number) => { - dragRef.current = { - index, - startX: clientX, - startWidth: columnWidths[index], - }; - document.body.classList.add('event-explorer--resizing'); - }, [columnWidths]); + const startResize = useCallback( + (index: number, clientX: number) => { + dragRef.current = { + index, + startX: clientX, + startWidth: columnWidths[index], + }; + document.body.classList.add('event-explorer--resizing'); + }, + [columnWidths], + ); async function syncCopyText(text: string) { if (navigator.clipboard?.writeText) { diff --git a/dashboard/src/components/EventFiltersBar.test.tsx b/dashboard/src/components/EventFiltersBar.test.tsx index 06e896cd..a5e990d7 100644 --- a/dashboard/src/components/EventFiltersBar.test.tsx +++ b/dashboard/src/components/EventFiltersBar.test.tsx @@ -33,4 +33,4 @@ test('EventList exposes list semantics', () => { const { getByRole, getAllByRole } = render(); expect(getByRole('list')).toBeInTheDocument(); expect(getAllByRole('listitem')).toHaveLength(2); -}); \ No newline at end of file +}); diff --git a/dashboard/src/components/EventFiltersBar.tsx b/dashboard/src/components/EventFiltersBar.tsx index 8d6f91a9..1ca371c9 100644 --- a/dashboard/src/components/EventFiltersBar.tsx +++ b/dashboard/src/components/EventFiltersBar.tsx @@ -24,12 +24,8 @@ export const EventFiltersBar = memo(function EventFiltersBar() {
- setSearch(value)} - /> + setSearch(value)} />
-
- {/* Sort control (#495) */}
@@ -72,13 +66,15 @@ export const EventFiltersBar = memo(function EventFiltersBar() { aria-label="Sort notifications" > {SORT_OPTIONS.map(({ value, label }) => ( - + ))}
- -

+

{totalCount.toLocaleString()} events loaded -

+

{' '} + ); }); diff --git a/dashboard/src/components/EventList.tsx b/dashboard/src/components/EventList.tsx index afb2d2a4..3869a674 100644 --- a/dashboard/src/components/EventList.tsx +++ b/dashboard/src/components/EventList.tsx @@ -1,12 +1,4 @@ -import { - memo, - useCallback, - useEffect, - useMemo, - useRef, - useState, - type UIEvent, -} from 'react'; +import { memo, useCallback, useEffect, useMemo, useRef, useState, type UIEvent } from 'react'; import type { BlockchainEvent } from '../types/event'; import { EventRow } from './EventRow'; @@ -65,10 +57,7 @@ export const EventList = memo(function EventList({ events }: EventListProps) { aria-label={`Blockchain events, ${events.length.toLocaleString()} total`} tabIndex={0} > -
+
{visibleEvents.map((event, index) => { const position = windowState.startIndex + index; return ( @@ -90,4 +79,4 @@ export const EventList = memo(function EventList({ events }: EventListProps) {
); -}); \ No newline at end of file +}); diff --git a/dashboard/src/components/EventListNaive.tsx b/dashboard/src/components/EventListNaive.tsx index bf7142b8..0a373568 100644 --- a/dashboard/src/components/EventListNaive.tsx +++ b/dashboard/src/components/EventListNaive.tsx @@ -7,12 +7,12 @@ interface EventListNaiveProps { } /** Naive list used only for benchmark comparisons β€” renders every row in the DOM. */ -export const EventListNaive = memo(function EventListNaive({ - events, -}: EventListNaiveProps) { +export const EventListNaive = memo(function EventListNaive({ events }: EventListNaiveProps) { return (
- {events.map((event) => )} + {events.map((event) => ( + + ))}
); }); diff --git a/dashboard/src/components/EventRow.test.tsx b/dashboard/src/components/EventRow.test.tsx index 4939ec1e..73a22681 100644 --- a/dashboard/src/components/EventRow.test.tsx +++ b/dashboard/src/components/EventRow.test.tsx @@ -35,6 +35,8 @@ describe('EventRow notification ID copy action', () => { fireEvent.click(screen.getByRole('button', { name: /copy notification id/i })); expect(navigator.clipboard.writeText).toHaveBeenCalledWith('notif-42'); - expect(await screen.findByRole('button', { name: /notification id copied/i })).toBeInTheDocument(); + expect( + await screen.findByRole('button', { name: /notification id copied/i }), + ).toBeInTheDocument(); }); }); diff --git a/dashboard/src/components/EventRow.tsx b/dashboard/src/components/EventRow.tsx index f0a71012..a02ce2ee 100644 --- a/dashboard/src/components/EventRow.tsx +++ b/dashboard/src/components/EventRow.tsx @@ -18,7 +18,11 @@ export const EventRow = memo(function EventRow({ event }: EventRowProps) { const label = event.eventName ?? event.type; return ( -
+
{label} Ledger {event.ledger} diff --git a/dashboard/src/components/EventsListSkeleton.tsx b/dashboard/src/components/EventsListSkeleton.tsx index 7b10a874..fbc86bff 100644 --- a/dashboard/src/components/EventsListSkeleton.tsx +++ b/dashboard/src/components/EventsListSkeleton.tsx @@ -3,7 +3,9 @@ interface EventsListSkeletonProps { } function SkeletonLine({ width }: { width: string }) { - return
); } - diff --git a/dashboard/src/components/ExportHistoryTable.tsx b/dashboard/src/components/ExportHistoryTable.tsx index d60bbf2a..4385237d 100644 --- a/dashboard/src/components/ExportHistoryTable.tsx +++ b/dashboard/src/components/ExportHistoryTable.tsx @@ -30,9 +30,7 @@ function formatDate(timestamp: number): string { // ────────────────────────────────────────────────────────────────── function FormatBadge({ format }: { format: NotificationExport['format'] }) { - return ( - {format} - ); + return {format}; } function StatusBadge({ status }: { status: NotificationExport['status'] }) { @@ -50,9 +48,7 @@ function StatusBadge({ status }: { status: NotificationExport['status'] }) { ); - return ( - {label} - ); + return {label}; } function DownloadButton({ diff --git a/dashboard/src/components/IndexingHealthPanel.test.tsx b/dashboard/src/components/IndexingHealthPanel.test.tsx index f3c2555c..45513a51 100644 --- a/dashboard/src/components/IndexingHealthPanel.test.tsx +++ b/dashboard/src/components/IndexingHealthPanel.test.tsx @@ -34,7 +34,7 @@ describe('IndexingHealthPanel', () => { + />, ); expect(await screen.findByText('Indexing Health')).toBeInTheDocument(); @@ -59,7 +59,7 @@ describe('IndexingHealthPanel', () => { + />, ); expect(await screen.findByText('Degraded')).toBeInTheDocument(); @@ -69,4 +69,3 @@ describe('IndexingHealthPanel', () => { expect(await screen.findByText('Behind by 20 ledger(s).')).toBeInTheDocument(); }); }); - diff --git a/dashboard/src/components/IndexingHealthPanel.tsx b/dashboard/src/components/IndexingHealthPanel.tsx index 6bda867c..597300b6 100644 --- a/dashboard/src/components/IndexingHealthPanel.tsx +++ b/dashboard/src/components/IndexingHealthPanel.tsx @@ -151,4 +151,3 @@ export function IndexingHealthPanel(props: { healthUrl: string; pollIntervalMs?: ); } - diff --git a/dashboard/src/components/MobileNavDrawer.test.tsx b/dashboard/src/components/MobileNavDrawer.test.tsx index 1052b161..7d8be64f 100644 --- a/dashboard/src/components/MobileNavDrawer.test.tsx +++ b/dashboard/src/components/MobileNavDrawer.test.tsx @@ -59,13 +59,7 @@ describe('MobileNavDrawer (#396)', () => { it('calls onSelectTab and onClose when a nav item is clicked', () => { const onSelectTab = jest.fn(); const onClose = jest.fn(); - render( - , - ); + render(); fireEvent.click(screen.getByText('Delivery Timeline')); expect(onSelectTab).toHaveBeenCalledWith('timeline'); expect(onClose).toHaveBeenCalledTimes(1); diff --git a/dashboard/src/components/MobileNavDrawer.tsx b/dashboard/src/components/MobileNavDrawer.tsx index 3e1b5cd0..5f036851 100644 --- a/dashboard/src/components/MobileNavDrawer.tsx +++ b/dashboard/src/components/MobileNavDrawer.tsx @@ -30,16 +30,16 @@ export interface NavItem { } export const NAV_ITEMS: NavItem[] = [ - { id: 'explorer', label: 'Event Explorer', group: 'Monitoring' }, - { id: 'timeline', label: 'Delivery Timeline', group: 'Monitoring' }, - { id: 'activity', label: 'Activity Feed', group: 'Monitoring' }, - { id: 'user-activity', label: 'User Activity', group: 'Monitoring' }, - { id: 'retry-stats', label: 'Retry Stats', group: 'Operations' }, - { id: 'webhooks', label: 'Webhook Performance', group: 'Operations' }, - { id: 'export-history', label: 'Export History', group: 'Operations' }, - { id: 'search', label: 'Notification Search', group: 'Search & Config' }, - { id: 'preferences', label: 'Preferences', group: 'Search & Config' }, - { id: 'templates', label: 'Templates', group: 'Search & Config' }, + { id: 'explorer', label: 'Event Explorer', group: 'Monitoring' }, + { id: 'timeline', label: 'Delivery Timeline', group: 'Monitoring' }, + { id: 'activity', label: 'Activity Feed', group: 'Monitoring' }, + { id: 'user-activity', label: 'User Activity', group: 'Monitoring' }, + { id: 'retry-stats', label: 'Retry Stats', group: 'Operations' }, + { id: 'webhooks', label: 'Webhook Performance', group: 'Operations' }, + { id: 'export-history', label: 'Export History', group: 'Operations' }, + { id: 'search', label: 'Notification Search', group: 'Search & Config' }, + { id: 'preferences', label: 'Preferences', group: 'Search & Config' }, + { id: 'templates', label: 'Templates', group: 'Search & Config' }, ]; const FOCUSABLE_SELECTOR = [ @@ -56,12 +56,7 @@ interface MobileNavDrawerProps { onSelectTab: (tab: Tab) => void; } -export function MobileNavDrawer({ - isOpen, - onClose, - activeTab, - onSelectTab, -}: MobileNavDrawerProps) { +export function MobileNavDrawer({ isOpen, onClose, activeTab, onSelectTab }: MobileNavDrawerProps) { const drawerRef = useRef(null); const previousActiveElement = useRef(null); @@ -132,11 +127,7 @@ export function MobileNavDrawer({ return ( <> {/* Backdrop */} -