feat: initial Pazuz react setup
This commit is contained in:
parent
dd0abc8a8b
commit
007c51472c
3 changed files with 539 additions and 0 deletions
137
new-lamassu-admin/src/pazuz/App.js
Normal file
137
new-lamassu-admin/src/pazuz/App.js
Normal 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
|
||||||
57
new-lamassu-admin/src/pazuz/apollo/Provider.js
Normal file
57
new-lamassu-admin/src/pazuz/apollo/Provider.js
Normal 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 }
|
||||||
345
new-lamassu-admin/src/pazuz/routing/routes.js
Normal file
345
new-lamassu-admin/src/pazuz/routing/routes.js
Normal 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 }
|
||||||
Loading…
Add table
Add a link
Reference in a new issue