feat: initial Pazuz react setup

This commit is contained in:
Sérgio Salgado 2021-03-03 17:05:49 +00:00 committed by Josh Harvey
parent dd0abc8a8b
commit 007c51472c
3 changed files with 539 additions and 0 deletions

View file

@ -0,0 +1,137 @@
import CssBaseline from '@material-ui/core/CssBaseline'
import Grid from '@material-ui/core/Grid'
import {
StylesProvider,
jssPreset,
MuiThemeProvider,
makeStyles
} from '@material-ui/core/styles'
import { create } from 'jss'
import extendJss from 'jss-plugin-extend'
import React, { useContext, useState } from 'react'
import {
useLocation,
useHistory,
BrowserRouter as Router
} from 'react-router-dom'
import AppContext from 'src/AppContext'
import Header from 'src/components/layout/Header'
import Sidebar from 'src/components/layout/Sidebar'
import TitleSection from 'src/components/layout/TitleSection'
import ApolloProvider from 'src/pazuz/apollo/Provider'
import { tree, hasSidebar, Routes, getParent } from 'src/pazuz/routing/routes'
import global from 'src/styling/global'
import theme from 'src/styling/theme'
import { backgroundColor, mainWidth } from 'src/styling/variables'
if (process.env.NODE_ENV !== 'production') {
const whyDidYouRender = require('@welldone-software/why-did-you-render')
whyDidYouRender(React)
}
const jss = create({
plugins: [extendJss(), ...jssPreset().plugins]
})
const fill = '100%'
const flexDirection = 'column'
const useStyles = makeStyles({
...global,
root: {
backgroundColor,
width: fill,
minHeight: fill,
display: 'flex',
flexDirection
},
wrapper: {
width: mainWidth,
height: fill,
margin: '0 auto',
flex: 1,
display: 'flex',
flexDirection
},
grid: {
flex: 1,
height: '100%'
},
contentWithSidebar: {
flex: 1,
marginLeft: 48,
paddingTop: 15
},
contentWithoutSidebar: {
width: mainWidth
}
})
const Main = () => {
const classes = useStyles()
const location = useLocation()
const history = useHistory()
const { wizardTested } = useContext(AppContext)
const route = location.pathname
const sidebar = hasSidebar(route)
const parent = sidebar ? getParent(route) : {}
const is404 = location.pathname === '/404'
const isSelected = it => location.pathname === it.route
const onClick = it => history.push(it.route)
const contentClassName = sidebar
? classes.contentWithSidebar
: classes.contentWithoutSidebar
return (
<div className={classes.root}>
{!is404 && wizardTested && <Header tree={tree} />}
<main className={classes.wrapper}>
{sidebar && !is404 && wizardTested && (
<TitleSection title={parent.title}></TitleSection>
)}
<Grid container className={classes.grid}>
{sidebar && !is404 && wizardTested && (
<Sidebar
data={parent.children}
isSelected={isSelected}
displayName={it => it.label}
onClick={onClick}
/>
)}
<div className={contentClassName}>
<Routes />
</div>
</Grid>
</main>
</div>
)
}
const App = () => {
const [wizardTested, setWizardTested] = useState(false)
return (
<AppContext.Provider value={{ wizardTested, setWizardTested }}>
<Router>
<ApolloProvider>
<StylesProvider jss={jss}>
<MuiThemeProvider theme={theme}>
<CssBaseline />
<Main />
</MuiThemeProvider>
</StylesProvider>
</ApolloProvider>
</Router>
</AppContext.Provider>
)
}
export default App

View file

@ -0,0 +1,57 @@
import { ApolloProvider } from '@apollo/react-hooks'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { ApolloLink } from 'apollo-link'
import { onError } from 'apollo-link-error'
import { HttpLink } from 'apollo-link-http'
import React from 'react'
import { useHistory, useLocation } from 'react-router-dom'
const URI =
process.env.NODE_ENV === 'development' ? 'https://localhost:8070' : ''
const getClient = (history, location) =>
new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path, extensions }) => {
if (extensions?.code === 'UNAUTHENTICATED') {
if (location.pathname !== '/404') history.push('/404')
}
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
})
if (networkError) console.log(`[Network error]: ${networkError}`)
}),
new HttpLink({
credentials: 'include',
uri: `${URI}/graphql`
})
]),
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
fetchPolicy: 'no-cache',
errorPolicy: 'ignore'
},
query: {
fetchPolicy: 'no-cache',
errorPolicy: 'all'
},
mutate: {
errorPolicy: 'all'
}
}
})
const Provider = ({ children }) => {
const history = useHistory()
const location = useLocation()
const client = getClient(history, location)
return <ApolloProvider client={client}>{children}</ApolloProvider>
}
export default Provider
export { URI }

