Files
MatrixGW/matrixgw_frontend/src/widgets/dashboard/BaseAuthenticatedPage.tsx
2025-11-11 17:19:23 +01:00

158 lines
4.3 KiB
TypeScript

import { Button } from "@mui/material";
import Box from "@mui/material/Box";
import { useTheme } from "@mui/material/styles";
import Toolbar from "@mui/material/Toolbar";
import useMediaQuery from "@mui/material/useMediaQuery";
import * as React from "react";
import { Outlet, useNavigate } from "react-router";
import { AuthApi, type UserInfo } from "../../api/AuthApi";
import { useAuth } from "../../App";
import { useAlert } from "../../hooks/contexts_provider/AlertDialogProvider";
import { useLoadingMessage } from "../../hooks/contexts_provider/LoadingMessageProvider";
import { AsyncWidget } from "../AsyncWidget";
import DashboardHeader from "./DashboardHeader";
import DashboardSidebar from "./DashboardSidebar";
interface UserInfoContext {
info: UserInfo;
reloadUserInfo: () => void;
signOut: () => void;
}
const UserInfoContextK = React.createContext<UserInfoContext | null>(null);
export default function BaseAuthenticatedPage(): React.ReactElement {
const theme = useTheme();
const alert = useAlert();
const loadingMessage = useLoadingMessage();
const [userInfo, setuserInfo] = React.useState<null | UserInfo>(null);
const loadUserInfo = async () => {
setuserInfo(await AuthApi.GetUserInfo());
};
const reloadUserInfo = async () => {
try {
loadingMessage.show("Refreshing user information...");
await loadUserInfo();
} catch (e) {
console.error(`Failed to load user information! ${e}`);
alert(`Failed to load user information! ${e}`);
} finally {
loadingMessage.hide();
}
};
const auth = useAuth();
const navigate = useNavigate();
const signOut = () => {
AuthApi.SignOut();
navigate("/");
auth.setSignedIn(false);
};
const [isDesktopNavigationExpanded, setIsDesktopNavigationExpanded] =
React.useState(false);
const [isMobileNavigationExpanded, setIsMobileNavigationExpanded] =
React.useState(false);
const isOverMdViewport = useMediaQuery(theme.breakpoints.up("md"));
const isNavigationExpanded = isOverMdViewport
? isDesktopNavigationExpanded
: isMobileNavigationExpanded;
const setIsNavigationExpanded = React.useCallback(
(newExpanded: boolean) => {
if (isOverMdViewport) {
setIsDesktopNavigationExpanded(newExpanded);
} else {
setIsMobileNavigationExpanded(newExpanded);
}
},
[
isOverMdViewport,
setIsDesktopNavigationExpanded,
setIsMobileNavigationExpanded,
]
);
const handleToggleHeaderMenu = React.useCallback(
(isExpanded: boolean) => {
setIsNavigationExpanded(isExpanded);
},
[setIsNavigationExpanded]
);
const layoutRef = React.useRef<HTMLDivElement>(null);
return (
<AsyncWidget
loadKey="1"
load={loadUserInfo}
errMsg="Failed to load user information!"
errAdditionalElement={() => (
<>
<Button onClick={signOut}>Sign out</Button>
</>
)}
build={() => (
<UserInfoContextK
value={{
info: userInfo!,
reloadUserInfo,
signOut,
}}
>
<Box
ref={layoutRef}
sx={{
position: "relative",
display: "flex",
overflow: "hidden",
height: "100%",
width: "100%",
}}
>
<DashboardHeader
menuOpen={isNavigationExpanded}
onToggleMenu={handleToggleHeaderMenu}
/>
<DashboardSidebar
expanded={isNavigationExpanded}
setExpanded={setIsNavigationExpanded}
container={layoutRef?.current ?? undefined}
/>
<Box
sx={{
display: "flex",
flexDirection: "column",
flex: 1,
minWidth: 0,
}}
>
<Toolbar sx={{ displayPrint: "none" }} />
<Box
component="main"
sx={{
display: "flex",
flexDirection: "column",
flex: 1,
overflow: "auto",
}}
>
<Outlet />
</Box>
</Box>
</Box>
</UserInfoContextK>
)}
/>
);
}
export function useUserInfo(): UserInfoContext {
return React.use(UserInfoContextK)!;
}