mirror of
https://github.com/OpenNebula/one.git
synced 2025-01-13 13:17:39 +03:00
parent
d892fde53b
commit
c6dd6f60a4
@ -13,28 +13,55 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { memo } from 'react'
|
||||
import { memo, useEffect } from 'react'
|
||||
|
||||
import { Box, Link, Typography } from '@mui/material'
|
||||
import { styled, Link, Typography } from '@mui/material'
|
||||
|
||||
import footerStyles from 'client/components/Footer/styles'
|
||||
import { useSystem, useSystemApi } from 'client/features/One'
|
||||
import { BY } from 'client/constants'
|
||||
|
||||
const FooterBox = styled('footer')(({ theme }) => ({
|
||||
color: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
left: 'auto',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
zIndex: theme.zIndex.appBar,
|
||||
textAlign: 'center',
|
||||
padding: theme.spacing(0.6)
|
||||
}))
|
||||
|
||||
const HeartIcon = styled('span')(({ theme }) => ({
|
||||
margin: theme.spacing(0, 1),
|
||||
color: theme.palette.error.dark,
|
||||
'&:before': {
|
||||
content: "'❤️'"
|
||||
}
|
||||
}))
|
||||
|
||||
const Footer = memo(() => {
|
||||
const classes = footerStyles()
|
||||
const { config, version } = useSystem()
|
||||
const { getOneVersion } = useSystemApi()
|
||||
|
||||
useEffect(() => {
|
||||
!version && getOneVersion()
|
||||
}, [])
|
||||
|
||||
console.log({ config, version })
|
||||
|
||||
return (
|
||||
<Box className={classes.footer} component="footer">
|
||||
<Typography variant="body2">
|
||||
<FooterBox>
|
||||
<Typography variant='body2'>
|
||||
{'Made with'}
|
||||
<span className={classes.heartIcon} role="img" aria-label="heart-emoji">
|
||||
{'❤️'}
|
||||
</span>
|
||||
<Link href={BY.url} className={classes.link}>
|
||||
<HeartIcon role='img' aria-label='heart-emoji' />
|
||||
<Link href={BY.url} color='primary.contrastText'>
|
||||
{BY.text}
|
||||
{version}
|
||||
</Link>
|
||||
</Typography>
|
||||
</Box>
|
||||
</FooterBox>
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -23,7 +23,7 @@ import { useListForm } from 'client/hooks'
|
||||
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
|
||||
import SelectCard, { Action } from 'client/components/Cards/SelectCard'
|
||||
import { AttachNicForm } from 'client/components/Forms/Vm'
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
|
||||
import { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration'
|
||||
import { SCHEMA as EXTRA_SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema'
|
||||
@ -68,7 +68,8 @@ const Networking = ({ data, setFormData, control }) => {
|
||||
buttonProps={{
|
||||
color: 'secondary',
|
||||
'data-cy': 'add-nic',
|
||||
label: Tr(T.AttachNic)
|
||||
label: T.AttachNic,
|
||||
variant: 'outlined'
|
||||
}}
|
||||
options={[{
|
||||
dialogProps: { title: T.AttachNic },
|
||||
|
@ -23,7 +23,7 @@ import { useListForm } from 'client/hooks'
|
||||
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
|
||||
import SelectCard, { Action } from 'client/components/Cards/SelectCard'
|
||||
import { PunctualForm, RelativeForm } from 'client/components/Forms/Vm'
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
|
||||
import { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration'
|
||||
import { T } from 'client/constants'
|
||||
@ -58,7 +58,8 @@ const ScheduleAction = ({ setFormData, control }) => {
|
||||
buttonProps={{
|
||||
color: 'secondary',
|
||||
'data-cy': 'add-sched-action',
|
||||
label: Tr(T.AddAction)
|
||||
label: T.AddAction,
|
||||
variant: 'outlined'
|
||||
}}
|
||||
options={[{
|
||||
cy: 'add-sched-action-punctual',
|
||||
|
@ -24,7 +24,7 @@ import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
|
||||
import SelectCard, { Action } from 'client/components/Cards/SelectCard'
|
||||
import { ImageSteps, VolatileSteps } from 'client/components/Forms/Vm'
|
||||
import { StatusCircle, StatusChip } from 'client/components/Status'
|
||||
import { Tr, Translate } from 'client/components/HOC'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
|
||||
import { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration'
|
||||
import { SCHEMA as EXTRA_SCHEMA } from 'client/components/Forms/VmTemplate/InstantiateForm/Steps/ExtraConfiguration/schema'
|
||||
@ -71,7 +71,8 @@ const Storage = ({ data, setFormData, hypervisor, control }) => {
|
||||
buttonProps={{
|
||||
color: 'secondary',
|
||||
'data-cy': 'add-disk',
|
||||
label: Tr(T.AttachDisk)
|
||||
label: T.AttachDisk,
|
||||
variant: 'outlined'
|
||||
}}
|
||||
options={[
|
||||
{
|
||||
|
@ -13,8 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useMemo, memo } from 'react'
|
||||
import { useMemo, memo, JSXElementConstructor } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Button } from '@mui/material'
|
||||
@ -40,11 +39,9 @@ const ButtonGroup = memo(({ group, handleClick }) => {
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={`switcher-group-${ID}`}
|
||||
fullWidth
|
||||
color='debug'
|
||||
variant='outlined'
|
||||
tooltip={<Translate Word={T.Group} />}
|
||||
onClick={() => {
|
||||
ID && changeGroup({ id: user.ID, group: ID })
|
||||
handleClick()
|
||||
@ -61,6 +58,12 @@ const ButtonGroup = memo(({ group, handleClick }) => {
|
||||
)
|
||||
}, (prev, next) => prev.group.ID === next.group.ID)
|
||||
|
||||
/**
|
||||
* Menu to select the user group that
|
||||
* will be used to filter the resources.
|
||||
*
|
||||
* @returns {JSXElementConstructor} Returns group list
|
||||
*/
|
||||
const Group = () => {
|
||||
const { user, groups } = useAuth()
|
||||
|
||||
@ -96,6 +99,7 @@ const Group = () => {
|
||||
maxResults={5}
|
||||
renderResult={group => (
|
||||
<ButtonGroup
|
||||
key={`switcher-group-${group?.ID}`}
|
||||
group={group}
|
||||
handleClick={handleClose}
|
||||
/>
|
||||
|
@ -14,110 +14,117 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { memo, useState, useRef } from 'react'
|
||||
import { memo, useState, useRef, useMemo, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Box, useMediaQuery, Popover, Typography, Tooltip, IconButton, Button } from '@mui/material'
|
||||
import { Cancel as CloseIcon, NavArrowDown as CaretIcon } from 'iconoir-react'
|
||||
|
||||
import {
|
||||
Paper,
|
||||
useMediaQuery,
|
||||
Popper,
|
||||
Typography,
|
||||
useTheme,
|
||||
IconButton,
|
||||
Button,
|
||||
Fade,
|
||||
Box
|
||||
} from '@mui/material'
|
||||
|
||||
const HeaderPopover = memo(({
|
||||
id,
|
||||
icon,
|
||||
tooltip,
|
||||
buttonLabel,
|
||||
buttonProps,
|
||||
headerTitle,
|
||||
disablePadding,
|
||||
popoverProps,
|
||||
children
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [tooltipOpen, setTooltipOpen] = useState(false)
|
||||
const anchorRef = useRef(null)
|
||||
const { zIndex } = useTheme()
|
||||
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'))
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen(prevOpen => !prevOpen)
|
||||
tooltip && setTooltipOpen(false)
|
||||
}
|
||||
const [open, setOpen] = useState(false)
|
||||
const [fix, setFix] = useState(false)
|
||||
const anchorRef = useRef(null)
|
||||
|
||||
const handleClose = () => setOpen(false)
|
||||
const handleToggle = () => isMobile && setFix(prevFix => !prevFix)
|
||||
|
||||
const mobileStyles = useMemo(() => ({
|
||||
...(isMobile && {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
})
|
||||
}), [isMobile])
|
||||
|
||||
useEffect(() => {
|
||||
!isMobile && fix && setFix(false)
|
||||
}, [isMobile])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
open={tooltipOpen}
|
||||
onOpen={() => tooltip && setTooltipOpen(!open)}
|
||||
onClose={() => tooltip && setTooltipOpen(false)}
|
||||
title={tooltip ?? ''}
|
||||
enterDelay={300}
|
||||
<div {...!isMobile && {
|
||||
onMouseOver: () => setOpen(true),
|
||||
onFocus: () => setOpen(true),
|
||||
onMouseOut: () => setOpen(false)
|
||||
}}>
|
||||
<Button
|
||||
ref={anchorRef}
|
||||
aria-controls={open ? `${id}-popover` : undefined}
|
||||
aria-haspopup
|
||||
aria-expanded={open ? 'true' : 'false'}
|
||||
onClick={handleToggle}
|
||||
size='small'
|
||||
sx={{ margin: '0 2px' }}
|
||||
endIcon={<CaretIcon />}
|
||||
startIcon={icon}
|
||||
{...buttonProps}
|
||||
>
|
||||
<Button
|
||||
ref={anchorRef}
|
||||
aria-controls={open ? `${id}-popover` : undefined}
|
||||
aria-haspopup='true'
|
||||
onClick={handleToggle}
|
||||
size='small'
|
||||
sx={{ margin: '0 2px' }}
|
||||
endIcon={<CaretIcon />}
|
||||
{...buttonProps}
|
||||
>
|
||||
{icon}
|
||||
{buttonLabel && (
|
||||
<Box pl={1} sx={{ display: { xs: 'none', sm: 'block' } }}>
|
||||
{buttonLabel}
|
||||
</Box>
|
||||
)}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Popover
|
||||
BackdropProps={{ invisible: !isMobile }}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
...(isMobile && {
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}),
|
||||
p: disablePadding ? 0 : 1
|
||||
}
|
||||
}}
|
||||
{!isMobile && buttonLabel}
|
||||
</Button>
|
||||
<Popper
|
||||
id={id}
|
||||
open={open}
|
||||
anchorEl={anchorRef.current}
|
||||
onClose={handleClose}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'right'
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right'
|
||||
open={fix || open}
|
||||
anchorEl={isMobile ? window.document : anchorRef.current}
|
||||
transition
|
||||
placement='bottom-end'
|
||||
keepMounted={false}
|
||||
style={{
|
||||
zIndex: zIndex.appBar + 1,
|
||||
...mobileStyles
|
||||
}}
|
||||
{...popoverProps}
|
||||
>
|
||||
{(headerTitle || isMobile) && (
|
||||
<Box
|
||||
display='flex'
|
||||
alignItems='center'
|
||||
justifyContent='flex-end'
|
||||
borderBottom='1px solid'
|
||||
borderBottomColor='action.disabledBackground'
|
||||
>
|
||||
{headerTitle && (
|
||||
<Typography sx={{ userSelect: 'none' }} variant='body1'>
|
||||
{headerTitle}
|
||||
</Typography>
|
||||
)}
|
||||
{isMobile && (
|
||||
<IconButton onClick={handleClose} size='large'>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
{({ TransitionProps }) => (
|
||||
<Fade {...TransitionProps} timeout={350}>
|
||||
<Paper
|
||||
variant='outlined'
|
||||
style={mobileStyles}
|
||||
sx={{ p: headerTitle ? 2 : 0 }}
|
||||
>
|
||||
{(headerTitle || isMobile) && (
|
||||
<Box
|
||||
display='flex'
|
||||
alignItems='center'
|
||||
justifyContent='space-between'
|
||||
borderBottom='1px solid'
|
||||
borderColor='divider'
|
||||
>
|
||||
{headerTitle && (
|
||||
<Typography variant='body1'>
|
||||
{headerTitle}
|
||||
</Typography>
|
||||
)}
|
||||
{isMobile && (
|
||||
<IconButton onClick={handleToggle} size='large'>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
{children({ handleClose: handleToggle })}
|
||||
</Paper>
|
||||
</Fade>
|
||||
)}
|
||||
{children({ handleClose })}
|
||||
</Popover>
|
||||
</>
|
||||
</Popper>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { memo } from 'react'
|
||||
import { JSXElementConstructor } from 'react'
|
||||
|
||||
import { MenuItem, MenuList, Link } from '@mui/material'
|
||||
import { ProfileCircled as UserIcon } from 'iconoir-react'
|
||||
@ -25,7 +25,12 @@ import { Translate } from 'client/components/HOC'
|
||||
import { isDevelopment } from 'client/utils'
|
||||
import { T, APPS, APP_URL } from 'client/constants'
|
||||
|
||||
const User = memo(() => {
|
||||
/**
|
||||
* Menu with actions about App: signOut, etc.
|
||||
*
|
||||
* @returns {JSXElementConstructor} Returns user actions list
|
||||
*/
|
||||
const User = () => {
|
||||
const { user } = useAuth()
|
||||
const { logout } = useAuthApi()
|
||||
|
||||
@ -39,12 +44,12 @@ const User = memo(() => {
|
||||
>
|
||||
{() => (
|
||||
<MenuList>
|
||||
<MenuItem onClick={logout} disableRipple data-cy='header-logout-button'>
|
||||
<MenuItem onClick={logout} data-cy='header-logout-button'>
|
||||
<Translate word={T.SignOut} />
|
||||
</MenuItem>
|
||||
{isDevelopment() &&
|
||||
APPS?.map(appName => (
|
||||
<MenuItem key={appName} disableRipple>
|
||||
<MenuItem key={appName}>
|
||||
<Link
|
||||
width='100%'
|
||||
color='secondary'
|
||||
@ -59,7 +64,7 @@ const User = memo(() => {
|
||||
)}
|
||||
</HeaderPopover>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
User.displayName = 'UserHeaderComponent'
|
||||
|
||||
|
@ -13,8 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useMemo, memo } from 'react'
|
||||
import { useMemo, memo, JSXElementConstructor } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Button } from '@mui/material'
|
||||
@ -33,7 +32,6 @@ const ButtonView = memo(({ view, handleClick }) => {
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={`view-${view}`}
|
||||
fullWidth
|
||||
color='debug'
|
||||
variant='outlined'
|
||||
@ -60,6 +58,14 @@ ButtonView.propTypes = {
|
||||
|
||||
ButtonView.displayName = 'ButtonView'
|
||||
|
||||
/**
|
||||
* Menu to select the view that
|
||||
* will be used to filter the resources.
|
||||
*
|
||||
* These views are defined in yaml config.
|
||||
*
|
||||
* @returns {JSXElementConstructor} Returns interface views list
|
||||
*/
|
||||
const View = () => {
|
||||
const { view: currentView, views = {} } = useAuth()
|
||||
const viewNames = useMemo(() => Object.keys(views), [currentView])
|
||||
@ -78,6 +84,7 @@ const View = () => {
|
||||
maxResults={5}
|
||||
renderResult={view => (
|
||||
<ButtonView
|
||||
key={`view-${view}`}
|
||||
view={view}
|
||||
handleClick={handleClose}
|
||||
/>
|
||||
|
@ -13,34 +13,52 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { memo } from 'react'
|
||||
import { useEffect, JSXElementConstructor } from 'react'
|
||||
|
||||
import { MenuItem, MenuList } from '@mui/material'
|
||||
import { MenuList, MenuItem } from '@mui/material'
|
||||
import { Language as ZoneIcon } from 'iconoir-react'
|
||||
|
||||
import { useZone, useZoneApi } from 'client/features/One'
|
||||
import HeaderPopover from 'client/components/Header/Popover'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const Zone = memo(() => (
|
||||
<HeaderPopover
|
||||
id='zone-menu'
|
||||
tooltip={T.Zone}
|
||||
icon={<ZoneIcon />}
|
||||
buttonProps={{
|
||||
'data-cy': 'header-zone-button'
|
||||
}}
|
||||
disablePadding
|
||||
>
|
||||
{({ handleClose }) => (
|
||||
<MenuList>
|
||||
<MenuItem onClick={handleClose}>
|
||||
<Translate word={T.Zone} />
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
)}
|
||||
</HeaderPopover>
|
||||
))
|
||||
/**
|
||||
* Menu to select the OpenNebula Zone.
|
||||
*
|
||||
* @returns {JSXElementConstructor} Returns Zone list
|
||||
*/
|
||||
const Zone = () => {
|
||||
const zones = useZone()
|
||||
const { getZones } = useZoneApi()
|
||||
|
||||
useEffect(() => {
|
||||
!zones?.length && getZones()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<HeaderPopover
|
||||
id='zone-menu'
|
||||
tooltip={T.Zone}
|
||||
icon={<ZoneIcon />}
|
||||
buttonProps={{ 'data-cy': 'header-zone-button' }}
|
||||
headerTitle={<Translate word={T.Zones} />}
|
||||
>
|
||||
{({ handleClose }) => (
|
||||
<MenuList>
|
||||
{zones?.length
|
||||
? zones?.map(({ ID, NAME }) => (
|
||||
<MenuItem key={`zone-${ID}`} onClick={handleClose}>
|
||||
{NAME}
|
||||
</MenuItem>
|
||||
))
|
||||
: <MenuItem disabled>{'Not zones found'}</MenuItem>
|
||||
}
|
||||
</MenuList>
|
||||
)}
|
||||
</HeaderPopover>
|
||||
)
|
||||
}
|
||||
|
||||
Zone.displayName = 'ZoneHeaderComponent'
|
||||
|
||||
|
@ -47,10 +47,17 @@ const Header = () => {
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Box flexGrow={1} ml={2} display='inline-flex'>
|
||||
<Box
|
||||
flexGrow={1}
|
||||
ml={2}
|
||||
sx={{
|
||||
display: { xs: 'none', sm: 'inline-flex' },
|
||||
userSelect: 'none'
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant='h6'
|
||||
data-cy='header-app-title'
|
||||
sx={{ userSelect: 'none', typography: 'h6' }}
|
||||
>
|
||||
{'One'}
|
||||
<Typography
|
||||
@ -75,7 +82,6 @@ const Header = () => {
|
||||
variant='h6'
|
||||
data-cy='header-description'
|
||||
sx={{
|
||||
useSelect: 'none',
|
||||
display: { xs: 'none', xl: 'block' },
|
||||
'&::before': {
|
||||
content: '"|"',
|
||||
@ -87,7 +93,7 @@ const Header = () => {
|
||||
{title}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack direction='row'>
|
||||
<Stack direction='row' flexGrow={1} justifyContent='end'>
|
||||
<User />
|
||||
<View />
|
||||
{!isOneAdmin && <Group />}
|
||||
|
@ -22,7 +22,7 @@ import clsx from 'clsx'
|
||||
import {
|
||||
List,
|
||||
Collapse,
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
ListItemIcon,
|
||||
useMediaQuery
|
||||
@ -56,7 +56,7 @@ const SidebarCollapseItem = ({ label, routes, icon: Icon }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListItem button onClick={handleExpand}>
|
||||
<ListItemButton onClick={handleExpand}>
|
||||
{Icon && (
|
||||
<ListItemIcon>
|
||||
<Icon />
|
||||
@ -69,7 +69,7 @@ const SidebarCollapseItem = ({ label, routes, icon: Icon }) => {
|
||||
data-min-label={label.slice(0, 3)}
|
||||
/>
|
||||
{expanded ? <CollapseIcon/> : <ExpandMoreIcon />}
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
{routes
|
||||
?.filter(({ sidebar = false, label }) => sidebar && typeof label === 'string')
|
||||
?.map((subItem, index) => (
|
||||
@ -80,7 +80,7 @@ const SidebarCollapseItem = ({ label, routes, icon: Icon }) => {
|
||||
unmountOnExit
|
||||
className={clsx({ [classes.subItemWrapper]: isUpLg && !isFixMenu })}
|
||||
>
|
||||
<List component='div' disablePadding>
|
||||
<List component='div'>
|
||||
<SidebarLink {...subItem} isSubItem />
|
||||
</List>
|
||||
</Collapse>
|
||||
|
@ -16,10 +16,9 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import PropTypes from 'prop-types'
|
||||
import { useHistory, useLocation } from 'react-router-dom'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import {
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
useMediaQuery
|
||||
@ -45,13 +44,10 @@ const SidebarLink = ({ label, path, icon: Icon, devMode, isSubItem }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
button
|
||||
component='li'
|
||||
<ListItemButton
|
||||
onClick={handleClick}
|
||||
selected={pathname === path}
|
||||
className={clsx({ [classes.subItem]: isSubItem })}
|
||||
classes={{ selected: classes.itemSelected }}
|
||||
className={isSubItem && classes.subItem}
|
||||
data-cy='main-menu-item'
|
||||
>
|
||||
{Icon && (
|
||||
@ -68,7 +64,7 @@ const SidebarLink = ({ label, path, icon: Icon, devMode, isSubItem }) => {
|
||||
) : label
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,7 @@ import {
|
||||
Divider,
|
||||
Box,
|
||||
IconButton,
|
||||
useMediaQuery,
|
||||
Tooltip
|
||||
useMediaQuery
|
||||
} from '@mui/material'
|
||||
|
||||
import {
|
||||
@ -36,8 +35,6 @@ import {
|
||||
|
||||
import { useGeneral, useGeneralApi } from 'client/features/General'
|
||||
import { OpenNebulaLogo } from 'client/components/Icons'
|
||||
import { Translate } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
import sidebarStyles from 'client/components/Sidebar/styles'
|
||||
import SidebarLink from 'client/components/Sidebar/SidebarLink'
|
||||
@ -88,22 +85,18 @@ const Sidebar = ({ endpoints }) => {
|
||||
disabledBetaText
|
||||
/>
|
||||
{!isUpLg || isFixMenu ? (
|
||||
<Tooltip title={<Translate word={T.Close} />}>
|
||||
<IconButton onClick={handleSwapMenu} variant='outlined' size='small'>
|
||||
{!isUpLg ? <CloseIcon/> : <ArrowLeftIcon />}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<IconButton onClick={handleSwapMenu}>
|
||||
{!isUpLg ? <CloseIcon/> : <ArrowLeftIcon />}
|
||||
</IconButton>
|
||||
) : (
|
||||
<Tooltip title={<Translate word={T.Pin} />}>
|
||||
<IconButton onClick={handleSwapMenu} variant='outlined' size='small'>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<IconButton onClick={handleSwapMenu}>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
<Divider />
|
||||
<Box className={classes.menu}>
|
||||
<List className={classes.list} disablePadding data-cy='main-menu'>
|
||||
<List className={classes.list} data-cy='main-menu'>
|
||||
{SidebarEndpoints}
|
||||
</List>
|
||||
</Box>
|
||||
|
@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { alpha } from '@mui/material'
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
import { sidebar, toolbar } from 'client/theme/defaults'
|
||||
|
||||
@ -145,9 +144,5 @@ export default makeStyles(theme => ({
|
||||
subItemWrapper: {},
|
||||
subItem: {
|
||||
paddingLeft: theme.spacing(4)
|
||||
},
|
||||
itemSelected: {
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: `${alpha(theme.palette.secondary.main, 0.60)} !important`
|
||||
}
|
||||
}))
|
||||
|
@ -16,7 +16,7 @@
|
||||
import { useEffect, useMemo, JSXElementConstructor } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { List, ListSubheader, ListItem, Typography, IconButton, Tooltip } from '@mui/material'
|
||||
import { List, ListSubheader, ListItemButton, Typography, IconButton, Tooltip } from '@mui/material'
|
||||
import { Cancel } from 'iconoir-react'
|
||||
import { UseFiltersInstanceProps } from 'react-table'
|
||||
|
||||
@ -78,9 +78,11 @@ const CategoryFilter = ({ title, column, accessorOption, multiple = false }) =>
|
||||
}
|
||||
|
||||
return (
|
||||
<List dense disablePadding>
|
||||
<List>
|
||||
{title && (
|
||||
<ListSubheader disableSticky disableGutters
|
||||
<ListSubheader
|
||||
disableSticky
|
||||
disableGutters
|
||||
title={Tr(title)}
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
>
|
||||
@ -103,7 +105,8 @@ const CategoryFilter = ({ title, column, accessorOption, multiple = false }) =>
|
||||
: value === filterValue
|
||||
|
||||
return (
|
||||
<ListItem key={i} button
|
||||
<ListItemButton
|
||||
key={i}
|
||||
selected={isSelected}
|
||||
onClick={() =>
|
||||
isSelected ? handleUnselect(value) : handleSelect(value)
|
||||
@ -112,7 +115,7 @@ const CategoryFilter = ({ title, column, accessorOption, multiple = false }) =>
|
||||
<Typography noWrap variant='subtitle2' title={value}>
|
||||
{`${value} (${count})`}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
|
@ -27,13 +27,13 @@ import { CreateStepsCallback, CreateFormCallback } from 'client/utils'
|
||||
|
||||
/**
|
||||
* @typedef {object} Option
|
||||
* @property {string} cy - Cypress selector
|
||||
* @property {string} name - Label of option
|
||||
* @property {DialogProps} [dialogProps] - Dialog properties
|
||||
* @property {JSXElementConstructor} [icon] - Icon
|
||||
* @property {boolean} isConfirmDialog
|
||||
* - If `true`, the form will be a dialog with confirmation buttons
|
||||
* @property {boolean} [isConfirmDialog] - If `true`, the form will be a dialog with confirmation buttons
|
||||
* @property {boolean|function(Row[]):boolean} [disabled] - If `true`, option will be disabled
|
||||
* @property {function(object, Row[])} onSubmit - Function to handle after finish the form
|
||||
* @property {function():CreateStepsCallback|CreateFormCallback} form - Form
|
||||
* @property {function(Row[]):(CreateStepsCallback|CreateFormCallback)} form - Form
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -43,11 +43,11 @@ import { CreateStepsCallback, CreateFormCallback } from 'client/utils'
|
||||
* @property {string} [label] - Label
|
||||
* @property {string} [color] - Color
|
||||
* @property {string} [icon] - Icon
|
||||
* @property {DialogProps} [dialogProps] - Dialog properties
|
||||
* @property {'text'|'outlined'|'contained'} [variant] - Button variant
|
||||
* @property {Option[]} [options] - Group of actions
|
||||
* @property {function(Row[])} [action] - Singular action without form
|
||||
* @property {boolean|{min: number, max: number}} [selected] - Condition for selected rows
|
||||
* @property {boolean} [disabled] - If `true`, action will be disabled
|
||||
* @property {boolean|function(Row[]):boolean} [disabled] - If `true`, action will be disabled
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -127,7 +127,10 @@ export const ActionPropTypes = PropTypes.shape({
|
||||
label: PropTypes.string,
|
||||
tooltip: PropTypes.string,
|
||||
icon: PropTypes.any,
|
||||
disabled: PropTypes.bool,
|
||||
disabled: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.func
|
||||
]),
|
||||
selected: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.shape({
|
||||
@ -142,9 +145,27 @@ export const ActionPropTypes = PropTypes.shape({
|
||||
accessor: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
icon: PropTypes.any,
|
||||
disabled: PropTypes.oneOfType([
|
||||
PropTypes.bool,
|
||||
PropTypes.func
|
||||
]),
|
||||
form: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
dialogProps: PropTypes.shape(DialogPropTypes)
|
||||
dialogProps: PropTypes.shape({
|
||||
...DialogPropTypes,
|
||||
description: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.func
|
||||
]),
|
||||
subheader: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.func
|
||||
]),
|
||||
title: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.func
|
||||
])
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
@ -81,7 +81,8 @@ const GlobalSort = ({ useTableProps }) => {
|
||||
buttonProps={{
|
||||
'data-cy': 'sort-by-button',
|
||||
disabled: headersNotSorted.length === 0,
|
||||
variant: 'outlined'
|
||||
variant: 'outlined',
|
||||
color: 'secondary'
|
||||
}}
|
||||
popoverProps= {{
|
||||
anchorOrigin: {
|
||||
|
@ -98,7 +98,7 @@ const LabelFilter = ({ title, column }) => {
|
||||
)
|
||||
|
||||
return (
|
||||
<List dense disablePadding>
|
||||
<List>
|
||||
{title && (
|
||||
<ListSubheader disableSticky disableGutters
|
||||
title={Tr(title)}
|
||||
|
@ -40,20 +40,17 @@ const Row = ({ original, value, ...props }) => {
|
||||
return [src, external]
|
||||
}, [LOGO])
|
||||
|
||||
const logo = String(LOGO).split('/').at(-1)
|
||||
const time = Helper.timeFromMilliseconds(+REGTIME)
|
||||
const timeAgo = `registered ${time.toRelative()}`
|
||||
|
||||
return (
|
||||
<div {...props}>
|
||||
{logo && (
|
||||
<div className={classes.figure}>
|
||||
<Image
|
||||
src={logoSource}
|
||||
imgProps={{ className: classes.image }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={classes.figure}>
|
||||
<Image
|
||||
src={logoSource}
|
||||
imgProps={{ className: classes.image }}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.main}>
|
||||
<div className={classes.title}>
|
||||
<Typography component='span'>
|
||||
|
@ -45,7 +45,7 @@ const InfoTab = memo(({ info }) => {
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Paper variant="outlined">
|
||||
<Paper variant='outlined'>
|
||||
<List className={clsx(classes.list, 'w-50')}>
|
||||
<ListItem className={classes.title}>
|
||||
<Typography>{Tr(T.Information)}</Typography>
|
||||
@ -79,7 +79,7 @@ const InfoTab = memo(({ info }) => {
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Paper variant="outlined" className={classes.permissions}>
|
||||
<Paper variant='outlined' className={classes.permissions}>
|
||||
<List className={clsx(classes.list, 'w-25')}>
|
||||
<ListItem className={classes.title}>
|
||||
<Typography>{Tr(T.Permissions)}</Typography>
|
||||
@ -108,7 +108,7 @@ const InfoTab = memo(({ info }) => {
|
||||
</ListItem>
|
||||
</List>
|
||||
</Paper>
|
||||
<Paper variant="outlined">
|
||||
<Paper variant='outlined'>
|
||||
<List className={clsx(classes.list, 'w-50')}>
|
||||
<ListItem className={classes.title}>
|
||||
<Typography>{Tr(T.Ownership)}</Typography>
|
||||
|
@ -45,7 +45,9 @@ function Dashboard () {
|
||||
return (
|
||||
<Container
|
||||
disableGutters
|
||||
className={withoutAnimations && classes.withoutAnimations}
|
||||
{...withoutAnimations && {
|
||||
className: classes.withoutAnimations
|
||||
}}
|
||||
>
|
||||
<Box py={3}>
|
||||
<Grid container spacing={3}>
|
||||
|
@ -50,7 +50,9 @@ function Dashboard () {
|
||||
return (
|
||||
<Container
|
||||
disableGutters
|
||||
className={withoutAnimations && classes.withoutAnimations}
|
||||
{...withoutAnimations && {
|
||||
className: classes.withoutAnimations
|
||||
}}
|
||||
>
|
||||
<Box py={3}>
|
||||
<Grid container spacing={3}>
|
||||
|
@ -54,7 +54,7 @@ const Info = memo(({ fetchProps }) => {
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Paper variant="outlined" className={classes.marginBottom}>
|
||||
<Paper variant='outlined' className={classes.marginBottom}>
|
||||
<List className={clsx(classes.list, 'w-50')}>
|
||||
<ListItem className={classes.title}>
|
||||
<Typography>{Tr(T.Information)}</Typography>
|
||||
@ -66,15 +66,15 @@ const Info = memo(({ fetchProps }) => {
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Typography>{Tr(T.Name)}</Typography>
|
||||
<Typography data-cy="provider-name">{NAME}</Typography>
|
||||
<Typography data-cy='provider-name'>{NAME}</Typography>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Typography>{Tr(T.Description)}</Typography>
|
||||
<Typography data-cy="provider-description" noWrap>{description}</Typography>
|
||||
<Typography data-cy='provider-description' noWrap>{description}</Typography>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Typography>{Tr(T.Provider)}</Typography>
|
||||
<Typography data-cy="provider-type">{providerName}</Typography>
|
||||
<Typography data-cy='provider-type'>{providerName}</Typography>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Typography>{Tr(T.RegistrationTime)}</Typography>
|
||||
@ -85,7 +85,7 @@ const Info = memo(({ fetchProps }) => {
|
||||
</List>
|
||||
</Paper>
|
||||
{hasConnection && (
|
||||
<Paper variant="outlined">
|
||||
<Paper variant='outlined'>
|
||||
<List className={clsx(classes.list, 'w-50')}>
|
||||
<ListItem className={classes.title}>
|
||||
<Typography>{Tr(T.Credentials)}</Typography>
|
||||
@ -115,7 +115,7 @@ const Info = memo(({ fetchProps }) => {
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs={12} md={6}>
|
||||
<Paper variant="outlined" className={classes.marginBottom}>
|
||||
<Paper variant='outlined' className={classes.marginBottom}>
|
||||
<List className={clsx(classes.list, 'w-25')}>
|
||||
<ListItem className={classes.title}>
|
||||
<Typography>{Tr(T.Permissions)}</Typography>
|
||||
@ -144,7 +144,7 @@ const Info = memo(({ fetchProps }) => {
|
||||
</ListItem>
|
||||
</List>
|
||||
</Paper>
|
||||
<Paper variant="outlined">
|
||||
<Paper variant='outlined'>
|
||||
<List className={clsx(classes.list, 'w-50')}>
|
||||
<ListItem className={classes.title}>
|
||||
<Typography>{Tr(T.Ownership)}</Typography>
|
||||
|
@ -32,6 +32,7 @@ export * from 'client/features/One/marketplace/hooks'
|
||||
export * from 'client/features/One/marketplaceApp/hooks'
|
||||
export * from 'client/features/One/provider/hooks'
|
||||
export * from 'client/features/One/provision/hooks'
|
||||
export * from 'client/features/One/system/hooks'
|
||||
export * from 'client/features/One/user/hooks'
|
||||
export * from 'client/features/One/vm/hooks'
|
||||
export * from 'client/features/One/vmGroup/hooks'
|
||||
|
@ -33,6 +33,7 @@ const RESOURCES = {
|
||||
image: 'images',
|
||||
marketplace: 'marketplaces',
|
||||
secgroups: 'securityGroups',
|
||||
system: 'system',
|
||||
template: 'templates',
|
||||
user: 'users',
|
||||
vdc: 'vdc',
|
||||
@ -65,6 +66,7 @@ const initial = {
|
||||
[RESOURCES.image]: [],
|
||||
[RESOURCES.marketplace]: [],
|
||||
[RESOURCES.secgroups]: [],
|
||||
[RESOURCES.system]: {},
|
||||
[RESOURCES.template]: [],
|
||||
[RESOURCES.user]: [],
|
||||
[RESOURCES.vdc]: [],
|
||||
|
@ -13,26 +13,31 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
import { createAction } from 'client/features/One/utils'
|
||||
import { systemService } from 'client/features/One/system/services'
|
||||
import { RESOURCES } from 'client/features/One/slice'
|
||||
|
||||
export default makeStyles(theme => ({
|
||||
footer: {
|
||||
color: theme.palette.primary.contrastText,
|
||||
backgroundColor: theme.palette.primary.light,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 'auto',
|
||||
right: 0,
|
||||
width: '100%',
|
||||
zIndex: 1100,
|
||||
textAlign: 'center',
|
||||
padding: 5
|
||||
},
|
||||
heartIcon: {
|
||||
margin: '0 0.5em',
|
||||
color: theme.palette.error.dark
|
||||
},
|
||||
link: {
|
||||
color: theme.palette.primary.contrastText
|
||||
}
|
||||
}))
|
||||
/** @see {@link RESOURCES.system} */
|
||||
const SYSTEM = 'system'
|
||||
|
||||
export const getOneVersion = createAction(
|
||||
`${SYSTEM}/one-version`,
|
||||
systemService.getOneVersion,
|
||||
(response, one) => ({
|
||||
[RESOURCES.system]: {
|
||||
...one[RESOURCES.system],
|
||||
version: response
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
export const getOneConfig = createAction(
|
||||
`${SYSTEM}/one-config`,
|
||||
systemService.getOneConfig,
|
||||
(response, one) => ({
|
||||
[RESOURCES.system]: {
|
||||
...one[RESOURCES.system],
|
||||
config: response
|
||||
}
|
||||
})
|
||||
)
|
44
src/fireedge/src/client/features/One/system/hooks.js
Normal file
44
src/fireedge/src/client/features/One/system/hooks.js
Normal file
@ -0,0 +1,44 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useCallback } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { unwrapResult } from '@reduxjs/toolkit'
|
||||
|
||||
import * as actions from 'client/features/One/system/actions'
|
||||
import { name, RESOURCES } from 'client/features/One/slice'
|
||||
|
||||
export const useSystem = () => (
|
||||
useSelector(state => state[name]?.[RESOURCES.system] ?? [])
|
||||
)
|
||||
|
||||
export const useSystemApi = () => {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const unwrapDispatch = useCallback(async action => {
|
||||
try {
|
||||
const response = await dispatch(action)
|
||||
return unwrapResult(response)
|
||||
} catch (error) {
|
||||
return error
|
||||
}
|
||||
}, [dispatch])
|
||||
|
||||
return {
|
||||
getOneVersion: () => unwrapDispatch(actions.getOneVersion()),
|
||||
getOneConfig: () => unwrapDispatch(actions.getOneConfig())
|
||||
}
|
||||
}
|
56
src/fireedge/src/client/features/One/system/services.js
Normal file
56
src/fireedge/src/client/features/One/system/services.js
Normal file
@ -0,0 +1,56 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { Actions, Commands } from 'server/utils/constants/commands/system'
|
||||
import { httpCodes } from 'server/utils/constants'
|
||||
import { requestConfig, RestClient } from 'client/utils'
|
||||
|
||||
export const systemService = ({
|
||||
/**
|
||||
* Returns the OpenNebula core version.
|
||||
*
|
||||
* @returns {object} The OpenNebula version
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
getOneVersion: async () => {
|
||||
const name = Actions.SYSTEM_VERSION
|
||||
const command = { name, ...Commands[name] }
|
||||
const config = requestConfig(undefined, command)
|
||||
|
||||
const res = await RestClient.request(config)
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res?.data
|
||||
|
||||
return res?.data
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the OpenNebula configuration.
|
||||
*
|
||||
* @returns {object} The loaded oned.conf file
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
getOneConfig: async () => {
|
||||
const name = Actions.SYSTEM_CONFIG
|
||||
const command = { name, ...Commands[name] }
|
||||
const config = requestConfig(undefined, command)
|
||||
|
||||
const res = await RestClient.request(config)
|
||||
|
||||
if (!res?.id || res?.id !== httpCodes.ok.id) throw res?.data
|
||||
|
||||
return res?.data?.OPENNEBULA_CONFIGURATION
|
||||
}
|
||||
})
|
@ -41,7 +41,7 @@ export const zoneService = ({
|
||||
/**
|
||||
* Retrieves information for all the zones in the pool.
|
||||
*
|
||||
* @returns {Array} List of zone
|
||||
* @returns {Array} List of zones
|
||||
* @throws Fails when response isn't code 200
|
||||
*/
|
||||
getZones: async () => {
|
||||
|
@ -255,6 +255,15 @@ export default (appTheme, mode = SCHEMES.DARK) => {
|
||||
disableTouchRipple: true
|
||||
}
|
||||
},
|
||||
MuiListItemButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
'&.Mui-selected, &.Mui-selected:hover': {
|
||||
backgroundColor: alpha(secondary.main, 0.60)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
MuiButton: {
|
||||
defaultProps: {
|
||||
disableTouchRipple: true
|
||||
|
@ -25,7 +25,7 @@ const ZONE_UPDATE = 'zone.update'
|
||||
const ZONE_RENAME = 'zone.rename'
|
||||
const ZONE_INFO = 'zone.info'
|
||||
const ZONE_RAFTSTATUS = 'zone.raftstatus'
|
||||
const ZONEPOOL_INFO = 'zonepool.info'
|
||||
const ZONE_POOL_INFO = 'zonepool.info'
|
||||
|
||||
const Actions = {
|
||||
ZONE_ALLOCATE,
|
||||
@ -34,7 +34,7 @@ const Actions = {
|
||||
ZONE_RENAME,
|
||||
ZONE_INFO,
|
||||
ZONE_RAFTSTATUS,
|
||||
ZONEPOOL_INFO
|
||||
ZONE_POOL_INFO
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@ -111,7 +111,7 @@ module.exports = {
|
||||
httpMethod: GET,
|
||||
params: {}
|
||||
},
|
||||
[ZONEPOOL_INFO]: {
|
||||
[ZONE_POOL_INFO]: {
|
||||
// inspected
|
||||
httpMethod: GET,
|
||||
params: {}
|
||||
|
Loading…
Reference in New Issue
Block a user