TRA-4205 React router (#684)

* React router

* removed comment
This commit is contained in:
lirazyehezkel
2022-01-25 10:45:07 +02:00
committed by GitHub
parent 85edd6e5b0
commit 80418f1802
18 changed files with 275 additions and 176 deletions

58
ui/package-lock.json generated
View File

@@ -38,6 +38,7 @@
"react-dom": "^17.0.2",
"react-graph-vis": "^1.0.7",
"react-lowlight": "^3.0.0",
"react-router-dom": "^6.2.1",
"react-scrollable-feed-virtualized": "^1.4.9",
"react-syntax-highlighter": "^15.4.3",
"react-toastify": "^8.0.3",
@@ -11790,6 +11791,14 @@
"node": ">=12.0.0"
}
},
"node_modules/history": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz",
"integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==",
"dependencies": {
"@babel/runtime": "^7.7.6"
}
},
"node_modules/hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -19663,6 +19672,30 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz",
"integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==",
"dependencies": {
"history": "^5.2.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz",
"integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==",
"dependencies": {
"history": "^5.2.0",
"react-router": "6.2.1"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-scripts": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.3.tgz",
@@ -35700,6 +35733,14 @@
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.3.1.tgz",
"integrity": "sha512-PUhCRnPjLtiLHZAQ5A/Dt5F8cWZeMyj9KRsACsWT+OD6OP0x6dp5OmT5jdx0JgEyPxPZZIPQpRN2TciUT7occw=="
},
"history": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz",
"integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==",
"requires": {
"@babel/runtime": "^7.7.6"
}
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -41850,6 +41891,23 @@
"integrity": "sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==",
"peer": true
},
"react-router": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz",
"integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==",
"requires": {
"history": "^5.2.0"
}
},
"react-router-dom": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz",
"integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==",
"requires": {
"history": "^5.2.0",
"react-router": "6.2.1"
}
},
"react-scripts": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-4.0.3.tgz",

View File

@@ -33,6 +33,7 @@
"react-dom": "^17.0.2",
"react-graph-vis": "^1.0.7",
"react-lowlight": "^3.0.0",
"react-router-dom": "^6.2.1",
"react-scrollable-feed-virtualized": "^1.4.9",
"react-syntax-highlighter": "^15.4.3",
"react-toastify": "^8.0.3",

View File

