feat: filter notifications to warnings/errors/ready only and add clear-all button
Notification filtering: - lifecycle_hooks.py: only instance.error and instance.health_changed with status=running generate notifications. All other lifecycle events (created, started, stopped, restarted, deleted) are filtered out. - health_monitor.py: only error and unhealthy states generate notifications. Running/recovered state no longer creates info notifications. - _derive_title now maps instance.health_changed to "Container ready". Clear-all button: - Added dismiss_all() to NotificationService - Added DELETE /notifications endpoint for bulk dismiss - Frontend: clearAllNotifications API, clearAll in notification context, "Clear all" button in notification drawer alongside "Mark all as read" - Added CSS for .notification-clear-all with danger hover state - Updated notification-center tests Quality gates: pytest (21 passed), vitest (11 passed)
This commit is contained in:
@@ -9,6 +9,7 @@ vi.mock("../api/notifications", () => ({
|
||||
markNotificationRead: vi.fn(),
|
||||
markAllNotificationsRead: vi.fn(),
|
||||
dismissNotification: vi.fn(),
|
||||
clearAllNotifications: vi.fn(),
|
||||
}));
|
||||
|
||||
import { getNotifications, getUnreadCount } from "../api/notifications";
|
||||
@@ -146,6 +147,26 @@ describe("NotificationCenter", () => {
|
||||
expect(vi.mocked(mockMarkAll)).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls clearAll on clear-all button click", async () => {
|
||||
mockedGetNotifications.mockResolvedValue({
|
||||
items: [makeNotification("1")],
|
||||
total: 1,
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
});
|
||||
|
||||
render(<NotificationCenter />, { wrapper });
|
||||
fireEvent.click(screen.getByRole("button", { name: /notifications/i }));
|
||||
|
||||
await vi.advanceTimersByTimeAsync(100);
|
||||
fireEvent.click(screen.getByRole("button", { name: /clear all/i }));
|
||||
|
||||
const { clearAllNotifications: mockClearAll } = await import(
|
||||
"../api/notifications"
|
||||
);
|
||||
expect(vi.mocked(mockClearAll)).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("refreshes list immediately on open", async () => {
|
||||
render(<NotificationCenter />, { wrapper });
|
||||
fireEvent.click(screen.getByRole("button", { name: /notifications/i }));
|
||||
|
||||
@@ -15,6 +15,7 @@ export function NotificationCenter({
|
||||
unreadCount,
|
||||
markRead,
|
||||
markAllRead,
|
||||
clearAll,
|
||||
dismiss,
|
||||
refreshList,
|
||||
isDropdownOpen,
|
||||
@@ -115,6 +116,15 @@ export function NotificationCenter({
|
||||
>
|
||||
Mark all as read
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="notification-clear-all"
|
||||
onClick={() => {
|
||||
void clearAll();
|
||||
}}
|
||||
>
|
||||
Clear all
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user