diff --git a/lib/new-admin/graphql/schema.js b/lib/new-admin/graphql/schema.js
index d299dd1e..9aa3b63d 100644
--- a/lib/new-admin/graphql/schema.js
+++ b/lib/new-admin/graphql/schema.js
@@ -3,6 +3,7 @@ const { parseAsync } = require('json2csv')
const { GraphQLDateTime } = require('graphql-iso-date')
const { GraphQLJSON, GraphQLJSONObject } = require('graphql-type-json')
const got = require('got')
+const DataLoader = require('dataloader')
const machineLoader = require('../../machine-loader')
const customers = require('../../customers')
@@ -265,6 +266,8 @@ const typeDefs = gql`
}
`
+const transactionsLoader = new DataLoader(ids => transactions.getCustomerTransactionsBatch(ids))
+
const notify = () => got.post('http://localhost:3030/dbChange')
.catch(e => console.error('Error: lamassu-server not responding'))
@@ -273,7 +276,7 @@ const resolvers = {
JSONObject: GraphQLJSONObject,
Date: GraphQLDateTime,
Customer: {
- transactions: parent => transactions.getCustomerTransactions(parent.id)
+ transactions: parent => transactionsLoader.load(parent.id)
},
Query: {
countries: () => countries,
diff --git a/lib/new-admin/transactions.js b/lib/new-admin/transactions.js
index 657ab4ea..79285e06 100644
--- a/lib/new-admin/transactions.js
+++ b/lib/new-admin/transactions.js
@@ -1,4 +1,5 @@
const _ = require('lodash/fp')
+const pgp = require('pg-promise')()
const db = require('../db')
const machineLoader = require('../machine-loader')
@@ -65,9 +66,8 @@ function batch (from = new Date(0).toISOString(), until = new Date().toISOString
.then(packager)
}
-function getCustomerTransactions (customerId) {
+function getCustomerTransactionsBatch (ids) {
const packager = _.flow(it => {
- console.log()
return it
}, _.flatten, _.orderBy(_.property('created'), ['desc']), _.map(camelize), addNames)
@@ -82,7 +82,7 @@ function getCustomerTransactions (customerId) {
((not txs.send_confirmed) and (txs.created <= now() - interval $2)) as expired
from cash_in_txs as txs
left outer join customers c on txs.customer_id = c.id
- where c.id = $1
+ where c.id IN ($1^)
order by created desc limit $3`
const cashOutSql = `select 'cashOut' as tx_class,
@@ -100,14 +100,16 @@ function getCustomerTransactions (customerId) {
inner join cash_out_actions actions on txs.id = actions.tx_id
and actions.action = 'provisionAddress'
left outer join customers c on txs.customer_id = c.id
- where c.id = $1
+ where c.id IN ($1^)
order by created desc limit $2`
-
return Promise.all([
- db.any(cashInSql, [customerId, cashInTx.PENDING_INTERVAL, NUM_RESULTS]),
- db.any(cashOutSql, [customerId, NUM_RESULTS, REDEEMABLE_AGE])
+ db.any(cashInSql, [_.map(pgp.as.text, ids).join(','), cashInTx.PENDING_INTERVAL, NUM_RESULTS]),
+ db.any(cashOutSql, [_.map(pgp.as.text, ids).join(','), NUM_RESULTS, REDEEMABLE_AGE])
])
- .then(packager)
+ .then(packager).then(transactions => {
+ const transactionMap = _.groupBy('customerId', transactions)
+ return ids.map(id => transactionMap[id])
+ })
}
function single (txId) {
@@ -156,4 +158,4 @@ function cancel (txId) {
.then(() => single(txId))
}
-module.exports = { batch, getCustomerTransactions, single, cancel }
+module.exports = { batch, single, cancel, getCustomerTransactionsBatch }
diff --git a/new-lamassu-admin/src/App.js b/new-lamassu-admin/src/App.js
index 0ff35c15..66e2ba1a 100644
--- a/new-lamassu-admin/src/App.js
+++ b/new-lamassu-admin/src/App.js
@@ -1,4 +1,5 @@
import CssBaseline from '@material-ui/core/CssBaseline'
+import Grid from '@material-ui/core/Grid'
import {
StylesProvider,
jssPreset,
@@ -8,12 +9,18 @@ import {
import { create } from 'jss'
import extendJss from 'jss-plugin-extend'
import React, { createContext, useContext, useState } from 'react'
-import { useLocation, BrowserRouter as Router } from 'react-router-dom'
+import {
+ useLocation,
+ useHistory,
+ BrowserRouter as Router
+} from 'react-router-dom'
+import Sidebar from 'src/components/layout/Sidebar'
+import TitleSection from 'src/components/layout/TitleSection'
import ApolloProvider from 'src/utils/apollo'
import Header from './components/layout/Header'
-import { tree, Routes } from './routing/routes'
+import { tree, hasSidebar, Routes, getParent } from './routing/routes'
import global from './styling/global'
import theme from './styling/theme'
import { backgroundColor, mainWidth } from './styling/variables'
@@ -46,6 +53,18 @@ const useStyles = makeStyles({
flex: 1,
display: 'flex',
flexDirection
+ },
+ grid: {
+ flex: 1,
+ height: '100%'
+ },
+ contentWithSidebar: {
+ flex: 1,
+ marginLeft: 48,
+ paddingTop: 15
+ },
+ contentWithoutSidebar: {
+ width: mainWidth
}
})
@@ -54,15 +73,45 @@ const AppContext = createContext()
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}
+ />
+ )}
+
+
+
+
)
diff --git a/new-lamassu-admin/src/components/layout/Header.js b/new-lamassu-admin/src/components/layout/Header.js
index 97ae6995..d21acb46 100644
--- a/new-lamassu-admin/src/components/layout/Header.js
+++ b/new-lamassu-admin/src/components/layout/Header.js
@@ -3,9 +3,11 @@ import classnames from 'classnames'
import React, { memo, useState } from 'react'
import { NavLink, useHistory } from 'react-router-dom'
-import { Link } from 'src/components/buttons'
+import ActionButton from 'src/components/buttons/ActionButton'
import { H4 } from 'src/components/typography'
import AddMachine from 'src/pages/AddMachine'
+import { ReactComponent as AddIconReverse } from 'src/styling/icons/button/add/white.svg'
+import { ReactComponent as AddIcon } from 'src/styling/icons/button/add/zodiac.svg'
import { ReactComponent as Logo } from 'src/styling/icons/menu/logo.svg'
import styles from './Header.styles'
@@ -76,9 +78,13 @@ const Header = memo(({ tree }) => {
))}
- setOpen(true)}>
- Add Machine
-
+ setOpen(true)}>
+ Add machine
+
diff --git a/new-lamassu-admin/src/pages/Maintenance/MachineDetailsCard.js b/new-lamassu-admin/src/pages/Maintenance/MachineDetailsCard.js
index d04dc8fa..4abaea31 100644
--- a/new-lamassu-admin/src/pages/Maintenance/MachineDetailsCard.js
+++ b/new-lamassu-admin/src/pages/Maintenance/MachineDetailsCard.js
@@ -128,19 +128,19 @@ const MachineDetailsRow = ({ it: machine, onActionSuccess }) => {
/>
{
setErrorMessage(null)
machineAction({
variables: {
deviceId: machine.deviceId,
- action: `${action?.command}`.toLowerCase(),
- ...(action?.command === 'Rename' && { newName: value })
+ action: `${action?.command}`,
+ ...(action?.command === 'rename' && { newName: value })
}
})
}}
@@ -174,7 +174,8 @@ const MachineDetailsRow = ({ it: machine, onActionSuccess }) => {
InverseIcon={EditReversedIcon}
onClick={() =>
setAction({
- command: 'Rename',
+ command: 'rename',
+ display: 'Rename',
confirmationMessage: 'Write the new name for this machine'
})
}>
@@ -188,7 +189,8 @@ const MachineDetailsRow = ({ it: machine, onActionSuccess }) => {
disabled={loading}
onClick={() =>
setAction({
- command: 'Unpair'
+ command: 'unpair',
+ display: 'Unpair'
})
}>
Unpair
@@ -201,26 +203,42 @@ const MachineDetailsRow = ({ it: machine, onActionSuccess }) => {
disabled={loading}
onClick={() =>
setAction({
- command: 'Reboot'
+ command: 'reboot',
+ display: 'Reboot'
})
}>
Reboot
setAction({
- command: 'Shutdown',
+ command: 'shutdown',
+ display: 'Shutdown',
message:
'In order to bring it back online, the machine will need to be visited and its power reset.'
})
}>
Shutdown
+
+ setAction({
+ command: 'restartServices',
+ display: 'Restart services for'
+ })
+ }>
+ Restart Services
+
diff --git a/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.js b/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.js
deleted file mode 100644
index 794530d2..00000000
--- a/new-lamassu-admin/src/pages/OperatorInfo/OperatorInfo.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import { makeStyles } from '@material-ui/core'
-import Grid from '@material-ui/core/Grid'
-import React from 'react'
-import {
- Route,
- Switch,
- Redirect,
- useLocation,
- useHistory
-} from 'react-router-dom'
-
-import Sidebar from 'src/components/layout/Sidebar'
-import TitleSection from 'src/components/layout/TitleSection'
-
-import CoinAtmRadar from './CoinATMRadar'
-import ContactInfo from './ContactInfo'
-import ReceiptPrinting from './ReceiptPrinting'
-import TermsConditions from './TermsConditions'
-
-const styles = {
- grid: {
- flex: 1,
- height: '100%'
- },
- content: {
- flex: 1,
- marginLeft: 48,
- paddingTop: 15
- }
-}
-
-const useStyles = makeStyles(styles)
-
-const innerRoutes = [
- {
- label: 'Contact information',
- route: '/settings/operator-info/contact-info',
- component: ContactInfo
- },
- {
- label: 'Receipt',
- route: '/settings/operator-info/receipt-printing',
- component: ReceiptPrinting
- },
- {
- label: 'Coin ATM Radar',
- route: '/settings/operator-info/coin-atm-radar',
- component: CoinAtmRadar
- },
- {
- label: 'Terms & Conditions',
- route: '/settings/operator-info/terms-conditions',
- component: TermsConditions
- }
-]
-
-const Routes = ({ wizard }) => (
-
-
-
- {innerRoutes.map(({ route, component: Page, key }) => (
-
-
-
- ))}
-
-)
-
-const OperatorInfo = ({ wizard = false }) => {
- const classes = useStyles()
- const history = useHistory()
- const location = useLocation()
-
- const isSelected = it => location.pathname === it.route
-
- const onClick = it => history.push(it.route)
-
- return (
- <>
-
-
- it.label}
- onClick={onClick}
- />
-
-
-
-
- >
- )
-}
-
-export default OperatorInfo
diff --git a/new-lamassu-admin/src/routing/routes.js b/new-lamassu-admin/src/routing/routes.js
index 404ade02..de280ba2 100644
--- a/new-lamassu-admin/src/routing/routes.js
+++ b/new-lamassu-admin/src/routing/routes.js
@@ -20,7 +20,10 @@ import MachineLogs from 'src/pages/MachineLogs'
import CashCassettes from 'src/pages/Maintenance/CashCassettes'
import MachineStatus from 'src/pages/Maintenance/MachineStatus'
import Notifications from 'src/pages/Notifications/Notifications'
-import OperatorInfo from 'src/pages/OperatorInfo/OperatorInfo'
+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 TokenManagement from 'src/pages/TokenManagement/TokenManagement'
@@ -125,7 +128,36 @@ const tree = [
key: namespaces.OPERATOR_INFO,
label: 'Operator Info',
route: '/settings/operator-info',
- component: OperatorInfo
+ 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
+ }
+ ]
}
]
},
@@ -181,10 +213,34 @@ const tree = [
]
const map = R.map(R.when(R.has('children'), R.prop('children')))
-const leafRoutes = R.compose(R.flatten, map)(tree)
-const parentRoutes = R.filter(R.has('children'))(tree)
+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 history = useHistory()
const location = useLocation()
@@ -215,4 +271,4 @@ const Routes = () => {
)
}
-export { tree, Routes }
+export { tree, getParent, hasSidebar, Routes }
diff --git a/package-lock.json b/package-lock.json
index cdc4c723..e14735c2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6131,6 +6131,25 @@
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
"integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dasherize": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz",
+ "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg="
+ },
+ "data-uri-to-buffer": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz",
+ "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ=="
+ },
+ "dataloader": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz",
+ "integrity": "sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ=="
+ },
+ "date-fns": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz",
+ "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==",
"dev": true
},
"expand-brackets": {
diff --git a/package.json b/package.json
index b2b7b9a5..9d79f3d3 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"console-log-level": "^1.4.0",
"cookie-parser": "^1.4.3",
"cors": "^2.8.5",
+ "dataloader": "^2.0.0",
"ethereumjs-tx": "^1.3.3",
"ethereumjs-util": "^5.2.0",
"ethereumjs-wallet": "^0.6.3",