@@ -1,41 +1,24 @@
import React, {useState} from 'react';
import './App.sass';
import {TLSWarning} from "./components/TLSWarning/TLSWarning";
import {Header} from "./components/Header/Header";
import {TrafficPage} from "./components/TrafficPage";
import {TrafficPage} from "./components/Pages/TrafficPage/TrafficPage";
import { ServiceMapModal } from './components/ServiceMapModal/ServiceMapModal';
import {useRecoilState} from "recoil";
import serviceMapModalOpenAtom from "./recoil/serviceMapModalOpen";
const App = () => {
const [analyzeStatus, setAnalyzeStatus] = useState(null);
const [showTLSWarning, setShowTLSWarning] = useState(false);
const [userDismissedTLSWarning, setUserDismissedTLSWarning] = useState(false);
const [addressesWithTLS, setAddressesWithTLS] = useState(new Set<string>());
const [openServiceMapModal, setOpenServiceMapModal] = useState(false);
const onTLSDetected = (destAddress: string) => {
addressesWithTLS.add(destAddress);
setAddressesWithTLS(new Set(addressesWithTLS));
if (!userDismissedTLSWarning) {
setShowTLSWarning(true);
}
};
const [serviceMapModalOpen, setServiceMapModalOpen] = useRecoilState(serviceMapModalOpenAtom);
return (
<div className="mizuApp">
<Header analyzeStatus={analyzeStatus} />
<TrafficPage setAnalyzeStatus={setAnalyzeStatus} onTLSDetected={onTLSDetected} setOpenServiceMapModal={setOpenServiceMapModal} />
<TLSWarning showTLSWarning={showTLSWarning}
setShowTLSWarning={setShowTLSWarning}
addressesWithTLS={addressesWithTLS}
setAddressesWithTLS={setAddressesWithTLS}
userDismissedTLSWarning={userDismissedTLSWarning}
setUserDismissedTLSWarning={setUserDismissedTLSWarning} />
<TrafficPage setAnalyzeStatus={setAnalyzeStatus}/>
{window["isServiceMapEnabled"] && <ServiceMapModal
isOpen={openServiceMapModal}
onOpen={() => setOpenServiceMapModal(true)}
onClose={() => setOpenServiceMapModal(false)}
isOpen={serviceMapModalOpen}
onOpen={() => setServiceMapModalOpen(true)}
onClose={() => setServiceMapModalOpen(false)}
/>}
</div>
);

View File

@@ -1,97 +1,15 @@
import React, {useCallback, useEffect, useState} from 'react';
import React from 'react';
import './App.sass';
import {TrafficPage} from "./components/TrafficPage";
import {TLSWarning} from "./components/TLSWarning/TLSWarning";
import {EntHeader} from "./components/Header/EntHeader";
import Api from "./helpers/api";
import {toast} from "react-toastify";
import InstallPage from "./components/InstallPage";
import LoginPage from "./components/LoginPage";
import LoadingOverlay from "./components/LoadingOverlay";
import AuthPageBase from './components/AuthPageBase';
import entPageAtom, {Page} from "./recoil/entPage";
import {useRecoilState} from "recoil";
import { ServiceMapModal } from './components/ServiceMapModal/ServiceMapModal';
const api = Api.getInstance();
import AppSwitchRoutes from "./components/AppSwitchRoutes";
import {BrowserRouter} from "react-router-dom";
const EntApp = () => {
const [isLoading, setIsLoading] = useState(true);
const [showTLSWarning, setShowTLSWarning] = useState(false);
const [userDismissedTLSWarning, setUserDismissedTLSWarning] = useState(false);
const [addressesWithTLS, setAddressesWithTLS] = useState(new Set<string>());
const [entPage, setEntPage] = useRecoilState(entPageAtom);
const [isFirstLogin, setIsFirstLogin] = useState(false);
const [openServiceMapModal, setOpenServiceMapModal] = useState(false);
const determinePage = useCallback(async () => { // TODO: move to state management
try {
const isInstallNeeded = await api.isInstallNeeded();
if (isInstallNeeded) {
setEntPage(Page.Setup);
} else {
const isAuthNeeded = await api.isAuthenticationNeeded();
if(isAuthNeeded) {
setEntPage(Page.Login);
}
}
} catch (e) {
toast.error("Error occured while checking Mizu API status, see console for mode details");
console.error(e);
} finally {
setIsLoading(false);
}
},[setEntPage]);
useEffect(() => {
determinePage();
}, [determinePage]);
const onTLSDetected = (destAddress: string) => {
addressesWithTLS.add(destAddress);
setAddressesWithTLS(new Set(addressesWithTLS));
if (!userDismissedTLSWarning) {
setShowTLSWarning(true);
}
};
let pageComponent: any;
switch (entPage) { // TODO: move to state management / proper routing
case Page.Traffic:
pageComponent = <TrafficPage onTLSDetected={onTLSDetected} setOpenServiceMapModal={setOpenServiceMapModal} />;
break;
case Page.Setup:
pageComponent = <AuthPageBase><InstallPage onFirstLogin={() => setIsFirstLogin(true)}/></AuthPageBase>;
break;
case Page.Login:
pageComponent = <AuthPageBase><LoginPage/></AuthPageBase>;
break;
default:
pageComponent = <div>Unknown Error</div>;
}
if (isLoading) {
return <LoadingOverlay/>;
}
return (
<div className="mizuApp">
{entPage === Page.Traffic && <EntHeader isFirstLogin={isFirstLogin} setIsFirstLogin={setIsFirstLogin} />}
{pageComponent}
{entPage === Page.Traffic && <TLSWarning showTLSWarning={showTLSWarning}
setShowTLSWarning={setShowTLSWarning}
addressesWithTLS={addressesWithTLS}
setAddressesWithTLS={setAddressesWithTLS}
userDismissedTLSWarning={userDismissedTLSWarning}
setUserDismissedTLSWarning={setUserDismissedTLSWarning} />}
{entPage === Page.Traffic && window["isServiceMapEnabled"] && <ServiceMapModal
isOpen={openServiceMapModal}
onOpen={() => setOpenServiceMapModal(true)}
onClose={() => setOpenServiceMapModal(false)}
/>}
<BrowserRouter>
<AppSwitchRoutes/>
</BrowserRouter>
</div>
);
}

View File

@@ -0,0 +1,80 @@
import React, {useCallback, useEffect, useState} from "react";
import {Route, Routes, useNavigate} from "react-router-dom";
import {RouterRoutes} from "../helpers/routes";
import {useRecoilState} from "recoil";
import entPageAtom, {Page} from "../recoil/entPage";
import {toast} from "react-toastify";
import AuthPageBase from "./Pages/AuthPage/AuthPageBase";
import InstallPage from "./Pages/AuthPage/InstallPage";
import LoginPage from "./Pages/AuthPage/LoginPage";
import LoadingOverlay from "./LoadingOverlay";
import SystemViewer from "./Pages/SystemViewer/SystemViewer";
import Api from "../helpers/api";
import {TrafficPage} from "./Pages/TrafficPage/TrafficPage";
const api = Api.getInstance();
const AppSwitchRoutes = () => {
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(true);
const [entPage, setEntPage] = useRecoilState(entPageAtom);
const [isFirstLogin, setIsFirstLogin] = useState(false);
const determinePage = useCallback(async () => {
try {
const isInstallNeeded = await api.isInstallNeeded();
if (isInstallNeeded) {
setEntPage(Page.Setup);
} else {
const isAuthNeeded = await api.isAuthenticationNeeded();
if(isAuthNeeded) {
setEntPage(Page.Login);
}
}
} catch (e) {
toast.error("Error occured while checking Mizu API status, see console for mode details");
console.error(e);
} finally {
setIsLoading(false);
}
},[setEntPage]);
useEffect(() => {
determinePage();
}, [determinePage]);
useEffect(() => {
switch (entPage) {
case Page.Traffic:
navigate("/");
break;
case Page.Setup:
navigate(RouterRoutes.SETUP);
break;
case Page.Login:
navigate(RouterRoutes.LOGIN);
break;
default:
navigate(RouterRoutes.LOGIN);
}
// eslint-disable-next-line
},[entPage])
if (isLoading) {
return <LoadingOverlay/>;
}
return <Routes>
<Route path={"/"} element={<SystemViewer isFirstLogin={isFirstLogin} setIsFirstLogin={setIsFirstLogin}/>}>
<Route path={"/"} element={<TrafficPage/>} />
<Route path={RouterRoutes.SETTINGS} element={<></>} /> {/*todo: set settings component*/}
</Route>
<Route path={RouterRoutes.LOGIN} element={<AuthPageBase><LoginPage/></AuthPageBase>}/>
<Route path={RouterRoutes.SETUP} element={<AuthPageBase><InstallPage onFirstLogin={() => setIsFirstLogin(true)}/></AuthPageBase>}/>
</Routes>
}
export default AppSwitchRoutes;

View File

@@ -11,6 +11,7 @@ import Api from "../../helpers/api";
import {toast} from "react-toastify";
import {useSetRecoilState} from "recoil";
import entPageAtom, {Page} from "../../recoil/entPage";
import {useNavigate} from "react-router-dom";
const api = Api.getInstance();
@@ -20,7 +21,7 @@ interface EntHeaderProps {
}
export const EntHeader: React.FC<EntHeaderProps> = ({isFirstLogin, setIsFirstLogin}) => {
const navigate = useNavigate();
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
useEffect(() => {
@@ -37,7 +38,7 @@ export const EntHeader: React.FC<EntHeaderProps> = ({isFirstLogin, setIsFirstLog
return <div className="header">
<div>
<div className="title">
<img style={{height: 55}} src={logo} alt="logo"/>
<img className="entLogo" style={{height: 55}} src={logo} alt="logo" onClick={() => navigate("/")}/>
</div>
</div>
<div style={{display: "flex", alignItems: "center"}}>

View File

@@ -21,3 +21,6 @@
.headerIcon
cursor: pointer
.entLogo
cursor: pointer

View File

@@ -1,4 +1,4 @@
@import "../../variables.module"
@import "src/variables.module"
.authContainer
height: 100vh

View File

@@ -1,8 +1,8 @@
import React from "react";
import background from "./assets/authBackground.png";
import logo from './assets/MizuEntLogoNoPowBy.svg';
import poweredBy from './assets/powered-by.svg'
import "./style/AuthBasePage.sass";
import background from "../../assets/authBackground.png";
import logo from '../../assets/MizuEntLogoNoPowBy.svg';
import poweredBy from '../../assets/powered-by.svg'
import "./AuthBasePage.sass";
export const AuthPageBase: React.FC = ({children}) => {

View File

@@ -1,14 +1,14 @@
import { Button } from "@material-ui/core";
import React, { useState,useRef } from "react";
import { adminUsername } from "../consts";
import Api, { FormValidationErrorType } from "../helpers/api";
import { adminUsername } from "../../../consts";
import Api, { FormValidationErrorType } from "../../../helpers/api";
import { toast } from 'react-toastify';
import LoadingOverlay from "./LoadingOverlay";
import { useCommonStyles } from "../helpers/commonStyle";
import LoadingOverlay from "../../LoadingOverlay";
import { useCommonStyles } from "../../../helpers/commonStyle";
import {useSetRecoilState} from "recoil";
import entPageAtom, {Page} from "../recoil/entPage";
import useKeyPress from "../hooks/useKeyPress"
import shortcutsKeyboard from "../configs/shortcutsKeyboard"
import entPageAtom, {Page} from "../../../recoil/entPage";
import useKeyPress from "../../../hooks/useKeyPress"
import shortcutsKeyboard from "../../../configs/shortcutsKeyboard"
const api = Api.getInstance();

View File

@@ -1,13 +1,13 @@
import { Button } from "@material-ui/core";
import React, { useState,useRef } from "react";
import { toast } from "react-toastify";
import Api from "../helpers/api";
import { useCommonStyles } from "../helpers/commonStyle";
import LoadingOverlay from "./LoadingOverlay";
import entPageAtom, {Page} from "../recoil/entPage";
import Api from "../../../helpers/api";
import { useCommonStyles } from "../../../helpers/commonStyle";
import LoadingOverlay from "../../LoadingOverlay";
import entPageAtom, {Page} from "../../../recoil/entPage";
import {useSetRecoilState} from "recoil";
import useKeyPress from "../hooks/useKeyPress"
import shortcutsKeyboard from "../configs/shortcutsKeyboard"
import useKeyPress from "../../../hooks/useKeyPress"
import shortcutsKeyboard from "../../../configs/shortcutsKeyboard"
const api = Api.getInstance();

View File

@@ -0,0 +1,23 @@
import React from "react";
import {Outlet} from "react-router-dom";
import {ServiceMapModal} from "../../ServiceMapModal/ServiceMapModal";
import {EntHeader} from "../../Header/EntHeader";
import {useRecoilState} from "recoil";
import serviceMapModalOpenAtom from "../../../recoil/serviceMapModalOpen";
const SystemViewer = ({isFirstLogin, setIsFirstLogin}) => {
const [serviceMapModalOpen, setServiceMapModalOpen] = useRecoilState(serviceMapModalOpenAtom);
return <>
<EntHeader isFirstLogin={isFirstLogin} setIsFirstLogin={setIsFirstLogin} />
<Outlet/>
{window["isServiceMapEnabled"] && <ServiceMapModal
isOpen={serviceMapModalOpen}
onOpen={() => setServiceMapModalOpen(true)}
onClose={() => setServiceMapModalOpen(false)}
/>}
</>
}
export default SystemViewer;

View File

@@ -1,4 +1,4 @@
@import '../../variables.module.scss'
@import 'src/variables.module'
.TrafficPage
width: 100%

View File

@@ -1,25 +1,27 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import { Filters } from "./Filters";
import { EntriesList } from "./EntriesList";
import { Filters } from "../../Filters";
import { EntriesList } from "../../EntriesList";
import { makeStyles, Button } from "@material-ui/core";
import "./style/TrafficPage.sass";
import styles from './style/EntriesList.module.sass';
import {EntryDetailed} from "./EntryDetailed";
import playIcon from './assets/run.svg';
import pauseIcon from './assets/pause.svg';
import variables from '../variables.module.scss';
import {StatusBar} from "./UI/StatusBar";
import Api, {MizuWebsocketURL} from "../helpers/api";
import "./TrafficPage.sass";
import styles from '../../style/EntriesList.module.sass';
import {EntryDetailed} from "../../EntryDetailed";
import playIcon from '../../assets/run.svg';
import pauseIcon from '../../assets/pause.svg';
import variables from '../../../variables.module.scss';
import {StatusBar} from "../../UI/StatusBar";
import Api, {MizuWebsocketURL} from "../../../helpers/api";
import { toast } from 'react-toastify';
import debounce from 'lodash/debounce';
import {useRecoilState, useRecoilValue} from "recoil";
import tappingStatusAtom from "../recoil/tappingStatus";
import entriesAtom from "../recoil/entries";
import focusedEntryIdAtom from "../recoil/focusedEntryId";
import websocketConnectionAtom, {WsConnectionStatus} from "../recoil/wsConnection";
import queryAtom from "../recoil/query";
import OasModal from "./OasModal/OasModal";
import {useCommonStyles} from "../helpers/commonStyle"
import {useRecoilState, useRecoilValue, useSetRecoilState} from "recoil";
import tappingStatusAtom from "../../../recoil/tappingStatus";
import entriesAtom from "../../../recoil/entries";
import focusedEntryIdAtom from "../../../recoil/focusedEntryId";
import websocketConnectionAtom, {WsConnectionStatus} from "../../../recoil/wsConnection";
import queryAtom from "../../../recoil/query";
import OasModal from "../../OasModal/OasModal";
import {useCommonStyles} from "../../../helpers/commonStyle"
import {TLSWarning} from "../../TLSWarning/TLSWarning";
import serviceMapModalOpenAtom from "../../../recoil/serviceMapModalOpen";
const useLayoutStyles = makeStyles(() => ({
details: {
@@ -43,19 +45,18 @@ const useLayoutStyles = makeStyles(() => ({
interface TrafficPageProps {
setAnalyzeStatus?: (status: any) => void;
onTLSDetected: (destAddress: string) => void;
setOpenServiceMapModal?: (open: boolean) => void;
}
const api = Api.getInstance();
export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus,onTLSDetected, setOpenServiceMapModal}) => {
export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus}) => {
const commonClasses = useCommonStyles();
const classes = useLayoutStyles();
const [tappingStatus, setTappingStatus] = useRecoilState(tappingStatusAtom);
const [entries, setEntries] = useRecoilState(entriesAtom);
const [focusedEntryId, setFocusedEntryId] = useRecoilState(focusedEntryIdAtom);
const [wsConnection, setWsConnection] = useRecoilState(websocketConnectionAtom);
const setServiceMapModalOpen = useSetRecoilState(serviceMapModalOpenAtom);
const query = useRecoilValue(queryAtom);
const [noMoreDataTop, setNoMoreDataTop] = useState(false);
@@ -76,6 +77,10 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus,onTLSD
const handleOpenModal = () => setOpenOasModal(true);
const handleCloseModal = () => setOpenOasModal(false);
const [showTLSWarning, setShowTLSWarning] = useState(false);
const [userDismissedTLSWarning, setUserDismissedTLSWarning] = useState(false);
const [addressesWithTLS, setAddressesWithTLS] = useState(new Set<string>());
const handleQueryChange = useMemo(
() =>
debounce(async (query: string) => {
@@ -216,6 +221,15 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus,onTLSD
}
}
const onTLSDetected = (destAddress: string) => {
addressesWithTLS.add(destAddress);
setAddressesWithTLS(new Set(addressesWithTLS));
if (!userDismissedTLSWarning) {
setShowTLSWarning(true);
}
};
const getConnectionStatusClass = (isContainer) => {
const container = isContainer ? "Container" : "";
switch (wsConnection) {
@@ -243,7 +257,7 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus,onTLSD
}
const openServiceMapModalDebounce = debounce(() => {
setOpenServiceMapModal(true)
setServiceMapModalOpen(true)
}, 500);
return (
@@ -320,6 +334,12 @@ export const TrafficPage: React.FC<TrafficPageProps> = ({setAnalyzeStatus,onTLSD
</div>
</div>}
{tappingStatus && !openOasModal && <StatusBar />}
<TLSWarning showTLSWarning={showTLSWarning}
setShowTLSWarning={setShowTLSWarning}
addressesWithTLS={addressesWithTLS}
setAddressesWithTLS={setAddressesWithTLS}
userDismissedTLSWarning={userDismissedTLSWarning}
setUserDismissedTLSWarning={setUserDismissedTLSWarning} />
</div>
);
};

5
ui/src/helpers/routes.ts Normal file
View File

@@ -0,0 +1,5 @@
export enum RouterRoutes {
LOGIN = "/login",
SETUP = "/setup",
SETTINGS = "/settings"
}

View File

@@ -6,23 +6,20 @@ import 'react-toastify/dist/ReactToastify.css';
import {RecoilRoot} from "recoil";
import AppChooser from "./AppChooser";
ReactDOM.render(
<React.StrictMode>
<>
<RecoilRoot>
<AppChooser/>
<ToastContainer
position="bottom-right"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
/>
</RecoilRoot>
</>
</React.StrictMode>,
ReactDOM.render( <>
<RecoilRoot>
<AppChooser/>
<ToastContainer
position="bottom-right"
autoClose={5000}
hideProgressBar={false}
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
/>
</RecoilRoot>
</>,
document.getElementById('root'));

View File

@@ -0,0 +1,8 @@
import { atom } from "recoil"
const serviceMapModalOpenAtom = atom({
key: "serviceMapModalOpenAtom",
default: false
})
export default serviceMapModalOpenAtom;

View File

@@ -0,0 +1,2 @@
import atom from "./atom";
export default atom;