refactor: #331 use Snackbar for event errors and add tests (#351)

Co-authored-by: Lê Nhân Phụng <lenhanphung@Phung-Mac-M4.local>
This commit is contained in:
lenhanphung
2025-11-25 17:17:06 +07:00
committed by GitHub
parent 25078c783e
commit ffe14b6b6c
2 changed files with 189 additions and 39 deletions
+47 -39
View File
@@ -1,48 +1,56 @@
import { Alert, Collapse } from "@mui/material";
import { useState } from "react";
import { useState, useMemo, useEffect } from "react";
import { useAppSelector } from "../../app/hooks";
import { EventErrorSnackbar } from "../../components/Error/ErrorSnackbar";
export default function ImportAlert() {
const [visibleAlerts, setVisibleAlerts] = useState<Record<string, boolean>>(
{}
);
const calendars = useAppSelector((state) => state.calendars.list);
const [dismissedErrors, setDismissedErrors] = useState<Set<string>>(
new Set()
);
const toggleEventAlert = (eventId: string) => {
setVisibleAlerts((prev) => ({
...prev,
[eventId]: false,
}));
// Collect all errors from all events in all calendars
const errors = useMemo(() => {
const errorList: { id: string; message: string }[] = [];
Object.values(calendars || {}).forEach((calendar) => {
if (calendar.events) {
Object.values(calendar.events).forEach((event) => {
if (event.error) {
errorList.push({
id: event.uid,
message: event.error,
});
}
});
}
});
return errorList;
}, [calendars]);
// Filter out dismissed errors
const activeErrors = useMemo(() => {
return errors.filter((e) => !dismissedErrors.has(e.id));
}, [errors, dismissedErrors]);
const messages = activeErrors.map((e) => e.message);
const handleClose = () => {
// Mark all currently active errors as dismissed
setDismissedErrors((prev) => {
const next = new Set(prev);
activeErrors.forEach((e) => next.add(e.id));
return next;
});
};
return (
<>
{Object.keys(calendars || {}).map((calendarId) =>
calendars?.[calendarId]?.events
? Object.keys(calendars[calendarId]?.events)
.filter((id) => calendars[calendarId]?.events[id].error)
.map((id) => {
const isVisible =
visibleAlerts[calendars[calendarId].events[id].uid] ?? true; // default to visible
// If new errors appear that were previously dismissed (e.g. re-fetch),
// we might want to un-dismiss them, but for now simple dismissal is enough.
// Actually, if the error persists in the store, it's the same error.
// If the user fixes it and it comes back, it might be a new fetch.
// But usually uid is stable.
return (
<Collapse
in={isVisible}
key={calendars[calendarId].events[id].uid}
>
<Alert
severity="error"
onClose={() =>
toggleEventAlert(calendars[calendarId].events[id].uid)
}
>
{calendars[calendarId].events[id].error}
</Alert>
</Collapse>
);
})
: []
)}
</>
);
if (messages.length === 0) {
return null;
}
return <EventErrorSnackbar messages={messages} onClose={handleClose} />;
}