supersam/src/router.jsx

152 lines
4.1 KiB
JavaScript

import React from "react";
import { Navigate, createBrowserRouter, useParams } from "react-router-dom";
import App from "./App";
import { useAuth } from "./context/AuthContext";
/**
* Code-splitting: client-facing pages (opened from SMS links on mobile) load a
* small chunk instead of the full 932KB bundle that includes the staff
* dashboard, admin panels, etc. This fixes 30-40s loads on slow mobile
* connections (customers in Crimea opening delivery links).
*
* Staff pages are also lazy so the login page stays light.
*/
const ClientDeliveryPage = React.lazy(() =>
import("./pages/ClientDeliveryPage").then((m) => ({ default: m.ClientDeliveryPage }))
);
const ClientTestPage = React.lazy(() =>
import("./pages/ClientTestPage").then((m) => ({ default: m.ClientTestPage }))
);
const ClientTestConsentPage = React.lazy(() =>
import("./pages/ClientTestConsentPage").then((m) => ({ default: m.ClientTestConsentPage }))
);
const DashboardPage = React.lazy(() =>
import("./pages/DashboardPage").then((m) => ({ default: m.DashboardPage }))
);
const GroupDetailPage = React.lazy(() =>
import("./pages/GroupDetailPage").then((m) => ({ default: m.GroupDetailPage }))
);
const LoginPage = React.lazy(() =>
import("./pages/LoginPage").then((m) => ({ default: m.LoginPage }))
);
const NotFoundPage = React.lazy(() =>
import("./pages/NotFoundPage").then((m) => ({ default: m.NotFoundPage }))
);
const ForbiddenPage = React.lazy(() =>
import("./pages/ForbiddenPage").then((m) => ({ default: m.ForbiddenPage }))
);
const SettingsPage = React.lazy(() =>
import("./pages/SettingsPage").then((m) => ({ default: m.SettingsPage }))
);
const PageLoader = () => (
<div
style={{
minHeight: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center",
color: "var(--color-text-muted, #888)",
fontFamily: "system-ui, sans-serif",
}}
>
Загрузка…
</div>
);
const withSuspense = (node) => <React.Suspense fallback={<PageLoader />}>{node}</React.Suspense>;
/**
* Protects routes that require authentication.
* Redirects to /login with return URL if no user and session is loaded.
* Shows nothing while session is being restored (avoids flash-of-login).
*/
const RequireAuth = ({ children }) => {
const { user, isSessionLoading } = useAuth();
if (isSessionLoading) {
return null;
}
if (!user) {
const redirect = encodeURIComponent(window.location.pathname + window.location.search);
return <Navigate to={`/login?redirect=${redirect}`} replace />;
}
return children;
};
export const router = createBrowserRouter([
{
path: "/",
element: <App />,
children: [
{
index: true,
element: <Navigate to="/client" replace />,
},
{
path: "login",
element: withSuspense(<LoginPage />),
},
{
path: "client",
element: withSuspense(<ClientDeliveryPage />),
},
{
path: "client/:token",
element: withSuspense(<ClientDeliveryPage />),
},
{
path: "client/test",
element: withSuspense(<ClientTestPage />),
},
{
path: "client/test-consent",
element: withSuspense(<ClientTestConsentPage />),
},
{
path: "delivery/:token",
element: <LegacyDeliveryRedirect />,
},
{
path: "forbidden",
element: withSuspense(<ForbiddenPage />),
},
{
path: "dashboard",
element: withSuspense(
<RequireAuth>
<DashboardPage />
</RequireAuth>
),
},
{
path: "dashboard/group/:groupId",
element: withSuspense(
<RequireAuth>
<GroupDetailPage />
</RequireAuth>
),
},
{
path: "settings",
element: withSuspense(
<RequireAuth>
<SettingsPage />
</RequireAuth>
),
},
{
path: "*",
element: withSuspense(<NotFoundPage />),
},
],
},
]);
function LegacyDeliveryRedirect() {
const { token } = useParams();
return <Navigate to={`/client/${encodeURIComponent(token || "")}`} replace />;
}