From 007c51472c8fc279ef76515284afb8f15f0c51f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 3 Mar 2021 17:05:49 +0000 Subject: [PATCH] feat: initial Pazuz react setup --- new-lamassu-admin/src/pazuz/App.js | 137 +++++++ .../src/pazuz/apollo/Provider.js | 57 +++ new-lamassu-admin/src/pazuz/routing/routes.js | 345 ++++++++++++++++++ 3 files changed, 539 insertions(+) create mode 100644 new-lamassu-admin/src/pazuz/App.js create mode 100644 new-lamassu-admin/src/pazuz/apollo/Provider.js create mode 100644 new-lamassu-admin/src/pazuz/routing/routes.js 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 }