fix: add machine actions to 'Dashboard > Machine Details'

This commit is contained in:
João Leal 2021-04-23 15:01:39 +01:00 committed by Josh Harvey
parent 3ac49760da
commit f69b71da65
6 changed files with 390 additions and 413 deletions

View file

@ -91,9 +91,54 @@ function getMachineName (machineId) {
.then(it => it.name)
}
function getMachine (machineId) {
function getMachine (machineId, config) {
const fullyFunctionalStatus = { label: 'Fully functional', type: 'success' }
const unresponsiveStatus = { label: 'Unresponsive', type: 'error' }
const stuckStatus = { label: 'Stuck', type: 'error' }
const sql = 'SELECT * FROM devices WHERE device_id=$1'
return db.oneOrNone(sql, [machineId]).then(res => _.mapKeys(_.camelCase)(res))
const queryMachine = db.oneOrNone(sql, [machineId]).then(r => ({
deviceId: r.device_id,
cashbox: r.cashbox,
cassette1: r.cassette1,
cassette2: r.cassette2,
version: r.version,
model: r.model,
pairedAt: new Date(r.created),
lastPing: new Date(r.last_online),
name: r.name,
paired: r.paired
}))
return Promise.all([queryMachine, dbm.machineEvents(), config])
.then(([machine, events, config]) => {
const pings = checkPings([machine])
const getStatus = (ping, stuck) => {
if (ping && ping.age) return unresponsiveStatus
if (stuck && stuck.age) return stuckStatus
return fullyFunctionalStatus
}
const addName = r => {
const cashOutConfig = configManager.getCashOut(r.deviceId, config)
const cashOut = !!cashOutConfig.active
const statuses = [
getStatus(
_.first(pings[r.deviceId]),
_.first(checkStuckScreen(events, r.name))
)
]
return _.assign(r, { cashOut, statuses })
}
return addName(machine)
})
}
function renameMachine (rec) {

View file

@ -0,0 +1,221 @@
import { useMutation, useLazyQuery } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag'
// import moment from 'moment'
import React, { memo, useState } from 'react'
import { ConfirmDialog } from 'src/components/ConfirmDialog'
import ActionButton from 'src/components/buttons/ActionButton'
// // import { Status } from 'src/components/Status'
import { ReactComponent as EditReversedIcon } from 'src/styling/icons/button/edit/white.svg'
import { ReactComponent as EditIcon } from 'src/styling/icons/button/edit/zodiac.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 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 UnpairIcon } from 'src/styling/icons/button/unpair/zodiac.svg'
import { machineActionsStyles } from './MachineActions.styles'
const useStyles = makeStyles(machineActionsStyles)
const MACHINE_ACTION = gql`
mutation MachineAction(
$deviceId: ID!
$action: MachineAction!
$newName: String
) {
machineAction(deviceId: $deviceId, action: $action, newName: $newName) {
deviceId
}
}
`
const MACHINE = gql`
query getMachine($deviceId: ID!) {
machine(deviceId: $deviceId) {
latestEvent {
note
}
}
}
`
const isStaticState = machineState => {
if (!machineState) {
return true
}
const staticStates = [
'chooseCoin',
'idle',
'pendingIdle',
'dualIdle',
'networkDown',
'unpaired',
'maintenance',
'virgin',
'wifiList'
]
return staticStates.includes(machineState)
}
const getState = machineEventsLazy =>
JSON.parse(machineEventsLazy.machine.latestEvent?.note ?? '{"state": null}')
.state
const Label = ({ children }) => {
const classes = useStyles()
return <div className={classes.label}>{children}</div>
}
const MachineActions = memo(({ machine, onActionSuccess }) => {
const [action, setAction] = useState({ command: null })
const [errorMessage, setErrorMessage] = useState(null)
const classes = useStyles()
const warningMessage = (
<span className={classes.warning}>
A user may be in the middle of a transaction and they could lose their
funds if you continue.
</span>
)
const [fetchMachineEvents, { loading: loadingEvents }] = useLazyQuery(
MACHINE,
{
variables: {
deviceId: machine.deviceId
},
onCompleted: machineEventsLazy => {
const message = !isStaticState(getState(machineEventsLazy))
? warningMessage
: null
setAction(action => ({ ...action, message }))
}
}
)
const [machineAction, { loading }] = useMutation(MACHINE_ACTION, {
onError: ({ message }) => {
const errorMessage = message ?? 'An error ocurred'
setErrorMessage(errorMessage)
},
onCompleted: () => {
onActionSuccess && onActionSuccess()
setAction({ command: null })
}
})
const confirmDialogOpen = Boolean(action.command)
const disabled = !!(action?.command === 'restartServices' && loadingEvents)
return (
<div>
<Label>Actions</Label>
<div className={classes.stack}>
<ActionButton
color="primary"
className={classes.mr}
Icon={EditIcon}
InverseIcon={EditReversedIcon}
disabled={loading}
onClick={() =>
setAction({
command: 'rename',
display: 'Rename',
confirmationMessage: 'Write the new name for this machine'
})
}>
Rename
</ActionButton>
<ActionButton
color="primary"
className={classes.mr}
Icon={UnpairIcon}
InverseIcon={UnpairReversedIcon}
disabled={loading}
onClick={() =>
setAction({
command: 'unpair',
display: 'Unpair'
})
}>
Unpair
</ActionButton>
<ActionButton
color="primary"
className={classes.mr}
Icon={RebootIcon}
InverseIcon={RebootReversedIcon}
disabled={loading}
onClick={() =>
setAction({
command: 'reboot',
display: 'Reboot'
})
}>
Reboot
</ActionButton>
<ActionButton
color="primary"
className={classes.mr}
Icon={ShutdownIcon}
InverseIcon={ShutdownReversedIcon}
disabled={loading}
onClick={() =>
setAction({
command: 'shutdown',
display: 'Shutdown',
message:
'In order to bring it back online, the machine will need to be visited and its power reset.'
})
}>
Shutdown
</ActionButton>
<ActionButton
color="primary"
className={classes.inlineChip}
Icon={RebootIcon}
InverseIcon={RebootReversedIcon}
disabled={loading}
onClick={() => {
fetchMachineEvents()
setAction({
command: 'restartServices',
display: 'Restart services for'
})
}}>
Restart Services
</ActionButton>
</div>
<ConfirmDialog
disabled={disabled}
open={confirmDialogOpen}
title={`${action?.display} this machine?`}
errorMessage={errorMessage}
toBeConfirmed={machine.name}
message={action?.message}
confirmationMessage={action?.confirmationMessage}
saveButtonAlwaysEnabled={action?.command === 'rename'}
onConfirmed={value => {
setErrorMessage(null)
machineAction({
variables: {
deviceId: machine.deviceId,
action: `${action?.command}`,
...(action?.command === 'rename' && { newName: value })
}
})
}}
onDissmised={() => {
setAction({ command: null })
setErrorMessage(null)
}}
/>
</div>
)
})
export default MachineActions

View file

@ -0,0 +1,82 @@
import { fade } from '@material-ui/core/styles/colorManipulator'
import typographyStyles from 'src/components/typography/styles'
import {
offColor,
spacer,
comet,
primaryColor,
fontSize4,
errorColor
} from 'src/styling/variables'
const { label1 } = typographyStyles
const machineActionsStyles = {
colDivider: {
width: 1,
margin: [[spacer * 2, spacer * 4]],
backgroundColor: comet,
border: 'none'
},
label: {
extend: label1,
color: offColor,
marginBottom: 4
},
inlineChip: {
marginInlineEnd: '0.25em'
},
stack: {
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'start'
},
wrapper: {
display: 'flex',
// marginTop: 24,
// marginBottom: 32,
marginTop: 12,
marginBottom: 16,
fontSize: fontSize4
},
row: {
display: 'flex',
flexDirection: 'row'
// marginBottom: 36
},
list: {
padding: 0,
margin: 0,
listStyle: 'none'
},
item: {
height: spacer * 3,
marginBottom: spacer * 1.5
},
link: {
color: primaryColor,
textDecoration: 'none'
},
divider: {
margin: '0 1rem'
},
mr: {
marginRight: spacer,
marginBottom: spacer
},
separator: {
width: 1,
height: 170,
zIndex: 1,
marginRight: 60,
marginLeft: 'auto',
background: fade(comet, 0.5)
},
warning: {
color: errorColor
}
}
export { machineActionsStyles }

View file

@ -1,36 +1,15 @@
import { useMutation } from '@apollo/react-hooks'
import { makeStyles } from '@material-ui/core/styles'
import BigNumber from 'bignumber.js'
import gql from 'graphql-tag'
import moment from 'moment'
import React, { useState } from 'react'
import React from 'react'
import { ConfirmDialog } from 'src/components/ConfirmDialog'
import { Status } from 'src/components/Status'
import ActionButton from 'src/components/buttons/ActionButton'
import MachineActions from 'src/components/machineActions/MachineActions'
import { H3, Label3, P } from 'src/components/typography'
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 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 UnpairIcon } from 'src/styling/icons/button/unpair/zodiac.svg'
import styles from '../Machines.styles'
const useStyles = makeStyles(styles)
const MACHINE_ACTION = gql`
mutation MachineAction(
$deviceId: ID!
$action: MachineAction!
$newName: String
) {
machineAction(deviceId: $deviceId, action: $action, newName: $newName) {
deviceId
}
}
`
const makeLastPing = lastPing => {
if (!lastPing) return null
const now = moment()
@ -51,25 +30,8 @@ const makeLastPing = lastPing => {
}
const Overview = ({ data, onActionSuccess }) => {
const [action, setAction] = useState('')
const [confirmActionDialogOpen, setConfirmActionDialogOpen] = useState(false)
const [errorMessage, setErrorMessage] = useState(null)
const classes = useStyles()
const [machineAction] = useMutation(MACHINE_ACTION, {
onError: ({ message }) => {
const errorMessage = message ?? 'An error ocurred'
setErrorMessage(errorMessage)
},
onCompleted: () => {
onActionSuccess && onActionSuccess()
setConfirmActionDialogOpen(false)
}
})
const confirmActionDialog = action =>
setAction(action) || setConfirmActionDialogOpen(true)
return (
<>
<div className={classes.row}>
@ -101,78 +63,10 @@ const Overview = ({ data, onActionSuccess }) => {
</div>
</div>
<div className={classes.row}>
<div className={classes.rowItem}>
<Label3 className={classes.label3}>Latency</Label3>
<P>
{data.responseTime
? new BigNumber(data.responseTime).toFixed(3).toString() + ' ms'
: 'unavailable'}
</P>
<MachineActions
machine={data}
onActionSuccess={onActionSuccess}></MachineActions>
</div>
</div>
<div className={classes.row}>
<div className={classes.rowItem}>
<Label3 className={classes.label3}>Packet Loss</Label3>
<P>
{data.packetLoss
? new BigNumber(data.packetLoss).toFixed(3).toString() + ' %'
: 'unavailable'}
</P>
</div>
</div>
<div className={classes.row}>
<div className={classes.rowItem}>
{' '}
<Label3 className={classes.label3}>Actions</Label3>
{data.name && (
<div className={classes.actionButtonsContainer}>
<ActionButton
color="primary"
className={classes.actionButton}
Icon={UnpairIcon}
InverseIcon={UnpairReversedIcon}
onClick={() => confirmActionDialog('Unpair')}>
Unpair
</ActionButton>
<ActionButton
color="primary"
className={classes.actionButton}
Icon={RebootIcon}
InverseIcon={RebootReversedIcon}
onClick={() => confirmActionDialog('Reboot')}>
Reboot
</ActionButton>
<ActionButton
className={classes.actionButton}
color="primary"
Icon={ShutdownIcon}
InverseIcon={ShutdownReversedIcon}
onClick={() => confirmActionDialog('Shutdown')}>
Shutdown
</ActionButton>
</div>
)}
</div>
</div>
<ConfirmDialog
open={confirmActionDialogOpen}
title={`${action} this machine?`}
errorMessage={errorMessage}
toBeConfirmed={data.name}
onConfirmed={() => {
setErrorMessage(null)
machineAction({
variables: {
deviceId: data.deviceId,
action: `${action}`.toLowerCase()
}
})
}}
onDissmised={() => {
setConfirmActionDialogOpen(false)
setErrorMessage(null)
}}
/>
</>
)
}

View file

@ -6,7 +6,7 @@ import NavigateNextIcon from '@material-ui/icons/NavigateNext'
import classnames from 'classnames'
import gql from 'graphql-tag'
import * as R from 'ramda'
import React from 'react'
import React, { useState, useEffect } from 'react'
import { Link, useLocation } from 'react-router-dom'
import { TL1, TL2, Label3 } from 'src/components/typography'
@ -19,11 +19,9 @@ import Transactions from './MachineComponents/Transactions'
import styles from './Machines.styles'
const useStyles = makeStyles(styles)
const getMachineInfo = R.compose(R.find, R.propEq('name'))
const GET_INFO = gql`
query getInfo {
machines {
query getMachine($deviceId: ID!) {
machine(deviceId: $deviceId) {
name
deviceId
paired
@ -41,26 +39,37 @@ const GET_INFO = gql`
downloadSpeed
responseTime
packetLoss
latestEvent {
note
}
}
config
}
`
const getMachines = R.path(['machines'])
const getMachineID = path => path.slice(path.lastIndexOf('/') + 1)
const Machines = () => {
const { data, refetch } = useQuery(GET_INFO)
const location = useLocation()
const [machine, setMachine] = useState({})
const [config, setConfig] = useState({})
const { data, refetch } = useQuery(GET_INFO, {
variables: {
deviceId: getMachineID(location.pathname)
},
onCompleted: () => {
setMachine(data.machine)
setConfig(data.config)
}
})
const classes = useStyles()
const selectedMachine =
R.path(['state', 'selectedMachine'])(location) ??
R.path(['machines', 0, 'name'])(data) ??
''
const machines = getMachines(data) ?? []
const machineInfo = getMachineInfo(selectedMachine)(machines) ?? {}
const timezone = R.path(['config', 'locale_timezone'], data) ?? {}
useEffect(() => {
if (data) setMachine(data.machine)
}, [data])
return (
<Grid container className={classes.grid}>
<Grid item xs={3}>
@ -73,10 +82,10 @@ const Machines = () => {
</Label3>
</Link>
<TL2 noMargin className={classes.subtitle}>
{selectedMachine}
{machine.name}
</TL2>
</Breadcrumbs>
<Overview data={machineInfo} onActionSuccess={refetch} />
<Overview data={machine} onActionSuccess={refetch} />
</div>
</Grid>
<Grid item xs={12}>
@ -94,26 +103,23 @@ const Machines = () => {
<div
className={classnames(classes.detailItem, classes.detailsMargin)}>
<TL1 className={classes.subtitle}>{'Details'}</TL1>
<Details data={machineInfo} timezone={timezone} />
<Details data={machine} timezone={timezone} />
</div>
<div className={classes.detailItem}>
<TL1 className={classes.subtitle}>{'Cash cassettes'}</TL1>
<Cassettes
refetchData={refetch}
machine={machineInfo}
config={data?.config ?? false}
machine={machine}
config={config ?? false}
/>
</div>
<div className={classes.transactionsItem}>
<TL1 className={classes.subtitle}>{'Latest transactions'}</TL1>
<Transactions id={machineInfo?.deviceId ?? null} />
<Transactions id={machine?.deviceId ?? null} />
</div>
<div className={classes.detailItem}>
<TL1 className={classes.subtitle}>{'Commissions'}</TL1>
<Commissions
name={'commissions'}
id={machineInfo?.deviceId ?? null}
/>
<Commissions name={'commissions'} id={machine?.deviceId ?? null} />
</div>
</div>
</Grid>

View file

@ -1,48 +1,16 @@
import { useMutation, useLazyQuery } from '@apollo/react-hooks'
import { Grid /*, Divider */ } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import gql from 'graphql-tag'
import React, { useState } from 'react'
import React from 'react'
import MachineActions from 'src/components/machineActions/MachineActions'
import { ConfirmDialog } from 'src/components/ConfirmDialog'
// import { Status } from 'src/components/Status'
import ActionButton from 'src/components/buttons/ActionButton'
import { ReactComponent as EditReversedIcon } from 'src/styling/icons/button/edit/white.svg'
import { ReactComponent as EditIcon } from 'src/styling/icons/button/edit/zodiac.svg'
// 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 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 UnpairIcon } from 'src/styling/icons/button/unpair/zodiac.svg'
import { modelPrettifier } from 'src/utils/machine'
import { formatDate } from 'src/utils/timezones'
import { labelStyles, machineDetailsStyles } from './MachineDetailsCard.styles'
const MACHINE_ACTION = gql`
mutation MachineAction(
$deviceId: ID!
$action: MachineAction!
$newName: String
) {
machineAction(deviceId: $deviceId, action: $action, newName: $newName) {
deviceId
}
}
`
const MACHINE = gql`
query getMachine($deviceId: ID!) {
machine(deviceId: $deviceId) {
latestEvent {
note
}
}
}
`
// const supportArtices = [
// {
// // Default article for non-maped statuses
@ -54,24 +22,6 @@ const MACHINE = gql`
// // TODO add Stuck and Fully Functional statuses articles for the new-admins
// ]
const isStaticState = machineState => {
if (!machineState) {
return true
}
const staticStates = [
'chooseCoin',
'idle',
'pendingIdle',
'dualIdle',
'networkDown',
'unpaired',
'maintenance',
'virgin',
'wifiList'
]
return staticStates.includes(machineState)
}
// const article = ({ code: status }) =>
// supportArtices.find(({ code: article }) => article === status)
@ -97,51 +47,9 @@ const Item = ({ children, ...props }) => (
</Grid>
)
const getState = machineEventsLazy =>
JSON.parse(machineEventsLazy.machine.latestEvent?.note ?? '{"state": null}')
.state
const MachineDetailsRow = ({ it: machine, onActionSuccess, timezone }) => {
const [action, setAction] = useState({ command: null })
const [errorMessage, setErrorMessage] = useState(null)
const classes = useMDStyles()
const warningMessage = (
<span className={classes.warning}>
A user may be in the middle of a transaction and they could lose their
funds if you continue.
</span>
)
const [fetchMachineEvents, { loading: loadingEvents }] = useLazyQuery(
MACHINE,
{
variables: {
deviceId: machine.deviceId
},
onCompleted: machineEventsLazy => {
const message = !isStaticState(getState(machineEventsLazy))
? warningMessage
: null
setAction(action => ({ ...action, message }))
}
}
)
const [machineAction, { loading }] = useMutation(MACHINE_ACTION, {
onError: ({ message }) => {
const errorMessage = message ?? 'An error ocurred'
setErrorMessage(errorMessage)
},
onCompleted: () => {
onActionSuccess && onActionSuccess()
setAction({ command: null })
}
})
const confirmDialogOpen = Boolean(action.command)
const disabled = !!(action?.command === 'restartServices' && loadingEvents)
return (
<Container className={classes.wrapper}>
{/* <Item xs={5}>
@ -181,30 +89,6 @@ const MachineDetailsRow = ({ it: machine, onActionSuccess, timezone }) => {
flexItem
className={classes.separator}
/> */}
<ConfirmDialog
disabled={disabled}
open={confirmDialogOpen}
title={`${action?.display} this machine?`}
errorMessage={errorMessage}
toBeConfirmed={machine.name}
message={action?.message}
confirmationMessage={action?.confirmationMessage}
saveButtonAlwaysEnabled={action?.command === 'rename'}
onConfirmed={value => {
setErrorMessage(null)
machineAction({
variables: {
deviceId: machine.deviceId,
action: `${action?.command}`,
...(action?.command === 'rename' && { newName: value })
}
})
}}
onDissmised={() => {
setAction({ command: null })
setErrorMessage(null)
}}
/>
<Item xs>
<Container className={classes.row}>
<Item xs={2}>
@ -219,166 +103,11 @@ const MachineDetailsRow = ({ it: machine, onActionSuccess, timezone }) => {
</span>
</Item>
<Item xs={6}>
<Label>Actions</Label>
<div className={classes.stack}>
<ActionButton
className={classes.mr}
disabled={loading}
color="primary"
Icon={EditIcon}
InverseIcon={EditReversedIcon}
onClick={() =>
setAction({
command: 'rename',
display: 'Rename',
confirmationMessage: 'Write the new name for this machine'
})
}>
Rename
</ActionButton>
<ActionButton
color="primary"
className={classes.mr}
Icon={UnpairIcon}
InverseIcon={UnpairReversedIcon}
disabled={loading}
onClick={() =>
setAction({
command: 'unpair',
display: 'Unpair'
})
}>
Unpair
</ActionButton>
<ActionButton
color="primary"
className={classes.mr}
Icon={RebootIcon}
InverseIcon={RebootReversedIcon}
disabled={loading}
onClick={() =>
setAction({
command: 'reboot',
display: 'Reboot'
})
}>
Reboot
</ActionButton>
<ActionButton
className={classes.mr}
disabled={loading}
color="primary"
Icon={ShutdownIcon}
InverseIcon={ShutdownReversedIcon}
onClick={() =>
setAction({
command: 'shutdown',
display: 'Shutdown',
message:
'In order to bring it back online, the machine will need to be visited and its power reset.'
})
}>
Shutdown
</ActionButton>
<ActionButton
color="primary"
className={classes.inlineChip}
Icon={RebootIcon}
InverseIcon={RebootReversedIcon}
disabled={loading}
onClick={() => {
fetchMachineEvents()
setAction({
command: 'restartServices',
display: 'Restart services for'
})
}}>
Restart Services
</ActionButton>
</div>
<MachineActions
machine={machine}
onActionSuccess={onActionSuccess}></MachineActions>
</Item>
</Container>
{/* <Container>
<Item>
<Label>Actions</Label>
<div className={classes.stack}>
<ActionButton
className={classes.mr}
disabled={loading}
color="primary"
Icon={EditIcon}
InverseIcon={EditReversedIcon}
onClick={() =>
setAction({
command: 'rename',
display: 'Rename',
confirmationMessage: 'Write the new name for this machine'
})
}>
Rename
</ActionButton>
<ActionButton
color="primary"
className={classes.mr}
Icon={UnpairIcon}
InverseIcon={UnpairReversedIcon}
disabled={loading}
onClick={() =>
setAction({
command: 'unpair',
display: 'Unpair'
})
}>
Unpair
</ActionButton>
<ActionButton
color="primary"
className={classes.mr}
Icon={RebootIcon}
InverseIcon={RebootReversedIcon}
disabled={loading}
onClick={() =>
setAction({
command: 'reboot',
display: 'Reboot'
})
}>
Reboot
</ActionButton>
<ActionButton
className={classes.mr}
disabled={loading}
color="primary"
Icon={ShutdownIcon}
InverseIcon={ShutdownReversedIcon}
onClick={() =>
setAction({
command: 'shutdown',
display: 'Shutdown',
message:
'In order to bring it back online, the machine will need to be visited and its power reset.'
})
}>
Shutdown
</ActionButton>
<ActionButton
color="primary"
className={classes.inlineChip}
Icon={RebootIcon}
InverseIcon={RebootReversedIcon}
disabled={loading}
onClick={() => {
fetchMachineEvents()
setAction({
command: 'restartServices',
display: 'Restart services for'
})
}}>
Restart Services
</ActionButton>
</div>
</Item>
</Container> */}
</Item>
</Container>
)