2b5223097f
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)
74 lines
1.8 KiB
TypeScript
74 lines
1.8 KiB
TypeScript
import { apiClient } from "./client";
|
|
|
|
export interface NotificationItem {
|
|
id: string;
|
|
user_id: string;
|
|
category: string;
|
|
severity: "info" | "warning" | "error" | "success";
|
|
title: string;
|
|
message: string | null;
|
|
source_type: string | null;
|
|
source_id: string | null;
|
|
metadata: Record<string, unknown>;
|
|
read_at: string | null;
|
|
dismissed_at: string | null;
|
|
created_at: string;
|
|
}
|
|
|
|
export interface NotificationListResponse {
|
|
items: NotificationItem[];
|
|
total: number;
|
|
limit: number;
|
|
offset: number;
|
|
}
|
|
|
|
export interface UnreadCountResponse {
|
|
count: number;
|
|
}
|
|
|
|
export interface MarkAllReadResponse {
|
|
marked_count: number;
|
|
}
|
|
|
|
export interface ClearAllResponse {
|
|
cleared_count: number;
|
|
}
|
|
|
|
export const getNotifications = async (): Promise<NotificationListResponse> => {
|
|
const response =
|
|
await apiClient.get<NotificationListResponse>("/notifications");
|
|
return response.data;
|
|
};
|
|
|
|
export const getUnreadCount = async (): Promise<number> => {
|
|
const response = await apiClient.get<UnreadCountResponse>(
|
|
"/notifications/unread",
|
|
);
|
|
return response.data.count;
|
|
};
|
|
|
|
export const markNotificationRead = async (
|
|
id: string,
|
|
): Promise<NotificationItem> => {
|
|
const response = await apiClient.patch<NotificationItem>(
|
|
`/notifications/${id}/read`,
|
|
);
|
|
return response.data;
|
|
};
|
|
|
|
export const markAllNotificationsRead = async (): Promise<number> => {
|
|
const response = await apiClient.post<MarkAllReadResponse>(
|
|
"/notifications/mark-all-read",
|
|
);
|
|
return response.data.marked_count;
|
|
};
|
|
|
|
export const dismissNotification = async (id: string): Promise<void> => {
|
|
await apiClient.delete(`/notifications/${id}`);
|
|
};
|
|
|
|
export const clearAllNotifications = async (): Promise<number> => {
|
|
const response = await apiClient.delete<ClearAllResponse>("/notifications");
|
|
return response.data.cleared_count;
|
|
};
|