import React, { useCallback, useEffect, useState } from "react"; import { Alert, Box, Button, CircularProgress, Divider, IconButton, Popover, Stack, Tooltip, Typography, } from "@mui/material"; import CloseIcon from "@mui/icons-material/Close"; import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline"; import DoneIcon from "@mui/icons-material/Done"; import { api, getApiErrorMessage } from "../api"; import { useI18n } from "../i18n/I18nProvider"; import { notificationDateLabel, UserNotification } from "../notifications"; type Props = { anchorEl: HTMLElement | null; onClose: () => void; onNavigate: (path: string) => void; onChanged: () => void; }; export default function NotificationsPopover({ anchorEl, onClose, onNavigate, onChanged }: Props) { const { t } = useI18n(); const open = Boolean(anchorEl); const [notifications, setNotifications] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [busyId, setBusyId] = useState(null); const load = useCallback(async () => { setLoading(true); try { const response = await api.get("/notifications?limit=10"); setNotifications(response.data ?? []); setError(null); } catch (requestError) { setError(getApiErrorMessage(requestError, t("notificationsLoadFailed"))); } finally { setLoading(false); } }, [t]); useEffect(() => { if (open) void load(); }, [load, open]); const markRead = async (notification: UserNotification) => { if (notification.readAtUtc || busyId) return; setBusyId(notification.id); try { await api.post(`/notifications/${notification.id}/read`); setNotifications((items) => items.map((item) => item.id === notification.id ? { ...item, readAtUtc: new Date().toISOString() } : item)); setError(null); onChanged(); } catch (requestError) { setError(getApiErrorMessage(requestError, t("notificationsActionFailed"))); } finally { setBusyId(null); } }; const dismiss = async (notification: UserNotification) => { if (busyId) return; setBusyId(notification.id); try { await api.delete(`/notifications/${notification.id}`); setNotifications((items) => items.filter((item) => item.id !== notification.id)); setError(null); onChanged(); } catch (requestError) { setError(getApiErrorMessage(requestError, t("notificationsActionFailed"))); } finally { setBusyId(null); } }; const openNotification = async (notification: UserNotification) => { if (!notification.linkPath) return; if (!notification.readAtUtc) await markRead(notification); onClose(); onNavigate(notification.linkPath); }; return ( {t("notifications")} {t("notificationsRecent")} {loading ? : null} {error ? void load()}>{t("retry")}} sx={{ m: 0.5 }}>{error} : null} {!loading && !error && notifications.length === 0 ? ( {t("notificationsEmptyTitle")} {t("notificationsEmptyBody")} ) : null} {!loading ? notifications.map((notification) => ( {notification.title} {notification.message} {notificationDateLabel(notification.createdAtUtc)} {notification.linkPath ? : null} {!notification.readAtUtc ? ( ) : null} )) : null} ); }