chore: udpate react, downshift and routing

This commit is contained in:
Rafael Taranto 2025-05-15 13:00:21 +01:00
parent 61285c9037
commit d9e570990c
30 changed files with 4131 additions and 2813 deletions

6582
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -18,7 +18,7 @@
"d3": "^6.2.0",
"date-fns": "^2.26.0",
"date-fns-tz": "^1.1.6",
"downshift": "3.3.4",
"downshift": "9.0.9",
"file-saver": "2.0.2",
"formik": "2.2.0",
"jss-plugin-extend": "^10.0.0",
@ -28,17 +28,18 @@
"pretty-ms": "^2.1.0",
"qrcode.react": "4.2.0",
"ramda": "^0.26.1",
"react": "17.0.2",
"react": "18.3.1",
"react-copy-to-clipboard": "^5.0.2",
"react-dom": "17.0.2",
"react-dom": "18.3.1",
"react-dropzone": "^11.4.2",
"react-number-format": "^4.4.1",
"react-otp-input": "3.1.1",
"react-router-dom": "5.1.2",
"react-virtualized": "^9.21.2",
"ua-parser-js": "1.0.40",
"uuid": "11.1.0",
"yup": "1.6.1"
"wouter": "^3.7.0",
"yup": "1.6.1",
"zustand": "^4.5.7"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.4",

View file

