mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-30 22:50:10 +03:00
parent
06a37707fe
commit
0aba914651
@ -94,6 +94,14 @@ module.exports = {
|
||||
type: SUCCESS_ONE_REQUEST,
|
||||
payload: { acl }
|
||||
}),
|
||||
setProvidersTemplates: providersTemplates => ({
|
||||
type: SUCCESS_ONE_REQUEST,
|
||||
payload: { providersTemplates }
|
||||
}),
|
||||
setProviders: providers => ({
|
||||
type: SUCCESS_ONE_REQUEST,
|
||||
payload: { providers }
|
||||
}),
|
||||
startOneRequest: () => ({
|
||||
type: START_ONE_REQUEST
|
||||
}),
|
||||
|
@ -45,9 +45,11 @@ const useStyles = makeStyles(theme => ({
|
||||
const ApplicationCard = memo(
|
||||
({ value, handleShow, handleRemove }) => {
|
||||
const classes = useStyles()
|
||||
const { NAME, TEMPLATE } = value
|
||||
const { ID, NAME, TEMPLATE } = value
|
||||
const { description, state } = TEMPLATE.BODY
|
||||
|
||||
const stateInfo = APPLICATION_STATES[state]
|
||||
|
||||
return (
|
||||
<Fade in unmountOnExit={false}>
|
||||
<Card className={classes.root}>
|
||||
@ -55,7 +57,7 @@ const ApplicationCard = memo(
|
||||
avatar={<FileIcon />}
|
||||
className={classes.header}
|
||||
classes={{ content: classes.headerContent }}
|
||||
title={NAME}
|
||||
title={`${ID} - ${NAME}`}
|
||||
titleTypographyProps={{
|
||||
variant: 'body2',
|
||||
noWrap: true,
|
||||
@ -71,7 +73,11 @@ const ApplicationCard = memo(
|
||||
/>
|
||||
<CardContent>
|
||||
<Box className={classes.content}>
|
||||
<Chip size="small" label={APPLICATION_STATES[state + 1]?.name} />
|
||||
<Chip
|
||||
size="small"
|
||||
label={stateInfo?.name}
|
||||
style={{ backgroundColor: stateInfo?.color }}
|
||||
/>
|
||||
</Box>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import clsx from 'clsx'
|
||||
@ -18,47 +18,17 @@ import AccountTreeIcon from '@material-ui/icons/AccountTree'
|
||||
import FolderOpenIcon from '@material-ui/icons/FolderOpen'
|
||||
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import SelectCard from './SelectCard'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
root: {
|
||||
height: '100%',
|
||||
minHeight: 140,
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
selected: {
|
||||
color: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
'& $badge': {
|
||||
color: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.common.white
|
||||
}
|
||||
},
|
||||
actionArea: {
|
||||
height: '100%'
|
||||
},
|
||||
header: {
|
||||
overflowX: 'hidden',
|
||||
flexGrow: 1
|
||||
},
|
||||
subheader: {
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'initial',
|
||||
display: '-webkit-box',
|
||||
lineClamp: 2,
|
||||
boxOrient: 'vertical'
|
||||
},
|
||||
badgesWrapper: {
|
||||
display: 'flex',
|
||||
gap: theme.typography.pxToRem(12)
|
||||
},
|
||||
badge: {},
|
||||
icon: {}
|
||||
}
|
||||
}))
|
||||
|
||||
const ClusterCard = React.memo(
|
||||
({ value, isSelected, handleSelect, handleUnselect }) => {
|
||||
const ClusterCard = memo(
|
||||
({ value, isSelected, handleClick }) => {
|
||||
const classes = useStyles()
|
||||
const { NAME, HOSTS, VNETS, DATASTORES } = value
|
||||
|
||||
@ -69,64 +39,48 @@ const ClusterCard = React.memo(
|
||||
const badgePosition = { vertical: 'top', horizontal: 'right' }
|
||||
|
||||
return (
|
||||
<Fade in unmountOnExit={false}>
|
||||
<Card
|
||||
className={clsx(classes.root, { [classes.selected]: isSelected })}
|
||||
>
|
||||
<CardActionArea
|
||||
className={classes.actionArea}
|
||||
onClick={() => (isSelected ? handleUnselect() : handleSelect())}
|
||||
<SelectCard
|
||||
title={NAME}
|
||||
icon={<StorageIcon />}
|
||||
isSelected={isSelected}
|
||||
handleClick={handleClick}
|
||||
>
|
||||
<Box className={classes.badgesWrapper}>
|
||||
<Badge
|
||||
showZero
|
||||
title={Tr('Hosts')}
|
||||
classes={{ badge: 'badge' }}
|
||||
color="primary"
|
||||
badgeContent={hosts.length}
|
||||
anchorOrigin={badgePosition}
|
||||
>
|
||||
<CardHeader
|
||||
avatar={<StorageIcon />}
|
||||
className={classes.header}
|
||||
classes={{ content: classes.headerContent }}
|
||||
title={NAME}
|
||||
titleTypographyProps={{
|
||||
variant: 'body2',
|
||||
noWrap: true,
|
||||
title: NAME
|
||||
}}
|
||||
/>
|
||||
<CardContent>
|
||||
<Box className={classes.badgesWrapper}>
|
||||
<Badge
|
||||
showZero
|
||||
title={Tr('Hosts')}
|
||||
classes={{ badge: classes.badge }}
|
||||
color="primary"
|
||||
badgeContent={hosts.length}
|
||||
anchorOrigin={badgePosition}
|
||||
>
|
||||
<VideogameAssetIcon />
|
||||
</Badge>
|
||||
<Badge
|
||||
showZero
|
||||
title={Tr('Virtual networks')}
|
||||
classes={{ badge: classes.badge }}
|
||||
color="primary"
|
||||
badgeContent={vnets.length}
|
||||
anchorOrigin={badgePosition}
|
||||
>
|
||||
<AccountTreeIcon />
|
||||
</Badge>
|
||||
<Badge
|
||||
showZero
|
||||
title={Tr('Datastores')}
|
||||
classes={{ badge: classes.badge }}
|
||||
color="primary"
|
||||
badgeContent={datastores.length}
|
||||
anchorOrigin={badgePosition}
|
||||
>
|
||||
<FolderOpenIcon />
|
||||
</Badge>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Fade>
|
||||
<VideogameAssetIcon />
|
||||
</Badge>
|
||||
<Badge
|
||||
showZero
|
||||
title={Tr('Virtual networks')}
|
||||
classes={{ badge: 'badge' }}
|
||||
color="primary"
|
||||
badgeContent={vnets.length}
|
||||
anchorOrigin={badgePosition}
|
||||
>
|
||||
<AccountTreeIcon />
|
||||
</Badge>
|
||||
<Badge
|
||||
showZero
|
||||
title={Tr('Datastores')}
|
||||
classes={{ badge: 'badge' }}
|
||||
color="primary"
|
||||
badgeContent={datastores.length}
|
||||
anchorOrigin={badgePosition}
|
||||
>
|
||||
<FolderOpenIcon />
|
||||
</Badge>
|
||||
</Box>
|
||||
</SelectCard>
|
||||
)
|
||||
}
|
||||
},
|
||||
(prev, next) => prev.isSelected === next.isSelected
|
||||
)
|
||||
|
||||
ClusterCard.propTypes = {
|
||||
@ -147,15 +101,13 @@ ClusterCard.propTypes = {
|
||||
])
|
||||
}),
|
||||
isSelected: PropTypes.bool,
|
||||
handleSelect: PropTypes.func,
|
||||
handleUnselect: PropTypes.func
|
||||
handleClick: PropTypes.func
|
||||
}
|
||||
|
||||
ClusterCard.defaultProps = {
|
||||
value: {},
|
||||
isSelected: false,
|
||||
handleSelect: undefined,
|
||||
handleUnselect: undefined
|
||||
handleClick: undefined
|
||||
}
|
||||
|
||||
ClusterCard.displayName = 'ClusterCard'
|
||||
|
@ -2,7 +2,7 @@ import React, { memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import clsx from 'clsx'
|
||||
import { Card, CardActionArea, Fade, makeStyles } from '@material-ui/core'
|
||||
import { Card, CardActionArea, CardContent, CardHeader, Fade, makeStyles } from '@material-ui/core'
|
||||
import { Skeleton } from '@material-ui/lab'
|
||||
|
||||
import useNearScreen from 'client/hooks/useNearScreen'
|
||||
@ -13,17 +13,24 @@ const useStyles = makeStyles(theme => ({
|
||||
},
|
||||
selected: {
|
||||
color: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.primary.main
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
'& .badge': {
|
||||
color: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.common.white
|
||||
}
|
||||
},
|
||||
actionArea: {
|
||||
height: '100%',
|
||||
minHeight: 140,
|
||||
padding: theme.spacing(1)
|
||||
},
|
||||
headerContent: {
|
||||
overflowX: 'hidden'
|
||||
}
|
||||
}))
|
||||
|
||||
const SelectCard = memo(
|
||||
({ title, isSelected, handleClick }) => {
|
||||
({ title, icon, isSelected, handleClick, children }) => {
|
||||
const classes = useStyles()
|
||||
const { isNearScreen, fromRef } = useNearScreen({
|
||||
distance: '100px'
|
||||
@ -35,7 +42,17 @@ const SelectCard = memo(
|
||||
<Fade in={isNearScreen}>
|
||||
<Card className={clsx(classes.root, { [classes.selected]: isSelected })}>
|
||||
<CardActionArea className={classes.actionArea} onClick={handleClick}>
|
||||
<span>{title}</span>
|
||||
<CardHeader
|
||||
avatar={icon}
|
||||
classes={{ content: classes.headerContent }}
|
||||
title={title}
|
||||
titleTypographyProps={{
|
||||
variant: 'body2',
|
||||
noWrap: true,
|
||||
title: title
|
||||
}}
|
||||
/>
|
||||
{children && <CardContent>{children}</CardContent>}
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Fade>
|
||||
@ -44,17 +61,23 @@ const SelectCard = memo(
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
(prev, next) => prev.isSelected === next.isSelected
|
||||
)
|
||||
|
||||
SelectCard.propTypes = {
|
||||
title: PropTypes.string,
|
||||
title: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.object
|
||||
]),
|
||||
icon: PropTypes.object,
|
||||
isSelected: PropTypes.bool,
|
||||
handleClick: PropTypes.func
|
||||
}
|
||||
|
||||
SelectCard.defaultProps = {
|
||||
title: undefined,
|
||||
icon: undefined,
|
||||
isSelected: false,
|
||||
handleClick: () => undefined
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import { useForm, FormProvider } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers'
|
||||
|
||||
import ButtonSubmit from 'client/components/FormControl/SubmitButton'
|
||||
import { Cancel, Save } from 'client/constants/translates'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
const DialogForm = memo(
|
||||
@ -29,7 +30,7 @@ const DialogForm = memo(
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'))
|
||||
|
||||
const { handleSubmit, formState, ...methods } = useForm({
|
||||
mode: 'onChange',
|
||||
mode: 'onBlur',
|
||||
reValidateMode: 'onSubmit',
|
||||
defaultValues: values,
|
||||
resolver: yupResolver(resolver)
|
||||
@ -56,7 +57,7 @@ const DialogForm = memo(
|
||||
<DialogActions>
|
||||
{onCancel && (
|
||||
<Button onClick={onCancel} color="primary">
|
||||
{Tr('Cancel')}
|
||||
{Tr(Cancel)}
|
||||
</Button>
|
||||
)}
|
||||
{onSubmit && (
|
||||
@ -64,7 +65,7 @@ const DialogForm = memo(
|
||||
data-cy="dg-form-submit-button"
|
||||
isSubmitting={formState.isSubmitting}
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
label={Tr('Save')}
|
||||
label={Tr(Save)}
|
||||
{...submitButtonProps}
|
||||
/>
|
||||
)}
|
||||
|
@ -51,7 +51,6 @@ export default makeStyles(theme => ({
|
||||
}
|
||||
},
|
||||
container: {
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
|
@ -26,13 +26,11 @@ const SidebarLink = ({ label, path, icon: Icon, devMode, isSubItem }) => {
|
||||
!isUpLg && fixMenu(false)
|
||||
}
|
||||
|
||||
const isCurrentPathname = pathname === path
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
button
|
||||
onClick={handleClick}
|
||||
selected={isCurrentPathname}
|
||||
selected={pathname === path}
|
||||
className={clsx({ [classes.subItem]: isSubItem })}
|
||||
classes={{ selected: classes.itemSelected }}
|
||||
>
|
||||
@ -71,4 +69,6 @@ SidebarLink.defaultProps = {
|
||||
isSubItem: false
|
||||
}
|
||||
|
||||
SidebarLink.displayName = 'SidebarLink'
|
||||
|
||||
export default SidebarLink
|
||||
|
@ -13,7 +13,9 @@
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
import React, { useMemo } from 'react'
|
||||
import React, { useMemo, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import clsx from 'clsx'
|
||||
import {
|
||||
List,
|
||||
@ -32,7 +34,7 @@ import SidebarLink from 'client/components/Sidebar/SidebarLink'
|
||||
import SidebarCollapseItem from 'client/components/Sidebar/SidebarCollapseItem'
|
||||
import Logo from 'client/icons/logo'
|
||||
|
||||
const Sidebar = ({ endpoints }) => {
|
||||
const Sidebar = memo(({ endpoints }) => {
|
||||
const classes = sidebarStyles()
|
||||
const { isFixMenu, fixMenu } = useGeneral()
|
||||
const isUpLg = useMediaQuery(theme => theme.breakpoints.up('lg'))
|
||||
@ -50,47 +52,54 @@ const Sidebar = ({ endpoints }) => {
|
||||
<SidebarLink key={`item-${index}`} {...endpoint} />
|
||||
)
|
||||
),
|
||||
[endpoints]
|
||||
[]
|
||||
)
|
||||
|
||||
return useMemo(
|
||||
() => (
|
||||
<Drawer
|
||||
variant={'permanent'}
|
||||
className={clsx({ [classes.drawerFixed]: isFixMenu })}
|
||||
classes={{
|
||||
paper: clsx(classes.drawerPaper, {
|
||||
[classes.drawerFixed]: isFixMenu
|
||||
})
|
||||
}}
|
||||
anchor="left"
|
||||
open={isFixMenu}
|
||||
>
|
||||
<Box className={classes.header}>
|
||||
<Logo
|
||||
width="100%"
|
||||
height={100}
|
||||
withText
|
||||
viewBox="0 0 640 640"
|
||||
className={classes.svg}
|
||||
/>
|
||||
<IconButton
|
||||
color={isFixMenu ? 'primary' : 'default'}
|
||||
onClick={handleSwapMenu}
|
||||
>
|
||||
{isUpLg ? <MenuIcon /> : <CloseIcon />}
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Divider />
|
||||
<Box className={classes.menu}>
|
||||
<List className={classes.list} disablePadding>
|
||||
{SidebarEndpoints}
|
||||
</List>
|
||||
</Box>
|
||||
</Drawer>
|
||||
),
|
||||
[isFixMenu, fixMenu, isUpLg]
|
||||
return (
|
||||
<Drawer
|
||||
variant={'permanent'}
|
||||
className={clsx({ [classes.drawerFixed]: isFixMenu })}
|
||||
classes={{
|
||||
paper: clsx(classes.drawerPaper, {
|
||||
[classes.drawerFixed]: isFixMenu
|
||||
})
|
||||
}}
|
||||
anchor="left"
|
||||
open={isFixMenu}
|
||||
>
|
||||
<Box className={classes.header}>
|
||||
<Logo
|
||||
width="100%"
|
||||
height={100}
|
||||
withText
|
||||
viewBox="0 0 640 640"
|
||||
className={classes.svg}
|
||||
/>
|
||||
<IconButton
|
||||
color={isFixMenu ? 'primary' : 'default'}
|
||||
onClick={handleSwapMenu}
|
||||
>
|
||||
{isUpLg ? <MenuIcon /> : <CloseIcon />}
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Divider />
|
||||
<Box className={classes.menu}>
|
||||
<List className={classes.list} disablePadding>
|
||||
{SidebarEndpoints}
|
||||
</List>
|
||||
</Box>
|
||||
</Drawer>
|
||||
)
|
||||
})
|
||||
|
||||
Sidebar.propTypes = {
|
||||
endpoints: PropTypes.array
|
||||
}
|
||||
|
||||
Sidebar.defaultProps = {
|
||||
endpoints: []
|
||||
}
|
||||
|
||||
Sidebar.displayName = 'Sidebar'
|
||||
|
||||
export default Sidebar
|
||||
|
@ -27,6 +27,10 @@ const BY = {
|
||||
|
||||
const ONEADMIN_ID = '0'
|
||||
|
||||
const REQUEST_ACTIONS = {
|
||||
INSTANTIATE: 'instantiate'
|
||||
}
|
||||
|
||||
const FILTER_POOL = {
|
||||
PRIMARY_GROUP_RESOURCES: '-4',
|
||||
USER_RESOURCES: '-3',
|
||||
@ -49,5 +53,6 @@ export {
|
||||
APPS,
|
||||
ONEADMIN_ID,
|
||||
FILTER_POOL,
|
||||
INPUT_TYPES
|
||||
INPUT_TYPES,
|
||||
REQUEST_ACTIONS
|
||||
}
|
||||
|
@ -1,74 +1,74 @@
|
||||
const PENDING = 'PENDING'
|
||||
const DEPLOYING = 'DEPLOYING'
|
||||
const RUNNING = 'RUNNING'
|
||||
const WARNING = 'WARNING'
|
||||
const SCALING = 'SCALING'
|
||||
const COOLDOWN = 'COOLDOWN'
|
||||
const UNDEPLOYING = 'UNDEPLOYING'
|
||||
const DONE = 'DONE'
|
||||
const FAILED_DEPLOYING = 'FAILED_DEPLOYING'
|
||||
const FAILED_UNDEPLOYING = 'FAILED_UNDEPLOYING'
|
||||
const FAILED_SCALING = 'FAILED_SCALING'
|
||||
export const PENDING = 'PENDING'
|
||||
export const DEPLOYING = 'DEPLOYING'
|
||||
export const RUNNING = 'RUNNING'
|
||||
export const UNDEPLOYING = 'UNDEPLOYING'
|
||||
export const WARNING = 'WARNING'
|
||||
export const DONE = 'DONE'
|
||||
export const FAILED_UNDEPLOYING = 'FAILED_UNDEPLOYING'
|
||||
export const FAILED_DEPLOYING = 'FAILED_DEPLOYING'
|
||||
export const SCALING = 'SCALING'
|
||||
export const FAILED_SCALING = 'FAILED_SCALING'
|
||||
export const COOLDOWN = 'COOLDOWN'
|
||||
|
||||
export const APPLICATION_STATES = [
|
||||
{
|
||||
name: PENDING,
|
||||
color: '',
|
||||
color: '#4DBBD3',
|
||||
meaning: `
|
||||
The Application starts in this state, and will stay in
|
||||
it until the LCM decides to deploy it`
|
||||
},
|
||||
{
|
||||
name: DEPLOYING,
|
||||
color: '',
|
||||
color: '#4DBBD3',
|
||||
meaning: 'Some Tiers are being deployed'
|
||||
},
|
||||
{
|
||||
name: RUNNING,
|
||||
color: '',
|
||||
color: '#3adb76',
|
||||
meaning: 'All Tiers are deployed successfully'
|
||||
},
|
||||
{
|
||||
name: WARNING,
|
||||
color: '',
|
||||
meaning: 'A VM was found in a failure state'
|
||||
},
|
||||
{
|
||||
name: SCALING,
|
||||
color: '',
|
||||
meaning: 'A Tier is scaling up or down'
|
||||
},
|
||||
{
|
||||
name: COOLDOWN,
|
||||
color: '',
|
||||
meaning: 'A Tier is in the cooldown period after a scaling operation'
|
||||
},
|
||||
{
|
||||
name: UNDEPLOYING,
|
||||
color: '',
|
||||
color: '#ffa07a',
|
||||
meaning: 'Some Tiers are being undeployed'
|
||||
},
|
||||
{
|
||||
name: WARNING,
|
||||
color: '#ffa07a',
|
||||
meaning: 'A VM was found in a failure state'
|
||||
},
|
||||
{
|
||||
name: DONE,
|
||||
color: '',
|
||||
color: '#ec5840',
|
||||
meaning: `
|
||||
The Applications will stay in this state after
|
||||
a successful undeployment. It can be deleted`
|
||||
},
|
||||
{
|
||||
name: FAILED_DEPLOYING,
|
||||
color: '',
|
||||
meaning: 'An error occurred while deploying the Application'
|
||||
},
|
||||
{
|
||||
name: FAILED_UNDEPLOYING,
|
||||
color: '',
|
||||
color: '#ec5840',
|
||||
meaning: 'An error occurred while undeploying the Application'
|
||||
},
|
||||
{
|
||||
name: FAILED_DEPLOYING,
|
||||
color: '#ec5840',
|
||||
meaning: 'An error occurred while deploying the Application'
|
||||
},
|
||||
{
|
||||
name: SCALING,
|
||||
color: '#ffa07a',
|
||||
meaning: 'A Tier is scaling up or down'
|
||||
},
|
||||
{
|
||||
name: FAILED_SCALING,
|
||||
color: '',
|
||||
color: '#ec5840',
|
||||
meaning: 'An error occurred while scaling the Application'
|
||||
},
|
||||
{
|
||||
name: COOLDOWN,
|
||||
color: '#ffa07a',
|
||||
meaning: 'A Tier is in the cooldown period after a scaling operation'
|
||||
}
|
||||
]
|
||||
|
||||
@ -96,12 +96,12 @@ export const TIER_STATES = [
|
||||
meaning: 'A VM was found in a failure state'
|
||||
},
|
||||
{
|
||||
name: 'SCALING',
|
||||
name: SCALING,
|
||||
color: '',
|
||||
meaning: 'The Tier is waiting for VMs to be deployed or to be shutdown'
|
||||
},
|
||||
{
|
||||
name: 'COOLDOWN',
|
||||
name: COOLDOWN,
|
||||
color: '',
|
||||
meaning: 'The Tier is in the cooldown period after a scaling operation'
|
||||
},
|
||||
@ -123,7 +123,7 @@ export const TIER_STATES = [
|
||||
meaning: 'An error occurred while deploying the VMs'
|
||||
},
|
||||
{
|
||||
name: 'FAILED UNDEPLOYING',
|
||||
name: FAILED_UNDEPLOYING,
|
||||
color: '',
|
||||
meaning: 'An error occurred while undeploying the VMs'
|
||||
},
|
||||
|
@ -1,17 +1,26 @@
|
||||
module.exports = {
|
||||
SignIn: 'Sign In',
|
||||
/* FORM */
|
||||
Back: 'Back',
|
||||
Next: 'Next',
|
||||
|
||||
/* VERBS */
|
||||
SignIn: 'Sign In',
|
||||
SignOut: 'Sign Out',
|
||||
Cancel: 'Cancel',
|
||||
Save: 'Save',
|
||||
Deploy: 'Deploy',
|
||||
Submit: 'Submit',
|
||||
|
||||
/* LOGIN */
|
||||
Username: 'Username',
|
||||
Password: 'Password',
|
||||
Token2FA: '2FA Token',
|
||||
KeepLoggedIn: 'Keep me logged in',
|
||||
|
||||
SelectGroup: 'Select a group',
|
||||
ShowAll: 'Show all',
|
||||
NotFound: 'Not found',
|
||||
Language: 'Language',
|
||||
KeepLoggedIn: 'Keep me logged in',
|
||||
SignOut: 'Sign Out',
|
||||
Submit: 'Submit',
|
||||
Response: 'Response',
|
||||
Settings: 'Settings'
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ const InfoTab = React.memo(({ info }) => {
|
||||
shutdown_action: shutdown
|
||||
} = TEMPLATE?.BODY
|
||||
|
||||
const stateInfo = APPLICATION_STATES[state]
|
||||
|
||||
const isChecked = checked =>
|
||||
checked === '1' ? <CheckBox /> : <CheckBoxOutlineBlank />
|
||||
|
||||
@ -52,7 +54,8 @@ const InfoTab = React.memo(({ info }) => {
|
||||
<Typography>{Tr('State')}</Typography>
|
||||
<Chip
|
||||
size="small"
|
||||
label={APPLICATION_STATES[state + 1]?.name}
|
||||
label={stateInfo?.name}
|
||||
style={{ backgroundColor: stateInfo?.color }}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { Box, LinearProgress } from '@material-ui/core'
|
||||
import { Alert } from '@material-ui/lab'
|
||||
@ -8,10 +8,11 @@ import useFetch from 'client/hooks/useFetch'
|
||||
|
||||
import ListCards from 'client/components/List/ListCards'
|
||||
import DialogInfo from 'client/containers/ApplicationsInstances/DialogInfo'
|
||||
|
||||
import { ApplicationCard, EmptyCard } from 'client/components/Cards'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
|
||||
import { DONE, APPLICATION_STATES } from 'client/constants/states'
|
||||
|
||||
function ApplicationsInstances () {
|
||||
const { applications, getApplications } = useApplication()
|
||||
const [showDialog, setShowDialog] = useState(false)
|
||||
@ -21,6 +22,14 @@ function ApplicationsInstances () {
|
||||
fetchRequest()
|
||||
}, [])
|
||||
|
||||
const list = useMemo(() => (
|
||||
applications.length > 0
|
||||
? applications?.filter(({ TEMPLATE: { BODY: { state } } }) =>
|
||||
APPLICATION_STATES[state]?.name !== DONE
|
||||
)
|
||||
: applications
|
||||
), [applications])
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Box pt={3} display="flex" justifyContent="center">
|
||||
@ -38,7 +47,7 @@ function ApplicationsInstances () {
|
||||
return (
|
||||
<Box p={3}>
|
||||
<ListCards
|
||||
list={applications}
|
||||
list={list}
|
||||
EmptyComponent={<EmptyCard name={'applications instances'} />}
|
||||
CardComponent={ApplicationCard}
|
||||
cardsProps={({ value }) => ({
|
||||
|
@ -30,11 +30,12 @@ const Clusters = () => ({
|
||||
list={clusters}
|
||||
EmptyComponent={<EmptyCard name={'clusters'} />}
|
||||
CardComponent={ClusterCard}
|
||||
cardsProps={({ value: { ID } }) => ({
|
||||
isSelected: data?.some(selected => selected === ID),
|
||||
handleSelect: () => handleSelect(ID),
|
||||
handleUnselect: () => handleUnselect(ID)
|
||||
})}
|
||||
cardsProps={({ value: { ID } }) => {
|
||||
const isSelected = data?.some(selected => selected === ID)
|
||||
const handleClick = () => isSelected ? handleUnselect(ID) : handleSelect(ID)
|
||||
|
||||
return { isSelected, handleClick }
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}, [])
|
||||
|
@ -52,7 +52,7 @@ const Policies = () => ({
|
||||
})
|
||||
|
||||
const { watch } = useFormContext()
|
||||
const policies = watch('policies')
|
||||
const policies = watch(STEP_ID)
|
||||
|
||||
useEffect(() => () => {
|
||||
handleSetList(policies)
|
||||
|
@ -102,6 +102,27 @@ export default function useOpennebula () {
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const instantiateApplicationTemplate = useCallback(
|
||||
({ id, data }) =>
|
||||
serviceApplication
|
||||
.instantiateTemplate({ id, data })
|
||||
.then(doc => {
|
||||
dispatch(
|
||||
setApplicationsTemplates(
|
||||
filterBy([doc].concat(applicationsTemplates), 'ID')
|
||||
)
|
||||
)
|
||||
|
||||
return dispatch(enqueueSuccess(`Template instantiate - ID: ${doc.ID}`))
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(
|
||||
enqueueError(err?.message ?? 'Error INSTANTIATE application template')
|
||||
)
|
||||
}),
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
return {
|
||||
applications,
|
||||
getApplication,
|
||||
@ -111,6 +132,7 @@ export default function useOpennebula () {
|
||||
getApplicationTemplate,
|
||||
getApplicationsTemplates,
|
||||
createApplicationTemplate,
|
||||
updateApplicationTemplate
|
||||
updateApplicationTemplate,
|
||||
instantiateApplicationTemplate
|
||||
}
|
||||
}
|
||||
|
87
src/fireedge/src/client/hooks/useProvision.js
Normal file
87
src/fireedge/src/client/hooks/useProvision.js
Normal file
@ -0,0 +1,87 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useSelector, useDispatch, shallowEqual } from 'react-redux'
|
||||
|
||||
import { setProvidersTemplates, setProviders } from 'client/actions/pool'
|
||||
|
||||
import { enqueueError, enqueueSuccess } from 'client/actions/general'
|
||||
|
||||
import * as serviceProvision from 'client/services/provision'
|
||||
|
||||
export default function useOpennebula () {
|
||||
const dispatch = useDispatch()
|
||||
const {
|
||||
providersTemplates,
|
||||
providers,
|
||||
filterPool: filter
|
||||
} = useSelector(
|
||||
state => ({
|
||||
...state?.Opennebula,
|
||||
filterPool: state?.Authenticated?.filterPool
|
||||
}),
|
||||
shallowEqual
|
||||
)
|
||||
|
||||
const getProvidersTemplates = useCallback(
|
||||
({ end, start } = { end: -1, start: -1 }) =>
|
||||
serviceProvision
|
||||
.getProvidersTemplates({ filter, end, start })
|
||||
.then(doc => {
|
||||
dispatch(setProvidersTemplates(doc))
|
||||
return doc
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(enqueueError(err ?? 'Error GET providers templates'))
|
||||
}),
|
||||
[dispatch, filter, providersTemplates]
|
||||
)
|
||||
|
||||
const getProvider = useCallback(
|
||||
({ id }) =>
|
||||
serviceProvision.getProvider({ id }).catch(err => {
|
||||
dispatch(enqueueError(err ?? `Error GET (${id}) provider`))
|
||||
}),
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const getProviders = useCallback(
|
||||
({ end, start } = { end: -1, start: -1 }) =>
|
||||
serviceProvision
|
||||
.getProviders({ filter, end, start })
|
||||
.then(doc => dispatch(setProviders(doc)))
|
||||
.catch(err => {
|
||||
dispatch(enqueueError(err ?? 'Error GET providers'))
|
||||
}),
|
||||
[dispatch, filter]
|
||||
)
|
||||
|
||||
const createProvider = useCallback(
|
||||
({ data }) =>
|
||||
serviceProvision
|
||||
.createProvider({ data })
|
||||
.then(doc => {
|
||||
/* dispatch(
|
||||
setApplicationsTemplates(
|
||||
filterBy([doc].concat(applicationsTemplates), 'ID')
|
||||
)
|
||||
) */
|
||||
|
||||
return dispatch(enqueueSuccess(`Template created - ID: ${doc?.ID}`))
|
||||
})
|
||||
.catch(err => {
|
||||
dispatch(
|
||||
enqueueError(err?.message ?? 'Error CREATE provider')
|
||||
)
|
||||
}),
|
||||
[dispatch, providers]
|
||||
)
|
||||
|
||||
return {
|
||||
providersTemplates,
|
||||
getProvidersTemplates,
|
||||
|
||||
providers,
|
||||
getProvider,
|
||||
getProviders,
|
||||
createProvider
|
||||
}
|
||||
}
|
@ -37,7 +37,10 @@ const initial = {
|
||||
users: [],
|
||||
groups: [],
|
||||
vdc: [],
|
||||
acl: []
|
||||
acl: [],
|
||||
providersTemplates: [],
|
||||
providers: [],
|
||||
provisions: []
|
||||
}
|
||||
|
||||
const Opennebula = (state = initial, action) => {
|
||||
|
@ -3,7 +3,6 @@ import {
|
||||
FormatListBulleted as TemplatesIcons,
|
||||
Apps as InstancesIcons
|
||||
} from '@material-ui/icons'
|
||||
import { matchPath } from 'react-router-dom'
|
||||
|
||||
import Login from 'client/containers/Login'
|
||||
import Dashboard from 'client/containers/Dashboard'
|
||||
|
@ -1,11 +1,13 @@
|
||||
import httpCodes from 'server/utils/constants/http-codes'
|
||||
import { httpMethod } from 'server/utils/constants/defaults'
|
||||
import {
|
||||
SERVICE,
|
||||
SERVICE_TEMPLATE
|
||||
SERVICE_TEMPLATE,
|
||||
SERVICE_TEMPLATE_ACTION
|
||||
} from 'server/routes/api/oneflow/string-routes'
|
||||
|
||||
import { requestData } from 'client/utils'
|
||||
import httpCodes from 'server/utils/constants/http-codes'
|
||||
import { REQUEST_ACTIONS } from 'client/constants'
|
||||
|
||||
export const getApplication = ({ id }) =>
|
||||
requestData(`/api/${SERVICE}/${id}`, {
|
||||
@ -75,6 +77,22 @@ export const updateTemplate = ({ id, data = {} }) =>
|
||||
return res?.data?.DOCUMENT ?? {}
|
||||
})
|
||||
|
||||
export const instantiateTemplate = ({ id, data = {} }) =>
|
||||
requestData(`/api/${SERVICE_TEMPLATE_ACTION}/${id}`, {
|
||||
data: {
|
||||
action: {
|
||||
perform: REQUEST_ACTIONS.INSTANTIATE,
|
||||
params: { merge_template: data }
|
||||
}
|
||||
},
|
||||
method: httpMethod.POST
|
||||
}).then(res => {
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res
|
||||
if (!res?.data?.DOCUMENT?.ID) throw new Error('Error')
|
||||
|
||||
return res?.data?.DOCUMENT ?? {}
|
||||
})
|
||||
|
||||
export default {
|
||||
getApplication,
|
||||
getApplications,
|
||||
@ -83,5 +101,6 @@ export default {
|
||||
getTemplate,
|
||||
getTemplates,
|
||||
createTemplate,
|
||||
updateTemplate
|
||||
updateTemplate,
|
||||
instantiateTemplate
|
||||
}
|
||||
|
17
src/fireedge/src/client/services/provision.js
Normal file
17
src/fireedge/src/client/services/provision.js
Normal file
@ -0,0 +1,17 @@
|
||||
export const getProvidersTemplates = () => Promise.resolve([])
|
||||
|
||||
export const getProvider = () => Promise.resolve({})
|
||||
|
||||
export const getProviders = () => Promise.resolve([])
|
||||
.then(res => [res?.data?.DOCUMENT_POOL?.DOCUMENT ?? []].flat())
|
||||
|
||||
export const createProvider = ({ data = {} }) =>
|
||||
Promise.resolve().then(res => res?.data?.DOCUMENT ?? {})
|
||||
|
||||
export default {
|
||||
getProvidersTemplates,
|
||||
|
||||
getProvider,
|
||||
getProviders,
|
||||
createProvider
|
||||
}
|
@ -6,15 +6,15 @@ export default {
|
||||
default: '#fafafa'
|
||||
},
|
||||
primary: {
|
||||
light: 'rgba(220, 185, 161, 1)',
|
||||
main: 'rgba(174, 143, 122, 1)',
|
||||
dark: 'rgba(164, 132, 112, 1)',
|
||||
light: 'rgba(254, 250, 224, 1)',
|
||||
main: 'rgba(221, 161, 94, 1)',
|
||||
dark: 'rgba(188, 108, 37, 1)',
|
||||
contrastText: '#fff'
|
||||
},
|
||||
secondary: {
|
||||
light: 'rgba(199, 201, 200, 1)',
|
||||
main: 'rgba(87, 92, 91, 1)',
|
||||
dark: 'rgba(53, 55, 53, 1)',
|
||||
light: 'rgba(195, 216, 156, 1)',
|
||||
main: 'rgba(96, 108, 56, 1)',
|
||||
dark: 'rgba(40, 54, 24, 1)',
|
||||
contrastText: '#fff'
|
||||
},
|
||||
error: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user