Chore: post-rebase fixes

This commit is contained in:
csrapr 2021-02-05 00:05:50 +00:00 committed by Josh Harvey
parent c0106592eb
commit ef60b15d82
29 changed files with 224 additions and 470 deletions

View file

@ -248,12 +248,6 @@ const typeDefs = gql`
rate: Float rate: Float
} }
type Rate {
code: String
name: String
rate: Float
}
type Notification { type Notification {
id: ID! id: ID!
type: String type: String
@ -297,6 +291,7 @@ const typeDefs = gql`
cryptoRates: JSONObject cryptoRates: JSONObject
fiatRates: [Rate] fiatRates: [Rate]
notifications: [Notification] notifications: [Notification]
alerts: [Notification]
hasUnreadNotifications: Boolean hasUnreadNotifications: Boolean
} }
@ -396,7 +391,8 @@ const resolvers = {
}), }),
fiatRates: () => forex.getFiatRates(), fiatRates: () => forex.getFiatRates(),
notifications: () => notifierQueries.getNotifications(), notifications: () => notifierQueries.getNotifications(),
hasUnreadNotifications: () => notifierQueries.hasUnreadNotifications() hasUnreadNotifications: () => notifierQueries.hasUnreadNotifications(),
alerts: () => notifierQueries.getAlerts()
}, },
Mutation: { Mutation: {
machineAction: (...[, { deviceId, action, cashbox, cassette1, cassette2, newName }]) => machineAction({ deviceId, action, cashbox, cassette1, cassette2, newName }), machineAction: (...[, { deviceId, action, cashbox, cassette1, cassette2, newName }]) => machineAction({ deviceId, action, cashbox, cassette1, cassette2, newName }),

View file

@ -356,207 +356,6 @@ const customerComplianceNotify = (customer, deviceId, code, days = null) => {
.catch(console.error) .catch(console.error)
} }
const clearOldCryptoNotifications = (balances) => {
// get valid crypto notifications from DB
// first, for each DB notification, if it doesn't exist in balances then it is old and should not be valid anymore
// if it exists in balances, add the index of it in balances to the array of duplicates
// return the array of duplicates so that balancesNotify doesn't add them
return queries.getAllValidNotifications(CRYPTO_BALANCE).then(res => {
const notifications = _.map(it => {
return {
id: it.id,
cryptoCode: it.detail.cryptoCode,
code: it.detail.code
}
}, res)
const duplicateIndexes = []
const idsToInvalidate = []
_.forEach(notification => {
const idx = _.findIndex(balance => {
return balance.code === notification.code && balance.cryptoCode === notification.cryptoCode
}, balances)
if (idx === -1) {
// if notification in DB doesnt exist in balances anymore then it is invalid now
idsToInvalidate.push(notification.id)
}
else {
// if it exists then it is a duplicate, add it to array
duplicateIndexes.push(idx)
}
}, notifications)
return (idsToInvalidate.length > 0 ? queries.batchInvalidate(idsToInvalidate) : Promise.resolve()).then(() => duplicateIndexes)
})
}
const cryptoBalancesNotify = (cryptoWarnings) => {
return clearOldCryptoNotifications(cryptoWarnings).then(duplicateIndexes => {
return cryptoWarnings.forEach((balance, idx) => {
if(duplicateIndexes.includes(idx)) {
return
}
const fiat = utils.formatCurrency(balance.fiatBalance.balance, balance.fiatCode)
const message = `${balance.code === 'HIGH_CRYPTO_BALANCE' ? 'High' : 'Low'} balance in ${balance.cryptoCode} [${fiat}]`
console.log(`Adding ${balance.code === 'HIGH_CRYPTO_BALANCE' ? 'high' : 'low'} balance notification for ${balance.cryptoCode}`)
const detailB = utils.buildDetail({cryptoCode: balance.cryptoCode, code: balance.code})
return queries.addNotification(CRYPTO_BALANCE, message, detailB)
})
})
}
const clearOldFiatNotifications = (balances) => {
return queries.getAllValidNotifications(FIAT_BALANCE).then(notifications => {
const duplicateIndexes = []
const idsToInvalidate = []
_.forEach(notification => {
const idx = _.findIndex(balance => {
return notification.detail.deviceId === balance.deviceId && notification.detail.cassette === balance.cassette
}, balances)
if (idx === -1) {
// if notification in DB doesnt exist in balances anymore then it is invalid now
idsToInvalidate.push(notification.id)
}
else {
// if it exists then it is a duplicate, add it to array
duplicateIndexes.push(idx)
}
}, notifications)
return (idsToInvalidate.length > 0 ? queries.batchInvalidate(idsToInvalidate) : Promise.resolve()).then(() => duplicateIndexes)
})
}
const fiatBalancesNotify = (fiatWarnings) => {
return clearOldFiatNotifications(fiatWarnings).then(duplicateIndexes => {
return fiatWarnings.forEach((balance, idx) => {
if(duplicateIndexes.includes(idx)) {
return
}
console.log(`Adding low cash balance notification for cassette ${balance.cassette} at ${balance.machineName}`)
const message = `Cash-out cassette ${balance.cassette} almost empty!`
const detailB = utils.buildDetail({deviceId: balance.deviceId, cassette: balance.cassette})
return queries.addNotification(FIAT_BALANCE, message, detailB)
})
})
}
const balancesNotify = (balances) => {
const cryptoFilter = o => o.code === 'HIGH_CRYPTO_BALANCE' || o.code === 'LOW_CRYPTO_BALANCE'
const fiatFilter = o => o.code === 'LOW_CASH_OUT'
const cryptoWarnings = _.filter(cryptoFilter, balances)
const fiatWarnings = _.filter(fiatFilter, balances)
return Promise.all([cryptoBalancesNotify(cryptoWarnings), fiatBalancesNotify(fiatWarnings)]).catch(console.error)
}
const clearOldErrorNotifications = (alerts) => {
return queries.getAllValidNotifications(ERROR).then(res => {
const indexesToInvalidate = []
_.forEach(notification => {
const idx = _.findIndex(alert => {
return alert.code === notification.detail.code && alert.deviceId === notification.detail.deviceId
}, alerts)
if(idx !== -1) {
return
}
// if the notification doesn't exist, then it is outdated and is not valid anymore
indexesToInvalidate.push(notification.id)
}, res)
return indexesToInvalidate.length > 0 ? queries.batchInvalidate(indexesToInvalidate) : null
}).catch(console.log)
}
const errorAlertsNotify = (alertRec) => {
let alerts = []
_.keys(alertRec.devices).forEach(function (device) {
// embed device ID in each alert object inside the deviceAlerts array
alertRec.devices[device].deviceAlerts = _.map(alert => {
return {...alert, deviceId: device}
}, alertRec.devices[device].deviceAlerts)
// concat every array into one
alerts = _.concat(alerts, alertRec.devices[device].deviceAlerts)
})
// now that we have all the alerts, we want to add PING and STALE alerts to the DB
// if there is a valid alert on the DB that doesn't exist on the new alerts array,
// that alert should be considered invalid
// after that, for the alerts array, we have to see if there is a valid alert of
// the sorts already on the DB
return clearOldErrorNotifications(alerts).then(() => {
_.forEach(alert => {
switch(alert.code) {
case PING: {
const detailB = utils.buildDetail({code: PING, age: alert.age ? alert.age : -1, deviceId: alert.deviceId})
return queries.getValidNotifications(ERROR, _.omit(['age'], detailB)).then(res => {
if(res.length > 0) {
return Promise.resolve()
}
console.log("Adding PING alert on database for " + alert.machineName)
const message = `Machine down`
return queries.addNotification(ERROR, message, detailB)
})
}
case STALE: {
const detailB = utils.buildDetail({code: STALE, deviceId: alert.deviceId})
return queries.getValidNotifications(ERROR, detailB).then(res => {
if(res.length > 0) {
return Promise.resolve()
}
console.log("Adding STALE alert on database for " + alert.machineName)
const message = `Machine is stuck on ${alert.state} screen`
return queries.addNotification(ERROR, message, detailB)
})
}
default:
return
}
}, alerts)
}).catch(console.error)
}
const blacklistNotify = (tx, isAddressReuse) => {
let message = ''
let detailB = {}
if(isAddressReuse) {
detail = `${tx.cryptoCode}_REUSED_${tx.toAddress}`
detailB = utils.buildDetail({cryptoCode: tx.cryptoCode, code: 'REUSED', cryptoAddress: tx.toAddress})
message = `Blocked reused address: ${tx.cryptoCode} ${tx.toAddress.substr(0,10)}...`
} else {
detail = `${tx.cryptoCode}_BLOCKED_${tx.toAddress}`
detailB = utils.buildDetail({cryptoCode: tx.cryptoCode, code: 'BLOCKED', cryptoAddress: tx.toAddress})
message = `Blocked blacklisted address: ${tx.cryptoCode} ${tx.toAddress.substr(0,10)}...`
}
return queries.addNotification(COMPLIANCE, message, detailB)
}
const clearBlacklistNotification = (cryptoCode, cryptoAddress) => {
return queries.clearBlacklistNotification(cryptoCode, cryptoAddress).catch(console.error)
}
const clearOldCustomerSuspendedNotifications = (customerId, deviceId) => {
const detailB = utils.buildDetail({code: 'SUSPENDED', customerId, deviceId})
return queries.invalidateNotification(detailB, 'compliance')
}
const customerComplianceNotify = (customer, deviceId, code, days = null) => {
// code for now can be "BLOCKED", "SUSPENDED"
const detailB = utils.buildDetail({customerId: customer.id, code, deviceId})
const date = new Date()
if (days) {
date.setDate(date.getDate() + days)
}
const message = code === "SUSPENDED" ? `Customer suspended until ${date.toLocaleString()}` : `Customer blocked`
return clearOldCustomerSuspendedNotifications(customer.id, deviceId).then(() => {
return queries.getValidNotifications(COMPLIANCE, detailB)
}).then(res => {
if (res.length > 0) {
return Promise.resolve()
}
return queries.addNotification(COMPLIANCE, message, detailB)
}).catch(console.error)
}
module.exports = { module.exports = {
transactionNotify, transactionNotify,
checkNotification, checkNotification,

View file

@ -46,27 +46,27 @@ const getValidNotifications = (type, detail) => {
return db.any(sql, [type, detail]) return db.any(sql, [type, detail])
} }
const getNotificationsGql = () => { const getNotifications = () => {
const sql = `SELECT * FROM notifications ORDER BY created DESC` const sql = `SELECT * FROM notifications ORDER BY created DESC`
return db.any(sql) return db.any(sql)
} }
const markAsReadGql = (id) => { const markAsRead = (id) => {
const sql = `UPDATE notifications SET read = 't' WHERE id = $1` const sql = `UPDATE notifications SET read = 't' WHERE id = $1`
return db.none(sql, [id]) return db.none(sql, [id])
} }
const markAllAsReadGql = () => { const markAllAsRead = () => {
const sql = `UPDATE notifications SET read = 't'` const sql = `UPDATE notifications SET read = 't'`
return db.none(sql) return db.none(sql)
} }
const hasUnreadNotificationsGql = () => { const hasUnreadNotifications = () => {
const sql = `SELECT EXISTS (SELECT 1 FROM notifications WHERE read = 'f' LIMIT 1)` const sql = `SELECT EXISTS (SELECT 1 FROM notifications WHERE read = 'f' LIMIT 1)`
return db.oneOrNone(sql).then(res => res.exists) return db.oneOrNone(sql).then(res => res.exists)
} }
const getAlertsGql = () => { const getAlerts = () => {
const types = ['fiatBalance', 'cryptoBalance', 'error'] const types = ['fiatBalance', 'cryptoBalance', 'error']
const sql = `SELECT * FROM notifications WHERE valid = 't' AND type IN ($1:list) ORDER BY created DESC` const sql = `SELECT * FROM notifications WHERE valid = 't' AND type IN ($1:list) ORDER BY created DESC`
return db.any(sql, [types]) return db.any(sql, [types])
@ -80,9 +80,9 @@ module.exports = {
batchInvalidate, batchInvalidate,
clearBlacklistNotification, clearBlacklistNotification,
getValidNotifications, getValidNotifications,
getNotificationsGql, getNotifications,
markAsReadGql, markAsRead,
markAllAsReadGql, markAllAsRead,
hasUnreadNotificationsGql, hasUnreadNotifications,
getAlertsGql getAlerts
} }

View file

@ -29,11 +29,6 @@ function printSmsAlerts (alertRec, config) {
_.map('cryptoCode', entry[1]), _.map('cryptoCode', entry[1]),
) )
const cryptoCodes = _.filter(
_.negate(_.isEmpty),
_.map('cryptoCode', entry[1])
)
return { return {
codeDisplay: utils.codeDisplay(code), codeDisplay: utils.codeDisplay(code),
machineNames, machineNames,

View file

@ -25,8 +25,6 @@ const promoCodes = require('./promo-codes')
const notifier = require('./notifier') const notifier = require('./notifier')
const notifier = require('./notifier/index')
const mapValuesWithKey = _.mapValues.convert({ const mapValuesWithKey = _.mapValues.convert({
cap: false cap: false
}) })
@ -164,41 +162,30 @@ function plugins (settings, deviceId) {
? argv.cassettes.split(',') ? argv.cassettes.split(',')
: rec.counts : rec.counts
return Promise.all([ const cassettes = [
dbm.cassetteCounts(deviceId), {
cashOutHelper.redeemableTxs(deviceId, excludeTxId) denomination: parseInt(denominations[0], 10),
]).then(([rec, _redeemableTxs]) => { count: parseInt(counts[0], 10)
const redeemableTxs = _.reject( },
_.matchesProperty('id', excludeTxId), {
_redeemableTxs denomination: parseInt(denominations[1], 10),
) count: parseInt(counts[1], 10)
}
]
const counts = argv.cassettes ? argv.cassettes.split(',') : rec.counts try {
return {
const cassettes = [ cassettes: computeAvailableCassettes(cassettes, redeemableTxs),
{ virtualCassettes
denomination: parseInt(denominations[0], 10), }
count: parseInt(counts[0], 10) } catch (err) {
}, logger.error(err)
{ return {
denomination: parseInt(denominations[1], 10), cassettes,
count: parseInt(counts[1], 10) virtualCassettes
}
} }
] })
try {
return {
cassettes: computeAvailableCassettes(cassettes, redeemableTxs),
virtualCassettes
}
} catch (err) {
logger.error(err)
return {
cassettes,
virtualCassettes
}
}
})
} }
function fetchCurrentConfigVersion () { function fetchCurrentConfigVersion () {

View file

@ -3,6 +3,7 @@ import { makeStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState } from 'react' import React, { useState } from 'react'
import ActionButton from 'src/components/buttons/ActionButton' import ActionButton from 'src/components/buttons/ActionButton'
import { H5 } from 'src/components/typography' import { H5 } from 'src/components/typography'
import { ReactComponent as NotificationIconZodiac } from 'src/styling/icons/menu/notification-zodiac.svg' import { ReactComponent as NotificationIconZodiac } from 'src/styling/icons/menu/notification-zodiac.svg'

View file

@ -3,6 +3,7 @@ import { makeStyles } from '@material-ui/core/styles'
import classnames from 'classnames' import classnames from 'classnames'
import prettyMs from 'pretty-ms' import prettyMs from 'pretty-ms'
import React from 'react' import React from 'react'
import { Label1, Label2, TL2 } from 'src/components/typography' import { Label1, Label2, TL2 } from 'src/components/typography'
import { ReactComponent as Wrench } from 'src/styling/icons/action/wrench/zodiac.svg' import { ReactComponent as Wrench } from 'src/styling/icons/action/wrench/zodiac.svg'
import { ReactComponent as Transaction } from 'src/styling/icons/arrow/transaction.svg' import { ReactComponent as Transaction } from 'src/styling/icons/arrow/transaction.svg'

View file

@ -6,6 +6,7 @@ import classnames from 'classnames'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import React, { memo, useState } from 'react' import React, { memo, useState } from 'react'
import { NavLink, useHistory } from 'react-router-dom' import { NavLink, useHistory } from 'react-router-dom'
import NotificationCenter from 'src/components/NotificationCenter' import NotificationCenter from 'src/components/NotificationCenter'
import ActionButton from 'src/components/buttons/ActionButton' import ActionButton from 'src/components/buttons/ActionButton'
import { H4 } from 'src/components/typography' import { H4 } from 'src/components/typography'

View file

@ -4,7 +4,9 @@ import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState, useEffect } from 'react' import React from 'react'
import { cardState as cardState_ } from 'src/components/CollapsibleCard'
import { Label1, H4 } from 'src/components/typography' import { Label1, H4 } from 'src/components/typography'
import styles from '../Dashboard.styles' import styles from '../Dashboard.styles'
@ -33,98 +35,58 @@ const GET_ALERTS = gql`
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const Alerts = ({ cardState, setRightSideState }) => { const Alerts = ({ onReset, onExpand, size }) => {
const classes = useStyles() const classes = useStyles()
const [showAllItems, setShowAllItems] = useState(false) const showAllItems = size === cardState_.EXPANDED
const { data } = useQuery(GET_ALERTS) const { data } = useQuery(GET_ALERTS)
const alerts = R.path(['alerts'])(data) ?? [] const alerts = R.path(['alerts'])(data) ?? []
const machines = R.compose( const machines = R.compose(
R.map(R.prop('name')), R.map(R.prop('name')),
R.indexBy(R.prop('deviceId')) R.indexBy(R.prop('deviceId'))
)(data?.machines ?? []) )(data?.machines ?? [])
const alertsLength = alerts.length
const showExpandButton = alerts.length > NUM_TO_RENDER && !showAllItems
useEffect(() => {
if (cardState.cardSize === 'small' || cardState.cardSize === 'default') {
setShowAllItems(false)
}
}, [cardState.cardSize])
const reset = () => {
setRightSideState({
systemStatus: { cardSize: 'default', buttonName: 'Show less' },
alerts: { cardSize: 'default', buttonName: 'Show less' }
})
setShowAllItems(false)
}
const showAllClick = () => {
setShowAllItems(true)
setRightSideState({
systemStatus: { cardSize: 'small', buttonName: 'Show machines' },
alerts: { cardSize: 'big', buttonName: 'Show less' }
})
}
return ( return (
<> <>
<div <div className={classes.container}>
style={{ <H4 className={classes.h4}>{`Alerts (${alertsLength})`}</H4>
display: 'flex', {showAllItems && (
justifyContent: 'space-between' <Label1 className={classes.upperButtonLabel}>
}}> <Button
<H4 className={classes.h4}>{`Alerts ${ onClick={() => {
data ? `(${alerts.length})` : 0 console.log('aaaa')
}`}</H4> onReset()
{(showAllItems || cardState.cardSize === 'small') && ( }}
<> size="small"
<Label1 disableRipple
style={{ disableFocusRipple
textAlign: 'center', className={classes.button}>
marginBottom: 0, {'Show less'}
marginTop: 0 </Button>
}}> </Label1>
)}
</div>
<Grid container spacing={1}>
<Grid item xs={12} className={classes.alertsTableMargin}>
<AlertsTable
numToRender={showAllItems ? alerts.length : NUM_TO_RENDER}
alerts={alerts}
machines={machines}
/>
{!showAllItems && alertsLength > NUM_TO_RENDER && (
<Label1 className={classes.centerLabel}>
<Button <Button
onClick={reset} onClick={() => onExpand('alerts')}
size="small" size="small"
disableRipple disableRipple
disableFocusRipple disableFocusRipple
className={classes.button}> className={classes.button}>
{cardState.buttonName} {`Show all (${alerts.length})`}
</Button> </Button>
</Label1> </Label1>
</> )}
)} </Grid>
</div> </Grid>
{cardState.cardSize !== 'small' && (
<>
<Grid container spacing={1}>
<Grid item xs={12}>
<AlertsTable
numToRender={showAllItems ? alerts.length : NUM_TO_RENDER}
alerts={alerts}
machines={machines}
/>
{showExpandButton && (
<>
<Label1 style={{ textAlign: 'center', marginBottom: 0 }}>
<Button
onClick={showAllClick}
size="small"
disableRipple
disableFocusRipple
className={classes.button}>
{`Show all (${alerts.length})`}
</Button>
</Label1>
</>
)}
</Grid>
</Grid>
</>
)}
</> </>
) )
} }

View file

@ -45,27 +45,10 @@ const styles = {
statusHeader: { statusHeader: {
marginLeft: 2 marginLeft: 2
}, },
/* table: {
maxHeight: 440,
'&::-webkit-scrollbar': {
width: 7
},
'&::-webkit-scrollbar-thumb': {
backgroundColor: offColor,
borderRadius: 5
}
}, */
table: { table: {
paddingTop: spacer * 4, marginTop: spacer * 4,
maxHeight: 465, maxHeight: 465,
overflow: 'auto', overflow: 'auto'
'&::-webkit-scrollbar': {
width: 7
},
'&::-webkit-scrollbar-thumb': {
backgroundColor: offColor,
borderRadius: 5
}
}, },
tableBody: { tableBody: {
overflow: 'auto' overflow: 'auto'
@ -78,11 +61,6 @@ const styles = {
marginBottom: 0, marginBottom: 0,
marginTop: 0 marginTop: 0
}, },
root: {
'&:nth-of-type(odd)': {
backgroundColor: backgroundColor
}
},
listItemText: { listItemText: {
margin: '8px 0 8px 0' margin: '8px 0 8px 0'
}, },

View file

@ -1,8 +1,10 @@
import { withStyles, makeStyles } from '@material-ui/core' import { makeStyles } from '@material-ui/core'
import List from '@material-ui/core/List' import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem' import ListItem from '@material-ui/core/ListItem'
import * as R from 'ramda'
import React from 'react' import React from 'react'
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
import { P } from 'src/components/typography/index' import { P } from 'src/components/typography/index'
import { ReactComponent as Wrench } from 'src/styling/icons/action/wrench/zodiac.svg' import { ReactComponent as Wrench } from 'src/styling/icons/action/wrench/zodiac.svg'
import { ReactComponent as LinkIcon } from 'src/styling/icons/button/link/zodiac.svg' import { ReactComponent as LinkIcon } from 'src/styling/icons/button/link/zodiac.svg'
@ -12,12 +14,6 @@ import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/to
import styles from './Alerts.styles' import styles from './Alerts.styles'
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const StyledListItem = withStyles(() => ({
root: {
...styles.root
}
}))(ListItem)
const icons = { const icons = {
error: <WarningIcon style={{ height: 20, width: 20, marginRight: 12 }} />, error: <WarningIcon style={{ height: 20, width: 20, marginRight: 12 }} />,
fiatBalance: ( fiatBalance: (
@ -34,25 +30,23 @@ const links = {
const AlertsTable = ({ numToRender, alerts, machines }) => { const AlertsTable = ({ numToRender, alerts, machines }) => {
const history = useHistory() const history = useHistory()
const classes = useStyles() const classes = useStyles()
const alertsToRender = R.slice(0, numToRender, alerts)
return ( return (
<List dense className={classes.table}> <List dense className={classes.table}>
{alerts.map((alert, idx) => { {alertsToRender.map((alert, idx) => {
if (idx < numToRender) { return (
return ( <ListItem key={idx}>
<StyledListItem key={idx}> {icons[alert.type] || (
{icons[alert.type] || ( <Wrench style={{ height: 23, width: 23, marginRight: 8 }} />
<Wrench style={{ height: 23, width: 23, marginRight: 8 }} /> )}
)} <P className={classes.listItemText}>{`${alert.message}${alert.detail
<P className={classes.listItemText}>{`${alert.message}${alert .deviceId && ' - ' + machines[alert.detail.deviceId]}`}</P>
.detail.deviceId && <LinkIcon
' - ' + machines[alert.detail.deviceId]}`}</P> className={classes.linkIcon}
<LinkIcon onClick={() => history.push(links[alert.type] || '/dashboard')}
className={classes.linkIcon} />
onClick={() => history.push(links[alert.type] || '/dashboard')} </ListItem>
/> )
</StyledListItem>
)
} else return null
})} })}
</List> </List>
) )

View file

@ -2,6 +2,7 @@ import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import classnames from 'classnames' import classnames from 'classnames'
import React from 'react' import React from 'react'
import TitleSection from 'src/components/layout/TitleSection' import TitleSection from 'src/components/layout/TitleSection'
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg' import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg' import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'

View file

@ -52,11 +52,17 @@ const styles = {
upperButtonLabel: { upperButtonLabel: {
textAlign: 'center', textAlign: 'center',
marginBottom: 0, marginBottom: 0,
marginTop: 16, marginTop: 0,
marginLeft: spacer marginLeft: spacer
}, },
alertsCard: { alertsCard: {
marginBottom: 16 marginBottom: 16
},
h4: {
marginTop: 0
},
alertsTableMargin: {
marginTop: -30
} }
} }

View file

@ -6,6 +6,7 @@ import classnames from 'classnames'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState } from 'react' import React, { useState } from 'react'
import { Label2 } from 'src/components/typography' import { Label2 } from 'src/components/typography'
import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg' import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'
import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg' import { ReactComponent as TxOutIcon } from 'src/styling/icons/direction/cash-out.svg'

View file

@ -2,10 +2,11 @@ import Button from '@material-ui/core/Button'
import Grid from '@material-ui/core/Grid' import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import React, { useState } from 'react' import React, { useState } from 'react'
import CollapsibleCard, { cardState } from 'src/components/CollapsibleCard' import CollapsibleCard, { cardState } from 'src/components/CollapsibleCard'
import { H4, Label1 } from 'src/components/typography' import { H4, Label1 } from 'src/components/typography'
// import Alerts from './Alerts' import Alerts from './Alerts'
import styles from './Dashboard.styles' import styles from './Dashboard.styles'
import SystemStatus from './SystemStatus' import SystemStatus from './SystemStatus'
@ -33,35 +34,35 @@ const ShrunkCard = ({ title, buttonName, onUnshrink }) => {
} }
const RightSide = () => { const RightSide = () => {
// const classes = useStyles() const classes = useStyles()
const [systemStatusSize, setSystemStatusSize] = useState(cardState.DEFAULT) const [systemStatusSize, setSystemStatusSize] = useState(cardState.DEFAULT)
// const [alertsSize, setAlertsSize] = useState(cardState.DEFAULT) const [alertsSize, setAlertsSize] = useState(cardState.DEFAULT)
const onReset = () => { const onReset = () => {
// setAlertsSize(cardState.DEFAULT) setAlertsSize(cardState.DEFAULT)
setSystemStatusSize(cardState.DEFAULT) setSystemStatusSize(cardState.DEFAULT)
} }
return ( return (
<Grid item xs={6}> <Grid item xs={6}>
{/* <CollapsibleCard <CollapsibleCard
className={classes.alertsCard} className={classes.alertsCard}
state={alertsSize} state={alertsSize}
shrunkComponent={ shrunkComponent={
<ShrunkCard <ShrunkCard
title={'Alerts'} title={'Alerts'}
buttonName={'Show alerts'} buttonName={'Show alerts'}
onUnshrink={onReset} onUnshrink={onReset}
/>
}>
<Alerts
onExpand={() => {
setAlertsSize(cardState.EXPANDED)
setSystemStatusSize(cardState.SHRUNK)
}}
onReset={onReset}
size={alertsSize}
/> />
</CollapsibleCard> */} }>
<Alerts
onExpand={() => {
setAlertsSize(cardState.EXPANDED)
setSystemStatusSize(cardState.SHRUNK)
}}
onReset={onReset}
size={alertsSize}
/>
</CollapsibleCard>
<CollapsibleCard <CollapsibleCard
state={systemStatusSize} state={systemStatusSize}
shrunkComponent={ shrunkComponent={
@ -74,7 +75,7 @@ const RightSide = () => {
<SystemStatus <SystemStatus
onExpand={() => { onExpand={() => {
setSystemStatusSize(cardState.EXPANDED) setSystemStatusSize(cardState.EXPANDED)
// setAlertsSize(cardState.SHRUNK) setAlertsSize(cardState.SHRUNK)
}} }}
onReset={onReset} onReset={onReset}
size={systemStatusSize} size={systemStatusSize}

View file

@ -1,9 +1,9 @@
import { makeStyles } from '@material-ui/core' import { makeStyles } from '@material-ui/core'
import classnames from 'classnames' import classnames from 'classnames'
import React from 'react' import React from 'react'
import { ReactComponent as CashIn } from 'src/styling/icons/direction/cash-in.svg'
import { ReactComponent as CashOut } from 'src/styling/icons/direction/cash-out.svg' import { Label1 } from 'src/components/typography/index'
import { fontSize3, fontSecondary, fontColor } from 'src/styling/variables' import { java, neon, white } from 'src/styling/variables'
const styles = { const styles = {
wrapper: { wrapper: {
@ -20,47 +20,52 @@ const styles = {
whiteSpace: 'pre' whiteSpace: 'pre'
}, },
label: { label: {
fontSize: fontSize3, color: white
fontFamily: fontSecondary,
fontWeight: 700,
color: fontColor
}, },
cashIn: ({ value }) => ({ inColor: {
width: `${value}%`, backgroundColor: java
marginRight: 4 },
}), outColor: {
cashOut: ({ value }) => ({ backgroundColor: neon
width: `${100 - value}%` },
}) other: {
minWidth: '6px',
borderRadius: 2
}
} }
const useStyles = makeStyles(styles) const useStyles = makeStyles(styles)
const PercentageChart = ({ cashIn, cashOut }) => { const PercentageChart = ({ cashIn, cashOut }) => {
const classes = useStyles()
const value = cashIn || cashOut !== 0 ? cashIn : 50 const value = cashIn || cashOut !== 0 ? cashIn : 50
const classes = useStyles({ value })
const buildPercentageView = (value, direction) => { const buildPercentageView = value => {
const Operation = direction === 'cashIn' ? CashIn : CashOut if (value > 15) {
if (value > 25) { return <Label1 className={classes.label}>{` ${value}%`}</Label1>
return (
<>
<Operation />
{value > 25 && <span className={classes.label}>{` ${value}%`}</span>}
</>
)
}
if (value >= 10) {
return <Operation />
} }
} }
return ( return (
<div className={classes.wrapper}> <div className={classes.wrapper}>
<div className={classnames(classes.percentageBox, classes.cashIn)}> <div
className={classnames(
classes.percentageBox,
classes.inColor,
// if value between [1, 4], percentage box should not go below 6 px and border radius is 2px
// if value is 0 or 100, then width will be allowed to be 0px in one of the boxes, making it disappear
value < 5 && value > 0 ? classes.other : null
)}
style={{ width: `${value}%`, marginRight: 4 }}>
{buildPercentageView(value, 'cashIn')} {buildPercentageView(value, 'cashIn')}
</div> </div>
<div className={classnames(classes.percentageBox, classes.cashOut)}> <div
className={classnames(
classes.percentageBox,
classes.outColor,
100 - value < 5 && 100 - value > 0 ? classes.other : null
)}
style={{ width: `${100 - value}%` }}>
{buildPercentageView(100 - value, 'cashOut')} {buildPercentageView(100 - value, 'cashOut')}
</div> </div>
</div> </div>

View file

@ -1,6 +1,7 @@
import * as d3 from 'd3' import * as d3 from 'd3'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useEffect, useRef, useCallback } from 'react' import React, { useEffect, useRef, useCallback } from 'react'
import { backgroundColor, zircon, primaryColor } from 'src/styling/variables' import { backgroundColor, zircon, primaryColor } from 'src/styling/variables'
const transactionProfit = tx => { const transactionProfit = tx => {

View file

@ -2,6 +2,7 @@ import * as d3 from 'd3'
import moment from 'moment' import moment from 'moment'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useEffect, useRef, useCallback } from 'react' import React, { useEffect, useRef, useCallback } from 'react'
import { backgroundColor, java, neon } from 'src/styling/variables' import { backgroundColor, java, neon } from 'src/styling/variables'
const RefScatterplot = ({ data: realData, timeFrame }) => { const RefScatterplot = ({ data: realData, timeFrame }) => {

View file

@ -2,6 +2,7 @@ import { makeStyles } from '@material-ui/core/styles'
import classnames from 'classnames' import classnames from 'classnames'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState } from 'react' import React, { useState } from 'react'
import { H4 } from 'src/components/typography' import { H4 } from 'src/components/typography'
import styles from './SystemPerformance.styles' import styles from './SystemPerformance.styles'

View file

@ -2,11 +2,13 @@ import { useQuery } from '@apollo/react-hooks'
import Grid from '@material-ui/core/Grid' import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import BigNumber from 'bignumber.js' import BigNumber from 'bignumber.js'
import classnames from 'classnames'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import moment from 'moment' import moment from 'moment'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState } from 'react' import React, { useState } from 'react'
import { Label2 } from 'src/components/typography/index'
import { Label1, Label2 } from 'src/components/typography/index'
import { ReactComponent as TriangleDown } from 'src/styling/icons/arrow/triangle_down.svg' import { ReactComponent as TriangleDown } from 'src/styling/icons/arrow/triangle_down.svg'
import { ReactComponent as TriangleUp } from 'src/styling/icons/arrow/triangle_up.svg' import { ReactComponent as TriangleUp } from 'src/styling/icons/arrow/triangle_up.svg'
import { fromNamespace } from 'src/utils/config' import { fromNamespace } from 'src/utils/config'
@ -206,14 +208,29 @@ const SystemPerformance = () => {
<LineChart timeFrame={selectedRange} data={transactionsToShow} /> <LineChart timeFrame={selectedRange} data={transactionsToShow} />
</Grid> </Grid>
<Grid item xs={4}> <Grid item xs={4}>
<Label2>Direction</Label2>
<Grid container> <Grid container>
<Grid item xs> <Grid item style={{ marginRight: 34 }}>
<PercentageChart <Label2>Direction</Label2>
cashIn={getDirectionPercent().cashIn}
cashOut={getDirectionPercent().cashOut}
/>
</Grid> </Grid>
<Grid
item
className={classnames(
classes.directionLabelContainer,
classes.dirLabContMargin
)}>
<div className={classes.outSquare} />
<Label1 className={classes.directionLabel}>Out</Label1>
</Grid>
<Grid item className={classes.directionLabelContainer}>
<div className={classes.inSquare} />
<Label1 className={classes.directionLabel}>In</Label1>
</Grid>
</Grid>
<Grid item xs>
<PercentageChart
cashIn={getDirectionPercent().cashIn}
cashOut={getDirectionPercent().cashOut}
/>
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>
@ -223,30 +240,4 @@ const SystemPerformance = () => {
) )
} }
/**
<Grid item xs={4}>
<div style={{ display: 'flex' }}>
<Label2>Direction</Label2>
<div style={{ marginLeft: 8, display: 'flex' }}>
<div
style={{
width: 8,
height: 8,
borderRadius: 2,
marginTop: 18,
marginRight: 8,
backgroundColor: 'pink'
}}
/>
<p>In</p>
</div>
</div>
<Grid container>
<Grid item xs>
<PercentageChart data={getDirectionPercent()} />
</Grid>
</Grid>
</Grid>
*/
export default SystemPerformance export default SystemPerformance

View file

@ -6,7 +6,9 @@ import {
fontSecondary, fontSecondary,
fontColor, fontColor,
spring4, spring4,
tomato tomato,
java,
neon
} from 'src/styling/variables' } from 'src/styling/variables'
const styles = { const styles = {
@ -90,6 +92,28 @@ const styles = {
}, },
gridContainer: { gridContainer: {
marginTop: 30 marginTop: 30
},
inSquare: {
width: 8,
height: 8,
borderRadius: 2,
marginTop: 18,
marginRight: 4,
backgroundColor: java
},
outSquare: {
width: 8,
height: 8,
borderRadius: 2,
marginTop: 18,
marginRight: 4,
backgroundColor: neon
},
directionLabelContainer: {
display: 'flex'
},
dirLabContMargin: {
marginRight: 20
} }
} }

View file

@ -8,6 +8,7 @@ import TableRow from '@material-ui/core/TableRow'
import classnames from 'classnames' import classnames from 'classnames'
import React from 'react' import React from 'react'
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
import { Status } from 'src/components/Status' import { Status } from 'src/components/Status'
import { Label2, TL2 } from 'src/components/typography' import { Label2, TL2 } from 'src/components/typography'
// import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg' // import { ReactComponent as TxInIcon } from 'src/styling/icons/direction/cash-in.svg'

View file

@ -4,6 +4,7 @@ import Grid from '@material-ui/core/Grid'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import React from 'react' import React from 'react'
import { cardState as cardState_ } from 'src/components/CollapsibleCard' import { cardState as cardState_ } from 'src/components/CollapsibleCard'
// import ActionButton from 'src/components/buttons/ActionButton' // import ActionButton from 'src/components/buttons/ActionButton'
import { H4, TL2, Label1 } from 'src/components/typography' import { H4, TL2, Label1 } from 'src/components/typography'

View file

@ -2,11 +2,12 @@ import { useMutation } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core' import { makeStyles } from '@material-ui/core'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import React from 'react' import React from 'react'
import * as Yup from 'yup'
import { Table as EditableTable } from 'src/components/editableTable' import { Table as EditableTable } from 'src/components/editableTable'
import { CashOut } from 'src/components/inputs/cashbox/Cashbox' import { CashOut } from 'src/components/inputs/cashbox/Cashbox'
import { NumberInput } from 'src/components/inputs/formik' import { NumberInput } from 'src/components/inputs/formik'
import { fromNamespace } from 'src/utils/config' import { fromNamespace } from 'src/utils/config'
import * as Yup from 'yup'
import styles from './Cassettes.styles' import styles from './Cassettes.styles'

View file

@ -2,6 +2,7 @@ import { useQuery, useMutation } from '@apollo/react-hooks'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import * as R from 'ramda' import * as R from 'ramda'
import React from 'react' import React from 'react'
import { Table as EditableTable } from 'src/components/editableTable' import { Table as EditableTable } from 'src/components/editableTable'
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config' import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'

View file

@ -1,6 +1,7 @@
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import moment from 'moment' import moment from 'moment'
import React from 'react' import React from 'react'
import { Label3, P } from 'src/components/typography' import { Label3, P } from 'src/components/typography'
import styles from '../Machines.styles' import styles from '../Machines.styles'

View file

@ -8,6 +8,7 @@ import {
CellMeasurer, CellMeasurer,
CellMeasurerCache CellMeasurerCache
} from 'react-virtualized' } from 'react-virtualized'
import { import {
Table, Table,
TBody, TBody,

View file

@ -5,6 +5,7 @@ import gql from 'graphql-tag'
import moment from 'moment' import moment from 'moment'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import DetailsRow from 'src/pages/Transactions/DetailsCard' import DetailsRow from 'src/pages/Transactions/DetailsCard'
import { mainStyles } from 'src/pages/Transactions/Transactions.styles' import { mainStyles } from 'src/pages/Transactions/Transactions.styles'
import { getStatus } from 'src/pages/Transactions/helper' import { getStatus } from 'src/pages/Transactions/helper'

View file

@ -8,6 +8,7 @@ import gql from 'graphql-tag'
import * as R from 'ramda' import * as R from 'ramda'
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { Link, useLocation } from 'react-router-dom' import { Link, useLocation } from 'react-router-dom'
import { TL1, TL2, Label3 } from 'src/components/typography' import { TL1, TL2, Label3 } from 'src/components/typography'
import Cassettes from './MachineComponents/Cassettes' import Cassettes from './MachineComponents/Cassettes'