Router.tsx 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /*
  2. * Copyright 2021-2024 Avaiga Private Limited
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  5. * the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  10. * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. * specific language governing permissions and limitations under the License.
  12. */
  13. import React, { useEffect, useReducer, useState } from "react";
  14. import axios from "axios";
  15. import Box from "@mui/material/Box";
  16. import CircularProgress from "@mui/material/CircularProgress";
  17. import CssBaseline from "@mui/material/CssBaseline";
  18. import { ThemeProvider } from "@mui/system";
  19. import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
  20. import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
  21. import { SnackbarProvider } from "notistack";
  22. import { HelmetProvider } from "react-helmet-async";
  23. import { BrowserRouter, Route, Routes } from "react-router-dom";
  24. import { ErrorBoundary } from "react-error-boundary";
  25. import { PageContext, TaipyContext } from "../context/taipyContext";
  26. import {
  27. createBlockAction,
  28. createSetLocationsAction,
  29. initializeWebSocket,
  30. INITIAL_STATE,
  31. retreiveBlockUi,
  32. taipyInitialize,
  33. taipyReducer,
  34. } from "../context/taipyReducers";
  35. import Alert from "./Taipy/Alert";
  36. import UIBlocker from "./Taipy/UIBlocker";
  37. import Navigate from "./Taipy/Navigate";
  38. import Menu from "./Taipy/Menu";
  39. import GuiDownload from "./Taipy/GuiDownload";
  40. import ErrorFallback from "../utils/ErrorBoundary";
  41. import MainPage from "./pages/MainPage";
  42. import TaipyRendered from "./pages/TaipyRendered";
  43. import NotFound404 from "./pages/NotFound404";
  44. import { getBaseURL } from "../utils";
  45. interface AxiosRouter {
  46. router: string;
  47. locations: Record<string, string>;
  48. blockUI: boolean;
  49. }
  50. const mainSx = { flexGrow: 1, bgcolor: "background.default" };
  51. const containerSx = { display: "flex" };
  52. const progressSx = { position: "fixed", bottom: "1em", right: "1em" };
  53. const pageStore = {};
  54. const Router = () => {
  55. const [state, dispatch] = useReducer(taipyReducer, INITIAL_STATE, taipyInitialize);
  56. const [routes, setRoutes] = useState<Record<string, string>>({});
  57. const refresh = !!Object.keys(routes).length;
  58. const themeClass = "taipy-" + state.theme.palette.mode;
  59. const baseURL = getBaseURL();
  60. useEffect(() => {
  61. if (refresh) {
  62. // no need to access the backend again, the routes are static
  63. return;
  64. }
  65. if (!state.isSocketConnected) {
  66. // initialize only when there is an existing ws connection
  67. // --> assuring that there is a session data scope on the backend
  68. return;
  69. }
  70. // Fetch Flask Rendered JSX React Router
  71. axios
  72. .get<AxiosRouter>("taipy-init", { params: { client_id: state.id || "", v: window.taipyVersion } })
  73. .then((result) => {
  74. dispatch(createSetLocationsAction(result.data.locations));
  75. setRoutes(result.data.locations);
  76. result.data.blockUI && dispatch(createBlockAction(retreiveBlockUi()));
  77. })
  78. .catch((error) => {
  79. // Fallback router if there is any error
  80. setRoutes({ "/": "/TaiPy_root_page" });
  81. console.log(error);
  82. });
  83. }, [refresh, state.isSocketConnected, state.id]);
  84. useEffect(() => {
  85. initializeWebSocket(state.socket, dispatch);
  86. }, [state.socket]);
  87. useEffect(() => {
  88. const classes = [themeClass];
  89. document.body.classList.forEach((cls) => {
  90. if (!cls.startsWith("taipy-")) {
  91. classes.push(cls);
  92. }
  93. });
  94. document.body.className = classes.join(" ");
  95. }, [themeClass]);
  96. return (
  97. <TaipyContext.Provider value={{ state, dispatch }}>
  98. <HelmetProvider>
  99. <ThemeProvider theme={state.theme}>
  100. <SnackbarProvider maxSnack={5}>
  101. <LocalizationProvider dateAdapter={AdapterDateFns}>
  102. <PageContext.Provider value={pageStore}>
  103. <BrowserRouter>
  104. <Box style={containerSx}>
  105. <CssBaseline />
  106. <ErrorBoundary FallbackComponent={ErrorFallback}>
  107. <Menu {...state.menu} />
  108. </ErrorBoundary>
  109. <Box component="main" sx={mainSx}>
  110. <ErrorBoundary FallbackComponent={ErrorFallback}>
  111. {Object.keys(routes).length ? (
  112. <Routes>
  113. <Route
  114. path={baseURL}
  115. element={
  116. <MainPage
  117. path={routes["/"]}
  118. route={Object.keys(routes).find(
  119. (path) => path !== "/"
  120. )}
  121. />
  122. }
  123. >
  124. {Object.entries(routes)
  125. .filter(([path]) => path !== "/")
  126. .map(([path, name]) => (
  127. <Route
  128. key={name}
  129. path={path.substring(1)}
  130. element={<TaipyRendered />}
  131. />
  132. ))}
  133. <Route path="*" key="NotFound" element={<NotFound404 />} />
  134. </Route>
  135. </Routes>
  136. ) : null}
  137. </ErrorBoundary>
  138. </Box>
  139. {state.ackList.length ? (
  140. <Box sx={progressSx} className="taipy-busy">
  141. <CircularProgress size="1em" disableShrink />
  142. </Box>
  143. ) : null}
  144. </Box>
  145. <ErrorBoundary FallbackComponent={ErrorFallback}>
  146. <Alert alerts={state.alerts} />
  147. <UIBlocker block={state.block} />
  148. <Navigate
  149. to={state.navigateTo}
  150. params={state.navigateParams}
  151. tab={state.navigateTab}
  152. force={state.navigateForce}
  153. />
  154. <GuiDownload download={state.download} />
  155. </ErrorBoundary>
  156. </BrowserRouter>
  157. </PageContext.Provider>
  158. </LocalizationProvider>
  159. </SnackbarProvider>
  160. </ThemeProvider>
  161. </HelmetProvider>
  162. </TaipyContext.Provider>
  163. );
  164. };
  165. export default Router;