View file

@ -0,0 +1,345 @@
import Fade from '@material-ui/core/Fade'
import Slide from '@material-ui/core/Slide'
import { makeStyles } from '@material-ui/core/styles'
import * as R from 'ramda'
import React, { useContext } from 'react'
import {
matchPath,
Route,
Redirect,
Switch,
useHistory,
useLocation
} from 'react-router-dom'
import AppContext from 'src/AppContext'
import AuthRegister from 'src/pages/AuthRegister'
import Blacklist from 'src/pages/Blacklist'
import Cashout from 'src/pages/Cashout'
import Commissions from 'src/pages/Commissions'
import ConfigMigration from 'src/pages/ConfigMigration'
import { Customers, CustomerProfile } from 'src/pages/Customers'
import Dashboard from 'src/pages/Dashboard'
import Funding from 'src/pages/Funding'
import Locales from 'src/pages/Locales'
import PromoCodes from 'src/pages/LoyaltyPanel/PromoCodes'
import MachineLogs from 'src/pages/MachineLogs'
import Machines from 'src/pages/Machines'
import CashCassettes from 'src/pages/Maintenance/CashCassettes'
import MachineStatus from 'src/pages/Maintenance/MachineStatus'
import Notifications from 'src/pages/Notifications/Notifications'
import CoinAtmRadar from 'src/pages/OperatorInfo/CoinATMRadar'
import ContactInfo from 'src/pages/OperatorInfo/ContactInfo'
import ReceiptPrinting from 'src/pages/OperatorInfo/ReceiptPrinting'
import TermsConditions from 'src/pages/OperatorInfo/TermsConditions'
import ServerLogs from 'src/pages/ServerLogs'
import Services from 'src/pages/Services/Services'
import Transactions from 'src/pages/Transactions/Transactions'
import Triggers from 'src/pages/Triggers'
import Wizard from 'src/pages/Wizard'
import { namespaces } from 'src/utils/config'
const useStyles = makeStyles({
wrapper: {
flex: 1,
display: 'flex',
flexDirection: 'column',
height: '100%'
}
})
const tree = [
{
key: 'transactions',
label: 'Transactions',
route: '/transactions',
component: Transactions
},
{
key: 'maintenance',
label: 'Maintenance',
route: '/maintenance',
get component() {
return () => <Redirect to={this.children[0].route} />
},
children: [
{
key: 'cash_cassettes',
label: 'Cash Cassettes',
route: '/maintenance/cash-cassettes',
component: CashCassettes
},
{
key: 'funding',
label: 'Funding',
route: '/maintenance/funding',
component: Funding
},
{
key: 'logs',
label: 'Machine Logs',
route: '/maintenance/logs',
component: MachineLogs
},
{
key: 'machine-status',
label: 'Machine Status',
route: '/maintenance/machine-status',
component: MachineStatus
},
{
key: 'server-logs',
label: 'Server',
route: '/maintenance/server-logs',
component: ServerLogs
}
]
},
{
key: 'settings',
label: 'Settings',
route: '/settings',
get component() {
return () => <Redirect to={this.children[0].route} />
},
children: [
{
key: namespaces.COMMISSIONS,
label: 'Commissions',
route: '/settings/commissions',
component: Commissions
},
{
key: namespaces.LOCALE,
label: 'Locales',
route: '/settings/locale',
component: Locales
},
{
key: namespaces.CASH_OUT,
label: 'Cash-out',
route: '/settings/cash-out',
component: Cashout
},
{
key: namespaces.NOTIFICATIONS,
label: 'Notifications',
route: '/settings/notifications',
component: Notifications
},
{
key: 'services',
label: '3rd party services',
route: '/settings/3rd-party-services',
component: Services
},
// {
// key: namespaces.WALLETS,
// label: 'Wallet',
// route: '/settings/wallet-settings',
// component: WalletSettings
// },
{
key: namespaces.OPERATOR_INFO,
label: 'Operator Info',
route: '/settings/operator-info',
title: 'Operator Information',
get component() {
return () => (
<Redirect
to={{
pathname: this.children[0].route,
state: { prev: this.state?.prev }
}}
/>
)
},
children: [
{
key: 'contact-info',
label: 'Contact information',
route: '/settings/operator-info/contact-info',
component: ContactInfo
},
{
key: 'receipt-printing',
label: 'Receipt',
route: '/settings/operator-info/receipt-printing',
component: ReceiptPrinting
},
{
key: 'coin-atm-radar',
label: 'Coin ATM Radar',
route: '/settings/operator-info/coin-atm-radar',
component: CoinAtmRadar
},
{
key: 'terms-conditions',
label: 'Terms & Conditions',
route: '/settings/operator-info/terms-conditions',
component: TermsConditions
}
]
}
]
},
{
key: 'compliance',
label: 'Compliance',
route: '/compliance',
get component() {
return () => <Redirect to={this.children[0].route} />
},
children: [
{
key: 'triggers',
label: 'Triggers',
route: '/compliance/triggers',
component: Triggers
},
{
key: 'customers',
label: 'Customers',
route: '/compliance/customers',
component: Customers
},
{
key: 'blacklist',
label: 'Blacklist',
route: '/compliance/blacklist',
component: Blacklist
},
{
key: 'promo-codes',
label: 'Promo Codes',
route: '/compliance/loyalty/codes',
component: PromoCodes
},
{
key: 'customer',
route: '/compliance/customer/:id',
component: CustomerProfile
}
]
}
// {
// key: 'system',
// label: 'System',
// route: '/system',
// get component() {
// return () => <Redirect to={this.children[0].route} />
// },
// children: [
// {
// key: 'token-management',
// label: 'Token Management',
// route: '/system/token-management',
// component: TokenManagement
// }
// ]
// }
]
const map = R.map(R.when(R.has('children'), R.prop('children')))
const mappedRoutes = R.compose(R.flatten, map)(tree)
const parentRoutes = R.filter(R.has('children'))(mappedRoutes).concat(
R.filter(R.has('children'))(tree)
)
const leafRoutes = R.compose(R.flatten, map)(mappedRoutes)
const flattened = R.concat(leafRoutes, parentRoutes)
const hasSidebar = route =>
R.any(r => r.route === route)(
R.compose(
R.flatten,
R.map(R.prop('children')),
R.filter(R.has('children'))
)(mappedRoutes)
)
const getParent = route =>
R.find(
R.propEq(
'route',
R.dropLast(
1,
R.dropLastWhile(x => x !== '/', route)
)
)
)(flattened)
const Routes = () => {
const classes = useStyles()
const history = useHistory()
const location = useLocation()
const { wizardTested } = useContext(AppContext)
const dontTriggerPages = ['/404', '/register', '/wizard']
if (!wizardTested && !R.contains(location.pathname)(dontTriggerPages)) {
history.push('/wizard')
}
const Transition = location.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)
? 'right'
: 'left'
}
: { timeout: 400 }
return (
<Switch>
<Route exact path="/">
<Redirect to={{ pathname: '/dashboard' }} />
</Route>
<Route path={'/dashboard'}>
<Transition
className={classes.wrapper}
{...transitionProps}
in={true}
mountOnEnter
unmountOnExit
children={
<div className={classes.wrapper}>
<Dashboard />
</div>
}
/>
</Route>
<Route path="/machines" component={Machines} />
<Route path="/wizard" component={Wizard} />
<Route path="/register" component={AuthRegister} />
<Route path="/configmigration" component={ConfigMigration} />
{flattened.map(({ route, component: Page, key }) => (
<Route path={route} key={key}>
<Transition
className={classes.wrapper}
{...transitionProps}
in={!!matchPath(location.pathname, { path: route })}
mountOnEnter
unmountOnExit
children={
<div className={classes.wrapper}>
<Page name={key} />
</div>
}
/>
</Route>
))}
<Route path="/404" />
<Route path="*">
<Redirect to={{ pathname: '/404' }} />
</Route>
</Switch>
)
}
export { tree, getParent, hasSidebar, Routes }