parent
9d52213ef9
commit
e038b809d9
4 changed files with 289 additions and 44 deletions
@ -1,39 +1,199 @@ |
|||||||
import { Grid } from '@mui/material'; |
import { Grid, Switch } from '@mui/material'; |
||||||
import { FC } from 'react'; |
import { FC, useMemo, useState } from 'react'; |
||||||
import ContainedButton, { ContainedButtonProps } from '../ContainedButton'; |
|
||||||
import { Panel, PanelHeader } from '../Panels'; |
|
||||||
import { HeaderText } from '../Text'; |
|
||||||
|
|
||||||
type SimpleOperationsPanelProps = { |
import api from '../../lib/api'; |
||||||
strikerHostName: string; |
import ContainedButton from '../ContainedButton'; |
||||||
}; |
import FlexBox from '../FlexBox'; |
||||||
|
import handleAPIError from '../../lib/handleAPIError'; |
||||||
|
import MessageBox, { Message } from '../MessageBox'; |
||||||
|
import { Panel, PanelHeader } from '../Panels'; |
||||||
|
import Spinner from '../Spinner'; |
||||||
|
import { BodyText, HeaderText } from '../Text'; |
||||||
|
|
||||||
const StretchedButton: FC<ContainedButtonProps> = (props) => ( |
const StretchedButton: FC<ContainedButtonProps> = (props) => ( |
||||||
<ContainedButton {...props} sx={{ width: '100%' }} /> |
<ContainedButton {...props} sx={{ width: '100%' }} /> |
||||||
); |
); |
||||||
|
|
||||||
const SimpleOperationsPanel: FC<SimpleOperationsPanelProps> = ({ |
const SimpleOperationsPanel: FC<SimpleOperationsPanelProps> = ({ |
||||||
strikerHostName, |
installTarget = 'disabled', |
||||||
}) => ( |
onSubmit, |
||||||
|
title, |
||||||
|
}) => { |
||||||
|
const [message, setMessage] = useState<Message | undefined>(); |
||||||
|
|
||||||
|
const headerElement = useMemo( |
||||||
|
() => |
||||||
|
title ? ( |
||||||
|
<HeaderText sx={{ textAlign: 'center' }}>{title}</HeaderText> |
||||||
|
) : ( |
||||||
|
<Spinner sx={{ marginTop: 0 }} /> |
||||||
|
), |
||||||
|
[title], |
||||||
|
); |
||||||
|
|
||||||
|
return ( |
||||||
<Panel> |
<Panel> |
||||||
<PanelHeader> |
<PanelHeader>{headerElement}</PanelHeader> |
||||||
<HeaderText text={strikerHostName} /> |
|
||||||
</PanelHeader> |
|
||||||
<Grid columns={{ xs: 1, sm: 2 }} container spacing="1em"> |
<Grid columns={{ xs: 1, sm: 2 }} container spacing="1em"> |
||||||
<Grid item sm={2} xs={1}> |
<Grid item sm={2} xs={1}> |
||||||
<StretchedButton>Update system</StretchedButton> |
<FlexBox row> |
||||||
|
<BodyText sx={{ flexGrow: 1 }}>Install target</BodyText> |
||||||
|
<Switch |
||||||
|
checked={installTarget === 'enabled'} |
||||||
|
edge="end" |
||||||
|
onChange={(event, isChecked) => { |
||||||
|
let actionText = 'disable'; |
||||||
|
let actionTextCap = 'Disable'; |
||||||
|
|
||||||
|
if (isChecked) { |
||||||
|
actionText = 'enable'; |
||||||
|
actionTextCap = 'Enable'; |
||||||
|
} |
||||||
|
|
||||||
|
onSubmit?.call(null, { |
||||||
|
actionProceedText: actionTextCap, |
||||||
|
content: ( |
||||||
|
<BodyText> |
||||||
|
Would you like to {actionText} "Install target" |
||||||
|
on this striker? It'll take a few moments to |
||||||
|
complete. |
||||||
|
</BodyText> |
||||||
|
), |
||||||
|
onProceedAppend: () => { |
||||||
|
api |
||||||
|
.put( |
||||||
|
'/host/local', |
||||||
|
{ isEnableInstallTarget: isChecked }, |
||||||
|
{ params: { handler: 'install-target' } }, |
||||||
|
) |
||||||
|
.catch((error) => { |
||||||
|
const emsg = handleAPIError(error); |
||||||
|
|
||||||
|
emsg.children = ( |
||||||
|
<> |
||||||
|
Failed to {actionText} "Install |
||||||
|
target"; CAUSE: |
||||||
|
{emsg.children} |
||||||
|
</> |
||||||
|
); |
||||||
|
|
||||||
|
setMessage(emsg); |
||||||
|
}); |
||||||
|
}, |
||||||
|
titleText: `${actionTextCap} "Install target" on ${title}?`, |
||||||
|
}); |
||||||
|
}} |
||||||
|
/> |
||||||
|
</FlexBox> |
||||||
|
</Grid> |
||||||
|
<Grid item sm={2} xs={1}> |
||||||
|
<StretchedButton |
||||||
|
onClick={() => { |
||||||
|
onSubmit?.call(null, { |
||||||
|
actionProceedText: 'Update', |
||||||
|
content: ( |
||||||
|
<BodyText> |
||||||
|
Would you like to update the operating system on this |
||||||
|
striker? It'll be placed into maintenance mode until |
||||||
|
the update completes. |
||||||
|
</BodyText> |
||||||
|
), |
||||||
|
onProceedAppend: () => { |
||||||
|
api.put('/command/update-system').catch((error) => { |
||||||
|
const emsg = handleAPIError(error); |
||||||
|
|
||||||
|
emsg.children = ( |
||||||
|
<> |
||||||
|
Failed to initiate system update; CAUSE: |
||||||
|
{emsg.children} |
||||||
|
</> |
||||||
|
); |
||||||
|
|
||||||
|
setMessage(emsg); |
||||||
|
}); |
||||||
|
}, |
||||||
|
titleText: `Update operating system on ${title}?`, |
||||||
|
}); |
||||||
|
}} |
||||||
|
> |
||||||
|
Update system |
||||||
|
</StretchedButton> |
||||||
</Grid> |
</Grid> |
||||||
|
|
||||||
<Grid item sm={2} xs={1}> |
<Grid item sm={2} xs={1}> |
||||||
<StretchedButton>Enable "Install target"</StretchedButton> |
<StretchedButton>Reconfigure striker</StretchedButton> |
||||||
</Grid> |
</Grid> |
||||||
<Grid item xs={1}> |
<Grid item xs={1}> |
||||||
<StretchedButton>Reboot</StretchedButton> |
<StretchedButton |
||||||
|
onClick={() => { |
||||||
|
onSubmit?.call(null, { |
||||||
|
actionProceedText: 'Reboot', |
||||||
|
content: ( |
||||||
|
<BodyText>Would you like to reboot this striker?</BodyText> |
||||||
|
), |
||||||
|
onProceedAppend: () => { |
||||||
|
api.put('/command/reboot-host').catch((error) => { |
||||||
|
const emsg = handleAPIError(error); |
||||||
|
|
||||||
|
emsg.children = ( |
||||||
|
<> |
||||||
|
Failed to initiate system reboot; CAUSE: |
||||||
|
{emsg.children} |
||||||
|
</> |
||||||
|
); |
||||||
|
|
||||||
|
setMessage(emsg); |
||||||
|
}); |
||||||
|
}, |
||||||
|
titleText: `Reboot ${title}?`, |
||||||
|
}); |
||||||
|
}} |
||||||
|
> |
||||||
|
Reboot |
||||||
|
</StretchedButton> |
||||||
</Grid> |
</Grid> |
||||||
<Grid item xs={1}> |
<Grid item xs={1}> |
||||||
<StretchedButton>Shutdown</StretchedButton> |
<StretchedButton |
||||||
|
onClick={() => { |
||||||
|
onSubmit?.call(null, { |
||||||
|
actionProceedText: 'Shutdown', |
||||||
|
content: ( |
||||||
|
<BodyText>Would you like to shutdown this striker?</BodyText> |
||||||
|
), |
||||||
|
onProceedAppend: () => { |
||||||
|
api.put('/command/poweroff-host').catch((error) => { |
||||||
|
const emsg = handleAPIError(error); |
||||||
|
|
||||||
|
emsg.children = ( |
||||||
|
<> |
||||||
|
Failed to initiate system shutdown; CAUSE: |
||||||
|
{emsg.children} |
||||||
|
</> |
||||||
|
); |
||||||
|
|
||||||
|
setMessage(emsg); |
||||||
|
}); |
||||||
|
}, |
||||||
|
titleText: `Shutdown ${title}?`, |
||||||
|
}); |
||||||
|
}} |
||||||
|
> |
||||||
|
Shutdown |
||||||
|
</StretchedButton> |
||||||
|
</Grid> |
||||||
|
<Grid item sm={2} xs={1}> |
||||||
|
{message && ( |
||||||
|
<MessageBox |
||||||
|
{...message} |
||||||
|
onClose={() => { |
||||||
|
setMessage(undefined); |
||||||
|
}} |
||||||
|
/> |
||||||
|
)} |
||||||
</Grid> |
</Grid> |
||||||
</Grid> |
</Grid> |
||||||
</Panel> |
</Panel> |
||||||
); |
); |
||||||
|
}; |
||||||
|
|
||||||
export default SimpleOperationsPanel; |
export default SimpleOperationsPanel; |
||||||
|
@ -1,25 +1,88 @@ |
|||||||
import { Box, Grid } from '@mui/material'; |
import { Box, Grid } from '@mui/material'; |
||||||
import { FC } from 'react'; |
import { FC, useEffect, useState } from 'react'; |
||||||
|
|
||||||
|
import ConfirmDialog from '../../components/ConfirmDialog'; |
||||||
import Header from '../../components/Header'; |
import Header from '../../components/Header'; |
||||||
|
|
||||||
import { |
import { |
||||||
ComplexOperationsPanel, |
ComplexOperationsPanel, |
||||||
SimpleOperationsPanel, |
SimpleOperationsPanel, |
||||||
} from '../../components/StrikerConfig'; |
} from '../../components/StrikerConfig'; |
||||||
|
import useProtect from '../../hooks/useProtect'; |
||||||
|
import useProtectedState from '../../hooks/useProtectedState'; |
||||||
|
import api from '../../lib/api'; |
||||||
|
|
||||||
|
const Config: FC = () => { |
||||||
|
const { protect } = useProtect(); |
||||||
|
|
||||||
|
const [isOpenConfirmDialog, setIsOpenConfirmDialog] = |
||||||
|
useState<boolean>(false); |
||||||
|
const [confirmDialogProps, setConfirmDialogProps] = |
||||||
|
useState<ConfirmDialogProps>({ |
||||||
|
actionProceedText: '', |
||||||
|
content: '', |
||||||
|
dialogProps: { open: isOpenConfirmDialog }, |
||||||
|
onCancelAppend: () => { |
||||||
|
setIsOpenConfirmDialog(false); |
||||||
|
}, |
||||||
|
onProceedAppend: () => { |
||||||
|
setIsOpenConfirmDialog(false); |
||||||
|
}, |
||||||
|
titleText: '', |
||||||
|
}); |
||||||
|
const [simpleOpsPanelHeader, setSimpleOpsPanelHeader] = |
||||||
|
useProtectedState<string>('', protect); |
||||||
|
const [simpleOpsInstallTarget, setSimpleOpsInstallTarget] = useState< |
||||||
|
APIHostInstallTarget | undefined |
||||||
|
>(); |
||||||
|
|
||||||
const Config: FC = () => ( |
useEffect(() => { |
||||||
|
if (!simpleOpsPanelHeader) { |
||||||
|
api |
||||||
|
.get<APIHostDetail>('/host/local') |
||||||
|
.then(({ data: { installTarget, shortHostName } }) => { |
||||||
|
setSimpleOpsInstallTarget(installTarget); |
||||||
|
setSimpleOpsPanelHeader(shortHostName); |
||||||
|
}) |
||||||
|
.catch(() => { |
||||||
|
setSimpleOpsPanelHeader('Unknown'); |
||||||
|
}); |
||||||
|
} |
||||||
|
}, [simpleOpsPanelHeader, setSimpleOpsPanelHeader]); |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
<Box sx={{ display: 'flex', flexDirection: 'column' }}> |
<Box sx={{ display: 'flex', flexDirection: 'column' }}> |
||||||
<Header /> |
<Header /> |
||||||
<Grid container columns={{ xs: 1, md: 3, lg: 4 }}> |
<Grid container columns={{ xs: 1, md: 3, lg: 4 }}> |
||||||
<Grid item xs={1}> |
<Grid item xs={1}> |
||||||
<SimpleOperationsPanel strikerHostName="STRIKER NAME" /> |
<SimpleOperationsPanel |
||||||
|
installTarget={simpleOpsInstallTarget} |
||||||
|
onSubmit={({ onProceedAppend, ...restConfirmDialogProps }) => { |
||||||
|
setConfirmDialogProps((previous) => ({ |
||||||
|
...previous, |
||||||
|
...restConfirmDialogProps, |
||||||
|
onProceedAppend: (...args) => { |
||||||
|
onProceedAppend?.call(null, ...args); |
||||||
|
setIsOpenConfirmDialog(false); |
||||||
|
}, |
||||||
|
})); |
||||||
|
|
||||||
|
setIsOpenConfirmDialog(true); |
||||||
|
}} |
||||||
|
title={simpleOpsPanelHeader} |
||||||
|
/> |
||||||
</Grid> |
</Grid> |
||||||
<Grid item md={2} xs={1}> |
<Grid item md={2} xs={1}> |
||||||
<ComplexOperationsPanel /> |
<ComplexOperationsPanel /> |
||||||
</Grid> |
</Grid> |
||||||
</Grid> |
</Grid> |
||||||
</Box> |
</Box> |
||||||
|
<ConfirmDialog |
||||||
|
{...confirmDialogProps} |
||||||
|
dialogProps={{ open: isOpenConfirmDialog }} |
||||||
|
/> |
||||||
|
</> |
||||||
); |
); |
||||||
|
}; |
||||||
|
|
||||||
export default Config; |
export default Config; |
||||||
|
@ -0,0 +1,13 @@ |
|||||||
|
type SimpleOperationsPanelOptionalProps = { |
||||||
|
onSubmit?: ( |
||||||
|
props: Pick< |
||||||
|
ConfirmDialogProps, |
||||||
|
'actionProceedText' | 'content' | 'onProceedAppend' | 'titleText' |
||||||
|
>, |
||||||
|
) => void; |
||||||
|
installTarget?: APIHostInstallTarget; |
||||||
|
}; |
||||||
|
|
||||||
|
type SimpleOperationsPanelProps = SimpleOperationsPanelOptionalProps & { |
||||||
|
title: string; |
||||||
|
}; |
Loading…
Reference in new issue