diff --git a/frontend/app/[locale]/(protected)/reports/error.tsx b/frontend/app/[locale]/(protected)/reports/error.tsx new file mode 100644 index 00000000..b5ccc296 --- /dev/null +++ b/frontend/app/[locale]/(protected)/reports/error.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { useEffect } from "react"; +import { Link } from "@/i18n/navigation"; + +export default function ReportsError({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error("Reports route error:", error); + }, [error]); + + return ( +
+

Reports unavailable

+

+ Reports could not be loaded. Try again or return to your dashboard while other pages remain available. +

+
+ + + Back to dashboard + +
+
+ ); +} diff --git a/frontend/test-utils/reports-error.test.tsx b/frontend/test-utils/reports-error.test.tsx new file mode 100644 index 00000000..866dcf37 --- /dev/null +++ b/frontend/test-utils/reports-error.test.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import { fireEvent, render, screen } from "@testing-library/react"; +import ReportsError from "@/app/[locale]/(protected)/reports/error"; + +const mockReset = jest.fn(); + +jest.mock("@/i18n/navigation", () => ({ + Link: ({ children, ...props }: React.AnchorHTMLAttributes) => ( + {children} + ), +})); + +describe("ReportsError", () => { + beforeEach(() => mockReset.mockClear()); + + it("renders a reports-scoped error with a retry action", () => { + render(); + + expect(screen.getByRole("heading", { name: "Reports unavailable" })).toBeInTheDocument(); + fireEvent.click(screen.getByRole("button", { name: "Try again" })); + expect(mockReset).toHaveBeenCalledTimes(1); + }); + + it("provides navigation back to the dashboard", () => { + render(); + + expect(screen.getByRole("link", { name: "Back to dashboard" })).toHaveAttribute("href", "/"); + }); +});