From 51962f6243113e322e54aa4e7237f262d427ae51 Mon Sep 17 00:00:00 2001
From: Bastien DUMONT <bdumont@grandlyon.com>
Date: Tue, 19 Sep 2023 14:51:56 +0000
Subject: [PATCH] feat(login): stay on page after refresh

---
 .env.template                                 |  1 -
 .vscode/settings.json                         |  4 +-
 package.json                                  |  1 +
 src/API.ts                                    | 26 ++++++
 src/App.tsx                                   | 13 ++-
 src/components/Consents/Consents.tsx          | 12 +--
 src/components/Layout/layout.module.scss      |  4 +-
 src/components/Loader/loader.scss             |  4 +-
 src/components/Login/Login.tsx                |  5 +-
 .../Newsletter/ImagePicker/ImagePicker.tsx    |  6 +-
 src/components/Newsletter/Newsletter.tsx      | 17 ++--
 src/components/Popups/Popups.tsx              | 13 ++-
 src/components/Prices/PriceSection.tsx        |  6 +-
 src/components/Routes/Router.tsx              | 32 +++----
 src/components/SideBar/SideBar.tsx            | 25 ++++--
 src/hooks/useAuth.ts                          | 50 -----------
 src/hooks/useFindUser.ts                      | 31 -------
 src/hooks/userContext.ts                      |  9 --
 yarn.lock                                     | 84 ++++++++++++++++++-
 19 files changed, 176 insertions(+), 167 deletions(-)
 create mode 100644 src/API.ts
 delete mode 100644 src/hooks/useAuth.ts
 delete mode 100644 src/hooks/useFindUser.ts
 delete mode 100644 src/hooks/userContext.ts

diff --git a/.env.template b/.env.template
index 232b9048..e63fc882 100644
--- a/.env.template
+++ b/.env.template
@@ -19,7 +19,6 @@ CLIENT_SECRET=
 AUTH_URL=
 TOKEN_URL=
 USERINFO_URL=