@ -1,7 +1,7 @@
import CssBaseline from '@mui/material/CssBaseline'
import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles'
import React, { useState } from 'react'
import { BrowserRouter as Router } from 'react-router-dom'
import { Router } from 'wouter'
import ApolloProvider from './utils/apollo'
import AppContext from './AppContext'
@ -9,10 +9,12 @@ import theme from './styling/theme'
import Main from './Main'
import './styling/global/global.css'
import useLocationWithConfirmation from './routing/useLocationWithConfirmation.js'
const App = () => {
const [wizardTested, setWizardTested] = useState(false)
const [userData, setUserData] = useState(null)
const [isDirtyForm, setDirtyForm] = useState(false)
const setRole = role => {
if (userData && role && userData.role !== role) {
@ -22,8 +24,16 @@ const App = () => {
return (
<AppContext.Provider
value={{ wizardTested, setWizardTested, userData, setUserData, setRole }}>
<Router>
value={{
wizardTested,
setWizardTested,
userData,
setUserData,
setRole,
isDirtyForm,
setDirtyForm,
}}>
<Router hook={useLocationWithConfirmation}>
<ApolloProvider>
<StyledEngineProvider enableCssLayer>
<ThemeProvider theme={theme}>

View file

@ -1,4 +1,4 @@
import { useHistory, useLocation } from 'react-router-dom'
import { useLocation } from 'wouter'
import React, { useContext, useState } from 'react'
import { gql, useQuery } from '@apollo/client'
import Slide from '@mui/material/Slide'
@ -27,8 +27,7 @@ const GET_USER_DATA = gql`
`
const Main = () => {
const location = useLocation()
const history = useHistory()
const [location, navigate] = useLocation()
const { wizardTested, userData, setUserData } = useContext(AppContext)
const [loading, setLoading] = useState(true)
@ -41,16 +40,14 @@ const Main = () => {
},
})
const route = location.pathname
const sidebar = hasSidebar(location)
const parent = sidebar ? getParent(location) : {}
const sidebar = hasSidebar(route)
const parent = sidebar ? getParent(route) : {}
const is404 = location === '/404'
const is404 = location.pathname === '/404'
const isSelected = it => location === it.route
const isSelected = it => location.pathname === it.route
const onClick = it => history.push(it.route)
const onClick = it => navigate(it.route)
const contentClassName = sidebar ? 'flex-1 ml-12 pt-4' : 'w-[1200px]'

View file

@ -1,28 +1,19 @@
import { useFormikContext } from 'formik'
import React, { useEffect } from 'react'
import { Prompt } from 'react-router-dom'
const PROMPT_DEFAULT_MESSAGE =
'You have unsaved changes on this page. Are you sure you want to leave?'
import useDirtyHandler from '../routing/dirtyHandler.js'
const PromptWhenDirty = ({ message = PROMPT_DEFAULT_MESSAGE }) => {
const PromptWhenDirty = () => {
const setIsDirty = useDirtyHandler(state => state.setIsDirty)
const formik = useFormikContext()
const hasChanges = formik.dirty && formik.submitCount === 0
useEffect(() => {
if (hasChanges) {
window.onbeforeunload = confirmExit
} else {
window.onbeforeunload = undefined
}
setIsDirty(hasChanges)
}, [hasChanges])
const confirmExit = () => {
return PROMPT_DEFAULT_MESSAGE
}
return <Prompt when={hasChanges} message={message} />
return <></>
}
export default PromptWhenDirty

View file

@ -4,7 +4,7 @@ import Popper from '@mui/material/Popper'
import classnames from 'classnames'
import * as R from 'ramda'
import React, { memo, useState, useEffect, useRef } from 'react'
import { NavLink, useHistory } from 'react-router-dom'
import { Link as WLink, useRoute, useLocation } from 'wouter'
import ActionButton from '../buttons/ActionButton'
import { H4 } from '../typography'
import AddIconReverse from '../../styling/icons/button/add/white.svg?react'
@ -23,6 +23,32 @@ const HAS_UNREAD = gql`
}
`
const Link = ({
setActive,
isParent,
className,
activeClassName,
item,
...props
}) => {
const [location] = useLocation()
const [isActive] = useRoute(props.to)
if (isActive) setActive(item)
const isParentActive = isParent && location.startsWith(props.to)
const classNames = classnames({
[className]: true,
[activeClassName]: isActive || isParentActive,
})
return (
<WLink {...props} asChild>
<a className={classNames}>{props.children}</a>
</WLink>
)
}
const Subheader = ({ item, user }) => {
const [prev, setPrev] = useState(null)
@ -35,17 +61,15 @@ const Subheader = ({ item, user }) => {
if (!R.includes(user.role, it.allowedRoles)) return <></>
return (
<li key={idx} className={styles.subheaderLi}>
<NavLink
to={{ pathname: it.route, state: { prev } }}
<Link
to={it.route}
state={{ prev }}
className={styles.subheaderLink}
activeClassName={styles.activeSubheaderLink}
isActive={match => {
if (!match) return false
setPrev(it.route)
return true
}}>
item={it.route}
setActive={setPrev}>
{it.label}
</NavLink>
</Link>
</li>
)
})}
@ -68,7 +92,7 @@ const Header = memo(({ tree, user }) => {
const { data, refetch, startPolling, stopPolling } = useQuery(HAS_UNREAD)
const notifCenterButtonRef = useRef()
const popperRef = useRef()
const history = useHistory()
const [, navigate] = useLocation()
useEffect(() => {
if (data?.hasUnreadNotifications) return setHasUnread(true)
@ -83,7 +107,7 @@ const Header = memo(({ tree, user }) => {
const onPaired = machine => {
setOpen(false)
history.push('/maintenance/machine-status', { id: machine.deviceId })
navigate('/maintenance/machine-status', { state: { id: machine.deviceId } })
}
// these inline styles prevent scroll bubbling: when the user reaches the bottom of the notifications list and keeps scrolling,
@ -114,7 +138,7 @@ const Header = memo(({ tree, user }) => {
<div
onClick={() => {
setActive(false)
history.push('/dashboard')
navigate('/dashboard')
}}
className={classnames(styles.logo, styles.logoLink)}>
<Logo />
@ -125,15 +149,13 @@ const Header = memo(({ tree, user }) => {
{tree.map((it, idx) => {
if (!R.includes(user.role, it.allowedRoles)) return <></>
return (
<NavLink
<Link
isParent
key={idx}
to={it.route || it.children[0].route}
isActive={match => {
if (!match) return false
setActive(it)
return true
}}
className={classnames(styles.link)}
setActive={setActive}
item={it}
className={styles.link}
activeClassName={styles.activeLink}>
<li className={styles.li}>
<span
@ -142,7 +164,7 @@ const Header = memo(({ tree, user }) => {
{it.label}
</span>
</li>
</NavLink>
</Link>
)
})}
</ul>

View file

@ -1,11 +1,8 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { createRoot } from 'react-dom/client'
import App from './App'
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
)
const container = document.getElementById('root')
const root = createRoot(container)
root.render(<App />)

View file

@ -1,7 +1,7 @@
import { useMutation, useLazyQuery, gql } from '@apollo/client'
import { Form, Formik } from 'formik'
import React, { useContext, useState } from 'react'
import { useHistory } from 'react-router-dom'
import { useLocation } from 'wouter'
import { TL1, P } from '../../components/typography'
import AppContext from '../../AppContext'
@ -37,7 +37,7 @@ const GET_USER_DATA = gql`
`
const Input2FAState = ({ state, dispatch }) => {
const history = useHistory()
const [, navigate] = useLocation()
const { setUserData } = useContext(AppContext)
const [invalidToken, setInvalidToken] = useState(false)
@ -45,7 +45,7 @@ const Input2FAState = ({ state, dispatch }) => {
const [getUserData, { error: queryError }] = useLazyQuery(GET_USER_DATA, {
onCompleted: ({ userData }) => {
setUserData(userData)
history.push('/')
navigate('/')
},
})

View file

@ -2,7 +2,7 @@ import { useMutation, useLazyQuery, gql } from '@apollo/client'
import { startAssertion } from '@simplewebauthn/browser'
import { Field, Form, Formik } from 'formik'
import React, { useState, useContext } from 'react'
import { useHistory } from 'react-router-dom'
import { useLocation } from 'wouter'
import { H2, Label2, P } from '../../components/typography'
import * as Yup from 'yup'
@ -61,7 +61,7 @@ const InputFIDOState = ({ state, strategy }) => {
}
`
const history = useHistory()
const [, navigate] = useLocation()
const { setUserData } = useContext(AppContext)
const [localClientField, setLocalClientField] = useState('')
@ -125,7 +125,7 @@ const InputFIDOState = ({ state, strategy }) => {
const [getUserData, { error: queryError }] = useLazyQuery(GET_USER_DATA, {
onCompleted: ({ userData }) => {
setUserData(userData)
history.push('/')
navigate('/')
},
})

View file

@ -2,7 +2,7 @@ import { useMutation, useLazyQuery, gql } from '@apollo/client'
import { startAssertion } from '@simplewebauthn/browser'
import { Field, Form, Formik } from 'formik'
import React, { useContext } from 'react'
import { useHistory } from 'react-router-dom'
import { useLocation } from 'wouter'
import { Label3, P } from '../../components/typography'
import * as Yup from 'yup'
@ -67,7 +67,7 @@ const getErrorMsg = (formikErrors, formikTouched, mutationError) => {
}
const LoginState = ({ dispatch, strategy }) => {
const history = useHistory()
const [, navigate] = useLocation()
const { setUserData } = useContext(AppContext)
const [login, { error: loginMutationError }] = useMutation(LOGIN)
@ -125,7 +125,7 @@ const LoginState = ({ dispatch, strategy }) => {
{
onCompleted: ({ userData }) => {
setUserData(userData)
history.push('/')
navigate('/')
},
},
)

View file

@ -3,7 +3,7 @@ import Grid from '@mui/material/Grid'
import Paper from '@mui/material/Paper'
import { Field, Form, Formik } from 'formik'
import React, { useReducer } from 'react'
import { useLocation, useHistory } from 'react-router-dom'
import { useLocation, useSearchParams } from 'wouter'
import { H2, Label3, P } from '../../components/typography'
import Logo from '../../styling/icons/menu/logo.svg?react'
import * as Yup from 'yup'
@ -12,8 +12,6 @@ import { Button } from '../../components/buttons'
import { SecretInput } from '../../components/inputs/formik'
import classes from './Authentication.module.css'
const QueryParams = () => new URLSearchParams(useLocation().search)
const VALIDATE_REGISTER_LINK = gql`
query validateRegisterLink($token: String!) {
validateRegisterLink(token: $token) {
@ -84,8 +82,9 @@ const getErrorMsg = (
}
const Register = () => {
const history = useHistory()
const token = QueryParams().get('t')
const [, navigate] = useLocation()
const [searchParams] = useSearchParams()
const token = searchParams.get('t')
const [state, dispatch] = useReducer(reducer, initialState)
@ -118,7 +117,7 @@ const Register = () => {
const [register, { error: mutationError }] = useMutation(REGISTER, {
onCompleted: ({ register: success }) => {
if (success) history.push('/wizard', { fromAuthRegister: true })
if (success) navigate('/')
},
})

View file

@ -4,7 +4,7 @@ import Paper from '@mui/material/Paper'
import { Form, Formik } from 'formik'
import { QRCodeSVG as QRCode } from 'qrcode.react'
import React, { useReducer, useState } from 'react'
import { useLocation, useHistory } from 'react-router-dom'
import { useLocation, useSearchParams } from 'wouter'
import { H2, Label2, Label3, P } from '../../components/typography'
import Logo from '../../styling/icons/menu/logo.svg?react'
@ -43,9 +43,9 @@ const reducer = (state, action) => {
}
const Reset2FA = () => {
const history = useHistory()
const QueryParams = () => new URLSearchParams(useLocation().search)
const token = QueryParams().get('t')
const [, navigate] = useLocation()
const [searchParams] = useSearchParams()
const token = searchParams.get('t')
const [isShowing, setShowing] = useState(false)
const [invalidToken, setInvalidToken] = useState(false)
@ -85,7 +85,7 @@ const Reset2FA = () => {
const [reset2FA, { error: mutationError }] = useMutation(RESET_2FA, {
onCompleted: ({ reset2FA: success }) => {
success ? history.push('/') : setInvalidToken(true)
success ? navigate('/') : setInvalidToken(true)
},
})

View file

@ -3,7 +3,7 @@ import Grid from '@mui/material/Grid'
import Paper from '@mui/material/Paper'
import { Field, Form, Formik } from 'formik'
import React, { useState } from 'react'
import { useLocation, useHistory } from 'react-router-dom'
import { useLocation, useSearchParams } from 'wouter'
import { H2, Label3, P } from '../../components/typography'
import Logo from '../../styling/icons/menu/logo.svg?react'
import * as Yup from 'yup'
@ -57,9 +57,9 @@ const getErrorMsg = (formikErrors, formikTouched, mutationError) => {
}
const ResetPassword = () => {
const history = useHistory()
const QueryParams = () => new URLSearchParams(useLocation().search)
const token = QueryParams().get('t')
const [, navigate] = useLocation()
const [searchParams] = useSearchParams()
const token = searchParams.get('t')
const [userID, setUserID] = useState(null)
const [isLoading, setLoading] = useState(true)
const [wasSuccessful, setSuccess] = useState(false)
@ -83,7 +83,7 @@ const ResetPassword = () => {
const [resetPassword, { error }] = useMutation(RESET_PASSWORD, {
onCompleted: ({ resetPassword: success }) => {
if (success) history.push('/')
if (success) navigate('/')
},
})

View file

@ -2,7 +2,7 @@ import { useMutation, useQuery, useLazyQuery, gql } from '@apollo/client'
import { Form, Formik } from 'formik'
import { QRCodeSVG as QRCode } from 'qrcode.react'
import React, { useContext, useState } from 'react'
import { useHistory } from 'react-router-dom'
import { useLocation } from 'wouter'
import { Label3, P } from '../../components/typography'
import AppContext from '../../AppContext'
@ -48,7 +48,7 @@ const GET_USER_DATA = gql`
`
const Setup2FAState = ({ state }) => {
const history = useHistory()
const [, navigate] = useLocation()
const { setUserData } = useContext(AppContext)
const [secret, setSecret] = useState(null)
@ -85,7 +85,7 @@ const Setup2FAState = ({ state }) => {
const [getUserData] = useLazyQuery(GET_USER_DATA, {
onCompleted: ({ userData }) => {
setUserData(userData)
history.push('/')
navigate('/')
},
})

View file

@ -4,7 +4,7 @@ import Switch from '@mui/material/Switch'
import NavigateNextIcon from '@mui/icons-material/NavigateNext'
import * as R from 'ramda'
import React, { memo, useState } from 'react'
import { useHistory, useParams } from 'react-router-dom'
import { useLocation, useParams } from 'wouter'
import { Label1, Label2 } from '../../components/typography'
import AuthorizeReversedIcon from '../../styling/icons/button/authorize/white.svg?react'
import AuthorizeIcon from '../../styling/icons/button/authorize/zodiac.svg?react'
@ -284,7 +284,7 @@ const CHECK_AGAINST_SANCTIONS = gql`
`
const CustomerProfile = memo(() => {
const history = useHistory()
const [, navigate] = useLocation()
const [showCompliance, setShowCompliance] = useState(false)
const [wizard, setWizard] = useState(false)
@ -515,7 +515,7 @@ const CustomerProfile = memo(() => {
<Label1
noMargin
className="cursor-pointer text-comet"
onClick={() => history.push('/compliance/customers')}>
onClick={() => navigate('/compliance/customers')}>
Customers
</Label1>
<Label2 noMargin className="cursor-pointer text-comet">

View file

@ -1,7 +1,7 @@
import { useQuery, useMutation, gql } from '@apollo/client'
import * as R from 'ramda'
import React, { useState } from 'react'
import { useHistory } from 'react-router-dom'
import { useLocation } from 'wouter'
import SearchBox from '../../components/SearchBox'
import SearchFilter from '../../components/SearchFilter'
import TitleSection from '../../components/layout/TitleSection'
@ -95,10 +95,10 @@ const getFiltersObj = filters =>
R.reduce((s, f) => ({ ...s, [f.type]: f.value }), {}, filters)
const Customers = () => {
const history = useHistory()
const [, navigate] = useLocation()
const handleCustomerClicked = customer =>
history.push(`/compliance/customer/${customer.id}`)
navigate(`/compliance/customer/${customer.id}`)
const [filteredCustomers, setFilteredCustomers] = useState([])
const [variables, setVariables] = useState({})

View file

@ -2,7 +2,7 @@ import List from '@mui/material/List'
import ListItem from '@mui/material/ListItem'
import * as R from 'ramda'
import React from 'react'
import { useHistory } from 'react-router-dom'
import { useLocation } from 'wouter'
import { P } from '../../../components/typography/index'
import Wrench from '../../../styling/icons/action/wrench/zodiac.svg?react'
import CashBoxEmpty from '../../../styling/icons/cassettes/cashbox-empty.svg?react'
@ -23,7 +23,7 @@ const links = {
}
const AlertsTable = ({ numToRender, alerts, machines }) => {
const history = useHistory()
const [, navigate] = useLocation()
const alertsToRender = R.slice(0, numToRender, alerts)
const alertMessage = alert => {
@ -45,7 +45,7 @@ const AlertsTable = ({ numToRender, alerts, machines }) => {
<P className="my-2">{alertMessage(alert)}</P>
<AlertLinkIcon
className="ml-auto cursor-pointer"
onClick={() => history.push(links[alert.type] || '/dashboard')}
onClick={() => navigate(links[alert.type] || '/dashboard')}
/>
</ListItem>
)

View file

@ -1,7 +1,7 @@
import { useQuery, gql } from '@apollo/client'
import * as R from 'ramda'
import React, { useState } from 'react'
import { useHistory } from 'react-router-dom'
import { useLocation } from 'wouter'
import TitleSection from '../../components/layout/TitleSection'
import { H1, Info2, TL2, Label1 } from '../../components/typography'
import TxInIcon from '../../styling/icons/direction/cash-in.svg?react'
@ -26,14 +26,14 @@ const GET_DATA = gql`
`
const Dashboard = () => {
const history = useHistory()
const [, navigate] = useLocation()
const [open, setOpen] = useState(false)
const { data, loading } = useQuery(GET_DATA)
const onPaired = machine => {
setOpen(false)
history.push('/maintenance/machine-status', { id: machine.deviceId })
navigate('/maintenance/machine-status', { state: { id: machine.deviceId } })
}
return !loading ? (

View file

@ -8,7 +8,7 @@ import TableHead from '@mui/material/TableHead'
import TableRow from '@mui/material/TableRow'
import * as R from 'ramda'
import React from 'react'
import { useHistory } from 'react-router-dom'
import { useLocation } from 'wouter'
import { Status } from '../../../components/Status'
import { Label2, TL2 } from '../../../components/typography'
import TxOutIcon from '../../../styling/icons/direction/cash-out.svg?react'
@ -39,7 +39,7 @@ const HeaderCell = styled(TableCell)({
})
const MachinesTable = ({ machines = [], numToRender }) => {
const history = useHistory()
const [, navigate] = useLocation()
const { data } = useQuery(GET_CONFIG)
const fillingPercentageSettings = fromNamespace(
@ -65,8 +65,10 @@ const MachinesTable = ({ machines = [], numToRender }) => {
}
const redirect = ({ name, deviceId }) => {
return history.push(`/machines/${deviceId}`, {
selectedMachine: name,
return navigate(`/machines/${deviceId}`, {
state: {
selectedMachine: name,
},
})
}

View file

@ -3,7 +3,7 @@ import Breadcrumbs from '@mui/material/Breadcrumbs'
import NavigateNextIcon from '@mui/icons-material/NavigateNext'
import * as R from 'ramda'
import React, { useState } from 'react'
import { Link, useLocation, useHistory } from 'react-router-dom'
import { Link, useLocation } from 'wouter'
import { TL1, TL2, Label3 } from '../../components/typography'
import Cassettes from './MachineComponents/Cassettes'
@ -61,17 +61,13 @@ const GET_INFO = gql`
const getMachineID = path => path.slice(path.lastIndexOf('/') + 1)
const MachineRoute = () => {
const location = useLocation()
const history = useHistory()
const id = getMachineID(location.pathname)
const [location, navigate] = useLocation()
const [loading, setLoading] = useState(true)
const id = getMachineID(location)
const { data, refetch } = useQuery(GET_INFO, {
onCompleted: data => {
if (data.machine === null)
return history.push('/maintenance/machine-status')
if (data.machine === null) return navigate('/maintenance/machine-status')
setLoading(false)
},
@ -85,7 +81,7 @@ const MachineRoute = () => {
})
const reload = () => {
return history.push(location.pathname)
return navigate(location)
}
return (

View file

@ -2,7 +2,7 @@ import { useQuery, gql } from '@apollo/client'
import { formatDistance } from 'date-fns'
import * as R from 'ramda'
import React from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import { useLocation } from 'wouter'
import { MainStatus } from '../../components/Status'
import Title from '../../components/Title'
import DataTable from '../../components/tables/DataTable'
@ -55,9 +55,8 @@ const GET_DATA = gql`
`
const MachineStatus = () => {
const history = useHistory()
const { state } = useLocation()
const addedMachineId = state?.id
const [, navigate] = useLocation()
const addedMachineId = history.state?.id
const {
data: machinesResponse,
refetch,
@ -77,7 +76,7 @@ const MachineStatus = () => {
{m.name}
<div
onClick={() => {
history.push(`/machines/${m.deviceId}`)
navigate(`/machines/${m.deviceId}`)
}}>
<MachineRedirectIcon />
</div>

View file

@ -3,7 +3,7 @@ import { toUnit, formatCryptoAddress } from '@lamassu/coins/lightUtils'
import BigNumber from 'bignumber.js'
import * as R from 'ramda'
import React, { useEffect, useState } from 'react'
import { useHistory } from 'react-router-dom'
import { useLocation } from 'wouter'
import LogsDowloaderPopover from '../../components/LogsDownloaderPopper'
import SearchBox from '../../components/SearchBox'
import SearchFilter from '../../components/SearchFilter'
@ -134,7 +134,7 @@ const getFiltersObj = filters =>
R.reduce((s, f) => ({ ...s, [f.type]: f.value }), {}, filters)
const Transactions = () => {
const history = useHistory()
const [, navigate] = useLocation()
const [filters, setFilters] = useState([])
const { data: filtersResponse, loading: filtersLoading } = useQuery(
@ -160,7 +160,7 @@ const Transactions = () => {
const timezone = R.path(['config', 'locale_timezone'], configResponse)
const redirect = customerId => {
return history.push(`/compliance/customer/${customerId}`)
return navigate(`/compliance/customer/${customerId}`)
}
const elements = [

View file

@ -3,7 +3,7 @@ import Dialog from '@mui/material/Dialog'
import DialogContent from '@mui/material/DialogContent'
import classnames from 'classnames'
import React, { useState, useContext } from 'react'
import { useHistory } from 'react-router-dom'
import { useLocation } from 'wouter'
import { getWizardStep, STEPS } from './helper'
import AppContext from '../../AppContext'
@ -23,7 +23,7 @@ const GET_DATA = gql`
const Wizard = () => {
const { data, loading } = useQuery(GET_DATA)
const history = useHistory()
const [, navigate] = useLocation()
const { setWizardTested } = useContext(AppContext)
const [step, setStep] = useState(0)
@ -52,7 +52,7 @@ const Wizard = () => {
if (step >= STEPS.length - 1) {
setOpen(false)
setWizardTested(true)
history.push('/')
navigate('/')
return
}

View file

@ -1,5 +1,5 @@
import React, { useContext } from 'react'
import { Route, Redirect } from 'react-router-dom'
import { Route, Redirect } from 'wouter'
import AppContext from '../AppContext'

View file

@ -1,24 +1,17 @@
import React, { useContext } from 'react'
import { Route, Redirect } from 'react-router-dom'
import { Route, Redirect } from 'wouter'
import AppContext from '../AppContext'
import { isLoggedIn } from './utils'
const PublicRoute = ({ component: Component, restricted, ...rest }) => {
const PublicRoute = ({ restricted, ...rest }) => {
const { userData } = useContext(AppContext)
return (
<Route
{...rest}
render={props =>
isLoggedIn(userData) && restricted ? (
<Redirect to="/" />
) : (
<Component {...props} />
)
}
/>
return isLoggedIn(userData) && restricted ? (
<Redirect to="/" />
) : (
<Route {...rest} />
)
}

View file

@ -0,0 +1,8 @@
import { create } from 'zustand'
const useDirtyHandler = create(set => ({
isDirty: false,
setIsDirty: it => set({ isDirty: it }),
}))
export default useDirtyHandler

View file

@ -1,5 +1,5 @@
import React from 'react'
import { Redirect } from 'react-router-dom'
import { Redirect } from 'wouter'
import Funding from '../pages/Funding/Funding.jsx'
import IndividualDiscounts from '../pages/LoyaltyPanel/IndividualDiscounts'
import PromoCodes from '../pages/LoyaltyPanel/PromoCodes'

View file

@ -2,7 +2,7 @@ import Fade from '@mui/material/Fade'
import Slide from '@mui/material/Slide'
import * as R from 'ramda'
import React, { useContext } from 'react'
import { matchPath, Redirect, Switch, useLocation } from 'react-router-dom'
import { Redirect, Switch, useLocation } from 'wouter'
import Login from '../pages/Authentication/Login'
import Register from '../pages/Authentication/Register'
import Reset2FA from '../pages/Authentication/Reset2FA'
@ -50,7 +50,7 @@ const getParent = route =>
)(flattened)
const Routes = () => {
const location = useLocation()
const [location] = useLocation()
const { userData } = useContext(AppContext)
const getFilteredRoutes = () => {
@ -63,14 +63,14 @@ const Routes = () => {
})
}
const Transition = location.state ? Slide : Fade
const Transition = history.state ? Slide : Fade
const transitionProps =
Transition === Slide
? {
direction:
R.findIndex(R.propEq('route', location.state.prev))(leafRoutes) >
R.findIndex(R.propEq('route', location.pathname))(leafRoutes)
R.findIndex(R.propEq('route', history.state.prev))(leafRoutes) >
R.findIndex(R.propEq('route', location))(leafRoutes)
? 'right'
: 'left',
}
@ -79,9 +79,9 @@ const Routes = () => {
return (
<Switch>
<PrivateRoute exact path="/">
<Redirect to={{ pathname: '/dashboard' }} />
<Redirect to="/dashboard" />
</PrivateRoute>
<PrivateRoute path={'/dashboard'}>
<PrivateRoute path="/dashboard">
<Transition
className={wrapperClasses}
{...transitionProps}
@ -93,7 +93,9 @@ const Routes = () => {
</div>
</Transition>
</PrivateRoute>
<PrivateRoute path="/machines" component={Machines} />
<PrivateRoute path="/machines">
<Machines />
</PrivateRoute>
<PublicRoute path="/register" component={Register} />
<PublicRoute path="/resetpassword" component={ResetPassword} />
<PublicRoute path="/reset2fa" component={Reset2FA} />
@ -103,20 +105,18 @@ const Routes = () => {
<Transition
className={wrapperClasses}
{...transitionProps}
in={!!matchPath(location.pathname, { path: route })}
in={location === route}
mountOnEnter
unmountOnExit>
<div className={wrapperClasses}>
<PrivateRoute path={route} key={key}>
<Page name={key} />
</PrivateRoute>
<Page name={key} />
</div>
</Transition>
</PrivateRoute>
))}
<PublicRoute path="/404" />
<PublicRoute path="*">
<Redirect to={{ pathname: '/404' }} />
<Redirect to="/404" />
</PublicRoute>
</Switch>
)

View file

@ -0,0 +1,34 @@
import useDirtyHandler from './dirtyHandler.js'
import { useEffect, useRef } from 'react'
import { useBrowserLocation } from 'wouter/use-browser-location'
const PROMPT_DEFAULT_MESSAGE =
'You have unsaved changes on this page. Are you sure you want to leave?'
const useLocationWithConfirmation = () => {
const setIsDirty = useDirtyHandler(state => state.setIsDirty)
const isDirtyRef = useRef(useDirtyHandler.getState().isDirty)
useEffect(
() =>
useDirtyHandler.subscribe(state => (isDirtyRef.current = state.isDirty)),
[],
)
const [location, setLocation] = useBrowserLocation()
return [
location,
newLocation => {
let perfomNavigation = true
if (isDirtyRef.current) {
perfomNavigation = window.confirm(PROMPT_DEFAULT_MESSAGE)
}
if (perfomNavigation) {
setLocation(newLocation)
setIsDirty(false)
}
},
]
}
export default useLocationWithConfirmation

View file

@ -7,7 +7,7 @@ import {
import { onError } from '@apollo/client/link/error'
import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'
import React, { useContext } from 'react'
import { useHistory, useLocation } from 'react-router-dom'
import { useLocation } from 'wouter'
import AppContext from '../AppContext'
@ -16,7 +16,7 @@ const uploadLink = createUploadLink({
uri: `/graphql`,
})
const getClient = (history, location, getUserData, setUserData, setRole) =>
const getClient = (navigate, location, getUserData, setUserData, setRole) =>
new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
@ -24,7 +24,7 @@ const getClient = (history, location, getUserData, setUserData, setRole) =>
graphQLErrors.forEach(({ message, locations, path, extensions }) => {
if (extensions?.code === 'UNAUTHENTICATED') {
setUserData(null)
if (location.pathname !== '/login') history.push('/login')
if (location !== '/login') navigate('/login')
}
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
@ -66,11 +66,10 @@ const getClient = (history, location, getUserData, setUserData, setRole) =>
})
const Provider = ({ children }) => {
const history = useHistory()
const location = useLocation()
const [location, navigate] = useLocation()
const { userData, setUserData, setRole } = useContext(AppContext)
const client = getClient(
history,
navigate,
location,
() => userData,
setUserData,