50 行
1.9 KiB
TypeScript
50 行
1.9 KiB
TypeScript
import { useEffect } from "react";
|
|
import { Navigate, Outlet, useLocation } from "react-router-dom";
|
|
import { ErrorBoundary } from "../components/common/ErrorBoundary";
|
|
import { MobileBottomNav } from "../components/mobile/MobileBottomNav";
|
|
import { Sidebar } from "../components/navigation/Sidebar";
|
|
import { Topbar } from "../components/navigation/Topbar";
|
|
import { resolvePageMeta } from "../features/navigation/schema";
|
|
import { writeRecentVisit } from "../lib/recentVisits";
|
|
import { shouldPreferMobileWorkplace } from "../lib/runtimeContext";
|
|
|
|
export function AppShellLayout() {
|
|
const location = useLocation();
|
|
const isMobileRoute = location.pathname.startsWith("/mobile");
|
|
|
|
useEffect(() => {
|
|
const pathname = location.pathname;
|
|
if (!pathname || pathname === "/login" || pathname.startsWith("/mobile/")) {
|
|
return;
|
|
}
|
|
writeRecentVisit(pathname, resolvePageMeta(pathname).title);
|
|
}, [location.pathname]);
|
|
|
|
if (location.pathname === "/portal" && shouldPreferMobileWorkplace()) {
|
|
return <Navigate to="/mobile" replace />;
|
|
}
|
|
|
|
return (
|
|
<div className="relative flex min-h-screen overflow-hidden bg-[var(--color-bg-canvas)]">
|
|
<div className="pointer-events-none fixed inset-0 -z-10 bg-[radial-gradient(circle_at_12%_8%,rgba(0,57,138,0.10),transparent_28rem),radial-gradient(circle_at_90%_12%,rgba(19,168,184,0.12),transparent_32rem)]" />
|
|
<Sidebar />
|
|
|
|
<main className="flex min-w-0 flex-1 flex-col lg:ml-72">
|
|
<div className={isMobileRoute ? "hidden md:block" : ""}>
|
|
<Topbar />
|
|
</div>
|
|
|
|
<div className={`flex-1 px-4 py-5 sm:px-7 sm:py-7 lg:px-8 ${isMobileRoute ? "pb-24 pt-4 md:pb-8" : ""}`}>
|
|
<div className="layout-container max-w-none">
|
|
<ErrorBoundary>
|
|
<Outlet />
|
|
</ErrorBoundary>
|
|
</div>
|
|
</div>
|
|
|
|
{isMobileRoute ? <MobileBottomNav /> : null}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|