diff --git a/src/fireedge/package.json b/src/fireedge/package.json index d1afda42c4..75cceeac88 100644 --- a/src/fireedge/package.json +++ b/src/fireedge/package.json @@ -82,7 +82,6 @@ "fuse.js": "^6.4.1", "guacamole-lite": "^0.6.3", "helmet": "^4.1.1", - "html-entities": "^1.3.1", "http": "^0.0.1-security", "http-proxy-middleware": "^1.0.5", "https": "^1.0.0", @@ -130,4 +129,4 @@ "yup": "^0.29.3", "zeromq": "^5.2.0" } -} +} \ No newline at end of file diff --git a/src/fireedge/src/client/components/AutoScrollBox/index.js b/src/fireedge/src/client/components/AutoScrollBox/index.js index 3770fe32df..a6cda462af 100644 --- a/src/fireedge/src/client/components/AutoScrollBox/index.js +++ b/src/fireedge/src/client/components/AutoScrollBox/index.js @@ -1,11 +1,24 @@ -import React, { memo, Children, useEffect, useRef, useState } from 'react' +import React, { memo, useEffect, useRef, useState } from 'react' import PropTypes from 'prop-types' -import clsx from 'clsx' import { makeStyles, Chip, Slide } from '@material-ui/core' import ArrowBottomIcon from '@material-ui/icons/VerticalAlignBottom' const useStyles = makeStyles(theme => ({ + scrollable: { + padding: theme.spacing(1), + overflowY: 'scroll', + '&::-webkit-scrollbar': { + width: 14 + }, + '&::-webkit-scrollbar-thumb': { + backgroundClip: 'content-box', + border: '4px solid transparent', + borderRadius: 7, + boxShadow: 'inset 0 0 0 10px', + color: theme.palette.secondary.light + } + }, wrapperButton: { top: 5, position: 'sticky', @@ -14,8 +27,6 @@ const useStyles = makeStyles(theme => ({ button: { padding: theme.spacing(0, 2) } })) -const baseClass = 'react-auto-scroll' - const AutoScrollBox = memo(({ children, className, @@ -30,9 +41,7 @@ const AutoScrollBox = memo(({ const containerElement = useRef(null) const style = { - padding: 8, height, - overflowY: 'scroll', scrollBehavior: 'auto', pointerEvents: preventInteraction ? 'none' : 'auto' } @@ -74,15 +83,9 @@ const AutoScrollBox = memo(({ }, [children, containerElement, autoScroll]) return ( -
+
({ - title: { display: 'flex', gap: '8px' }, - avatar: { - width: '5ch', - height: '5ch', - color: theme.palette.primary.contrastText - }, - card: { backgroundColor: theme.palette.primary.main } + card: { backgroundColor: theme.palette.primary.main }, + title: { display: 'flex', gap: '0.5rem' } })) - const DatastoreCard = memo( ({ value, isSelected, handleClick, actions }) => { + const classes = useStyles() + const { ID, NAME, TYPE, STATE } = value const type = Datastore.TYPES[TYPE] const state = Datastore.STATES[STATE] - const classes = useStyles() - - const renderChip = ({ label, ...props }) => - return ( - {ID} + + } - title={( - - {NAME} - {renderChip({ label: type.name })} - - )} - cardHeaderProps={{ disableTypography: true }} + title={ + + + {NAME} + + {type.name} + + } + subheader={`#${ID}`} isSelected={isSelected} handleClick={handleClick} actions={actions} diff --git a/src/fireedge/src/client/components/Cards/HostCard.js b/src/fireedge/src/client/components/Cards/HostCard.js index bb9d4ca0c1..eb070f6356 100644 --- a/src/fireedge/src/client/components/Cards/HostCard.js +++ b/src/fireedge/src/client/components/Cards/HostCard.js @@ -1,48 +1,45 @@ import React, { memo } from 'react' import PropTypes from 'prop-types' -import { makeStyles, Avatar, Chip, Box, Typography } from '@material-ui/core' +import { makeStyles, Chip, Box, Typography } from '@material-ui/core' +import HostIcon from '@material-ui/icons/VideogameAsset' import SelectCard from 'client/components/Cards/SelectCard' -import { StatusBadge } from 'client/components/Status' +import { StatusBadge, StatusChip } from 'client/components/Status' import Host from 'client/constants/host' const useStyles = makeStyles(theme => ({ - title: { display: 'flex', gap: '8px' }, - avatar: { - width: '5ch', - height: '5ch', - color: theme.palette.primary.contrastText - }, - card: { backgroundColor: theme.palette.primary.main } + card: { backgroundColor: theme.palette.primary.main }, + title: { display: 'flex', gap: '0.5rem' } })) const HostCard = memo( ({ value, isSelected, handleClick, actions }) => { + const classes = useStyles() + const { ID, NAME, STATE, IM_MAD: imMad, VM_MAD: vmMad } = value const state = Host.STATES[STATE] const mad = imMad === vmMad ? imMad : `${imMad}/${vmMad}` - const classes = useStyles() - - const renderChip = ({ label, ...props }) => - - return ( - {ID} + } - title={( - - {NAME} - {renderChip({ label: mad })} - - )} + + title={ + + + {NAME} + + {mad} + + } + subheader={`#${ID}`} isSelected={isSelected} handleClick={handleClick} actions={actions} diff --git a/src/fireedge/src/client/components/Cards/NetworkCard.js b/src/fireedge/src/client/components/Cards/NetworkCard.js index 28ac452c30..6df213e6ef 100644 --- a/src/fireedge/src/client/components/Cards/NetworkCard.js +++ b/src/fireedge/src/client/components/Cards/NetworkCard.js @@ -1,16 +1,11 @@ import React, { memo } from 'react' import PropTypes from 'prop-types' -import { makeStyles, Avatar } from '@material-ui/core' +import { makeStyles } from '@material-ui/core' +import NetworkIcon from '@material-ui/icons/AccountTree' import SelectCard from 'client/components/Cards/SelectCard' const useStyles = makeStyles(theme => ({ - title: { display: 'flex', gap: '8px' }, - avatar: { - width: '5ch', - height: '5ch', - color: theme.palette.primary.contrastText - }, card: { backgroundColor: theme.palette.primary.main } })) @@ -23,8 +18,9 @@ const NetworkCard = memo( {ID}} + icon={} title={NAME} + subheader={`#${ID}`} isSelected={isSelected} handleClick={handleClick} actions={actions} diff --git a/src/fireedge/src/client/components/Cards/ProvisionCard.js b/src/fireedge/src/client/components/Cards/ProvisionCard.js index fc9fcb69a2..4c3a89f958 100644 --- a/src/fireedge/src/client/components/Cards/ProvisionCard.js +++ b/src/fireedge/src/client/components/Cards/ProvisionCard.js @@ -38,10 +38,13 @@ const ProvisionCard = memo( return ( )} + action={actions?.map(action => + + )} icon={ isProvider ? ( diff --git a/src/fireedge/src/client/components/Cards/ProvisionTemplateCard-delete.js b/src/fireedge/src/client/components/Cards/ProvisionTemplateCard-delete.js deleted file mode 100644 index 49313db3fb..0000000000 --- a/src/fireedge/src/client/components/Cards/ProvisionTemplateCard-delete.js +++ /dev/null @@ -1,56 +0,0 @@ -import React, { memo, useMemo } from 'react' -import PropTypes from 'prop-types' - -import SelectCard from 'client/components/Cards/SelectCard' -import { isExternalURL } from 'client/utils' -import { PROVIDER_IMAGES_URL, PROVISION_IMAGES_URL } from 'client/constants' - -const ProvisionTemplateCard = memo( - ({ value, title, isSelected, isProvider, handleClick }) => { - const IMAGES_URL = isProvider ? PROVIDER_IMAGES_URL : PROVISION_IMAGES_URL - const { image } = (isProvider ? value?.plain : value) ?? {} - - const imgSource = useMemo(() => - isExternalURL(image) ? image : `${IMAGES_URL}/${image}` - , [image]) - - return ( - - ) - }, (prev, next) => prev.isSelected === next.isSelected -) - -ProvisionTemplateCard.propTypes = { - value: PropTypes.shape({ - name: PropTypes.string.isRequired, - plain: PropTypes.shape({ - image: PropTypes.string - }) - }), - title: PropTypes.string, - isProvider: PropTypes.bool, - isSelected: PropTypes.bool, - handleClick: PropTypes.func -} - -ProvisionTemplateCard.defaultProps = { - value: {}, - title: undefined, - isProvider: undefined, - isSelected: undefined, - handleClick: undefined -} - -ProvisionTemplateCard.displayName = 'ProvisionTemplateCard' - -export default ProvisionTemplateCard diff --git a/src/fireedge/src/client/components/Cards/SelectCard/index.js b/src/fireedge/src/client/components/Cards/SelectCard/index.js index 25b4dc8d42..4e867fc6f2 100644 --- a/src/fireedge/src/client/components/Cards/SelectCard/index.js +++ b/src/fireedge/src/client/components/Cards/SelectCard/index.js @@ -59,29 +59,32 @@ const SelectCard = memo(({ } > {/* CARD HEADER */} - {(title || subheader || icon || action) && } + {(title || subheader || icon || action) && ( + + )} {/* CARD CONTENT */} {children} @@ -150,10 +153,7 @@ SelectCard.propTypes = { PropTypes.string, PropTypes.object ]), - cardHeaderProps: PropTypes.shape({ - titleTypographyProps: PropTypes.object, - subheaderTypographyProps: PropTypes.object - }), + cardHeaderProps: PropTypes.object, mediaProps: PropTypes.shape({ classes: PropTypes.object, className: PropTypes.string, diff --git a/src/fireedge/src/client/components/Cards/SelectCard/styles.js b/src/fireedge/src/client/components/Cards/SelectCard/styles.js index 2acb059cbb..82f7f38a99 100644 --- a/src/fireedge/src/client/components/Cards/SelectCard/styles.js +++ b/src/fireedge/src/client/components/Cards/SelectCard/styles.js @@ -34,11 +34,12 @@ export default makeStyles(theme => ({ } }, media: {}, + headerRoot: { alignItems: 'end' }, + headerContent: { overflow: 'auto' }, headerAvatar: { display: 'flex', color: theme.palette.primary.contrastText }, - headerContent: { overflowX: 'hidden' }, header: { color: theme.palette.primary.contrastText }, diff --git a/src/fireedge/src/client/components/DebugLog/index.js b/src/fireedge/src/client/components/DebugLog/index.js index 57ae9e12e7..6f3b3b44ad 100644 --- a/src/fireedge/src/client/components/DebugLog/index.js +++ b/src/fireedge/src/client/components/DebugLog/index.js @@ -1,71 +1,30 @@ import React, { useEffect, useState, memo } from 'react' import PropTypes from 'prop-types' -import clsx from 'clsx' import { makeStyles, Box } from '@material-ui/core' import AutoScrollBox from 'client/components/AutoScrollBox' +import Message from 'client/components/DebugLog/message' import { DEBUG_LEVEL } from 'client/constants' -import AnsiHtml from 'client/components/DebugLog/ansiHtml' - -const useStyles = makeStyles(theme => ({ +const debugLogStyles = makeStyles(theme => ({ root: { - display: 'flex', - marginBottom: '0.3em', - padding: '0.5em 0', - cursor: 'default', - fontFamily: 'monospace', - '&:hover': { - background: '#333537' + fontSize: '1.1em', + wordBreak: 'break-word', + '&::-webkit-scrollbar': { + width: 14 + }, + '&::-webkit-scrollbar-thumb': { + backgroundClip: 'content-box', + border: '4px solid transparent', + borderRadius: 7, + boxShadow: 'inset 0 0 0 10px', + color: theme.palette.primary.light } - }, - time: { - paddingLeft: '0.5em', - minWidth: '220px' - }, - message: { - color: '#fafafa' - }, - [DEBUG_LEVEL.ERROR]: { borderLeft: `0.3em solid ${theme.palette.error.light}` }, - [DEBUG_LEVEL.WARN]: { borderLeft: `0.3em solid ${theme.palette.warning.main}` }, - [DEBUG_LEVEL.INFO]: { borderLeft: `0.3em solid ${theme.palette.info.main}` }, - [DEBUG_LEVEL.DEBUG]: { borderLeft: `0.3em solid ${theme.palette.debug.main}` } + } })) -// -------------------------------------------- -// MESSAGE COMPONENT -// -------------------------------------------- - -const Message = memo(({ timestamp = '', severity = DEBUG_LEVEL.DEBUG, message }) => { - const classes = useStyles() - const sanitize = AnsiHtml(message) - - return ( -
-
{timestamp}
-
{sanitize}
-
- ) -}) - -Message.propTypes = { - timestamp: PropTypes.string, - severity: PropTypes.string, - message: PropTypes.string, - index: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.number - ]) -} - -Message.defaultProps = { log: [], message: '', index: 0 } -Message.displayName = 'Message' - -// -------------------------------------------- -// DEBUG LOG COMPONENT -// -------------------------------------------- - const DebugLog = memo(({ uuid, socket, logDefault }) => { + const classes = debugLogStyles() const [log, setLog] = useState(logDefault) useEffect(() => { @@ -83,7 +42,7 @@ const DebugLog = memo(({ uuid, socket, logDefault }) => { }, []) return ( - + {Object.entries(log)?.map(([command, entries]) => Object.entries(entries)?.map(([commandId, messages]) => diff --git a/src/fireedge/src/client/components/DebugLog/message.js b/src/fireedge/src/client/components/DebugLog/message.js new file mode 100644 index 0000000000..e0bb8880c9 --- /dev/null +++ b/src/fireedge/src/client/components/DebugLog/message.js @@ -0,0 +1,80 @@ +import React, { memo, useState, useRef } from 'react' +import PropTypes from 'prop-types' +import clsx from 'clsx' + +import { makeStyles } from '@material-ui/core' +import ChevronRightIcon from '@material-ui/icons/ChevronRight' + +import { DEBUG_LEVEL } from 'client/constants' +import AnsiHtml from 'client/components/DebugLog/ansiHtml' + +const MAX_CHARS = 80 + +const useStyles = makeStyles(theme => ({ + root: { + display: 'flex', + alignItems: 'center', + marginBottom: '0.3em', + padding: '0.5em 0', + cursor: ({ isCollapsed }) => isCollapsed ? 'pointer' : 'default', + fontFamily: 'monospace', + '&:hover': { + background: '#333537' + } + }, + arrow: { + padding: '0 0.5em', + width: '32px' + }, + time: { + minWidth: '220px' + }, + message: { + color: '#fafafa' + }, + [DEBUG_LEVEL.ERROR]: { borderLeft: `0.3em solid ${theme.palette.error.light}` }, + [DEBUG_LEVEL.WARN]: { borderLeft: `0.3em solid ${theme.palette.warning.light}` }, + [DEBUG_LEVEL.INFO]: { borderLeft: `0.3em solid ${theme.palette.info.light}` }, + [DEBUG_LEVEL.DEBUG]: { borderLeft: `0.3em solid ${theme.palette.debug.main}` } +})) + +// -------------------------------------------- +// MESSAGE COMPONENT +// -------------------------------------------- + +const Message = memo(({ timestamp, severity, message }) => { + const [isCollapsed, setCollapse] = useState(() => message?.length >= MAX_CHARS) + const classes = useStyles({ isCollapsed }) + const sanitize = AnsiHtml(message) + + return ( +
setCollapse(false)} + > +
+ {isCollapsed && } +
+
{timestamp}
+
+ {isCollapsed ? `${sanitize.slice(0, MAX_CHARS)}...` : sanitize} +
+
+ ) +}) + +Message.propTypes = { + timestamp: PropTypes.string, + severity: PropTypes.oneOf([Object.keys(DEBUG_LEVEL)]), + message: PropTypes.string +} + +Message.defaultProps = { + timestamp: '', + severity: DEBUG_LEVEL.DEBUG, + message: '' +} + +Message.displayName = 'Message' + +export default Message diff --git a/src/fireedge/src/client/components/Header/styles.js b/src/fireedge/src/client/components/Header/styles.js index b491f3e298..efc087233a 100644 --- a/src/fireedge/src/client/components/Header/styles.js +++ b/src/fireedge/src/client/components/Header/styles.js @@ -9,6 +9,7 @@ export default makeStyles(theme => ({ : theme.palette.primary.main }, title: { + userSelect: 'none', flexGrow: 1, display: 'inline-flex', color: theme.palette.primary.contrastText, diff --git a/src/fireedge/src/client/components/Sidebar/styles.js b/src/fireedge/src/client/components/Sidebar/styles.js index cc6fa82d8f..6c7b590d7d 100644 --- a/src/fireedge/src/client/components/Sidebar/styles.js +++ b/src/fireedge/src/client/components/Sidebar/styles.js @@ -68,6 +68,7 @@ export default makeStyles(theme => ({ // HEADER MENU // ------------------------------- header: { + userSelect: 'none', display: 'flex', alignItems: 'center', padding: '1rem', diff --git a/src/fireedge/src/client/components/Status/Chip.js b/src/fireedge/src/client/components/Status/Chip.js index 903c898627..e3f6dec901 100644 --- a/src/fireedge/src/client/components/Status/Chip.js +++ b/src/fireedge/src/client/components/Status/Chip.js @@ -1,21 +1,19 @@ import React, { memo } from 'react' import PropTypes from 'prop-types' -import { makeStyles, Typography } from '@material-ui/core' +import { makeStyles, Typography, lighten } from '@material-ui/core' import { addOpacityToColor } from 'client/utils' const useStyles = makeStyles(theme => ({ - root: ({ stateColor }) => ({ - color: stateColor, - backgroundColor: addOpacityToColor(stateColor, 0.08), + root: ({ stateColor = theme.palette.primary.main }) => ({ + color: lighten(stateColor, 0.75), + backgroundColor: addOpacityToColor(stateColor, 0.2), 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' + fontWeight: theme.typography.fontWeightBold }) })) diff --git a/src/fireedge/src/client/containers/Providers/Sections/info.js b/src/fireedge/src/client/containers/Providers/Sections/info.js index f4049e730c..7844d13599 100644 --- a/src/fireedge/src/client/containers/Providers/Sections/info.js +++ b/src/fireedge/src/client/containers/Providers/Sections/info.js @@ -40,7 +40,7 @@ const Info = memo(({ data }) => { {NAME} - {Tr(T.Description)} + {Tr(T.Description)} {description} diff --git a/src/fireedge/src/client/containers/Providers/index.js b/src/fireedge/src/client/containers/Providers/index.js index 71ac418b00..a6cda9a8a1 100644 --- a/src/fireedge/src/client/containers/Providers/index.js +++ b/src/fireedge/src/client/containers/Providers/index.js @@ -29,6 +29,8 @@ function Providers () { useEffect(() => { fetchRequest() }, []) + const handleCancel = () => setShowDialog(false) + return ( setShowDialog({ id: ID, - title: `(ID: ${ID}) ${NAME}` + title: NAME, + subheader: `#${ID}` }), actions: [ { @@ -64,7 +67,7 @@ function Providers () { { handleClick: () => setShowDialog({ id: ID, - title: `DELETE provider - (ID: ${ID}) ${NAME}`, + title: `DELETE provider - #${ID} - ${NAME}`, handleAccept: () => { deleteProvider({ id: ID }) setShowDialog(false) @@ -82,11 +85,7 @@ function Providers () { {showDialog !== false && ( getProvider({ id: showDialog.id })} - dialogProps={{ - title: showDialog.title, - handleCancel: () => setShowDialog(false), - handleAccept: showDialog.handleAccept - }} + dialogProps={{ handleCancel, ...showDialog }} > {({ data }) => } diff --git a/src/fireedge/src/client/containers/Provisions/index.js b/src/fireedge/src/client/containers/Provisions/index.js index 52746e6925..42771d08ab 100644 --- a/src/fireedge/src/client/containers/Provisions/index.js +++ b/src/fireedge/src/client/containers/Provisions/index.js @@ -52,13 +52,14 @@ function Provisions () { cardsProps={({ value: { ID, NAME } }) => ({ handleClick: () => setShowDialog({ id: ID, - title: `(ID: ${ID}) ${NAME}`, + title: NAME, + subheader: `#${ID}`, content: DialogInfo }), actions: [{ handleClick: () => setShowDialog({ id: ID, - title: `DELETE provision - (ID: ${ID}) ${NAME}`, + title: `DELETE provision - #${ID} - ${NAME}`, handleAccept: () => { deleteProvision({ id: ID }) setShowDialog(false) @@ -77,11 +78,7 @@ function Provisions () { getProvision({ id: showDialog.id })} - dialogProps={{ - title: showDialog.title, - handleCancel, - handleAccept: showDialog.handleAccept - }} + dialogProps={{ handleCancel, ...showDialog }} > {props => createElement(showDialog.content, props)}