fix: machine status layout bugs
fix: reboot icon looks cropped fix: confirm dialog layout fix: Status chip background colors fix: detailed machine status layout fix: machine detailed status layout fix: machine status article links, status chip size fix: confirmDialog for all machine actions fix: confirm dialog on every action. reload when success fix: verbose input label fix: display software version and machine model fix: eslint fixes fix: removed machine version and update button fix: get machines statuses from ping chore: removed the support articles until they're ready fix: reset value and error states when closing the confirm dialog fix: removed unused info from the machine table styles: fixed styles in the machine details card chore: moved styles to another file fix: fixed the version gql property
This commit is contained in:
parent
825a9bfe09
commit
db014a3ed4
9 changed files with 278 additions and 214 deletions
|
|
@ -4,6 +4,8 @@ const axios = require('axios')
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
const db = require('./db')
|
const db = require('./db')
|
||||||
const pairing = require('./pairing')
|
const pairing = require('./pairing')
|
||||||
|
const notifier = require('./notifier')
|
||||||
|
const dbm = require('./postgresql_interface')
|
||||||
const configManager = require('./new-config-manager')
|
const configManager = require('./new-config-manager')
|
||||||
const settingsLoader = require('./new-settings-loader')
|
const settingsLoader = require('./new-settings-loader')
|
||||||
|
|
||||||
|
|
@ -34,15 +36,40 @@ function getConfig (defaultConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMachineNames (config) {
|
function getMachineNames (config) {
|
||||||
|
const fullyFunctionalStatus = {label: 'Fully functional', type: 'success'}
|
||||||
|
const unresponsiveStatus = {label: 'Unresponsive', type: 'error'}
|
||||||
|
const stuckStatus = {label: 'Stuck', type: 'error'}
|
||||||
|
|
||||||
return Promise.all([getMachines(), getConfig(config)])
|
return Promise.all([getMachines(), getConfig(config)])
|
||||||
.then(([machines, config]) => {
|
.then(([machines, config]) => Promise.all(
|
||||||
|
[machines, notifier.checkPings(machines), dbm.machineEvents(), config]
|
||||||
|
))
|
||||||
|
.then(([machines, pings, events, config]) => {
|
||||||
|
const getPingStatus = (ping) => {
|
||||||
|
if (!ping) return fullyFunctionalStatus
|
||||||
|
|
||||||
|
if (ping.age) return unresponsiveStatus
|
||||||
|
|
||||||
|
return fullyFunctionalStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStuckStatus = (stuck) => {
|
||||||
|
if (!stuck || !stuck.age) return undefined
|
||||||
|
|
||||||
|
return stuckStatus
|
||||||
|
}
|
||||||
|
|
||||||
const addName = r => {
|
const addName = r => {
|
||||||
const cashOutConfig = configManager.getCashOut(r.deviceId, config)
|
const cashOutConfig = configManager.getCashOut(r.deviceId, config)
|
||||||
|
|
||||||
const cashOut = !!cashOutConfig.active
|
const cashOut = !!cashOutConfig.active
|
||||||
|
|
||||||
// TODO new-admin actually load status based on ping.
|
const ping = getPingStatus(_.first(pings[r.deviceId]))
|
||||||
const statuses = [{label: 'Unknown detailed status', type: 'warning'}]
|
const stuck = getStuckStatus(_.first(notifier.checkStuckScreen(events, r.name)))
|
||||||
|
|
||||||
|
const statuses = [ping]
|
||||||
|
|
||||||
|
if (stuck) statuses.push(stuck)
|
||||||
|
|
||||||
return _.assign(r, {cashOut, statuses})
|
return _.assign(r, {cashOut, statuses})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -344,4 +344,8 @@ function buildAlertFingerprint (alertRec, notifications) {
|
||||||
return crypto.createHash('sha256').update(subject).digest('hex')
|
return crypto.createHash('sha256').update(subject).digest('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { checkNotification }
|
module.exports = {
|
||||||
|
checkNotification,
|
||||||
|
checkPings,
|
||||||
|
checkStuckScreen
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,51 +2,50 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogContentText,
|
|
||||||
makeStyles
|
makeStyles
|
||||||
} from '@material-ui/core'
|
} from '@material-ui/core'
|
||||||
import React, { useEffect, useState, memo } from 'react'
|
import React, { memo, useState } from 'react'
|
||||||
|
|
||||||
import { Button, IconButton } from 'src/components/buttons'
|
import { Button, IconButton } from 'src/components/buttons'
|
||||||
|
import { TextInput } from 'src/components/inputs'
|
||||||
|
import { H4 } from 'src/components/typography'
|
||||||
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
import { ReactComponent as CloseIcon } from 'src/styling/icons/action/close/zodiac.svg'
|
||||||
import { fontSize3 } from 'src/styling/variables'
|
import { spacer } from 'src/styling/variables'
|
||||||
|
|
||||||
import { TextInput } from './inputs'
|
import ErrorMessage from './ErrorMessage'
|
||||||
import { H4, P } from './typography'
|
|
||||||
|
|
||||||
const useStyles = makeStyles({
|
const useStyles = makeStyles({
|
||||||
label: {
|
dialogContent: {
|
||||||
fontSize: fontSize3
|
width: 434,
|
||||||
|
padding: spacer * 2,
|
||||||
|
paddingRight: spacer * 3.5
|
||||||
},
|
},
|
||||||
spacing: {
|
dialogTitle: {
|
||||||
padding: 32
|
padding: spacer * 2,
|
||||||
|
paddingRight: spacer * 1.5,
|
||||||
|
display: 'flex',
|
||||||
|
'justify-content': 'space-between',
|
||||||
|
'& > h4': {
|
||||||
|
margin: 0
|
||||||
|
},
|
||||||
|
'& > button': {
|
||||||
|
padding: 0,
|
||||||
|
marginTop: -(spacer / 2)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
wrapper: {
|
dialogActions: {
|
||||||
display: 'flex'
|
padding: spacer * 4,
|
||||||
},
|
paddingTop: spacer * 2
|
||||||
title: {
|
|
||||||
margin: [[20, 0, 24, 16]]
|
|
||||||
},
|
|
||||||
closeButton: {
|
|
||||||
padding: 0,
|
|
||||||
margin: [[12, 12, 'auto', 'auto']]
|
|
||||||
// position: 'absolute',
|
|
||||||
// right: spacer,
|
|
||||||
// top: spacer
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const DialogTitle = ({ children, onClose }) => {
|
export const DialogTitle = ({ children, onClose }) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
return (
|
return (
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.dialogTitle}>
|
||||||
{children}
|
{children}
|
||||||
{onClose && (
|
{onClose && (
|
||||||
<IconButton
|
<IconButton size={16} aria-label="close" onClick={onClose}>
|
||||||
size={16}
|
|
||||||
aria-label="close"
|
|
||||||
className={classes.closeButton}
|
|
||||||
onClick={onClose}>
|
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
|
|
@ -57,50 +56,59 @@ export const DialogTitle = ({ children, onClose }) => {
|
||||||
export const ConfirmDialog = memo(
|
export const ConfirmDialog = memo(
|
||||||
({
|
({
|
||||||
title = 'Confirm action',
|
title = 'Confirm action',
|
||||||
subtitle = 'This action requires confirmation',
|
errorMessage = 'This action requires confirmation',
|
||||||
open,
|
open,
|
||||||
toBeConfirmed,
|
toBeConfirmed,
|
||||||
onConfirmed,
|
onConfirmed,
|
||||||
onDissmised,
|
onDissmised,
|
||||||
className,
|
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
const [value, setValue] = useState('')
|
const [value, setValue] = useState('')
|
||||||
const [error, setError] = useState(false)
|
const [error, setError] = useState(false)
|
||||||
useEffect(() => setValue(''), [open])
|
const handleChange = event => setValue(event.target.value)
|
||||||
const handleChange = event => {
|
|
||||||
setValue(event.target.value)
|
const innerOnClose = () => {
|
||||||
|
setValue('')
|
||||||
|
setError(false)
|
||||||
|
onDissmised()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} aria-labelledby="form-dialog-title" {...props}>
|
<Dialog open={open} aria-labelledby="form-dialog-title" {...props}>
|
||||||
<DialogTitle id="customized-dialog-title" onClose={onDissmised}>
|
<DialogTitle id="customized-dialog-title" onClose={innerOnClose}>
|
||||||
<H4 className={classes.title}>{title}</H4>
|
<H4>{title}</H4>
|
||||||
{subtitle && (
|
|
||||||
<DialogContentText>
|
|
||||||
<P>{subtitle}</P>
|
|
||||||
</DialogContentText>
|
|
||||||
)}
|
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent className={className}>
|
{errorMessage && (
|
||||||
|
<DialogTitle>
|
||||||
|
<ErrorMessage>
|
||||||
|
{errorMessage.split(':').map(error => (
|
||||||
|
<>
|
||||||
|
{error}
|
||||||
|
<br />
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</ErrorMessage>
|
||||||
|
</DialogTitle>
|
||||||
|
)}
|
||||||
|
<DialogContent className={classes.dialogContent}>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={`Write '${toBeConfirmed}' to confirm`}
|
label={`Write '${toBeConfirmed}' to confirm this action`}
|
||||||
name="confirm-input"
|
name="confirm-input"
|
||||||
autoFocus
|
autoFocus
|
||||||
id="confirm-input"
|
id="confirm-input"
|
||||||
type="text"
|
type="text"
|
||||||
size="lg"
|
size="sm"
|
||||||
fullWidth
|
fullWidth
|
||||||
value={value}
|
value={value}
|
||||||
touched={{}}
|
touched={{}}
|
||||||
error={error}
|
error={error}
|
||||||
InputLabelProps={{ shrink: true, className: classes.label }}
|
InputLabelProps={{ shrink: true }}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onBlur={() => setError(toBeConfirmed !== value)}
|
onBlur={() => setError(toBeConfirmed !== value)}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions classes={{ spacing: classes.spacing }}>
|
<DialogActions className={classes.dialogActions}>
|
||||||
<Button
|
<Button
|
||||||
color="green"
|
color="green"
|
||||||
disabled={toBeConfirmed !== value}
|
disabled={toBeConfirmed !== value}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ import {
|
||||||
spring3,
|
spring3,
|
||||||
smallestFontSize,
|
smallestFontSize,
|
||||||
inputFontFamily,
|
inputFontFamily,
|
||||||
spacer
|
spacer,
|
||||||
|
linen
|
||||||
} from '../styling/variables'
|
} from '../styling/variables'
|
||||||
|
|
||||||
const colors = {
|
const colors = {
|
||||||
|
|
@ -22,7 +23,7 @@ const colors = {
|
||||||
|
|
||||||
const backgroundColors = {
|
const backgroundColors = {
|
||||||
error: mistyRose,
|
error: mistyRose,
|
||||||
warning: mistyRose,
|
warning: linen,
|
||||||
success: spring3
|
success: spring3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,21 +41,15 @@ const useStyles = makeStyles({
|
||||||
fontSize: smallestFontSize,
|
fontSize: smallestFontSize,
|
||||||
fontWeight: inputFontWeight,
|
fontWeight: inputFontWeight,
|
||||||
fontFamily: inputFontFamily,
|
fontFamily: inputFontFamily,
|
||||||
padding: [[spacer / 2, spacer]],
|
paddingRight: spacer / 2,
|
||||||
|
paddingLeft: spacer / 2,
|
||||||
color: ({ type }) => colors[type]
|
color: ({ type }) => colors[type]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const Status = ({ status, className }) => {
|
const Status = ({ status }) => {
|
||||||
const classes = useStyles({ type: status.type })
|
const classes = useStyles({ type: status.type })
|
||||||
return (
|
return <Chip type={status.type} label={status.label} classes={classes} />
|
||||||
<Chip
|
|
||||||
type={status.type}
|
|
||||||
label={status.label}
|
|
||||||
className={className ?? null}
|
|
||||||
classes={classes}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MainStatus = ({ statuses }) => {
|
const MainStatus = ({ statuses }) => {
|
||||||
|
|
@ -65,7 +60,7 @@ const MainStatus = ({ statuses }) => {
|
||||||
const plus = { label: `+${statuses.length - 1}`, type: mainStatus.type }
|
const plus = { label: `+${statuses.length - 1}`, type: mainStatus.type }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ marginLeft: -3 }}>
|
<div>
|
||||||
<Status status={mainStatus} />
|
<Status status={mainStatus} />
|
||||||
{statuses.length > 1 && <Status status={plus} />}
|
{statuses.length > 1 && <Status status={plus} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
import { useMutation } from '@apollo/react-hooks'
|
import { useMutation } from '@apollo/react-hooks'
|
||||||
import { Dialog, DialogContent } from '@material-ui/core'
|
import { Grid, Divider } from '@material-ui/core'
|
||||||
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 moment from 'moment'
|
import moment from 'moment'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { DialogTitle, ConfirmDialog } from 'src/components/ConfirmDialog'
|
import { ConfirmDialog } from 'src/components/ConfirmDialog'
|
||||||
import { Status } from 'src/components/Status'
|
import { Status } from 'src/components/Status'
|
||||||
import ActionButton from 'src/components/buttons/ActionButton'
|
import ActionButton from 'src/components/buttons/ActionButton'
|
||||||
import { Label1, H4 } from 'src/components/typography'
|
import { ReactComponent as LinkIcon } from 'src/styling/icons/button/link/zodiac.svg'
|
||||||
import { ReactComponent as RebootReversedIcon } from 'src/styling/icons/button/reboot/white.svg'
|
import { ReactComponent as RebootReversedIcon } from 'src/styling/icons/button/reboot/white.svg'
|
||||||
import { ReactComponent as RebootIcon } from 'src/styling/icons/button/reboot/zodiac.svg'
|
import { ReactComponent as RebootIcon } from 'src/styling/icons/button/reboot/zodiac.svg'
|
||||||
|
import { ReactComponent as ShutdownReversedIcon } from 'src/styling/icons/button/shut down/white.svg'
|
||||||
|
import { ReactComponent as ShutdownIcon } from 'src/styling/icons/button/shut down/zodiac.svg'
|
||||||
import { ReactComponent as UnpairReversedIcon } from 'src/styling/icons/button/unpair/white.svg'
|
import { ReactComponent as UnpairReversedIcon } from 'src/styling/icons/button/unpair/white.svg'
|
||||||
import { ReactComponent as UnpairIcon } from 'src/styling/icons/button/unpair/zodiac.svg'
|
import { ReactComponent as UnpairIcon } from 'src/styling/icons/button/unpair/zodiac.svg'
|
||||||
|
|
||||||
import styles from './MachineDetailsCard.styles'
|
import { labelStyles, machineDetailsStyles } from './MachineDetailsCard.styles'
|
||||||
|
|
||||||
const MACHINE_ACTION = gql`
|
const MACHINE_ACTION = gql`
|
||||||
mutation MachineAction($deviceId: ID!, $action: MachineAction!) {
|
mutation MachineAction($deviceId: ID!, $action: MachineAction!) {
|
||||||
|
|
@ -24,150 +26,172 @@ const MACHINE_ACTION = gql`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const useStyles = makeStyles(styles)
|
const supportArtices = [
|
||||||
|
{
|
||||||
|
// Default article for non-maped statuses
|
||||||
|
code: undefined,
|
||||||
|
label: 'Troubleshooting',
|
||||||
|
article:
|
||||||
|
'https://support.lamassu.is/hc/en-us/categories/115000075249-Troubleshooting'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const article = ({ code: status }) =>
|
||||||
|
supportArtices.find(({ code: article }) => article === status)
|
||||||
|
|
||||||
|
const useLStyles = makeStyles(labelStyles)
|
||||||
|
|
||||||
const Label = ({ children }) => {
|
const Label = ({ children }) => {
|
||||||
const classes = useStyles()
|
const classes = useLStyles()
|
||||||
return <Label1 className={classes.label}>{children}</Label1>
|
|
||||||
|
return <div className={classes.label}>{children}</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
const MachineDetailsRow = ({ it: machine }) => {
|
const useMDStyles = makeStyles(machineDetailsStyles)
|
||||||
const [errorDialog, setErrorDialog] = useState(false)
|
|
||||||
const [dialogOpen, setOpen] = useState(false)
|
|
||||||
const [actionMessage, setActionMessage] = useState(null)
|
|
||||||
const classes = useStyles()
|
|
||||||
|
|
||||||
const unpairDialog = () => setOpen(true)
|
const Container = ({ children, ...props }) => (
|
||||||
|
<Grid container spacing={4} {...props}>
|
||||||
|
{children}
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
|
||||||
|
const Item = ({ children, ...props }) => (
|
||||||
|
<Grid item xs {...props}>
|
||||||
|
{children}
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
|
||||||
|
const MachineDetailsRow = ({ it: machine, onActionSuccess }) => {
|
||||||
|
const [action, setAction] = useState('')
|
||||||
|
const [dialogOpen, setOpen] = useState(false)
|
||||||
|
const [errorMessage, setErrorMessage] = useState(null)
|
||||||
|
const classes = useMDStyles()
|
||||||
|
|
||||||
|
const confirmDialog = action => setAction(action) || setOpen(true)
|
||||||
|
|
||||||
const [machineAction, { loading }] = useMutation(MACHINE_ACTION, {
|
const [machineAction, { loading }] = useMutation(MACHINE_ACTION, {
|
||||||
onError: ({ graphQLErrors, message }) => {
|
onError: ({ message }) => {
|
||||||
const errorMessage = graphQLErrors[0] ? graphQLErrors[0].message : message
|
const errorMessage = message ?? 'An error ocurred'
|
||||||
setActionMessage(errorMessage)
|
setErrorMessage(errorMessage)
|
||||||
setErrorDialog(true)
|
},
|
||||||
|
onCompleted: () => {
|
||||||
|
// TODO: custom onActionSuccess needs to be passed down from the machinestatus table
|
||||||
|
onActionSuccess ? onActionSuccess() : window.location.reload()
|
||||||
|
setOpen(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Dialog open={errorDialog} aria-labelledby="form-dialog-title">
|
<Container className={classes.wrapper}>
|
||||||
<DialogTitle
|
<Item xs={5}>
|
||||||
id="customized-dialog-title"
|
<Container>
|
||||||
onClose={() => setErrorDialog(false)}>
|
<Item>
|
||||||
<H4>Error</H4>
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogContent>{actionMessage}</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
<div className={classes.wrapper}>
|
|
||||||
<div className={classes.column1}>
|
|
||||||
<div className={classes.lastRow}>
|
|
||||||
<div className={classes.status}>
|
|
||||||
<Label>Statuses</Label>
|
<Label>Statuses</Label>
|
||||||
<div>
|
<ul className={classes.list}>
|
||||||
{machine.statuses.map((status, index) => (
|
{machine.statuses.map((status, index) => (
|
||||||
<Status
|
<li key={index}>
|
||||||
className={classes.chips}
|
<Status status={status} />
|
||||||
status={status}
|
</li>
|
||||||
key={index}
|
|
||||||
/>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
</Item>
|
||||||
<div>
|
<Item>
|
||||||
<Label>Lamassu Support article</Label>
|
<Label>Lamassu Support article</Label>
|
||||||
<div>
|
<ul className={classes.list}>
|
||||||
{machine.statuses.map((...[, index]) => (
|
{machine.statuses
|
||||||
// TODO new-admin: support articles
|
.map(article)
|
||||||
<span key={index}></span>
|
.map(({ label, article }, index) => (
|
||||||
))}
|
<li key={index}>
|
||||||
</div>
|
<a
|
||||||
</div>
|
target="_blank"
|
||||||
<div className={classes.separator} />
|
rel="noopener noreferrer"
|
||||||
</div>
|
href={article}>
|
||||||
</div>
|
'{label}' <LinkIcon />
|
||||||
<div className={classes.column2}>
|
</a>
|
||||||
<div className={classes.row}>
|
</li>
|
||||||
<div className={classes.machineModel}>
|
))}
|
||||||
|
</ul>
|
||||||
|
</Item>
|
||||||
|
</Container>
|
||||||
|
</Item>
|
||||||
|
<Divider
|
||||||
|
orientation="vertical"
|
||||||
|
flexItem
|
||||||
|
className={classes.separator}
|
||||||
|
/>
|
||||||
|
<ConfirmDialog
|
||||||
|
open={dialogOpen}
|
||||||
|
title={`${action} this machine?`}
|
||||||
|
errorMessage={errorMessage}
|
||||||
|
toBeConfirmed={machine.name}
|
||||||
|
onConfirmed={() => {
|
||||||
|
setErrorMessage(null)
|
||||||
|
machineAction({
|
||||||
|
variables: {
|
||||||
|
deviceId: machine.deviceId,
|
||||||
|
action: `${action}`.toLowerCase()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
onDissmised={() => {
|
||||||
|
setOpen(false)
|
||||||
|
setErrorMessage(null)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Item xs>
|
||||||
|
<Container className={classes.row}>
|
||||||
|
<Item xs={4}>
|
||||||
<Label>Machine Model</Label>
|
<Label>Machine Model</Label>
|
||||||
<div>{machine.model ?? 'unknown'}</div>
|
<span>{machine.model}</span>
|
||||||
</div>
|
</Item>
|
||||||
<div>
|
{/* <Item>
|
||||||
|
<Label>Address</Label>
|
||||||
|
<span>{machine.machineLocation}</span>
|
||||||
|
</Item> */}
|
||||||
|
<Item xs={4}>
|
||||||
<Label>Paired at</Label>
|
<Label>Paired at</Label>
|
||||||
<div>
|
<span>
|
||||||
{machine.pairedAt
|
{moment(machine.pairedAt).format('YYYY-MM-DD HH:mm:ss')}
|
||||||
? moment(machine.pairedAt).format('YYYY-MM-DD HH:mm:ss')
|
</span>
|
||||||
: 'N/A'}
|
</Item>
|
||||||
</div>
|
</Container>
|
||||||
</div>
|
<Container>
|
||||||
</div>
|
<Item>
|
||||||
<div className={classes.lastRow}>
|
|
||||||
<div>
|
|
||||||
<Label>Actions</Label>
|
<Label>Actions</Label>
|
||||||
<div className={classes.actionRow}>
|
<div className={classes.stack}>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
className={classes.action}
|
|
||||||
color="primary"
|
color="primary"
|
||||||
|
className={classes.mr}
|
||||||
Icon={UnpairIcon}
|
Icon={UnpairIcon}
|
||||||
InverseIcon={UnpairReversedIcon}
|
InverseIcon={UnpairReversedIcon}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
onClick={unpairDialog}>
|
onClick={() => confirmDialog('Unpair')}>
|
||||||
Unpair
|
Unpair
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
<ConfirmDialog
|
|
||||||
open={dialogOpen}
|
|
||||||
className={classes.dialog}
|
|
||||||
title="Unpair this machine?"
|
|
||||||
subtitle={false}
|
|
||||||
toBeConfirmed={machine.name}
|
|
||||||
onConfirmed={() => {
|
|
||||||
setOpen(false)
|
|
||||||
machineAction({
|
|
||||||
variables: {
|
|
||||||
deviceId: machine.deviceId,
|
|
||||||
action: 'unpair'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
onDissmised={() => {
|
|
||||||
setOpen(false)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ActionButton
|
<ActionButton
|
||||||
className={classes.action}
|
|
||||||
color="primary"
|
color="primary"
|
||||||
|
className={classes.mr}
|
||||||
Icon={RebootIcon}
|
Icon={RebootIcon}
|
||||||
InverseIcon={RebootReversedIcon}
|
InverseIcon={RebootReversedIcon}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
onClick={() => {
|
onClick={() => confirmDialog('Reboot')}>
|
||||||
machineAction({
|
|
||||||
variables: {
|
|
||||||
deviceId: machine.deviceId,
|
|
||||||
action: 'reboot'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}>
|
|
||||||
Reboot
|
Reboot
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
className={classes.action}
|
className={classes.inlineChip}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
color="primary"
|
color="primary"
|
||||||
Icon={RebootIcon}
|
Icon={ShutdownIcon}
|
||||||
InverseIcon={RebootReversedIcon}
|
InverseIcon={ShutdownReversedIcon}
|
||||||
onClick={() => {
|
onClick={() => confirmDialog('Shutdown')}>
|
||||||
machineAction({
|
Shutdown
|
||||||
variables: {
|
|
||||||
deviceId: machine.deviceId,
|
|
||||||
action: 'restartServices'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}>
|
|
||||||
Restart Services
|
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Item>
|
||||||
</div>
|
</Container>
|
||||||
</div>
|
</Item>
|
||||||
</div>
|
</Container>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,55 @@
|
||||||
import { fade } from '@material-ui/core/styles/colorManipulator'
|
import { fade } from '@material-ui/core/styles/colorManipulator'
|
||||||
|
|
||||||
import { fontSize4, offColor, comet } from 'src/styling/variables'
|
import {
|
||||||
|
detailsRowStyles,
|
||||||
|
labelStyles
|
||||||
|
} from 'src/pages/Transactions/Transactions.styles'
|
||||||
|
import { spacer, comet, primaryColor, fontSize4 } from 'src/styling/variables'
|
||||||
|
|
||||||
export default {
|
const machineDetailsStyles = {
|
||||||
|
...detailsRowStyles,
|
||||||
|
colDivider: {
|
||||||
|
width: 1,
|
||||||
|
margin: [[spacer * 2, spacer * 4]],
|
||||||
|
backgroundColor: comet,
|
||||||
|
border: 'none'
|
||||||
|
},
|
||||||
|
inlineChip: {
|
||||||
|
marginInlineEnd: '0.25em'
|
||||||
|
},
|
||||||
|
stack: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row'
|
||||||
|
},
|
||||||
wrapper: {
|
wrapper: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
marginTop: 24,
|
marginTop: 24,
|
||||||
marginBottom: 32,
|
marginBottom: 32,
|
||||||
fontSize: fontSize4
|
fontSize: fontSize4
|
||||||
},
|
},
|
||||||
column1: {
|
|
||||||
width: 600
|
|
||||||
},
|
|
||||||
column2: {
|
|
||||||
flex: 1
|
|
||||||
},
|
|
||||||
lastRow: {
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row'
|
|
||||||
},
|
|
||||||
row: {
|
row: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
marginBottom: 36
|
marginBottom: 36
|
||||||
},
|
},
|
||||||
actionRow: {
|
list: {
|
||||||
display: 'flex',
|
padding: 0,
|
||||||
flexDirection: 'row',
|
margin: 0,
|
||||||
marginLeft: -4
|
listStyle: 'none',
|
||||||
|
'& > li': {
|
||||||
|
height: spacer * 3,
|
||||||
|
marginBottom: spacer * 1.5,
|
||||||
|
'& > a, & > a:visited': {
|
||||||
|
color: primaryColor,
|
||||||
|
textDecoration: 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
action: {
|
divider: {
|
||||||
marginRight: 4,
|
margin: '0 1rem'
|
||||||
marginLeft: 4
|
|
||||||
},
|
},
|
||||||
dialog: {
|
mr: {
|
||||||
width: 434
|
marginRight: spacer
|
||||||
},
|
|
||||||
label: {
|
|
||||||
color: offColor,
|
|
||||||
margin: [[0, 0, 6, 0]]
|
|
||||||
},
|
|
||||||
chips: {
|
|
||||||
marginLeft: -2
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
width: 248
|
|
||||||
},
|
|
||||||
machineModel: {
|
|
||||||
width: 198
|
|
||||||
},
|
},
|
||||||
separator: {
|
separator: {
|
||||||
width: 1,
|
width: 1,
|
||||||
|
|
@ -58,3 +60,5 @@ export default {
|
||||||
background: fade(comet, 0.5)
|
background: fade(comet, 0.5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { labelStyles, machineDetailsStyles }
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,12 @@ import moment from 'moment'
|
||||||
import * as R from 'ramda'
|
import * as R from 'ramda'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import { MainStatus } from 'src/components/Status'
|
||||||
|
import Title from 'src/components/Title'
|
||||||
import DataTable from 'src/components/tables/DataTable'
|
import DataTable from 'src/components/tables/DataTable'
|
||||||
|
import { mainStyles } from 'src/pages/Transactions/Transactions.styles'
|
||||||
import { MainStatus } from '../../components/Status'
|
import { ReactComponent as WarningIcon } from 'src/styling/icons/status/pumpkin.svg'
|
||||||
import Title from '../../components/Title'
|
import { ReactComponent as ErrorIcon } from 'src/styling/icons/status/tomato.svg'
|
||||||
import { ReactComponent as WarningIcon } from '../../styling/icons/status/pumpkin.svg'
|
|
||||||
import { ReactComponent as ErrorIcon } from '../../styling/icons/status/tomato.svg'
|
|
||||||
import { mainStyles } from '../Transactions/Transactions.styles'
|
|
||||||
|
|
||||||
import MachineDetailsRow from './MachineDetailsCard'
|
import MachineDetailsRow from './MachineDetailsCard'
|
||||||
|
|
||||||
|
|
@ -22,10 +21,13 @@ const GET_MACHINES = gql`
|
||||||
deviceId
|
deviceId
|
||||||
lastPing
|
lastPing
|
||||||
pairedAt
|
pairedAt
|
||||||
|
version
|
||||||
paired
|
paired
|
||||||
cashbox
|
cashbox
|
||||||
cassette1
|
cassette1
|
||||||
cassette2
|
cassette2
|
||||||
|
version
|
||||||
|
model
|
||||||
statuses {
|
statuses {
|
||||||
label
|
label
|
||||||
type
|
type
|
||||||
|
|
@ -68,7 +70,7 @@ const MachineStatus = () => {
|
||||||
width: 200,
|
width: 200,
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
view: m => m.softwareVersion || 'unknown'
|
view: m => m.version || 'unknown'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<svg width="12px" height="12px" viewBox="-0.493 -0.5 12.993 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
|
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
|
||||||
<desc>Created with Sketch.</desc>
|
<desc>Created with Sketch.</desc>
|
||||||
<g id="icon/button/reboot/white" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
<g id="icon/button/reboot/white" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<svg width="12px" height="12px" viewBox="-0.493 -0.5 12.993 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
|
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
|
||||||
<desc>Created with Sketch.</desc>
|
<desc>Created with Sketch.</desc>
|
||||||
<g id="icon/button/reboot/zodiac" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
<g id="icon/button/reboot/zodiac" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Loading…
Add table
Add a link
Reference in a new issue