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>
);
};
|