F #3951: Add images & fix provision styles (#532)

This commit is contained in:
Sergio Betanzos authored and GitHub committed 2020-12-09 16:42:19 +01:00
1 parent ccf56611d8
commit 7fd7553d0e
28 files changed
+194 -118

No files matched your search

+3 -2
View File
@@ -13,7 +13,8 @@
"cypress:run": "cypress run --headless --browser chrome --spec \"cypress/integration/**/*.spec.js\"",
"pot": "node potfile.js",
"po2json": "node po2json.js",
"lint": "eslint ./src/client/"
"lint": "eslint ./src/client/",
"lint-fix": "eslint ./src/client/ --fix"
},
"author": "opennebula.io",
"license": "ISC",
@@ -126,4 +127,4 @@
"yup": "^0.29.3",
"zeromq": "^5.2.0"
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

@@ -3,13 +3,17 @@ import PropTypes from 'prop-types'
import { makeStyles, Chip, Box, Typography, Avatar } from '@material-ui/core'
import StatusBadge from 'client/components/StatusBadge'
import SelectCard from 'client/components/Cards/SelectCard'
import { StatusBadge } from 'client/components/Status'
import Datastore from 'client/constants/datastore'
const useStyles = makeStyles(theme => ({
title: { display: 'flex', gap: '8px' },
avatar: { color: theme.palette.primary.contrastText },
avatar: {
width: '5ch',
height: '5ch',
color: theme.palette.primary.contrastText
},
card: { backgroundColor: theme.palette.primary.main }
}))
@@ -4,12 +4,16 @@ import PropTypes from 'prop-types'
import { makeStyles, Avatar, Chip, Box, Typography } from '@material-ui/core'
import SelectCard from 'client/components/Cards/SelectCard'
import StatusBadge from 'client/components/StatusBadge'
import { StatusBadge } from 'client/components/Status'
import Host from 'client/constants/host'
const useStyles = makeStyles(theme => ({
title: { display: 'flex', gap: '8px' },
avatar: { color: theme.palette.primary.contrastText },
avatar: {
width: '5ch',
height: '5ch',
color: theme.palette.primary.contrastText
},
card: { backgroundColor: theme.palette.primary.main }
}))
@@ -6,7 +6,11 @@ import SelectCard from 'client/components/Cards/SelectCard'
const useStyles = makeStyles(theme => ({
title: { display: 'flex', gap: '8px' },
avatar: { color: theme.palette.primary.contrastText },
avatar: {
width: '5ch',
height: '5ch',
color: theme.palette.primary.contrastText
},
card: { backgroundColor: theme.palette.primary.main }
}))
@@ -1,20 +1,18 @@
import React, { memo, useState, useEffect } from 'react'
import React, { memo, useState, useEffect, useMemo } from 'react'
import PropTypes from 'prop-types'
import { Avatar } from '@material-ui/core'
import ProviderIcon from '@material-ui/icons/Public'
import ProvisionIcon from '@material-ui/icons/Cloud'
import SelectCard from 'client/components/Cards/SelectCard'
import Action from 'client/components/Cards/SelectCard/Action'
import { IMAGES_URL } from 'client/constants'
import { isExternalURL } from 'client/utils'
import { IMAGES_URL, DEFAULT_IMAGE } from 'client/constants'
const ProvisionCard = memo(
({ value, isSelected, handleClick, isProvider, imgAsAvatar, actions }) => {
const DEFAULT_IMAGE = isProvider ? 'provider.webp' : 'provision.webp'
({ value, isSelected, handleClick, isProvider, actions }) => {
const [image, setImage] = useState(undefined)
const { ID, NAME, TEMPLATE: { PLAIN = '{}' } } = value
const { NAME, TEMPLATE: { PLAIN = '{}' } } = value
useEffect(() => {
try {
@@ -26,32 +24,24 @@ const ProvisionCard = memo(
}
}, [])
const isExternalURL = RegExp(/^(http|https):/g).test(image)
const onError = evt => { evt.target.src = DEFAULT_IMAGE }
const onError = evt => {
evt.target.src = `${IMAGES_URL}/${DEFAULT_IMAGE}`
}
const imgSource = useMemo(() =>
isExternalURL(image) ? image : `${IMAGES_URL}/${image}`
, [image])
return (
<SelectCard
title={`${ID} - ${NAME}`}
title={NAME}
isSelected={isSelected}
handleClick={handleClick}
action={actions?.map(action => <Action key={action?.cy} {...action} />)}
icon={isProvider ? <ProviderIcon /> : <ProvisionIcon />}
{...(imgAsAvatar
? {
icon: image && (
<Avatar
src={isExternalURL ? image : `${IMAGES_URL}/${image}`}
variant="rounded"
style={{ width: 100, height: 80 }}
imgProps={{ onError }}
/>
)
}
: { mediaProps: { component: 'img', image, onError } }
)}
mediaProps={{
component: 'img',
image: imgSource,
onError
}}
/>
)
}, (prev, next) => prev.isSelected === next.isSelected
@@ -72,7 +62,6 @@ ProvisionCard.propTypes = {
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
isProvider: PropTypes.bool,
imgAsAvatar: PropTypes.bool,
actions: PropTypes.arrayOf(
PropTypes.shape({
handleClick: PropTypes.func.isRequired,
@@ -87,7 +76,6 @@ ProvisionCard.defaultProps = {
isSelected: undefined,
handleClick: undefined,
isProvider: false,
imgAsAvatar: false,
actions: undefined
}
@@ -1,35 +1,28 @@
import React, { memo } from 'react'
import React, { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import { Avatar } from '@material-ui/core'
import SelectCard from 'client/components/Cards/SelectCard'
import { isExternalURL } from 'client/utils'
import { IMAGES_URL } from 'client/constants'
const ProvisionTemplateCard = memo(
({ value, isProvider, isSelected, handleClick }) => {
const DEFAULT_IMAGE = isProvider ? 'provider.webp' : 'provision.webp'
const { name, plain: { image = DEFAULT_IMAGE } = {} } = value
({ value, title, isSelected, handleClick }) => {
const { plain: { image } = {} } = value
const isExternalURL = RegExp(/^(http|https):/g).test(image)
const onError = evt => {
evt.target.src = `${IMAGES_URL}/${DEFAULT_IMAGE}`
}
const imgSource = useMemo(() =>
isExternalURL(image) ? image : `${IMAGES_URL}/${image}`
, [image])
return (
<SelectCard
stylesProps={{ minHeight: 80 }}
title={name}
isSelected={isSelected}
handleClick={handleClick}
icon={
<Avatar
src={isExternalURL ? image : `${IMAGES_URL}/${image}`}
variant="rounded"
style={{ width: 100, height: 80 }}
imgProps={{ onError }}
/>
}
title={title}
mediaProps={image && {
component: 'img',
image: imgSource
}}
/>
)
}, (prev, next) => prev.isSelected === next.isSelected
@@ -42,6 +35,7 @@ ProvisionTemplateCard.propTypes = {
image: PropTypes.string
})
}),
title: PropTypes.string,
isProvider: PropTypes.bool,
isSelected: PropTypes.bool,
handleClick: PropTypes.func
@@ -49,6 +43,7 @@ ProvisionTemplateCard.propTypes = {
ProvisionTemplateCard.defaultProps = {
value: {},
title: undefined,
isProvider: undefined,
isSelected: undefined,
handleClick: undefined
@@ -3,51 +3,14 @@ import PropTypes from 'prop-types'
import clsx from 'clsx'
import {
makeStyles, Card, CardActionArea, CardHeader, CardActions, CardMedia
Card, CardActionArea, CardHeader, CardActions, CardMedia
} from '@material-ui/core'
import { Skeleton } from '@material-ui/lab'
import useNearScreen from 'client/hooks/useNearScreen'
import ConditionalWrap from 'client/components/HOC/ConditionalWrap'
import Action from 'client/components/Cards/SelectCard/Action'
const useStyles = makeStyles(theme => ({
root: { height: '100%' },
selected: {
color: theme.palette.secondary.contrastText,
backgroundColor: theme.palette.secondary.main,
'& .badge': {
color: theme.palette.secondary.main,
backgroundColor: theme.palette.secondary.contrastText
},
'& $subheader': {
color: 'inherit'
}
},
actionArea: {
height: '100%',
minHeight: ({ minHeight = 140 }) => minHeight
},
mediaImage: {
height: ({ mediaHeight = 140 }) => mediaHeight
},
headerAvatar: {
display: 'flex',
color: theme.palette.primary.contrastText
},
headerContent: { overflowX: 'hidden' },
header: {
color: theme.palette.primary.contrastText
},
subheader: {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'initial',
display: '-webkit-box',
lineClamp: 2,
boxOrient: 'vertical'
}
}))
import selectCardStyles from 'client/components/Cards/SelectCard/styles'
const SelectCard = memo(({
stylesProps,
@@ -65,7 +28,7 @@ const SelectCard = memo(({
observerOff,
children
}) => {
const classes = useStyles(stylesProps)
const classes = selectCardStyles(stylesProps)
const { isNearScreen, fromRef } = useNearScreen({
distance: '100px'
})
@@ -96,7 +59,7 @@ const SelectCard = memo(({
}
>
{/* CARD HEADER */}
<CardHeader
{(title || subheader || icon || action) && <CardHeader
action={action}
avatar={icon}
classes={{
@@ -118,7 +81,7 @@ const SelectCard = memo(({
title: subheader
}}
{...cardHeaderProps}
/>
/>}
{/* CARD CONTENT */}
{children}
@@ -128,12 +91,15 @@ const SelectCard = memo(({
<ConditionalWrap
condition={handleClick && action}
wrap={children =>
<CardActionArea onClick={handleClick}>
<CardActionArea
className={classes.mediaActionArea}
onClick={handleClick}
>
{children}
</CardActionArea>
}
>
<CardMedia className={classes.mediaImage} {...mediaProps} />
<CardMedia className={classes.media} {...mediaProps} />
</ConditionalWrap>
)}
@@ -0,0 +1,53 @@
import { makeStyles } from '@material-ui/core'
export default makeStyles(theme => ({
root: {
height: '100%',
'& $actionArea, & $mediaActionArea, & $media': {
transition: theme.transitions.create(
['filter', 'background-color'],
{ duration: '0.2s' }
)
}
},
selected: {
color: theme.palette.secondary.contrastText,
backgroundColor: theme.palette.secondary.main,
'& .badge': {
color: theme.palette.secondary.main,
backgroundColor: theme.palette.secondary.contrastText
},
'& $subheader': { color: 'inherit' }
},
actionArea: {
height: '100%',
minHeight: ({ minHeight = 140 }) => minHeight,
'& $media': { filter: 'contrast(0) brightness(2)' }
},
mediaActionArea: {
display: 'flex',
height: ({ mediaHeight = 140 }) => mediaHeight,
'& $media': { filter: 'contrast(0) brightness(2)' },
'&:hover': {
backgroundColor: theme.palette.primary.contrastText,
'& $media': { filter: 'none' }
}
},
media: {},
headerAvatar: {
display: 'flex',
color: theme.palette.primary.contrastText
},
headerContent: { overflowX: 'hidden' },
header: {
color: theme.palette.primary.contrastText
},
subheader: {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'initial',
display: '-webkit-box',
lineClamp: 2,
boxOrient: 'vertical'
}
}))
@@ -46,7 +46,7 @@ const StatusBadge = memo(({ stateColor, children }) => {
</Badge>
)
},
(prev, next) => prev.isSelected === next.isSelected
(prev, next) => prev.stateColor === next.stateColor
)
StatusBadge.propTypes = {
@@ -0,0 +1,49 @@
import React, { memo } from 'react'
import PropTypes from 'prop-types'
import { makeStyles, Typography } from '@material-ui/core'
import { addOpacityToColor } from 'client/utils'
const useStyles = makeStyles(theme => ({
root: ({ stateColor }) => ({
color: stateColor,
backgroundColor: addOpacityToColor(stateColor, 0.08),
cursor: 'default',
padding: theme.spacing('0.25rem', '0.5rem'),
minWidth: 20,
borderRadius: 2,
textTransform: 'uppercase',
fontSize: theme.typography.overline.fontSize,
fontWeight: theme.typography.fontWeightBold,
lineHeight: 'normal'
})
}))
const StatusChip = memo(({ stateColor, children }) => {
const classes = useStyles({ stateColor })
return (
<Typography component="span" className={classes.root}>
{children}
</Typography>
)
},
(prev, next) => prev.stateColor === next.stateColor
)
StatusChip.propTypes = {
stateColor: PropTypes.string,
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.node
])
}
StatusChip.defaultProps = {
stateColor: undefined,
children: ''
}
StatusChip.displayName = 'StatusChip'
export default StatusChip
@@ -0,0 +1,7 @@
import StatusBadge from 'client/components/Status/Badge'
import StatusChip from 'client/components/Status/Chip'
export {
StatusBadge,
StatusChip
}
@@ -28,6 +28,7 @@ export const APP_URL = defaultAppName ? `/${defaultAppName}` : ''
export const WEBSOCKET_URL = `${APP_URL}/websocket`
export const STATIC_FILES_URL = `${APP_URL}/client/assets`
export const IMAGES_URL = `${STATIC_FILES_URL}/images`
export const DEFAULT_IMAGE = `${STATIC_FILES_URL}/images/default.webp`
export const FONTS_URL = `${STATIC_FILES_URL}/fonts`
export const LANGUAGES_URL = `${STATIC_FILES_URL}/languages`
@@ -1,11 +1,12 @@
import React, { memo } from 'react'
import PropTypes from 'prop-types'
import { List, ListItem, Typography, Chip, Grid, Paper, Divider } from '@material-ui/core'
import { List, ListItem, Typography, Grid, Paper, Divider } from '@material-ui/core'
import { CheckBox, CheckBoxOutlineBlank } from '@material-ui/icons'
import clsx from 'clsx'
import useStyles from 'client/containers/ApplicationsInstances/DialogInfo/styles'
import { StatusChip } from 'client/components/Status'
import { APPLICATION_STATES } from 'client/constants/flow'
import { Tr } from 'client/components/HOC'
import { T } from 'client/constants'
@@ -53,11 +54,9 @@ const InfoTab = memo(({ info }) => {
</ListItem>
<ListItem>
<Typography>{Tr(T.State)}</Typography>
<Chip
size="small"
label={stateInfo?.name}
style={{ backgroundColor: stateInfo?.color }}
/>
<StatusChip stateColor={stateInfo?.color}>
{stateInfo?.name}
</StatusChip>
</ListItem>
<ListItem>
<Typography>{Tr(T.ReadyStatusGate)}</Typography>
@@ -57,7 +57,6 @@ const Provider = () => ({
handleClick: () => handleClick(name, isSelected)
}
}}
breakpoints={{ xs: 12, sm: 6, md: 4 }}
/>
)
}, [])
@@ -47,7 +47,7 @@ function ProviderCreateForm () {
const providerTemplate = providersTemplates
.find(({ name }) => name === providerSelected) ?? {}
if (!providerSelected) {
if (!providerTemplate) {
showError({
message: `
Cannot found provider template (${providerSelected}),
@@ -56,15 +56,15 @@ function ProviderCreateForm () {
history.push(PATH.PROVISIONS.LIST)
}
const { plain, location_key: locationKey } = providerTemplate
const formatData = {
...(!isUpdate && { name: `${providerSelected}_${locationSelected}` }),
...(providerTemplate?.image && {
plain: { image: providerTemplate.image }
}),
...(plain && { plain }),
provider: providerSelected,
connection: {
...connection,
[providerTemplate.location_key]: locationSelected
[locationKey]: locationSelected
},
registration_time: time
}
@@ -1,11 +1,12 @@
import React, { memo } from 'react'
import PropTypes from 'prop-types'
import { List, ListItem, Typography, Grid, Paper, Divider, Chip } from '@material-ui/core'
import { List, ListItem, Typography, Grid, Paper, Divider } from '@material-ui/core'
import { CheckBox, CheckBoxOutlineBlank } from '@material-ui/icons'
import clsx from 'clsx'
import useStyles from 'client/containers/Provisions/DialogInfo/styles'
import { StatusChip } from 'client/components/Status'
import { Tr } from 'client/components/HOC'
import { T, PROVISIONS_STATES } from 'client/constants'
@@ -64,11 +65,9 @@ const Info = memo(({ data }) => {
</ListItem>
<ListItem>
<Typography>{Tr(T.State)}</Typography>
<Chip
size="small"
label={stateInfo?.name}
style={{ backgroundColor: stateInfo?.color }}
/>
<StatusChip stateColor={stateInfo?.color}>
{stateInfo?.name}
</StatusChip>
</ListItem>
</List>
</Paper>
@@ -50,7 +50,6 @@ const Provider = () => ({
const isSelected = data?.some(selected => selected === NAME)
return {
imgAsAvatar: true,
isProvider: true,
isSelected,
handleClick: () =>
@@ -54,6 +54,7 @@ const Provision = () => ({
return {
isSelected,
title: name,
handleClick: () => handleClick(name, provider, isSelected)
}
}}
+1 -1
View File
@@ -1,7 +1,7 @@
export default {
palette: {
type: 'dark',
common: { black: '#000', white: '#ffffff' },
common: { black: '#000', white: '#fff' },
background: {
paper: '#2a2d3d',
default: '#222431'
+7
View File
@@ -1,5 +1,12 @@
export const fakeDelay = ms => new Promise(resolve => setTimeout(resolve, ms))
export const isExternalURL = url => RegExp(/^(http|https):/g).test(url)
export const addOpacityToColor = (color, opacity) => {
const opacityHex = Math.round(opacity * 255).toString(16)
return `${color}${opacityHex}`
}
export const capitalize = ([firstLetter, ...restOfWord]) =>
firstLetter.toUpperCase() + restOfWord.join('')