1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-20 10:50:08 +03:00

F #5422: Add link to dashboard widgets (#2019)

Co-authored-by: Tino Vázquez <cvazquez@opennebula.io>
This commit is contained in:
Sergio Betanzos 2022-05-11 13:19:57 +02:00 committed by GitHub
parent 9ab5aa7869
commit d311a95a8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 142 additions and 109 deletions

View File

@ -16,93 +16,95 @@
import { memo } from 'react'
import PropTypes from 'prop-types'
import clsx from 'clsx'
import { Paper, Typography, lighten, darken } from '@mui/material'
import makeStyles from '@mui/styles/makeStyles'
import { styled, keyframes, lighten, darken } from '@mui/material'
import Paper from '@mui/material/Paper'
import Typography from '@mui/material/Typography'
import { addOpacityToColor } from 'client/utils'
import { SCHEMES } from 'client/constants'
const useStyles = makeStyles((theme) => {
const Card = styled(Paper)(({ theme, bgcolor, onClick }) => {
const getBackgroundColor =
theme.palette.mode === SCHEMES.DARK ? darken : lighten
const getContrastBackgroundColor =
theme.palette.mode === SCHEMES.LIGHT ? darken : lighten
return {
root: {
padding: '2em',
position: 'relative',
overflow: 'hidden',
backgroundColor: ({ bgColor }) => getBackgroundColor(bgColor, 0.3),
[theme.breakpoints.only('xs')]: {
display: 'flex',
alignItems: 'baseline',
gap: '1em',
padding: '2em',
position: 'relative',
overflow: 'hidden',
backgroundColor: getBackgroundColor(bgcolor, 0.3),
...(onClick && {
'&:hover': {
backgroundColor: getBackgroundColor(bgcolor, 0.4),
boxShadow: theme.shadows[10],
cursor: 'pointer',
},
},
icon: {
position: 'absolute',
top: 0,
right: 0,
width: '100%',
height: '100%',
textAlign: 'end',
'& > svg': {
color: addOpacityToColor(theme.palette.common.white, 0.2),
height: '100%',
width: '30%',
},
},
wave: {
display: 'block',
position: 'absolute',
opacity: 0.4,
top: '-5%',
left: '50%',
width: 220,
height: 220,
borderRadius: '43%',
},
wave1: {
backgroundColor: ({ bgColor }) =>
getContrastBackgroundColor(bgColor, 0.3),
animation: '$drift 7s infinite linear',
},
wave2: {
backgroundColor: ({ bgColor }) =>
getContrastBackgroundColor(bgColor, 0.5),
animation: '$drift 5s infinite linear',
},
'@keyframes drift': {
from: { transform: 'rotate(0deg)' },
to: { transform: 'rotate(360deg)' },
}),
[theme.breakpoints.only('xs')]: {
display: 'flex',
alignItems: 'baseline',
gap: '1em',
},
}
})
const WavesCard = memo(
({ text, value, bgColor, icon: Icon }) => {
const classes = useStyles({ bgColor })
return (
<Paper className={classes.root}>
<Typography variant="h6" zIndex={2}>
{text}
</Typography>
<Typography variant="h4" zIndex={2}>
{value}
</Typography>
<span className={clsx(classes.wave, classes.wave1)} />
<span className={clsx(classes.wave, classes.wave2)} />
{Icon && (
<span className={classes.icon}>
<Icon />
</span>
)}
</Paper>
)
const OverSizeIcon = styled('span')(({ theme }) => ({
position: 'absolute',
top: 0,
right: 0,
width: '100%',
height: '100%',
textAlign: 'end',
'& > svg': {
color: theme.palette.action.active,
height: '100%',
width: '30%',
},
}))
const drift = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`
const Wave = styled('span')(({ theme, bgcolor, duration = 1 }) => {
const getContrastBackgroundColor =
theme.palette.mode === SCHEMES.DARK ? lighten : darken
return {
display: 'block',
position: 'absolute',
opacity: 0.4,
top: '-5%',
left: '50%',
width: 220,
height: 220,
borderRadius: '43%',
backgroundColor: getContrastBackgroundColor(bgcolor, duration / 10),
animation: `${drift} ${duration}s infinite linear`,
}
})
const WavesCard = memo(
({ text, value, bgColor, icon: Icon, onClick }) => (
<Card bgcolor={bgColor} onClick={onClick || undefined}>
<Typography variant="h6" zIndex={2}>
{text}
</Typography>
<Typography variant="h4" zIndex={2}>
{value}
</Typography>
<Wave bgcolor={bgColor} duration={7} />
<Wave bgcolor={bgColor} duration={5} />
{Icon && (
<OverSizeIcon>
<Icon />
</OverSizeIcon>
)}
</Card>
),
(prev, next) => prev.value === next.value
)
@ -115,6 +117,7 @@ WavesCard.propTypes = {
]),
bgColor: PropTypes.string,
icon: PropTypes.any,
onClick: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]),
}
WavesCard.defaultProps = {
@ -122,6 +125,7 @@ WavesCard.defaultProps = {
value: undefined,
bgColor: '#ffffff00',
icon: undefined,
onClick: undefined,
}
WavesCard.displayName = 'WavesCard'

View File

@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, ReactElement } from 'react'
import { memo, useMemo, ReactElement } from 'react'
import PropTypes from 'prop-types'
import { Box, CircularProgress, Grid } from '@mui/material'
import { useHistory } from 'react-router-dom'
import { Container, Box, CircularProgress, Grid } from '@mui/material'
import {
ModernTv as VmsIcons,
List as TemplatesIcon,
@ -23,19 +24,30 @@ import {
NetworkAlt as NetworkIcon,
} from 'iconoir-react'
import { useAuth } from 'client/features/Auth'
import { useAuth, useViews } from 'client/features/Auth'
import { useGetVmsQuery } from 'client/features/OneApi/vm'
import { useGetTemplatesQuery } from 'client/features/OneApi/vmTemplate'
import { useGetImagesQuery } from 'client/features/OneApi/image'
import { useGetVNetworksQuery } from 'client/features/OneApi/network'
import NumberEasing from 'client/components/NumberEasing'
import WavesCard from 'client/components/Cards/WavesCard'
import { stringToBoolean } from 'client/models/Helper'
import { T } from 'client/constants'
import { PATH } from 'client/apps/sunstone/routesOne'
import { T, RESOURCE_NAMES } from 'client/constants'
const { VM, VM_TEMPLATE, IMAGE, VNET } = RESOURCE_NAMES
/** @returns {ReactElement} Sunstone dashboard container */
function SunstoneDashboard() {
const { settings: { DISABLE_ANIMATIONS } = {} } = useAuth()
const { view, hasAccessToResource } = useViews()
const { push: goTo } = useHistory()
const vmAccess = useMemo(() => hasAccessToResource(VM), [view])
const templateAccess = useMemo(() => hasAccessToResource(VM_TEMPLATE), [view])
const imageAccess = useMemo(() => hasAccessToResource(IMAGE), [view])
const vnetAccess = useMemo(() => hasAccessToResource(VNET), [view])
return (
<Box
@ -48,37 +60,42 @@ function SunstoneDashboard() {
},
})}
>
<Grid
container
data-cy="dashboard-widget-total-sunstone-resources"
spacing={3}
>
<ResourceWidget
query={useGetVmsQuery}
bgColor="#fa7892"
text={T.VMs}
icon={VmsIcons}
/>
<ResourceWidget
query={useGetTemplatesQuery}
bgColor="#b25aff"
text={T.VMTemplates}
icon={TemplatesIcon}
/>
<ResourceWidget
query={useGetImagesQuery}
bgColor="#1fbbc6"
text={T.Images}
icon={ImageIcon}
/>
<ResourceWidget
query={useGetVNetworksQuery}
bgColor="#f09d42"
text={T.VirtualNetworks}
icon={NetworkIcon}
/>
</Grid>
</Box>
<Box py={3}>
<Grid
container
data-cy="dashboard-widget-total-sunstone-resources"
spacing={3}
>
<ResourceWidget
query={useGetVmsQuery}
bgColor="#fa7892"
text={T.VMs}
icon={VmsIcons}
onClick={vmAccess && (() => goTo(PATH.INSTANCE.VMS.LIST))}
/>
<ResourceWidget
query={useGetTemplatesQuery}
bgColor="#b25aff"
text={T.VMTemplates}
icon={TemplatesIcon}
onClick={templateAccess && (() => goTo(PATH.TEMPLATE.VMS.LIST))}
/>
<ResourceWidget
query={useGetImagesQuery}
bgColor="#1fbbc6"
text={T.Images}
icon={ImageIcon}
onClick={imageAccess && (() => goTo(PATH.STORAGE.IMAGES.LIST))}
/>
<ResourceWidget
query={useGetVNetworksQuery}
bgColor="#f09d42"
text={T.VirtualNetworks}
icon={NetworkIcon}
onClick={vnetAccess && (() => goTo(PATH.NETWORK.VNETS.LIST))}
/>
</Grid>
</Box>
)
}

View File

@ -125,6 +125,17 @@ export const useViews = () => {
[view]
)
/**
* Check if user has a view for a resource.
*
* @param {RESOURCE_NAMES} resourceName - Name of resource
* @returns {boolean} Returns true if user has a view for a resource
*/
const hasAccessToResource = useCallback(
(resourceName) => !!getResourceView(resourceName),
[view]
)
return useMemo(
() => ({
...Object.values(RESOURCE_NAMES).reduce(
@ -134,6 +145,7 @@ export const useViews = () => {
}),
{}
),
hasAccessToResource,
getResourceView,
views,
view,