Files
qqq-frontend-material-dashb…/src/App.tsx

344 lines
10 KiB
TypeScript

import React, {
useState,
useEffect,
JSXElementConstructor,
Key,
ReactElement,
} from "react";
// react-router components
import {
Routes, Route, Navigate, useLocation,
} from "react-router-dom";
import {useAuth0} from "@auth0/auth0-react";
// @mui material components
import {LicenseInfo} from "@mui/x-license-pro";
import {ThemeProvider} from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import Icon from "@mui/material/Icon";
// Material Dashboard 2 PRO React TS components
import MDBox from "components/MDBox";
// Material Dashboard 2 PRO React TS exampless
import Sidenav from "examples/Sidenav";
import Configurator from "examples/Configurator";
// Material Dashboard 2 PRO React TS themes
import theme from "assets/theme";
// Material Dashboard 2 PRO React TS Dark Mode themes
import themeDark from "assets/theme-dark";
// Material Dashboard 2 PRO React TS contexts
import {useMaterialUIController, setMiniSidenav, setOpenConfigurator} from "context";
// Images
import nfLogo from "assets/images/nutrifresh_one_icon_white.png";
import {Md5} from "ts-md5/dist/md5";
import {useCookies} from "react-cookie";
import EntityCreate from "./qqq/pages/entity-create";
import EntityList from "./qqq/pages/entity-list";
import EntityView from "./qqq/pages/entity-view";
import EntityEdit from "./qqq/pages/entity-edit";
import ProcessRun from "./qqq/pages/process-run";
import MDAvatar from "./components/MDAvatar";
import ProfileOverview from "./layouts/pages/profile/profile-overview";
import Settings from "./layouts/pages/account/settings";
import Analytics from "./layouts/dashboards/analytics";
import Sales from "./layouts/dashboards/sales";
import QClient from "./qqq/utils/QClient";
///////////////////////////////////////////////////////////////////////////////////////////////
// define the parts of the nav that are static - before the qqq tables etc get dynamic added //
///////////////////////////////////////////////////////////////////////////////////////////////
function getStaticRoutes()
{
return [
{type: "divider", key: "divider-0"},
{
type: "collapse",
name: "Dashboards",
key: "dashboards",
icon: <Icon fontSize="medium">dashboard</Icon>,
collapse: [
{
name: "Analytics",
key: "analytics",
route: "/dashboards/analytics",
component: <Analytics />,
},
{
name: "Sales",
key: "sales",
route: "/dashboards/sales",
component: <Sales />,
},
],
},
{type: "divider", key: "divider-1"},
{type: "title", title: "Tables", key: "title-docs"},
];
}
export const SESSION_ID_COOKIE_NAME = "sessionId";
LicenseInfo.setLicenseKey(process.env.REACT_APP_MATERIAL_UI_LICENSE_KEY);
export default function App()
{
const [, setCookie] = useCookies([SESSION_ID_COOKIE_NAME]);
const {
user, getAccessTokenSilently, getIdTokenClaims, logout, loginWithRedirect,
} = useAuth0();
const [loadingToken, setLoadingToken] = useState(false);
const [isFullyAuthenticated, setIsFullyAuthenticated] = useState(false);
useEffect(() =>
{
if (loadingToken)
{
return;
}
setLoadingToken(true);
(async () =>
{
try
{
console.log("Loading token...");
await getAccessTokenSilently();
const idToken = await getIdTokenClaims();
setCookie(SESSION_ID_COOKIE_NAME, idToken.__raw, {path: "/"});
setIsFullyAuthenticated(true);
console.log("Token load complete.");
}
catch (e)
{
console.log(`Error loading token: ${JSON.stringify(e)}`);
logout();
}
})();
}, [loadingToken]);
const [controller, dispatch] = useMaterialUIController();
const {
miniSidenav,
direction,
layout,
openConfigurator,
sidenavColor,
darkMode,
} = controller;
const [onMouseEnter, setOnMouseEnter] = useState(false);
const {pathname} = useLocation();
const [needToLoadRoutes, setNeedToLoadRoutes] = useState(true);
const [routes, setRoutes] = useState(getStaticRoutes());
////////////////////////////////////////////
// load qqq meta data to make more routes //
////////////////////////////////////////////
useEffect(() =>
{
if (!needToLoadRoutes || !isFullyAuthenticated)
{
return;
}
setNeedToLoadRoutes(false);
(async () =>
{
try
{
const metaData = await QClient.getInstance().loadMetaData();
// get the keys sorted
const keys = [...metaData.tables.keys()].sort((a, b): number =>
{
const labelA = metaData.tables.get(a).label;
const labelB = metaData.tables.get(b).label;
return (labelA.localeCompare(labelB));
});
const tableList = [] as any[];
keys.forEach((key) =>
{
const table = metaData.tables.get(key);
if (!table.isHidden)
{
tableList.push({
name: `${table.label}`,
key: table.name,
route: `/${table.name}`,
component: <EntityList table={table} />,
});
}
});
let profileRoute = {};
const gravatarBase = "http://www.gravatar.com/avatar/";
const hash = Md5.hashStr(user.email);
const profilePicture = `${gravatarBase}${hash}`;
profileRoute = {
type: "collapse",
name: user.name,
key: user.name,
icon: <MDAvatar src={profilePicture} alt="{user.name}" size="sm" />,
collapse: [
{
name: "My Profile",
key: "my-profile",
route: "/pages/profile/profile-overview",
component: <ProfileOverview />,
},
{
name: "Settings",
key: "profile-settings",
route: "/pages/account/settings",
component: <Settings />,
},
],
};
const tables = {
type: "collapse",
name: "Tables",
key: "tables",
icon: <Icon fontSize="medium">dashboard</Icon>,
collapse: tableList,
};
const newDynamicRoutes = getStaticRoutes();
// @ts-ignore
newDynamicRoutes.unshift(profileRoute);
newDynamicRoutes.push(tables);
setRoutes(newDynamicRoutes);
}
catch (e)
{
if (e.toString().indexOf("status code 401") !== -1)
{
logout();
}
}
})();
}, [needToLoadRoutes, isFullyAuthenticated]);
// Open sidenav when mouse enter on mini sidenav
const handleOnMouseEnter = () =>
{
if (miniSidenav && !onMouseEnter)
{
setMiniSidenav(dispatch, false);
setOnMouseEnter(true);
}
};
// Close sidenav when mouse leave mini sidenav
const handleOnMouseLeave = () =>
{
if (onMouseEnter)
{
setMiniSidenav(dispatch, true);
setOnMouseEnter(false);
}
};
// Change the openConfigurator state
const handleConfiguratorOpen = () => setOpenConfigurator(dispatch, !openConfigurator);
// Setting the dir attribute for the body element
useEffect(() =>
{
document.body.setAttribute("dir", direction);
}, [direction]);
// Setting page scroll to 0 when changing the route
useEffect(() =>
{
document.documentElement.scrollTop = 0;
document.scrollingElement.scrollTop = 0;
}, [pathname]);
const getRoutes = (allRoutes: any[]): any => allRoutes.map(
(route: {
collapse: any;
route: string;
component: ReactElement<any, string | JSXElementConstructor<any>>;
key: Key;
}) =>
{
if (route.collapse)
{
return getRoutes(route.collapse);
}
if (route.route)
{
return <Route path={route.route} element={route.component} key={route.key} />;
}
return null;
},
);
const configsButton = (
<MDBox
display="flex"
justifyContent="center"
alignItems="center"
width="3.25rem"
height="3.25rem"
bgColor="white"
shadow="sm"
borderRadius="50%"
position="fixed"
right="2rem"
bottom="2rem"
zIndex={99}
color="dark"
sx={{cursor: "pointer"}}
onClick={handleConfiguratorOpen}
>
<Icon fontSize="small" color="inherit">
settings
</Icon>
</MDBox>
);
const entityListElement = <EntityList />;
const entityCreateElement = <EntityCreate />;
const entityViewElement = <EntityView />;
const entityEditElement = <EntityEdit />;
const processRunElement = <ProcessRun />;
return (
<ThemeProvider theme={darkMode ? themeDark : theme}>
<CssBaseline />
{layout === "dashboard" && (
<>
<Sidenav
color={sidenavColor}
brand={nfLogo}
brandName="Nutrifresh One"
routes={routes}
onMouseEnter={handleOnMouseEnter}
onMouseLeave={handleOnMouseLeave}
/>
<Configurator />
{configsButton}
</>
)}
<Routes>
<Route path="*" element={<Navigate to="/dashboards/analytics" />} />
<Route path="/:tableName" element={entityListElement} key="entity-list" />
<Route path="/:tableName/create" element={entityCreateElement} key="entity-create" />
<Route path="/processes/:processName" element={processRunElement} key="process-run" />
<Route path="/:tableName/:id" element={entityViewElement} key="entity-view" />
<Route path="/:tableName/:id/edit" element={entityEditElement} key="entity-edit" />
{getRoutes(routes)}
</Routes>
</ThemeProvider>
);
}