diff --git a/src/fireedge/src/client/components/Tables/Clusters/row.js b/src/fireedge/src/client/components/Tables/Clusters/row.js index e046b06c5c..db11f9264d 100644 --- a/src/fireedge/src/client/components/Tables/Clusters/row.js +++ b/src/fireedge/src/client/components/Tables/Clusters/row.js @@ -6,16 +6,18 @@ import { Typography } from '@material-ui/core' import { rowStyles } from 'client/components/Tables/styles' -const Row = ({ value, ...props }) => { +const Row = ({ original, value, ...props }) => { const classes = rowStyles() const { ID, NAME, HOSTS, DATASTORES, VNETS, PROVIDER_NAME } = value return (
- - {NAME} - +
+ + {NAME} + +
{`#${ID}`} @@ -41,6 +43,7 @@ const Row = ({ value, ...props }) => { } Row.propTypes = { + original: PropTypes.object, value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func diff --git a/src/fireedge/src/client/components/Tables/Datastores/columns.js b/src/fireedge/src/client/components/Tables/Datastores/columns.js index 3c2b0dca85..85810748f2 100644 --- a/src/fireedge/src/client/components/Tables/Datastores/columns.js +++ b/src/fireedge/src/client/components/Tables/Datastores/columns.js @@ -1,3 +1,4 @@ +import CategoryFilter from 'client/components/Tables/Enhanced/Utils/CategoryFilter' import * as DatastoreModel from 'client/models/Datastore' export default [ @@ -8,7 +9,14 @@ export default [ { Header: 'State', id: 'STATE', - accessor: row => DatastoreModel.getState(row) + accessor: row => DatastoreModel.getState(row)?.name, + disableFilters: false, + Filter: ({ column }) => CategoryFilter({ + column, + multiple: true, + title: 'State' + }), + filter: 'includesValue' }, { Header: 'Type', diff --git a/src/fireedge/src/client/components/Tables/Datastores/row.js b/src/fireedge/src/client/components/Tables/Datastores/row.js index 7a24d0829a..f54f41d84b 100644 --- a/src/fireedge/src/client/components/Tables/Datastores/row.js +++ b/src/fireedge/src/client/components/Tables/Datastores/row.js @@ -9,25 +9,29 @@ import { rowStyles } from 'client/components/Tables/styles' import * as DatastoreModel from 'client/models/Datastore' -const Row = ({ value, ...props }) => { +const Row = ({ original, value, ...props }) => { const classes = rowStyles() - const { ID, NAME, UNAME, GNAME, STATE, TYPE, CLUSTERS, LOCK, PROVISION_ID } = value + const { ID, NAME, UNAME, GNAME, TYPE, CLUSTERS, LOCK, PROVISION_ID } = value const { percentOfUsed, percentLabel } = DatastoreModel.getCapacityInfo(value) + const { color: stateColor, name: stateName } = DatastoreModel.getState(original) + return (
- +
- - {NAME} +
+ + {NAME} + {LOCK && } - +
{`#${ID}`} @@ -56,6 +60,7 @@ const Row = ({ value, ...props }) => { } Row.propTypes = { + original: PropTypes.object, value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func diff --git a/src/fireedge/src/client/components/Tables/Enhanced/Utils/CategoryFilter.js b/src/fireedge/src/client/components/Tables/Enhanced/Utils/CategoryFilter.js new file mode 100644 index 0000000000..762fe2c33e --- /dev/null +++ b/src/fireedge/src/client/components/Tables/Enhanced/Utils/CategoryFilter.js @@ -0,0 +1,111 @@ +import * as React from 'react' +import PropTypes from 'prop-types' + +import { List, ListSubheader, ListItem, Typography, IconButton } from '@material-ui/core' +import { Cancel } from 'iconoir-react' + +import { Tr } from 'client/components/HOC' + +// FILTER FUNCTION +export const categoryFilterFn = (accessorOption, rows, columnsId, filterValue) => + rows.filter(row => + columnsId.some(id => { + const val = row.values[id][accessorOption] ?? row.values[id] + + return filterValue.includes(val) + }) + ) + +categoryFilterFn.autoRemove = val => !val || !val.length + +// *************************************************************** + +const CategoryFilter = ({ title, column, accessorOption, multiple }) => { + /** @type {import('react-table').UseFiltersInstanceProps} */ + const { + setFilter, + id, + preFilteredRows, + filterValue = multiple ? [] : undefined + } = column + + React.useEffect(() => () => setFilter(multiple ? [] : undefined), []) + + // Calculate the options for filtering using the preFilteredRows + const options = React.useMemo(() => { + const options = new Set() + + preFilteredRows?.forEach(row => { + options.add(row.values[id]) + }) + + return [...options.values()] + }, [id]) + + const handleSelect = value => setFilter( + multiple ? [...filterValue, value] : value + ) + + const handleUnselect = value => setFilter( + multiple ? filterValue.filter(v => v !== value) : undefined + ) + + const handleClear = () => setFilter(multiple ? [] : undefined) + + const isFiltered = React.useMemo(() => ( + multiple ? filterValue?.length > 0 : filterValue !== undefined + ), [filterValue]) + + if (options.length === 0) { + return null + } + + return ( + + {title && ( + + {Tr(title)} + {isFiltered && ( + + + + )} + + )} + + {options.map((option, i) => { + const value = option[accessorOption] ?? option + + const isSelected = multiple + ? filterValue?.includes?.(value) + : value === filterValue + + return ( + + isSelected ? handleUnselect(value) : handleSelect(value) + } + > + + {value} + + + ) + })} + + ) +} + +CategoryFilter.propTypes = { + column: PropTypes.object, + accessorOption: PropTypes.string, + icon: PropTypes.node, + title: PropTypes.string, + multiple: PropTypes.bool +} + +export default CategoryFilter diff --git a/src/fireedge/src/client/components/Tables/Enhanced/Utils/GlobalFilter.js b/src/fireedge/src/client/components/Tables/Enhanced/Utils/GlobalFilter.js index ee8f254d14..c1abbad064 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/Utils/GlobalFilter.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/Utils/GlobalFilter.js @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import PropTypes from 'prop-types' import { makeStyles, fade, debounce, InputBase } from '@material-ui/core' @@ -14,7 +14,6 @@ const useStyles = makeStyles(({ spacing, palette, shape, breakpoints }) => ({ }, width: '100%', [breakpoints.up('sm')]: { - marginLeft: spacing(1), width: 'auto' } }, @@ -38,14 +37,14 @@ const useStyles = makeStyles(({ spacing, palette, shape, breakpoints }) => ({ } })) -const GlobalFilter = props => { +const GlobalFilter = ({ useTableProps }) => { + const classes = useStyles() + /** * @type {import('react-table').UseGlobalFiltersInstanceProps & - * import('react-table').UseGlobalFiltersState} + * { state: import('react-table').UseGlobalFiltersState }} */ - const { globalFilter, setGlobalFilter } = props - - const classes = useStyles() + const { setGlobalFilter, state: { globalFilter } } = useTableProps const [value, setValue] = React.useState(globalFilter) @@ -77,9 +76,7 @@ const GlobalFilter = props => { } GlobalFilter.propTypes = { - preGlobalFilteredRows: PropTypes.array.isRequired, - globalFilter: PropTypes.string, - setGlobalFilter: PropTypes.func.isRequired + useTableProps: PropTypes.object.isRequired } export default GlobalFilter diff --git a/src/fireedge/src/client/components/Tables/Enhanced/Utils/GlobalSort.js b/src/fireedge/src/client/components/Tables/Enhanced/Utils/GlobalSort.js index 95ef807a1d..5979949a4b 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/Utils/GlobalSort.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/Utils/GlobalSort.js @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import PropTypes from 'prop-types' import { makeStyles, MenuItem, MenuList, Chip } from '@material-ui/core' @@ -7,24 +7,26 @@ import { SortDown, ArrowDown, ArrowUp } from 'iconoir-react' import HeaderPopover from 'client/components/Header/Popover' import { T } from 'client/constants' -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles({ root: { display: 'flex', flexWrap: 'wrap', gap: 6, alignItems: 'center' } -})) +}) -const GlobalSort = props => { +const GlobalSort = ({ useTableProps }) => { const classes = useStyles() + React.useEffect(() => () => setSortBy([]), []) + /** - * @type {import('react-table').TableInstance & - * import('react-table').UseSortByInstanceProps & - * import('react-table').UseSortByState} + * @type {import('react-table').UseSortByInstanceProps & + * import('react-table').TableInstance & + * { state: import('react-table').UseSortByState }} */ - const { headers, sortBy, setSortBy } = props + const { headers, setSortBy, state: { sortBy } } = useTableProps const headersNotSorted = React.useMemo(() => headers.filter(({ isSorted, canSort, isVisible }) => @@ -95,10 +97,7 @@ const GlobalSort = props => { } GlobalSort.propTypes = { - headers: PropTypes.array.isRequired, - preSortedRows: PropTypes.array.isRequired, - sortBy: PropTypes.array.isRequired, - setSortBy: PropTypes.func.isRequired + useTableProps: PropTypes.object.isRequired } export default GlobalSort diff --git a/src/fireedge/src/client/components/Tables/Enhanced/filters.js b/src/fireedge/src/client/components/Tables/Enhanced/filters.js new file mode 100644 index 0000000000..bbc93bbf68 --- /dev/null +++ b/src/fireedge/src/client/components/Tables/Enhanced/filters.js @@ -0,0 +1,75 @@ +import * as React from 'react' +import PropTypes from 'prop-types' + +import clsx from 'clsx' +import { makeStyles, useMediaQuery, Card, CardContent } from '@material-ui/core' + +import GlobalFilter from 'client/components/Tables/Enhanced/Utils/GlobalFilter' + +const useToolbarStyles = makeStyles({ + root: { + display: 'flex' + }, + rootNoFilters: { + gridColumn: '1/3', + '& ~ div': { + gridColumn: '1/3' + } + }, + content: { + flexGrow: 1 + }, + contentWithFilter: { + display: 'flex', + flexDirection: 'column', + gap: '1em' + }, + filters: { + flexGrow: 1, + overflow: 'auto' + } +}) + +const Filters = ({ useTableProps }) => { + const classes = useToolbarStyles() + const isMobile = useMediaQuery(theme => theme.breakpoints.down('sm')) + + /** @type {import('react-table').UseTableInstanceProps} */ + const { rows, columns } = useTableProps + + const filters = React.useMemo(() => ( + columns + .filter(({ canFilter }) => canFilter) + .map(column => column.canFilter ? column.render('Filter') : null) + ), [rows]) + + if (isMobile) { + return + } + + const noFilters = filters.length === 0 + + return ( + + + + + {!noFilters &&
{filters}
} +
+
+ ) +} + +Filters.propTypes = { + useTableProps: PropTypes.object +} + +Filters.defaultProps = { + useTableProps: {} +} + +export default Filters diff --git a/src/fireedge/src/client/components/Tables/Enhanced/index.js b/src/fireedge/src/client/components/Tables/Enhanced/index.js index 81ced60499..3d8e3c5659 100644 --- a/src/fireedge/src/client/components/Tables/Enhanced/index.js +++ b/src/fireedge/src/client/components/Tables/Enhanced/index.js @@ -1,9 +1,11 @@ import * as React from 'react' import PropTypes from 'prop-types' -import { makeStyles, Box, LinearProgress } from '@material-ui/core' +import { InfoEmpty } from 'iconoir-react' +import { Box, LinearProgress, Typography } from '@material-ui/core' import { useGlobalFilter, + useFilters, usePagination, useRowSelect, useSortBy, @@ -13,63 +15,12 @@ import { import SplitPane from 'client/components/SplitPane' import Toolbar from 'client/components/Tables/Enhanced/toolbar' import Pagination from 'client/components/Tables/Enhanced/pagination' +import Filters from 'client/components/Tables/Enhanced/filters' +import DefaultFilter from 'client/components/Table/Filters/DefaultFilter' +import EnhancedTableStyles from 'client/components/Tables/Enhanced/styles' -import { addOpacityToColor } from 'client/utils' - -const useStyles = makeStyles(({ palette, typography, breakpoints }) => ({ - root: { - height: '100%', - display: 'flex', - flexDirection: 'column' - }, - body: { - overflow: 'auto', - display: 'grid', - gap: '1em', - gridTemplateColumns: '1fr', - '& > [role=row]': { - padding: '0.8em', - cursor: 'pointer', - color: palette.text.primary, - backgroundColor: palette.background.paper, - fontWeight: typography.fontWeightMedium, - fontSize: '1em', - borderRadius: 6, - display: 'flex', - gap: 8, - '&:hover': { - backgroundColor: palette.action.hover - }, - '&.selected': { - backgroundColor: addOpacityToColor(palette.secondary.main, 0.2), - border: `1px solid ${palette.secondary.main}` - } - } - }, - toolbar: { - ...typography.body1, - marginBottom: 16, - display: 'flex', - gap: '1em', - alignItems: 'start', - justifyContent: 'space-between', - '& > div:first-child': { - flexGrow: 1 - }, - [breakpoints.down('sm')]: { - flexWrap: 'wrap' - } - }, - pagination: { - flexShrink: 0, - display: 'flex', - alignItems: 'center', - gap: '1em' - }, - loading: { - transition: '200ms' - } -})) +import { Tr } from 'client/components/HOC' +import { T } from 'client/constants' const EnhancedTable = ({ canFetchMore, @@ -84,10 +35,11 @@ const EnhancedTable = ({ RowComponent, showPageCount }) => { - const classes = useStyles() + const classes = EnhancedTableStyles() const defaultColumn = React.useMemo(() => ({ - // Filter: DefaultFilter, + Filter: DefaultFilter, + disableFilters: true }), []) const sortTypes = React.useMemo(() => ({ @@ -118,6 +70,7 @@ const EnhancedTable = ({ } }, useGlobalFilter, + useFilters, useSortBy, usePagination, useRowSelect @@ -143,7 +96,7 @@ const EnhancedTable = ({ const canNextPage = pageCount === -1 ? page.length >= pageSize : newPage < pageCount - 1 - newPage > pageIndex && canFetchMore && !canNextPage && fetchMore() + newPage > pageIndex && canFetchMore && !canNextPage && fetchMore?.() } return ( @@ -167,30 +120,44 @@ const EnhancedTable = ({ )} -
- {page.map(row => { - prepareRow(row) +
+ - /** @type {import('react-table').UseRowSelectRowProps} */ - const { getRowProps, values, toggleRowSelected, isSelected } = row - const { key, ...rowProps } = getRowProps() +
+ {/* NO DATA MESSAGE */} + {((!isLoading && data.length === 0) || page?.length === 0) && ( + + + {Tr(T.NoDataAvailable)} + + )} - return ( - toggleRowSelected(!isSelected)} - /> - ) - })} + {/* DATALIST PER PAGE */} + {page.map(row => { + prepareRow(row) + + /** @type {import('react-table').UseRowSelectRowProps} */ + const { getRowProps, original, values, toggleRowSelected, isSelected } = row + const { key, ...rowProps } = getRowProps() + + return ( + toggleRowSelected(!isSelected)} + /> + ) + })} +
{selectedRows?.length === 1 && renderDetail - ? renderDetail(selectedRows[0]?.values) + ? renderDetail?.(selectedRows[0]?.values) : renderAllSelected && (
               
diff --git a/src/fireedge/src/client/components/Tables/Enhanced/styles.js b/src/fireedge/src/client/components/Tables/Enhanced/styles.js
new file mode 100644
index 0000000000..9e7f490ca7
--- /dev/null
+++ b/src/fireedge/src/client/components/Tables/Enhanced/styles.js
@@ -0,0 +1,73 @@
+import { makeStyles } from '@material-ui/core'
+
+import { addOpacityToColor } from 'client/utils'
+
+export default makeStyles(({ palette, typography, breakpoints }) => ({
+  root: {
+    height: '100%',
+    display: 'flex',
+    flexDirection: 'column'
+  },
+  toolbar: {
+    ...typography.body1,
+    marginBottom: 16,
+    display: 'flex',
+    gap: '1em',
+    alignItems: 'start',
+    justifyContent: 'space-between',
+    '& > div:first-child': {
+      flexGrow: 1
+    }
+  },
+  pagination: {
+    flexShrink: 0,
+    display: 'flex',
+    alignItems: 'center',
+    gap: '1em'
+  },
+  loading: {
+    transition: '200ms'
+  },
+  table: {
+    display: 'grid',
+    gridTemplateColumns: 'minmax(auto, 300px) 1fr',
+    gap: 8,
+    overflow: 'auto',
+    [breakpoints.down('sm')]: {
+      gridTemplateColumns: 'minmax(0, 1fr)'
+    }
+  },
+  body: {
+    overflow: 'auto',
+    display: 'grid',
+    gap: '1em',
+    gridTemplateColumns: 'minmax(0, 1fr)',
+    gridAutoRows: 'max-content',
+    '& > [role=row]': {
+      padding: '0.8em',
+      cursor: 'pointer',
+      color: palette.text.primary,
+      backgroundColor: palette.background.paper,
+      fontWeight: typography.fontWeightMedium,
+      fontSize: '1em',
+      borderRadius: 6,
+      display: 'flex',
+      gap: 8,
+      '&:hover': {
+        backgroundColor: palette.action.hover
+      },
+      '&.selected': {
+        backgroundColor: addOpacityToColor(palette.secondary.main, 0.2),
+        border: `1px solid ${palette.secondary.main}`
+      }
+    }
+  },
+  noDataMessage: {
+    ...typography.h6,
+    color: palette.text.hint,
+    display: 'inline-flex',
+    alignItems: 'center',
+    gap: 6,
+    padding: '1em'
+  }
+}))
diff --git a/src/fireedge/src/client/components/Tables/Enhanced/toolbar.js b/src/fireedge/src/client/components/Tables/Enhanced/toolbar.js
index 6d18efb1cf..de79684ba1 100644
--- a/src/fireedge/src/client/components/Tables/Enhanced/toolbar.js
+++ b/src/fireedge/src/client/components/Tables/Enhanced/toolbar.js
@@ -1,77 +1,26 @@
 import * as React from 'react'
 import PropTypes from 'prop-types'
 
-import { makeStyles, Button } from '@material-ui/core'
-import { Filter as FilterIcon } from 'iconoir-react'
+import { makeStyles, useMediaQuery } from '@material-ui/core'
 
-import GlobalFilter from 'client/components/Tables/Enhanced/Utils/GlobalFilter'
 import GlobalSort from 'client/components/Tables/Enhanced/Utils/GlobalSort'
 
-import { Tr } from 'client/components/HOC'
-import { T } from 'client/constants'
-
-const useToolbarStyles = makeStyles(() => ({
+const useToolbarStyles = makeStyles({
   root: {
     display: 'flex',
     flexDirection: 'column',
     alignItems: 'start',
     gap: '1em'
-  },
-  filterWrapper: {
-    flexGrow: 1,
-    display: 'flex',
-    gap: '1em'
-  },
-  filterButton: {
-    minWidth: 123,
-    textAlign: 'left'
   }
-}))
+})
 
 const Toolbar = ({ useTableProps }) => {
   const classes = useToolbarStyles()
-
-  /**
-   * @type {import('react-table').UseGlobalFiltersInstanceProps &
-   *        import('react-table').UseSortByInstanceProps &
-   *        import('react-table').TableInstance &
-   * { state: import('react-table').UseGlobalFiltersState &
-   *          import('react-table').TableState
-   *          import('react-table').UseSortByState }}
-   */
-  const {
-    headers,
-    preGlobalFilteredRows,
-    setGlobalFilter,
-    preSortedRows,
-    setSortBy,
-    state: { globalFilter, sortBy }
-  } = useTableProps
-
-  // const numSelected = Object.keys(selectedRowIds).length
+  const isMobile = useMediaQuery(theme => theme.breakpoints.down('sm'))
 
   return (
     
-
- - -
- + {!isMobile && }
) } diff --git a/src/fireedge/src/client/components/Tables/Hosts/columns.js b/src/fireedge/src/client/components/Tables/Hosts/columns.js index f52478908a..213c71cad6 100644 --- a/src/fireedge/src/client/components/Tables/Hosts/columns.js +++ b/src/fireedge/src/client/components/Tables/Hosts/columns.js @@ -1,3 +1,4 @@ +import CategoryFilter from 'client/components/Tables/Enhanced/Utils/CategoryFilter' import * as HostModel from 'client/models/Host' const getTotalOfResources = resources => [resources?.ID ?? []].flat().length || 0 @@ -12,7 +13,14 @@ export default [ { Header: 'State', id: 'STATE', - accessor: row => HostModel.getState(row) + accessor: row => HostModel.getState(row)?.name, + disableFilters: false, + Filter: ({ column }) => CategoryFilter({ + column, + multiple: true, + title: 'State' + }), + filter: 'includesValue' }, { Header: 'Cluster', accessor: 'CLUSTER' }, { Header: 'IM MAD', accessor: 'IM_MAD' }, diff --git a/src/fireedge/src/client/components/Tables/Hosts/row.js b/src/fireedge/src/client/components/Tables/Hosts/row.js index e8032c105d..587f55cc4a 100644 --- a/src/fireedge/src/client/components/Tables/Hosts/row.js +++ b/src/fireedge/src/client/components/Tables/Hosts/row.js @@ -9,11 +9,11 @@ import { rowStyles } from 'client/components/Tables/styles' import * as HostModel from 'client/models/Host' -const Row = ({ value, ...props }) => { +const Row = ({ original, value, ...props }) => { const classes = rowStyles() const { - ID, NAME, IM_MAD, VM_MAD, STATE, - RUNNING_VMS, TOTAL_VMS, CLUSTER, TEMPLATE + ID, NAME, IM_MAD, VM_MAD, RUNNING_VMS, + TOTAL_VMS, CLUSTER, TEMPLATE } = value const { @@ -23,22 +23,26 @@ const Row = ({ value, ...props }) => { percentMemLabel } = HostModel.getAllocatedInfo(value) + const { color: stateColor, name: stateName } = HostModel.getState(original) + const labels = [...new Set([IM_MAD, VM_MAD])] return (
- +
- - {TEMPLATE?.NAME ?? NAME} +
+ + {TEMPLATE?.NAME ?? NAME} + {labels.map(label => ( ))} - +
{`#${ID}`} @@ -60,6 +64,7 @@ const Row = ({ value, ...props }) => { } Row.propTypes = { + original: PropTypes.object, value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func diff --git a/src/fireedge/src/client/components/Tables/Images/columns.js b/src/fireedge/src/client/components/Tables/Images/columns.js index 5efade194c..5df5c6fda5 100644 --- a/src/fireedge/src/client/components/Tables/Images/columns.js +++ b/src/fireedge/src/client/components/Tables/Images/columns.js @@ -1,3 +1,4 @@ +import CategoryFilter from 'client/components/Tables/Enhanced/Utils/CategoryFilter' import * as ImageModel from 'client/models/Image' const getTotalOfResources = resources => [resources?.ID ?? []].flat().length || 0 @@ -10,7 +11,14 @@ export default [ { Header: 'State', id: 'STATE', - accessor: row => ImageModel.getState(row) + accessor: row => ImageModel.getState(row)?.name, + disableFilters: false, + Filter: ({ column }) => CategoryFilter({ + column, + multiple: true, + title: 'State' + }), + filter: 'includesValue' }, { Header: 'Type', diff --git a/src/fireedge/src/client/components/Tables/Images/row.js b/src/fireedge/src/client/components/Tables/Images/row.js index eda3ee7a71..e81e8695bf 100644 --- a/src/fireedge/src/client/components/Tables/Images/row.js +++ b/src/fireedge/src/client/components/Tables/Images/row.js @@ -7,14 +7,15 @@ import { Typography } from '@material-ui/core' import { StatusCircle, StatusChip } from 'client/components/Status' import { rowStyles } from 'client/components/Tables/styles' +import * as ImageModel from 'client/models/Image' import * as Helper from 'client/models/Helper' -const Row = ({ value, ...props }) => { +const Row = ({ original, value, ...props }) => { const classes = rowStyles() const { - ID, NAME, UNAME, GNAME, REGTIME, - STATE, TYPE, DISK_TYPE, PERSISTENT, - LOCK, DATASTORE, VMS, RUNNING_VMS + ID, NAME, UNAME, GNAME, REGTIME, TYPE, + DISK_TYPE, PERSISTENT, LOCK, DATASTORE, + VMS, RUNNING_VMS } = value const usedByVms = [VMS?.ID ?? []].flat().length || 0 @@ -22,24 +23,28 @@ const Row = ({ value, ...props }) => { const labels = [...new Set([ PERSISTENT && 'PERSISTENT', TYPE, DISK_TYPE])].filter(Boolean) + const { color: stateColor, name: stateName } = ImageModel.getState(original) + const time = Helper.timeFromMilliseconds(+REGTIME) const timeAgo = `registered ${time.toRelative()}` return (
- +
- - {NAME} +
+ + {NAME} + {LOCK && } {labels.map(label => ( ))} - +
{`#${ID} ${timeAgo}`} @@ -68,6 +73,7 @@ const Row = ({ value, ...props }) => { } Row.propTypes = { + original: PropTypes.object, value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func diff --git a/src/fireedge/src/client/components/Tables/MarketplaceApps/columns.js b/src/fireedge/src/client/components/Tables/MarketplaceApps/columns.js index 53e1338c23..3019b50a3e 100644 --- a/src/fireedge/src/client/components/Tables/MarketplaceApps/columns.js +++ b/src/fireedge/src/client/components/Tables/MarketplaceApps/columns.js @@ -1,3 +1,4 @@ +import CategoryFilter from 'client/components/Tables/Enhanced/Utils/CategoryFilter' import * as MarketplaceAppModel from 'client/models/MarketplaceApp' export default [ @@ -8,7 +9,14 @@ export default [ { Header: 'State', id: 'STATE', - accessor: row => MarketplaceAppModel.getState(row) + accessor: row => MarketplaceAppModel.getState(row)?.name, + disableFilters: false, + Filter: ({ column }) => CategoryFilter({ + column, + multiple: true, + title: 'State' + }), + filter: 'includesValue' }, { Header: 'Type', @@ -17,6 +25,16 @@ export default [ }, { Header: 'Size', accessor: 'SIZE' }, { Header: 'Registration Time', accessor: 'REGTIME' }, - { Header: 'Marketplace', accessor: 'MARKETPLACE' }, + { + Header: 'Marketplace', + accessor: 'MARKETPLACE', + disableFilters: false, + Filter: ({ column }) => CategoryFilter({ + column, + multiple: true, + title: 'Marketplace' + }), + filter: 'includesValue' + }, { Header: 'Zone ID', accessor: 'ZONE_ID' } ] diff --git a/src/fireedge/src/client/components/Tables/MarketplaceApps/row.js b/src/fireedge/src/client/components/Tables/MarketplaceApps/row.js index 81904c9ec2..97045d8824 100644 --- a/src/fireedge/src/client/components/Tables/MarketplaceApps/row.js +++ b/src/fireedge/src/client/components/Tables/MarketplaceApps/row.js @@ -7,32 +7,37 @@ import { Typography } from '@material-ui/core' import { StatusCircle, StatusChip } from 'client/components/Status' import { rowStyles } from 'client/components/Tables/styles' +import * as MarketplaceAppModel from 'client/models/MarketplaceApp' import * as Helper from 'client/models/Helper' import { prettyBytes } from 'client/utils' -const Row = ({ value, ...props }) => { +const Row = ({ original, value, ...props }) => { const classes = rowStyles() const { - ID, NAME, UNAME, GNAME, LOCK, TYPE, STATE, + ID, NAME, UNAME, GNAME, LOCK, TYPE, REGTIME, MARKETPLACE, ZONE_ID, SIZE } = value + const { color: stateColor, name: stateName } = MarketplaceAppModel.getState(original) + const time = Helper.timeFromMilliseconds(+REGTIME) const timeAgo = `registered ${time.toRelative()}` return (
- +
- - {NAME} +
+ + {NAME} + {LOCK && } - +
{`#${ID} ${timeAgo}`} @@ -62,6 +67,7 @@ const Row = ({ value, ...props }) => { } Row.propTypes = { + original: PropTypes.object, value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func diff --git a/src/fireedge/src/client/components/Tables/Marketplaces/columns.js b/src/fireedge/src/client/components/Tables/Marketplaces/columns.js index a9d8060bfa..74ed17e8e4 100644 --- a/src/fireedge/src/client/components/Tables/Marketplaces/columns.js +++ b/src/fireedge/src/client/components/Tables/Marketplaces/columns.js @@ -1,3 +1,4 @@ +import CategoryFilter from 'client/components/Tables/Enhanced/Utils/CategoryFilter' import * as MarketplaceModel from 'client/models/Datastore' const getTotalOfResources = resources => [resources?.ID ?? []].flat().length || 0 @@ -10,7 +11,14 @@ export default [ { Header: 'State', id: 'STATE', - accessor: row => MarketplaceModel.getState(row) + accessor: row => MarketplaceModel.getState(row)?.name, + disableFilters: false, + Filter: ({ column }) => CategoryFilter({ + column, + multiple: true, + title: 'State' + }), + filter: 'includesValue' }, { Header: 'Market', accessor: 'MARKET_MAD' }, { Header: 'Total Capacity', accessor: 'TOTAL_MB' }, diff --git a/src/fireedge/src/client/components/Tables/Marketplaces/row.js b/src/fireedge/src/client/components/Tables/Marketplaces/row.js index 9a86010765..a54f66c3bc 100644 --- a/src/fireedge/src/client/components/Tables/Marketplaces/row.js +++ b/src/fireedge/src/client/components/Tables/Marketplaces/row.js @@ -9,24 +9,28 @@ import { rowStyles } from 'client/components/Tables/styles' import * as MarketplaceModel from 'client/models/Datastore' -const Row = ({ value, ...props }) => { +const Row = ({ original, value, ...props }) => { const classes = rowStyles() - const { ID, NAME, UNAME, GNAME, STATE, MARKET_MAD, TOTAL_APPS } = value + const { ID, NAME, UNAME, GNAME, MARKET_MAD, TOTAL_APPS } = value + + const { name: stateName, color: stateColor } = MarketplaceModel.getState(original) const { percentOfUsed, percentLabel } = MarketplaceModel.getCapacityInfo(value) return (
- +
- - {NAME} +
+ + {NAME} + - +
{`#${ID}`} @@ -52,6 +56,7 @@ const Row = ({ value, ...props }) => { Row.propTypes = { value: PropTypes.object, + original: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func } diff --git a/src/fireedge/src/client/components/Tables/Vms/columns.js b/src/fireedge/src/client/components/Tables/Vms/columns.js index 4b085714b8..b24ee0d20d 100644 --- a/src/fireedge/src/client/components/Tables/Vms/columns.js +++ b/src/fireedge/src/client/components/Tables/Vms/columns.js @@ -1,3 +1,4 @@ +import CategoryFilter from 'client/components/Tables/Enhanced/Utils/CategoryFilter' import * as VirtualMachineModel from 'client/models/VirtualMachine' export default [ @@ -6,7 +7,14 @@ export default [ { Header: 'State', id: 'STATE', - accessor: row => VirtualMachineModel.getState(row) + accessor: row => VirtualMachineModel.getState(row)?.name, + disableFilters: false, + Filter: ({ column }) => CategoryFilter({ + column, + multiple: true, + title: 'State' + }), + filter: 'includesValue' }, { Header: 'Owner', accessor: 'UNAME' }, { Header: 'Group', accessor: 'GNAME' }, @@ -22,6 +30,6 @@ export default [ { Header: 'Hostname', id: 'HOSTNAME', - accessor: row => VirtualMachineModel.getLastHistory(row)?.HOSTNAME ?? '--' + accessor: row => VirtualMachineModel.getLastHistory(row)?.HOSTNAME } ] diff --git a/src/fireedge/src/client/components/Tables/Vms/multiple.js b/src/fireedge/src/client/components/Tables/Vms/multiple.js index 369d26ab67..c5b45efe2a 100644 --- a/src/fireedge/src/client/components/Tables/Vms/multiple.js +++ b/src/fireedge/src/client/components/Tables/Vms/multiple.js @@ -16,20 +16,22 @@ const Multiple = ({ tags, limitTags = 1 }) => { )) - return [ - ...Tags, - (more > 0 && ( - ( - {tag} - ))} - > - - {`+${more} more`} - - - )) - ] + return ( +
+ {Tags} + {more > 0 && ( + ( + {tag} + ))} + > + + {`+${more} more`} + + + )} +
+ ) } Multiple.propTypes = { diff --git a/src/fireedge/src/client/components/Tables/Vms/row.js b/src/fireedge/src/client/components/Tables/Vms/row.js index 7e2318667b..c3ec3f915c 100644 --- a/src/fireedge/src/client/components/Tables/Vms/row.js +++ b/src/fireedge/src/client/components/Tables/Vms/row.js @@ -8,28 +8,32 @@ import { StatusCircle } from 'client/components/Status' import Multiple from 'client/components/Tables/Vms/multiple' import { rowStyles } from 'client/components/Tables/styles' +import * as VirtualMachineModel from 'client/models/VirtualMachine' import * as Helper from 'client/models/Helper' -const Row = ({ value, ...props }) => { +const Row = ({ original, value, ...props }) => { const classes = rowStyles() - const { - ID, NAME, UNAME, GNAME, STATE, - IPS, STIME, ETIME, HOSTNAME, LOCK - } = value + const { ID, NAME, UNAME, GNAME, IPS, STIME, ETIME, HOSTNAME = '--', LOCK } = value const time = Helper.timeFromMilliseconds(+ETIME || +STIME) const timeAgo = `${+ETIME ? 'done' : 'started'} ${time.toRelative()}` + const { color: stateColor, name: stateName } = VirtualMachineModel.getState(original) + return (
- +
- - {NAME} - {LOCK && } - +
+ + {NAME} + + + {LOCK && } + +
{`#${ID} ${timeAgo}`} @@ -56,6 +60,7 @@ const Row = ({ value, ...props }) => { } Row.propTypes = { + original: PropTypes.object, value: PropTypes.object, isSelected: PropTypes.bool, handleClick: PropTypes.func diff --git a/src/fireedge/src/client/components/Tables/styles.js b/src/fireedge/src/client/components/Tables/styles.js index a23634c8d8..2f9658ab38 100644 --- a/src/fireedge/src/client/components/Tables/styles.js +++ b/src/fireedge/src/client/components/Tables/styles.js @@ -3,17 +3,20 @@ import { makeStyles } from '@material-ui/core' export const rowStyles = makeStyles( ({ palette, typography, breakpoints }) => ({ main: { - flex: 'auto' + flex: 'auto', + overflow: 'hidden' }, title: { color: palette.text.primary, display: 'flex', - alignItems: 'center' + gap: 6, + alignItems: 'center', + flexWrap: 'wrap', + marginBottom: 8 }, labels: { display: 'inline-flex', - gap: 6, - marginLeft: 6 + gap: 6 }, caption: { ...typography.caption, diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js index 16775a9af1..fd6c50f3d4 100644 --- a/src/fireedge/src/client/constants/translates.js +++ b/src/fireedge/src/client/constants/translates.js @@ -49,6 +49,7 @@ module.exports = { NotFound: 'Not found', None: 'None', Empty: 'Empty', + NoDataAvailable: 'There is no data available', /* steps form */ /* steps form - flow */