Add alert toasts and optimize alert status handling
Introduced a toast notification system to display new alerts and warnings. Updated the handling of alert status by centralizing it in the auth context and removing redundant API calls from individual pages. Improved styling for better user experience with alert notifications.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { createContext, useContext, useMemo, useState } from "react";
|
||||
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { API_URL } from "./api";
|
||||
|
||||
const AuthCtx = createContext(null);
|
||||
@@ -27,6 +27,10 @@ export function AuthProvider({ children }) {
|
||||
const [tokens, setTokens] = useState(initial?.tokens || null);
|
||||
const [me, setMe] = useState(initial?.me || null);
|
||||
const [uiMode, setUiModeState] = useState(loadUiMode);
|
||||
const [alertStatus, setAlertStatus] = useState({ warnings: [], alerts: [], warning_count: 0, alert_count: 0 });
|
||||
const [alertToasts, setAlertToasts] = useState([]);
|
||||
const knownAlertKeysRef = useRef(new Set());
|
||||
const hasAlertSnapshotRef = useRef(false);
|
||||
|
||||
const persist = (nextTokens, nextMe) => {
|
||||
if (nextTokens && nextMe) {
|
||||
@@ -90,13 +94,103 @@ export function AuthProvider({ children }) {
|
||||
}
|
||||
};
|
||||
|
||||
const dismissAlertToast = (toastId) => {
|
||||
setAlertToasts((prev) => prev.filter((t) => t.id !== toastId));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!tokens?.accessToken) {
|
||||
setAlertStatus({ warnings: [], alerts: [], warning_count: 0, alert_count: 0 });
|
||||
setAlertToasts([]);
|
||||
knownAlertKeysRef.current = new Set();
|
||||
hasAlertSnapshotRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let mounted = true;
|
||||
|
||||
const pushToastsForNewItems = (items) => {
|
||||
if (!items.length) return;
|
||||
const createdAt = Date.now();
|
||||
const nextToasts = items.slice(0, 4).map((item, idx) => ({
|
||||
id: `${createdAt}-${idx}-${item.alert_key}`,
|
||||
severity: item.severity,
|
||||
title: item.name,
|
||||
target: item.target_name,
|
||||
message: item.message,
|
||||
}));
|
||||
setAlertToasts((prev) => [...nextToasts, ...prev].slice(0, 6));
|
||||
for (const toast of nextToasts) {
|
||||
setTimeout(() => {
|
||||
if (!mounted) return;
|
||||
dismissAlertToast(toast.id);
|
||||
}, 8000);
|
||||
}
|
||||
};
|
||||
|
||||
const loadAlertStatus = async () => {
|
||||
const request = async (accessToken) =>
|
||||
fetch(`${API_URL}/alerts/status`, {
|
||||
headers: { Authorization: `Bearer ${accessToken}` },
|
||||
});
|
||||
|
||||
let res = await request(tokens.accessToken);
|
||||
if (res.status === 401 && tokens.refreshToken) {
|
||||
const refreshed = await refresh();
|
||||
if (refreshed?.accessToken) {
|
||||
res = await request(refreshed.accessToken);
|
||||
}
|
||||
}
|
||||
if (!res.ok) return;
|
||||
|
||||
const payload = await res.json();
|
||||
if (!mounted) return;
|
||||
setAlertStatus(payload);
|
||||
|
||||
const currentItems = [...(payload.warnings || []), ...(payload.alerts || [])];
|
||||
const currentKeys = new Set(currentItems.map((item) => item.alert_key));
|
||||
if (!hasAlertSnapshotRef.current) {
|
||||
knownAlertKeysRef.current = currentKeys;
|
||||
hasAlertSnapshotRef.current = true;
|
||||
return;
|
||||
}
|
||||
const newItems = currentItems.filter((item) => !knownAlertKeysRef.current.has(item.alert_key));
|
||||
knownAlertKeysRef.current = currentKeys;
|
||||
pushToastsForNewItems(newItems);
|
||||
};
|
||||
|
||||
loadAlertStatus().catch(() => {});
|
||||
const timer = setInterval(() => {
|
||||
loadAlertStatus().catch(() => {});
|
||||
}, 8000);
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
clearInterval(timer);
|
||||
};
|
||||
}, [tokens?.accessToken, tokens?.refreshToken]);
|
||||
|
||||
const setUiMode = (nextMode) => {
|
||||
const mode = nextMode === "easy" ? "easy" : "dba";
|
||||
setUiModeState(mode);
|
||||
localStorage.setItem(UI_MODE_KEY, mode);
|
||||
};
|
||||
|
||||
const value = useMemo(() => ({ tokens, me, login, logout, refresh, uiMode, setUiMode }), [tokens, me, uiMode]);
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
tokens,
|
||||
me,
|
||||
login,
|
||||
logout,
|
||||
refresh,
|
||||
uiMode,
|
||||
setUiMode,
|
||||
alertStatus,
|
||||
alertToasts,
|
||||
dismissAlertToast,
|
||||
}),
|
||||
[tokens, me, uiMode, alertStatus, alertToasts]
|
||||
);
|
||||
return <AuthCtx.Provider value={value}>{children}</AuthCtx.Provider>;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user