diff --git a/src/fireedge/package.json b/src/fireedge/package.json index d04c01c703..49046ab49b 100644 --- a/src/fireedge/package.json +++ b/src/fireedge/package.json @@ -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" } -} +} \ No newline at end of file diff --git a/src/fireedge/src/client/assets/images/ALIBABA.webp b/src/fireedge/src/client/assets/images/ALIBABA.webp new file mode 100644 index 0000000000..68830128cd Binary files /dev/null and b/src/fireedge/src/client/assets/images/ALIBABA.webp differ diff --git a/src/fireedge/src/client/assets/images/AWS.webp b/src/fireedge/src/client/assets/images/AWS.webp new file mode 100644 index 0000000000..fb79bb4a1f Binary files /dev/null and b/src/fireedge/src/client/assets/images/AWS.webp differ diff --git a/src/fireedge/src/client/assets/images/AZURE.webp b/src/fireedge/src/client/assets/images/AZURE.webp new file mode 100644 index 0000000000..1bf6f3dfb1 Binary files /dev/null and b/src/fireedge/src/client/assets/images/AZURE.webp differ diff --git a/src/fireedge/src/client/assets/images/EQUINIX.webp b/src/fireedge/src/client/assets/images/EQUINIX.webp new file mode 100644 index 0000000000..330acc0e8b Binary files /dev/null and b/src/fireedge/src/client/assets/images/EQUINIX.webp differ diff --git a/src/fireedge/src/client/assets/images/GOOGLECLOUD.webp b/src/fireedge/src/client/assets/images/GOOGLECLOUD.webp new file mode 100644 index 0000000000..28409854fd Binary files /dev/null and b/src/fireedge/src/client/assets/images/GOOGLECLOUD.webp differ diff --git a/src/fireedge/src/client/assets/images/default.webp b/src/fireedge/src/client/assets/images/default.webp new file mode 100644 index 0000000000..df9427f1c5 Binary files /dev/null and b/src/fireedge/src/client/assets/images/default.webp differ diff --git a/src/fireedge/src/client/assets/images/provider.webp b/src/fireedge/src/client/assets/images/provider.webp deleted file mode 100644 index e16cac65e6..0000000000 Binary files a/src/fireedge/src/client/assets/images/provider.webp and /dev/null differ diff --git a/src/fireedge/src/client/assets/images/provision.webp b/src/fireedge/src/client/assets/images/provision.webp deleted file mode 100644 index 1ea9f8c06a..0000000000 Binary files a/src/fireedge/src/client/assets/images/provision.webp and /dev/null differ diff --git a/src/fireedge/src/client/components/Cards/DatastoreCard.js b/src/fireedge/src/client/components/Cards/DatastoreCard.js index d33af737eb..45a28fef76 100644 --- a/src/fireedge/src/client/components/Cards/DatastoreCard.js +++ b/src/fireedge/src/client/components/Cards/DatastoreCard.js @@ -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 } })) diff --git a/src/fireedge/src/client/components/Cards/HostCard.js b/src/fireedge/src/client/components/Cards/HostCard.js index 6f1c526fa9..bb9d4ca0c1 100644 --- a/src/fireedge/src/client/components/Cards/HostCard.js +++ b/src/fireedge/src/client/components/Cards/HostCard.js @@ -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 } })) diff --git a/src/fireedge/src/client/components/Cards/NetworkCard.js b/src/fireedge/src/client/components/Cards/NetworkCard.js index 92a5379042..28ac452c30 100644 --- a/src/fireedge/src/client/components/Cards/NetworkCard.js +++ b/src/fireedge/src/client/components/Cards/NetworkCard.js @@ -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 } })) diff --git a/src/fireedge/src/client/components/Cards/ProvisionCard.js b/src/fireedge/src/client/components/Cards/ProvisionCard.js index 949e7d9e95..4251912c56 100644 --- a/src/fireedge/src/client/components/Cards/ProvisionCard.js +++ b/src/fireedge/src/client/components/Cards/ProvisionCard.js @@ -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 ( )} icon={isProvider ? : } - {...(imgAsAvatar - ? { - icon: image && ( - - ) - } - : { 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 } diff --git a/src/fireedge/src/client/components/Cards/ProvisionTemplateCard.js b/src/fireedge/src/client/components/Cards/ProvisionTemplateCard.js index 49067ae380..cf3de448ae 100644 --- a/src/fireedge/src/client/components/Cards/ProvisionTemplateCard.js +++ b/src/fireedge/src/client/components/Cards/ProvisionTemplateCard.js @@ -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 ( - } + 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 diff --git a/src/fireedge/src/client/components/Cards/SelectCard/index.js b/src/fireedge/src/client/components/Cards/SelectCard/index.js index 27ca5ed32d..6fa6794d7a 100644 --- a/src/fireedge/src/client/components/Cards/SelectCard/index.js +++ b/src/fireedge/src/client/components/Cards/SelectCard/index.js @@ -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 */} - + />} {/* CARD CONTENT */} {children} @@ -128,12 +91,15 @@ const SelectCard = memo(({ - + {children} } > - + )} diff --git a/src/fireedge/src/client/components/Cards/SelectCard/styles.js b/src/fireedge/src/client/components/Cards/SelectCard/styles.js new file mode 100644 index 0000000000..2acb059cbb --- /dev/null +++ b/src/fireedge/src/client/components/Cards/SelectCard/styles.js @@ -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' + } +})) diff --git a/src/fireedge/src/client/components/StatusBadge/index.js b/src/fireedge/src/client/components/Status/Badge.js similarity index 96% rename from src/fireedge/src/client/components/StatusBadge/index.js rename to src/fireedge/src/client/components/Status/Badge.js index 6f8204fd33..39bb2e53c2 100644 --- a/src/fireedge/src/client/components/StatusBadge/index.js +++ b/src/fireedge/src/client/components/Status/Badge.js @@ -46,7 +46,7 @@ const StatusBadge = memo(({ stateColor, children }) => { ) }, -(prev, next) => prev.isSelected === next.isSelected +(prev, next) => prev.stateColor === next.stateColor ) StatusBadge.propTypes = { diff --git a/src/fireedge/src/client/components/Status/Chip.js b/src/fireedge/src/client/components/Status/Chip.js new file mode 100644 index 0000000000..903c898627 --- /dev/null +++ b/src/fireedge/src/client/components/Status/Chip.js @@ -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 ( + + {children} + + ) +}, +(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 diff --git a/src/fireedge/src/client/components/Status/index.js b/src/fireedge/src/client/components/Status/index.js new file mode 100644 index 0000000000..7a14d44044 --- /dev/null +++ b/src/fireedge/src/client/components/Status/index.js @@ -0,0 +1,7 @@ +import StatusBadge from 'client/components/Status/Badge' +import StatusChip from 'client/components/Status/Chip' + +export { + StatusBadge, + StatusChip +} diff --git a/src/fireedge/src/client/constants/index.js b/src/fireedge/src/client/constants/index.js index 9c2bcb4db9..377464110e 100644 --- a/src/fireedge/src/client/constants/index.js +++ b/src/fireedge/src/client/constants/index.js @@ -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` diff --git a/src/fireedge/src/client/containers/ApplicationsInstances/DialogInfo/info.js b/src/fireedge/src/client/containers/ApplicationsInstances/DialogInfo/info.js index a6f89cbbfd..cde91946e5 100644 --- a/src/fireedge/src/client/containers/ApplicationsInstances/DialogInfo/info.js +++ b/src/fireedge/src/client/containers/ApplicationsInstances/DialogInfo/info.js @@ -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 }) => { {Tr(T.State)} - + + {stateInfo?.name} + {Tr(T.ReadyStatusGate)} diff --git a/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Provider/index.js b/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Provider/index.js index 8ed09d96b0..ec844d7212 100644 --- a/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Provider/index.js +++ b/src/fireedge/src/client/containers/Providers/Form/Create/Steps/Provider/index.js @@ -57,7 +57,6 @@ const Provider = () => ({ handleClick: () => handleClick(name, isSelected) } }} - breakpoints={{ xs: 12, sm: 6, md: 4 }} /> ) }, []) diff --git a/src/fireedge/src/client/containers/Providers/Form/Create/index.js b/src/fireedge/src/client/containers/Providers/Form/Create/index.js index 493115d2c8..6102a48299 100644 --- a/src/fireedge/src/client/containers/Providers/Form/Create/index.js +++ b/src/fireedge/src/client/containers/Providers/Form/Create/index.js @@ -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 } diff --git a/src/fireedge/src/client/containers/Provisions/DialogInfo/info.js b/src/fireedge/src/client/containers/Provisions/DialogInfo/info.js index 16d3e6b4f3..3957632010 100644 --- a/src/fireedge/src/client/containers/Provisions/DialogInfo/info.js +++ b/src/fireedge/src/client/containers/Provisions/DialogInfo/info.js @@ -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 }) => { {Tr(T.State)} - + + {stateInfo?.name} + diff --git a/src/fireedge/src/client/containers/Provisions/Form/Create/Steps/Provider/index.js b/src/fireedge/src/client/containers/Provisions/Form/Create/Steps/Provider/index.js index 627c12f845..a377ab5c34 100644 --- a/src/fireedge/src/client/containers/Provisions/Form/Create/Steps/Provider/index.js +++ b/src/fireedge/src/client/containers/Provisions/Form/Create/Steps/Provider/index.js @@ -50,7 +50,6 @@ const Provider = () => ({ const isSelected = data?.some(selected => selected === NAME) return { - imgAsAvatar: true, isProvider: true, isSelected, handleClick: () => diff --git a/src/fireedge/src/client/containers/Provisions/Form/Create/Steps/Provision/index.js b/src/fireedge/src/client/containers/Provisions/Form/Create/Steps/Provision/index.js index 5b93dad309..950e842379 100644 --- a/src/fireedge/src/client/containers/Provisions/Form/Create/Steps/Provision/index.js +++ b/src/fireedge/src/client/containers/Provisions/Form/Create/Steps/Provision/index.js @@ -54,6 +54,7 @@ const Provision = () => ({ return { isSelected, + title: name, handleClick: () => handleClick(name, provider, isSelected) } }} diff --git a/src/fireedge/src/client/theme/provision.js b/src/fireedge/src/client/theme/provision.js index d817fd801b..08678cf6a4 100644 --- a/src/fireedge/src/client/theme/provision.js +++ b/src/fireedge/src/client/theme/provision.js @@ -1,7 +1,7 @@ export default { palette: { type: 'dark', - common: { black: '#000', white: '#ffffff' }, + common: { black: '#000', white: '#fff' }, background: { paper: '#2a2d3d', default: '#222431' diff --git a/src/fireedge/src/client/utils/helpers.js b/src/fireedge/src/client/utils/helpers.js index a11a52bb20..11bb83ca98 100644 --- a/src/fireedge/src/client/utils/helpers.js +++ b/src/fireedge/src/client/utils/helpers.js @@ -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('')