-LOGOUT_URL=
 
 # Access to the database
 DATABASE_USER=
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 9d92bf0c..537a52d4 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -34,8 +34,8 @@
   "editor.defaultFormatter": "esbenp.prettier-vscode",
   "peacock.color": "#2aa63d",
   "sonarlint.connectedMode.project": {
-    "connectionId": "Sonar",
-    "projectKey": "web-et-numerique-factory-llle-project-backoffice-client"
+    "connectionId": "sonarqube-forge-grandlyon",
+    "projectKey": "web-et-numerique-llle-project-backoffice-client"
   },
   "cSpell.words": [
     "backoffice",
diff --git a/package.json b/package.json
index 53d9a24e..ba1fc249 100644
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
     "react-draft-wysiwyg": "^1.15.0",
+    "react-query": "^3.39.3",
     "react-router-dom": "^6.0.0",
     "react-scripts": "^5.0.1",
     "react-toastify": "^9.0.0",
diff --git a/src/API.ts b/src/API.ts
new file mode 100644
index 00000000..e8e2df1f
--- /dev/null
+++ b/src/API.ts
@@ -0,0 +1,26 @@
+import axios from 'axios'
+import { useQuery } from 'react-query'
+import { toast } from 'react-toastify'
+import { User } from './models/user.model'
+
+const fetchWhoAmI = async () => {
+  const { data } = await axios.get<User | null>('/api/common/WhoAmI')
+  return data
+}
+
+export const useWhoAmI = () => {
+  return useQuery({
+    queryKey: ['WhoAmI'],
+    queryFn: fetchWhoAmI,
+    retry: false,
+    onError: error => {
+      console.error('error whoami', error)
+      toast.error('Accès refusé, veuillez vous connecter')
+    },
+    refetchOnMount: false,
+  })
+}
+
+export const fetchLogout = async () => {
+  return await axios.get('/Logout')
+}
diff --git a/src/App.tsx b/src/App.tsx
index 292c788d..980923de 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,22 +1,21 @@
+import { QueryClient, QueryClientProvider } from 'react-query'
 import { BrowserRouter } from 'react-router-dom'
 import { ToastContainer } from 'react-toastify'
 import 'react-toastify/dist/ReactToastify.css'
 import Layout from './components/Layout/Layout'
 import Router from './components/Routes/Router'
-import useFindUser from './hooks/useFindUser'
-import { UserContext } from './hooks/userContext'
 
-function App() {
-  const { user, setUser, isLoading } = useFindUser()
+const queryClient = new QueryClient()
 
+function App() {
   return (
     <BrowserRouter>
-      <UserContext.Provider value={{ user, setUser, isLoading }}>
+      <QueryClientProvider client={queryClient}>
         <Layout>
           <Router />
         </Layout>
-      </UserContext.Provider>
-      <ToastContainer theme="colored" />
+        <ToastContainer theme="colored" />
+      </QueryClientProvider>
     </BrowserRouter>
   )
 }
diff --git a/src/components/Consents/Consents.tsx b/src/components/Consents/Consents.tsx
index 23cee711..d14ab99d 100644
--- a/src/components/Consents/Consents.tsx
+++ b/src/components/Consents/Consents.tsx
@@ -13,15 +13,9 @@ import 'ag-grid-community/dist/styles/ag-grid.css'
 import 'ag-grid-community/dist/styles/ag-theme-alpine-dark.css'
 import { AgGridReact } from 'ag-grid-react'
 import { DateTime } from 'luxon'
-import React, {
-  useCallback,
-  useContext,
-  useEffect,
-  useMemo,
-  useState,
-} from 'react'
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import { useWhoAmI } from '../../API'
 import { getAxiosXSRFHeader } from '../../axios.config'
-import { UserContext, UserContextProps } from '../../hooks/userContext'
 import { IConsent } from '../../models/consent.model'
 import { ConsentService } from '../../services/consent.service'
 import DowloadModal from './DowloadModal'
@@ -39,7 +33,7 @@ const Consents: React.FC = () => {
   const [page, setPage] = useState<number>(0)
   const [rowsPerPage, setRowsPerPage] = useState<number>(50)
   const [totalRows, setTotalRows] = useState<number>(50)
-  const { user }: Partial<UserContextProps> = useContext(UserContext)
+  const { data: user } = useWhoAmI()
   const consentService = useMemo(() => {
     return new ConsentService()
   }, [])
diff --git a/src/components/Layout/layout.module.scss b/src/components/Layout/layout.module.scss
index e4a59aad..8735c26f 100644
--- a/src/components/Layout/layout.module.scss
+++ b/src/components/Layout/layout.module.scss
@@ -3,6 +3,8 @@
 
 .root {
   display: flex;
+  background: $dark-bg;
+  height: 100vh;
 }
 
 .wrapper {
@@ -11,9 +13,7 @@
     0px 5px 5px rgb(0 0 0 / 20%),
     0px 3px 14px rgb(0 0 0 / 12%),
     0px 8px 10px rgb(0 0 0 / 14%);
-  background: $dark-bg;
   overflow-y: scroll;
-  height: 100vh;
 
   @media screen and (max-width: $width-tablet) {
     margin-left: 0;
diff --git a/src/components/Loader/loader.scss b/src/components/Loader/loader.scss
index 872512dc..0f9e2144 100644
--- a/src/components/Loader/loader.scss
+++ b/src/components/Loader/loader.scss
@@ -1,9 +1,8 @@
 @import '../../styles/config/colors.scss';
 
 .loader-container {
-  width: 100%;
-  height: 80vh;
   display: flex;
+  height: 100%;
   overflow: hidden;
 }
 .loader {
@@ -23,7 +22,6 @@
   background: -o-linear-gradient(left, $gold 10%, rgba(255, 255, 255, 0) 42%);
   background: -ms-linear-gradient(left, $gold 10%, rgba(255, 255, 255, 0) 42%);
   background: linear-gradient(to right, $gold 10%, rgba(255, 255, 255, 0) 42%);
-  position: relative;
   -webkit-animation: load3 1.4s infinite linear;
   animation: load3 1.4s infinite linear;
   -webkit-transform: translateZ(0);
diff --git a/src/components/Login/Login.tsx b/src/components/Login/Login.tsx
index 1f20b820..1d7b3016 100644
--- a/src/components/Login/Login.tsx
+++ b/src/components/Login/Login.tsx
@@ -1,9 +1,10 @@
 import React from 'react'
-import { useAuth } from '../../hooks/useAuth'
 import './login.scss'
 
 const Login: React.FC = () => {
-  const { loginUser } = useAuth()
+  const loginUser = () => {
+    window.location.href = '/OAuth2Login'
+  }
 
   return (
     <div className="login">
diff --git a/src/components/Newsletter/ImagePicker/ImagePicker.tsx b/src/components/Newsletter/ImagePicker/ImagePicker.tsx
index fa7946a0..6a58e17e 100644
--- a/src/components/Newsletter/ImagePicker/ImagePicker.tsx
+++ b/src/components/Newsletter/ImagePicker/ImagePicker.tsx
@@ -1,7 +1,7 @@
 import Pagination from '@material-ui/lab/Pagination'
-import React, { useContext, useEffect, useState } from 'react'
+import React, { useEffect, useState } from 'react'
+import { useWhoAmI } from '../../../API'
 import { getAxiosXSRFHeader } from '../../../axios.config'
-import { UserContext, UserContextProps } from '../../../hooks/userContext'
 import { NewsletterService } from '../../../services/newsletter.service'
 import { EditorType } from '../CustomEditor'
 import Modal from '../Modal/Modal'
@@ -16,12 +16,12 @@ const ImagePicker: React.FC<ImagePickerProps> = ({
   imageURL,
   handleChange,
 }) => {
+  const { data: user } = useWhoAmI()
   const [imageNames, setImageNames] = useState<string[][]>([])
   const [selectedImageURL, setSelectedImageURL] = useState<string>(
     imageURL && imageURL !== null ? imageURL : ''
   )
   const [openModal, setOpenModal] = useState<boolean>(false)
-  const { user }: Partial<UserContextProps> = useContext(UserContext)
   const [currentPage, setCurrentPage] = useState(1)
   const [pageCount, setPageCount] = useState<number>(1)
   const [preSelectImage, setPreSelectImage] = useState<string>('')
diff --git a/src/components/Newsletter/Newsletter.tsx b/src/components/Newsletter/Newsletter.tsx
index 8d2fbf56..63a192db 100644
--- a/src/components/Newsletter/Newsletter.tsx
+++ b/src/components/Newsletter/Newsletter.tsx
@@ -1,12 +1,6 @@
-import React, {
-  useCallback,
-  useContext,
-  useEffect,
-  useMemo,
-  useState,
-} from 'react'
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import { useWhoAmI } from '../../API'
 import { getAxiosXSRFHeader } from '../../axios.config'
-import { UserContext, UserContextProps } from '../../hooks/userContext'
 import { IMailSubject } from '../../models/mailSubject.model'
 import { IMonthlyInfo } from '../../models/monthlyInfo.model'
 import { IMonthlyNews } from '../../models/monthlyNews.model'
@@ -30,9 +24,9 @@ export type ContentItems =
   | ''
 
 const Newsletter: React.FC = () => {
-  /**
-   * Display previous month until the newsletter is sent on the 3rd day of the month
-   */
+  const { data: user } = useWhoAmI()
+
+  /** Display previous month until the newsletter is sent on the 3rd day of the month */
   const getCurrentNewsletterDate = (): Date => {
     const today = new Date()
     const currentDay = today.getDate()
@@ -57,7 +51,6 @@ const Newsletter: React.FC = () => {
   const [isLoading, setIsLoading] = useState<boolean>(false)
   const [warningModal, setWarningModal] = useState<boolean>(false)
   const [toDelete, setToDelete] = useState<ContentItems>('')
-  const { user }: Partial<UserContextProps> = useContext(UserContext)
   const newsletterService = useMemo(() => {
     return new NewsletterService()
   }, [])
diff --git a/src/components/Popups/Popups.tsx b/src/components/Popups/Popups.tsx
index f5a8a2eb..5f868efb 100644
--- a/src/components/Popups/Popups.tsx
+++ b/src/components/Popups/Popups.tsx
@@ -1,9 +1,9 @@
 import { DateTime } from 'luxon'
-import React, { useCallback, useContext, useEffect, useState } from 'react'
+import React, { useCallback, useEffect, useState } from 'react'
 import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'
+import { useWhoAmI } from '../../API'
 import { getAxiosXSRFHeader } from '../../axios.config'
 import { CheckboxType } from '../../enum/checkboxType.enum'
-import { UserContext, UserContextProps } from '../../hooks/userContext'
 import { ICustomPopup, PopupDuration } from '../../models/customPopup.model'
 import {
   Option,
@@ -34,6 +34,8 @@ const OPTIONS: Option[] = [
 ]
 
 const Popups: React.FC = () => {
+  const { data: user } = useWhoAmI()
+
   const [refreshData, setRefreshData] = useState(false)
   const [isLoading, setIsLoading] = useState(false)
   const [partnersInfo, setPartnersInfo] = useState<IPartnersInfo>({
@@ -54,16 +56,12 @@ const Popups: React.FC = () => {
     duration: 0,
   })
 
-  const { user }: Partial<UserContextProps> = useContext(UserContext)
-
   const isPartnerNotificationOn = () =>
     partnersInfo.enedis_failure ||
     partnersInfo.egl_failure ||
     partnersInfo.grdf_failure
 
-  /**
-   * Only one type of popup can be enabled
-   */
+  /** Only one type of popup can be enabled */
   const isPageValid = () =>
     !(isPartnerNotificationOn() && customPopup.popupEnabled)
 
@@ -105,6 +103,7 @@ const Popups: React.FC = () => {
     }))
   }
 
+  // clean this
   const handleCancel = useCallback(() => {
     setRefreshData(true)
   }, [setRefreshData])
diff --git a/src/components/Prices/PriceSection.tsx b/src/components/Prices/PriceSection.tsx
index 9b67c826..8e6d6d87 100644
--- a/src/components/Prices/PriceSection.tsx
+++ b/src/components/Prices/PriceSection.tsx
@@ -1,13 +1,13 @@
 import dayjs from 'dayjs'
 import timezone from 'dayjs/plugin/timezone'
 import utc from 'dayjs/plugin/utc'
-import React, { useCallback, useContext, useEffect, useState } from 'react'
+import React, { useCallback, useEffect, useState } from 'react'
 import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'
+import { useWhoAmI } from '../../API'
 import arrowDown from '../../assets/icons/down-arrow.png'
 import { getAxiosXSRFHeader } from '../../axios.config'
 import { FluidType } from '../../enum/fluidTypes'
 import { FrequencyInMonth } from '../../enum/frequency.enum'
-import { UserContext, UserContextProps } from '../../hooks/userContext'
 import { IPrice } from '../../models/price.model'
 import { PricesService } from '../../services/prices.service'
 import Loader from '../Loader/Loader'
@@ -23,6 +23,7 @@ interface PriceSectionProps {
 }
 
 const PriceSection: React.FC<PriceSectionProps> = ({ fluid, frequency }) => {
+  const { data: user } = useWhoAmI()
   const [prices, setPrices] = useState<IPrice[]>([])
   const [nextPrice, setNextPrice] = useState<IPrice>()
   const [isLoading, setIsLoading] = useState<boolean>(false)
@@ -35,7 +36,6 @@ const PriceSection: React.FC<PriceSectionProps> = ({ fluid, frequency }) => {
     startDate: '',
     endDate: null,
   })
-  const { user }: Partial<UserContextProps> = useContext(UserContext)
   const maxPerList = 8
 
   const handlePriceSelection = useCallback((val: string) => {
diff --git a/src/components/Routes/Router.tsx b/src/components/Routes/Router.tsx
index 7a048cc8..7a723f12 100644
--- a/src/components/Routes/Router.tsx
+++ b/src/components/Routes/Router.tsx
@@ -1,7 +1,7 @@
-import React, { useContext } from 'react'
 import { Navigate, Route, Routes } from 'react-router-dom'
-import { UserContext } from '../../hooks/userContext'
+import { useWhoAmI } from '../../API'
 import Consents from '../Consents/Consents'
+import Loader from '../Loader/Loader'
 import Login from '../Login/Login'
 import Newsletter from '../Newsletter/Newsletter'
 import Popups from '../Popups/Popups'
@@ -36,32 +36,32 @@ export const routes = Object.keys(links).map(key => ({
   adminOnly: links[key].adminOnly,
 }))
 
-const Router: React.FC = () => {
-  const { user } = useContext(UserContext)
+const Router = () => {
+  const { data: user, isLoading } = useWhoAmI()
 
-  return (
-    <Routes>
-      {user ? (
+  if (isLoading) return <Loader />
+
+  if (user) {
+    return (
+      <Routes>
         <>
           <Route path={links.newsletter.path} element={<Newsletter />} />
           <Route path={links.prices.path} element={<Prices />} />
-          <Route path={links.popups.path} element={<Popups />} />
+          <Route path="/popups" element={<Popups />} />
           {user.isAdmin && (
             <Route path={links.consents.path} element={<Consents />} />
           )}
+          <Route path="/login" element={<Login />} />
           <Route
             path="*"
             element={<Navigate replace to={links.newsletter.path} />}
           />
         </>
-      ) : (
-        <>
-          <Route path="/login" element={<Login />} />
-          <Route path="*" element={<Navigate replace to="/login" />} />
-        </>
-      )}
-    </Routes>
-  )
+      </Routes>
+    )
+  }
+
+  return <Login />
 }
 
 export default Router
diff --git a/src/components/SideBar/SideBar.tsx b/src/components/SideBar/SideBar.tsx
index e55ec3a6..a00cda24 100644
--- a/src/components/SideBar/SideBar.tsx
+++ b/src/components/SideBar/SideBar.tsx
@@ -1,14 +1,25 @@
-import React, { useContext } from 'react'
-import { NavLink } from 'react-router-dom'
+import React from 'react'
+import { useMutation, useQueryClient } from 'react-query'
+import { NavLink, useNavigate } from 'react-router-dom'
+import { fetchLogout, useWhoAmI } from '../../API'
 import logo from '../../assets/icons/ecolyo-logo.svg'
-import { useAuth } from '../../hooks/useAuth'
-import { UserContext } from '../../hooks/userContext'
 import { routes } from '../Routes/Router'
 import './sidebar.scss'
 
 const SideBar: React.FC = () => {
-  const { user } = useContext(UserContext)
-  const { logoutUser } = useAuth()
+  const queryClient = useQueryClient()
+  const navigate = useNavigate()
+  const { data: user } = useWhoAmI()
+
+  const { mutate: logout } = useMutation({
+    mutationFn: fetchLogout,
+    onSuccess: () => {
+      queryClient.clear()
+      navigate('/login')
+    },
+  })
+
+  if (!user) return <div />
 
   return (
     <nav className="menu">
@@ -37,7 +48,7 @@ const SideBar: React.FC = () => {
       </div>
       <div className="bottom">
         {user ? (
-          <button className="btnValid" onClick={logoutUser}>
+          <button className="btnValid" onClick={() => logout()}>
             Logout
           </button>
         ) : (
diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts
deleted file mode 100644
index dd4cc745..00000000
--- a/src/hooks/useAuth.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import axios from 'axios'
-import { useContext, useState } from 'react'
-import { useNavigate } from 'react-router-dom'
-import { links } from '../components/Routes/Router'
-import { UserContext } from './userContext'
-
-interface Auth {
-  loginUser: () => Promise<void>
-  error: unknown
-  logoutUser: () => void
-}
-export const useAuth = (): Auth => {
-  const navigate = useNavigate()
-  const { setUser } = useContext(UserContext)
-  const [error, setError] = useState<unknown>(null)
-
-  //login user
-  const loginUser = async (): Promise<void> => {
-    try {
-      window.location.href = '/OAuth2Login'
-      await setUserContext()
-    } catch (e) {
-      setError(e)
-    }
-  }
-
-  const logoutUser = async (): Promise<void> => {
-    try {
-      if (setUser) setUser(null)
-      window.location.href = '/Logout'
-    } catch (e) {
-      setError(e)
-    }
-  }
-
-  //set user in context and push them home
-  const setUserContext = async (): Promise<void> => {
-    try {
-      const { data } = await axios.get(`/api/common/WhoAmI`)
-      if (data && setUser) {
-        setUser(data)
-        navigate(links.newsletter.path)
-      }
-    } catch (e) {
-      setError(e)
-    }
-  }
-
-  return { loginUser, error, logoutUser }
-}
diff --git a/src/hooks/useFindUser.ts b/src/hooks/useFindUser.ts
deleted file mode 100644
index 201bd31a..00000000
--- a/src/hooks/useFindUser.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import axios from 'axios'
-import { useEffect, useState } from 'react'
-import { toast } from 'react-toastify'
-import { User } from '../models/user.model'
-
-const useFindUser = () => {
-  const [user, setUser] = useState<User | null>(null)
-  const [isLoading, setLoading] = useState<boolean>(true)
-
-  useEffect(() => {
-    async function findUser() {
-      try {
-        const { data } = await axios.get(`/api/common/WhoAmI`)
-        if (data) {
-          setUser(data)
-          setLoading(false)
-        }
-      } catch (error) {
-        toast.error('Accès refusé, veuillez vous connecter')
-      }
-    }
-    findUser()
-  }, [])
-  return {
-    user,
-    setUser,
-    isLoading,
-  }
-}
-
-export default useFindUser
diff --git a/src/hooks/userContext.ts b/src/hooks/userContext.ts
deleted file mode 100644
index e2f69367..00000000
--- a/src/hooks/userContext.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { createContext, Dispatch, SetStateAction } from 'react'
-import { User } from '../models/user.model'
-
-export interface UserContextProps {
-  user: User | null
-  setUser: Dispatch<SetStateAction<User | null>>
-  isLoading: boolean
-}
-export const UserContext = createContext<Partial<UserContextProps>>({})
diff --git a/yarn.lock b/yarn.lock
index c71cbde7..34b8d452 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1189,6 +1189,13 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2":
+  version "7.22.15"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8"
+  integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==
+  dependencies:
+    regenerator-runtime "^0.14.0"
+
 "@babel/template@^7.18.10":
   version "7.18.10"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
@@ -3264,7 +3271,7 @@ bfj@^7.0.2:
     hoopy "^0.1.4"
     tryer "^1.0.1"
 
-big-integer@^1.6.44:
+big-integer@^1.6.16, big-integer@^1.6.44:
   version "1.6.51"
   resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686"
   integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==
@@ -3346,6 +3353,20 @@ braces@^3.0.2, braces@~3.0.2:
   dependencies:
     fill-range "^7.0.1"
 
+broadcast-channel@^3.4.1:
+  version "3.7.0"
+  resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937"
+  integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==
+  dependencies:
+    "@babel/runtime" "^7.7.2"
+    detect-node "^2.1.0"
+    js-sha3 "0.8.0"
+    microseconds "0.2.0"
+    nano-time "1.0.0"
+    oblivious-set "1.0.0"
+    rimraf "3.0.2"
+    unload "2.2.0"
+
 browser-process-hrtime@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
@@ -4126,7 +4147,7 @@ detect-newline@^3.0.0:
   resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
   integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
 
-detect-node@^2.0.4:
+detect-node@^2.0.4, detect-node@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
   integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
@@ -6470,6 +6491,11 @@ jest@^27.4.3:
     import-local "^3.0.2"
     jest-cli "^27.5.1"
 
+js-sha3@0.8.0:
+  version "0.8.0"
+  resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
+  integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
+
 "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -6866,6 +6892,14 @@ makeerror@1.0.12:
   dependencies:
     tmpl "1.0.5"
 
+match-sorter@^6.0.2:
+  version "6.3.1"
+  resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda"
+  integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==
+  dependencies:
+    "@babel/runtime" "^7.12.5"
+    remove-accents "0.4.2"
+
 mdn-data@2.0.14:
   version "2.0.14"
   resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
@@ -6916,6 +6950,11 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
     braces "^3.0.2"
     picomatch "^2.3.1"
 
+microseconds@0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
+  integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
+
 mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
   version "1.52.0"
   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
@@ -7011,6 +7050,13 @@ multicast-dns@^7.2.5:
     dns-packet "^5.2.2"
     thunky "^1.0.2"
 
+nano-time@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
+  integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==
+  dependencies:
+    big-integer "^1.6.16"
+
 nanoid@^3.3.4:
   version "3.3.4"
   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
@@ -7184,6 +7230,11 @@ object.values@^1.1.0, object.values@^1.1.5:
     define-properties "^1.1.3"
     es-abstract "^1.19.1"
 
+oblivious-set@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
+  integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
+
 obuf@^1.0.0, obuf@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
@@ -8305,6 +8356,15 @@ react-is@^18.0.0:
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
   integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
 
+react-query@^3.39.3:
+  version "3.39.3"
+  resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.3.tgz#4cea7127c6c26bdea2de5fb63e51044330b03f35"
+  integrity sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==
+  dependencies:
+    "@babel/runtime" "^7.5.5"
+    broadcast-channel "^3.4.1"
+    match-sorter "^6.0.2"
+
 react-refresh@^0.11.0:
   version "0.11.0"
   resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
@@ -8465,6 +8525,11 @@ regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.9:
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
   integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
 
+regenerator-runtime@^0.14.0:
+  version "0.14.0"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
+  integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
+
 regenerator-transform@^0.15.0:
   version "0.15.0"
   resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537"
@@ -8520,6 +8585,11 @@ relateurl@^0.2.7:
   resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
   integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==
 
+remove-accents@0.4.2:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
+  integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==
+
 renderkid@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a"
@@ -8612,7 +8682,7 @@ reusify@^1.0.4:
   resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
   integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
 
-rimraf@^3.0.0, rimraf@^3.0.2:
+rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
   integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
@@ -9570,6 +9640,14 @@ universalify@^2.0.0:
   resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
   integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
 
+unload@2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
+  integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
+  dependencies:
+    "@babel/runtime" "^7.6.2"
+    detect-node "^2.0.4"
+
 unpipe@1.0.0, unpipe@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
-- 
GitLab