diff --git a/__test__/features/Events/ImportAlert.test.tsx b/__test__/features/Events/ImportAlert.test.tsx
new file mode 100644
index 0000000..17c7422
--- /dev/null
+++ b/__test__/features/Events/ImportAlert.test.tsx
@@ -0,0 +1,142 @@
+import { screen, fireEvent } from "@testing-library/react";
+import { renderWithProviders } from "../../utils/Renderwithproviders";
+import ImportAlert from "../../../src/features/Events/ImportAlert";
+import { clearError } from "../../../src/features/Calendars/CalendarSlice";
+
+// Mock the ErrorSnackbar component since we want to test ImportAlert logic,
+// but we can also test integration if we don't mock it.
+// Given ImportAlert uses EventErrorSnackbar which uses MUI Snackbar,
+// it's better to render it and check for text presence.
+
+describe("ImportAlert", () => {
+ const preloadedState = {
+ calendars: {
+ list: {
+ cal1: {
+ id: "cal1",
+ name: "Calendar 1",
+ events: {
+ event1: {
+ uid: "event1",
+ title: "Event 1",
+ error: "Error message 1",
+ },
+ event2: {
+ uid: "event2",
+ title: "Event 2",
+ // No error
+ },
+ },
+ },
+ cal2: {
+ id: "cal2",
+ name: "Calendar 2",
+ events: {
+ event3: {
+ uid: "event3",
+ title: "Event 3",
+ error: "Error message 2",
+ },
+ },
+ },
+ },
+ },
+ };
+
+ it("renders nothing when there are no errors", () => {
+ const state = {
+ calendars: {
+ list: {
+ cal1: {
+ id: "cal1",
+ events: {
+ event1: { uid: "event1" },
+ },
+ },
+ },
+ },
+ };
+
+ const { container } = renderWithProviders(, state);
+ expect(container).toBeEmptyDOMElement();
+ });
+
+ it("renders snackbar with error message when there is one error", () => {
+ renderWithProviders(, preloadedState);
+
+ // EventErrorSnackbar shows "multipleEvents" if > 1, or the message if 1.
+ // Here we have 2 errors in preloadedState.
+ // Let's adjust state for single error test.
+ const singleErrorState = {
+ calendars: {
+ list: {
+ cal1: {
+ id: "cal1",
+ events: {
+ event1: {
+ uid: "event1",
+ error: "Single Error",
+ },
+ },
+ },
+ },
+ },
+ };
+
+ renderWithProviders(, singleErrorState);
+ expect(screen.getByText("Single Error")).toBeInTheDocument();
+ });
+
+ it("renders summary message when there are multiple errors", () => {
+ renderWithProviders(, preloadedState);
+ // 2 errors in preloadedState
+ // "error.multipleEvents" key is used.
+ // In test environment, t function usually returns the key or formatted string.
+ // We should check what the real t function does or if it's mocked.
+ // Based on other tests, it seems we might need to check for the translation key or value.
+ // Let's assume standard behavior: "error.multipleEvents"
+
+ // Actually, EventErrorSnackbar uses: t("error.multipleEvents", { count: messages.length })
+ // If we don't have full i18n setup in tests, it might be tricky.
+ // But renderWithProviders likely sets up i18n.
+
+ // Let's check for the text that appears.
+ // If translation is missing, it might show "error.multipleEvents".
+ // Or if we can match regex.
+
+ // Let's try to match partial text or use a custom state with known behavior.
+ // If we look at EventErrorSnackbar:
+ // const summary = messages.length === 1 ? messages[0] : t("error.multipleEvents", { count: messages.length });
+
+ // We can check if the Alert is present.
+ expect(screen.getByRole("alert")).toBeInTheDocument();
+ });
+
+ it("dismisses error when close button is clicked", () => {
+ const singleErrorState = {
+ calendars: {
+ list: {
+ cal1: {
+ id: "cal1",
+ events: {
+ event1: {
+ uid: "event1",
+ error: "Dismiss Me",
+ },
+ },
+ },
+ },
+ },
+ };
+
+ renderWithProviders(, singleErrorState);
+ expect(screen.getByText("Dismiss Me")).toBeInTheDocument();
+
+ const closeButton = screen.getByRole("button", { name: /ok/i }); // EventErrorSnackbar has an "OK" button
+ fireEvent.click(closeButton);
+
+ // After clicking, it should disappear (or at least be removed from DOM or hidden)
+ // Since we use local state to filter dismissed errors, it should re-render with null.
+ expect(screen.queryByText("Dismiss Me")).not.toBeInTheDocument();
+ });
+});
diff --git a/src/features/Events/ImportAlert.tsx b/src/features/Events/ImportAlert.tsx
index 6f359f6..ba6d27c 100644
--- a/src/features/Events/ImportAlert.tsx
+++ b/src/features/Events/ImportAlert.tsx
@@ -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>(
- {}
- );
const calendars = useAppSelector((state) => state.calendars.list);
+ const [dismissedErrors, setDismissedErrors] = useState>(
+ 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 (
-
-
- toggleEventAlert(calendars[calendarId].events[id].uid)
- }
- >
- {calendars[calendarId].events[id].error}
-
-
- );
- })
- : []
- )}
- >
- );
+ if (messages.length === 0) {
+ return null;
+ }
+
+ return ;
}