diff --git a/new-lamassu-admin/src/pazuz/App.js b/new-lamassu-admin/src/pazuz/App.js
new file mode 100644
index 00000000..e8eb77f3
--- /dev/null
+++ b/new-lamassu-admin/src/pazuz/App.js
@@ -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 (
+
+ {!is404 && wizardTested &&
}
+
+ {sidebar && !is404 && wizardTested && (
+
+ )}
+
+
+ {sidebar && !is404 && wizardTested && (
+ it.label}
+ onClick={onClick}
+ />
+ )}
+
+
+
+
+
+
+ )
+}
+
+const App = () => {
+ const [wizardTested, setWizardTested] = useState(false)
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default App
diff --git a/new-lamassu-admin/src/pazuz/apollo/Provider.js b/new-lamassu-admin/src/pazuz/apollo/Provider.js
new file mode 100644
index 00000000..1dfaa322
--- /dev/null
+++ b/new-lamassu-admin/src/pazuz/apollo/Provider.js
@@ -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 {children}
+}
+
+export default Provider
+export { URI }
diff --git a/new-lamassu-admin/src/pazuz/routing/routes.js b/new-lamassu-admin/src/pazuz/routing/routes.js
new file mode 100644
index 00000000..0f39326d
--- /dev/null
+++ b/new-lamassu-admin/src/pazuz/routing/routes.js
@@ -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 () =>
+ },
+ 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 () =>
+ },
+ 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 () => (
+
+ )
+ },
+ 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 () =>
+ },
+ 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 () =>
+ // },
+ // 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 (
+
+
+
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+ {flattened.map(({ route, component: Page, key }) => (
+
+
+
+
+ }
+ />
+
+ ))}
+
+
+
+
+
+ )
+}
+export { tree, getParent, hasSidebar, Routes }