All files / src/router index.jsx

54.54% Statements 12/22
66.66% Branches 4/6
0% Functions 0/10
75% Lines 12/16

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125                            1x 1x     1x 1x 1x   1x     1x                 1x                                                         1x                                                                               1x   1x                       1x              
import { Suspense, lazy } from "react";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { Box } from "@mui/material";
import { ThemeProvider } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import { createUiTheme } from "@/theme/ui";
import { LandingPageRoutes } from "@/app/landing-page/LandingPageRoutes.jsx";
import { TeacherPageRoutes } from "@/app/teacher-page/TeacherPageRoutes.jsx";
import { StudentPageRoutes } from "@/app/student-page/StudentPageRoutes.jsx";
import ProtectedRoute from "@/router/ProtectedRoute.jsx";
import PageError from "@/utils/components/page-error/PageError";
import SimpleLoading from "@/utils/components/SimpleLoading";
 
// Named exports — map to { default } so React.lazy can resolve them.
const LoginPage = lazy(() => import("@/app/login-page/LoginPage").then((m) => ({ default: m.LoginPage })));
const ForgotPasswordPage = lazy(() =>
    import("@/app/forgot-password-page/ForgotPasswordPage").then((m) => ({ default: m.ForgotPasswordPage }))
);
const ExcelUpload = lazy(() => import("@/lib/excel/ExcelUpload"));
const GraphEditorStandalone = lazy(() => import("@/lib/graph2d/GraphEditorStandalone"));
const CalculusPanel = lazy(() => import("@/lib/graph2d/calculus/CalculusPanel"));
 
const graphEditorTheme = createUiTheme({ mode: "light", preset: "default" });
 
// EI-3152 — the Calculus Visualization tool (derivative/tangent + integral/area + export).
const CalculusRoute = () => (
    <ThemeProvider theme={graphEditorTheme}>
        <CssBaseline />
        <Box sx={{ minHeight: "100dvh", overflow: "auto", bgcolor: "background.default" }}>
            <CalculusPanel />
        </Box>
    </ThemeProvider>
);
 
const GraphEditorRoute = () => (
    <ThemeProvider theme={graphEditorTheme}>
        <CssBaseline />
        {/*
          Fill the host viewport: in a TinyMCE URL-dialog iframe, `100dvh` is the outer window, so the
          layout runs taller than the iframe and the drawing-tools column is clipped. `fixed` + `inset: 0`
          uses the iframe (or tab) as the containing block; flex + minHeight 0 lets the tools panel scroll.
        */}
        <Box
            sx={{
                position: "fixed",
                inset: 0,
                boxSizing: "border-box",
                display: "flex",
                flexDirection: "column",
                minHeight: 0,
                overflow: "hidden",
            }}
        >
            <Box sx={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column", overflow: "hidden" }}>
                <GraphEditorStandalone />
            </Box>
        </Box>
    </ThemeProvider>
);
 
// Exported so the route table can be asserted directly in unit tests: role
// gating, the documented ordering constraint below, and the catch-all's
// position are all invariants a regression would break silently.
export const routesConfig = [
    /** Must be registered before `LandingPageRoutes` (`path: "/"` + child `*`), or `/graph-editor` can match the landing splat and show PageError. */
    {
        path: "graph-editor",
        // Include `admin`: otherwise `ProtectedRoute` sends them to `/admin`, which is not a route (same 404 as PageError).
        element: <ProtectedRoute allowedRole={["teacher", "student"]} />,
        children: [
            {
                index: true,
                element: <GraphEditorRoute />,
            },
            {
                path: "calculus",
                element: <CalculusRoute />,
            },
        ],
    },
    {
        path: "file-upload",
        element: <ExcelUpload />,
    },
    {
        path: "login",
        element: <LoginPage />,
    },
    {
        path: "forgot-password",
        element: <ForgotPasswordPage />,
    },
    LandingPageRoutes,
    TeacherPageRoutes,
    StudentPageRoutes,
    /** Last: unknown paths (do not place before `/graph-editor` or other app routes). */
    {
        path: "*",
        element: <PageError />,
    },
];
 
const routerBasename =
    import.meta.env.BASE_URL && import.meta.env.BASE_URL !== "/" ? import.meta.env.BASE_URL.replace(/\/$/, "") : undefined;
 
const routes = createBrowserRouter(routesConfig, {
    ...(routerBasename ? { basename: routerBasename } : {}),
    future: {
        v7_fetcherPersist: true,
        v7_normalizeFormMethod: true,
        v7_partialHydration: true,
        v7_relativeSplatPath: true,
        v7_skipActionErrorRevalidation: true,
    },
});
 
// Route Components
export const RouteComponents = () => {
    return (
        <Suspense fallback={<SimpleLoading showLoading={true} />}>
            <RouterProvider router={routes} future={{ v7_startTransition: true }} />
        </Suspense>
    );
};