diff --git a/src/fireedge/src/client/components/Cards/DatastoreCard.js b/src/fireedge/src/client/components/Cards/DatastoreCard.js index 5b5935ca3f..3475e296b0 100644 --- a/src/fireedge/src/client/components/Cards/DatastoreCard.js +++ b/src/fireedge/src/client/components/Cards/DatastoreCard.js @@ -7,8 +7,7 @@ import { Folder as DatastoreIcon } from 'iconoir-react' import SelectCard, { Action } from 'client/components/Cards/SelectCard' import { StatusBadge, StatusChip, LinearProgressWithLabel } from 'client/components/Status' -import { prettyBytes } from 'client/utils' -import Datastore from 'client/constants/datastore' +import * as DatastoreModel from 'client/models/Datastore' const useStyles = makeStyles(({ title: { @@ -27,14 +26,15 @@ const DatastoreCard = memo( ({ value, isSelected, handleClick, actions }) => { const classes = useStyles() - const { ID, NAME, TYPE, STATE, TOTAL_MB, USED_MB } = value - const type = Datastore.TYPES[TYPE] - const state = Datastore.STATES[STATE] + const { ID, NAME } = value - const percentOfUsed = +USED_MB * 100 / +TOTAL_MB || 0 - const usedBytes = prettyBytes(+USED_MB, 'MB') - const totalBytes = prettyBytes(+TOTAL_MB, 'MB') - const percentLabel = `${usedBytes} / ${totalBytes} (${Math.round(percentOfUsed)}%)` + const type = DatastoreModel.getType(value) + const state = DatastoreModel.getState(value) + + const { + percentOfUsed, + percentLabel + } = DatastoreModel.getCapacityInfo(value) return ( { const classes = useStyles() - const { ID, NAME, STATE, IM_MAD, VM_MAD, HOST_SHARE } = value - const { CPU_USAGE, TOTAL_CPU, MEM_USAGE, TOTAL_MEM } = HOST_SHARE + const { ID, NAME, IM_MAD, VM_MAD } = value - const percentCpuUsed = +CPU_USAGE * 100 / +TOTAL_CPU || 0 - const percentCpuLabel = `${CPU_USAGE} / ${TOTAL_CPU} (${Math.round(percentCpuUsed)}%)` + const { + percentCpuUsed, + percentCpuLabel, + percentMemUsed, + percentMemLabel + } = HostModel.getAllocatedInfo(value) - const percentMemUsed = +MEM_USAGE * 100 / +TOTAL_MEM || 0 - const usedMemBytes = prettyBytes(+MEM_USAGE) - const totalMemBytes = prettyBytes(+TOTAL_MEM) - const percentMemLabel = `${usedMemBytes} / ${totalMemBytes} (${Math.round(percentMemUsed)}%)` + const state = HostModel.getState(value) - const state = Host.STATES[STATE] const mad = IM_MAD === VM_MAD ? IM_MAD : `${IM_MAD}/${VM_MAD}` return ( diff --git a/src/fireedge/src/client/components/DebugLog/index.js b/src/fireedge/src/client/components/DebugLog/index.js index 9f12071ef8..6901b70b7a 100644 --- a/src/fireedge/src/client/components/DebugLog/index.js +++ b/src/fireedge/src/client/components/DebugLog/index.js @@ -28,7 +28,7 @@ const debugLogStyles = makeStyles(theme => ({ border: '4px solid transparent', borderRadius: 7, boxShadow: 'inset 0 0 0 10px', - color: theme.palette.primary.light + color: theme.palette.secondary.light } } })) diff --git a/src/fireedge/src/client/components/HOC/InternalLayout/styles.js b/src/fireedge/src/client/components/HOC/InternalLayout/styles.js index 118ea5fc19..1b4d1bba52 100644 --- a/src/fireedge/src/client/components/HOC/InternalLayout/styles.js +++ b/src/fireedge/src/client/components/HOC/InternalLayout/styles.js @@ -47,7 +47,7 @@ export default makeStyles(theme => ({ border: '4px solid transparent', borderRadius: 7, boxShadow: 'inset 0 0 0 10px', - color: theme.palette.primary.light + color: theme.palette.secondary.light } }, /* ROUTES TRANSITIONS */ diff --git a/src/fireedge/src/client/components/List/ListVirtualized.js b/src/fireedge/src/client/components/List/ListVirtualized.js index d294cb660c..00b0f2c20c 100644 --- a/src/fireedge/src/client/components/List/ListVirtualized.js +++ b/src/fireedge/src/client/components/List/ListVirtualized.js @@ -2,10 +2,35 @@ import * as React from 'react' import PropTypes from 'prop-types' import { useVirtual } from 'react-virtual' -import { debounce, Box, LinearProgress } from '@material-ui/core' +import { debounce, makeStyles, Box, LinearProgress } from '@material-ui/core' import { useNearScreen } from 'client/hooks' +const useStyles = makeStyles(theme => ({ + root: { + height: '100%', + overflow: 'auto', + '&::-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 + } + }, + container: { + width: '100%', + position: 'relative' + }, + loading: { + width: '100%', + marginTop: 10 + } +})) + const ListVirtualized = ({ canFetchMore, containerProps, @@ -14,6 +39,9 @@ const ListVirtualized = ({ fetchMore, children }) => { + // STYLES + const classes = useStyles() + // OBSERVER const loaderRef = React.useRef() const { isNearScreen } = useNearScreen({ @@ -39,11 +67,10 @@ const ListVirtualized = ({ }, [isNearScreen, canFetchMore, debounceHandleNextPage]) return ( - + {children(rowVirtualizer.virtualItems)} @@ -52,7 +79,7 @@ const ListVirtualized = ({ )} diff --git a/src/fireedge/src/client/components/Sidebar/styles.js b/src/fireedge/src/client/components/Sidebar/styles.js index dafd3a3827..be45e92c6c 100644 --- a/src/fireedge/src/client/components/Sidebar/styles.js +++ b/src/fireedge/src/client/components/Sidebar/styles.js @@ -107,15 +107,8 @@ export default makeStyles(theme => ({ backgroundClip: 'content-box', border: '4px solid transparent', borderRadius: 7, - boxShadow: 'inset 0 0 0 10px' - }, - '&::-webkit-scrollbar-button': { - width: 0, - height: 0, - display: 'none' - }, - '&::-webkit-scrollbar-corner': { - backgroundColor: 'transparent' + boxShadow: 'inset 0 0 0 10px', + color: theme.palette.secondary.light } }, list: { diff --git a/src/fireedge/src/client/components/Status/LinearProgressWithLabel.js b/src/fireedge/src/client/components/Status/LinearProgressWithLabel.js index 57eb5e374e..b924281e9a 100644 --- a/src/fireedge/src/client/components/Status/LinearProgressWithLabel.js +++ b/src/fireedge/src/client/components/Status/LinearProgressWithLabel.js @@ -14,7 +14,7 @@ const BorderLinearProgress = withStyles(({ palette }) => ({ }, bar: { borderRadius: 5, - backgroundColor: palette.primary.main + backgroundColor: palette.secondary.main } }))(LinearProgress) diff --git a/src/fireedge/src/client/components/Tables/Datastores/columns.js b/src/fireedge/src/client/components/Tables/Datastores/columns.js new file mode 100644 index 0000000000..671141114f --- /dev/null +++ b/src/fireedge/src/client/components/Tables/Datastores/columns.js @@ -0,0 +1,57 @@ +import * as React from 'react' + +import { LinearProgressWithLabel } from 'client/components/Status' +import * as DatastoreModel from 'client/models/Datastore' + +export default [ + /* { + id: 'selection', + // The header can use the table's getToggleAllRowsSelectedProps method + // to render a checkbox. + // Pagination is a problem since this will select all rows even though + // not all rows are on the current page. + // The solution should be server side pagination. + // For one, the clients should not download all rows in most cases. + // The client should only download data for the current page. + // In that case, getToggleAllRowsSelectedProps works fine. + Header: ({ getToggleAllRowsSelectedProps }) => ( + + ), + // The cell can use the individual row's getToggleRowSelectedProps method + // to the render a checkbox + Cell: ({ row }) => ( + + ) + }, */ + { + Header: '#', accessor: 'ID' + }, + { Header: 'Name', accessor: 'NAME' }, + { + Header: 'State', + id: 'STATE', + accessor: row => DatastoreModel.getState(row)?.name + }, + { + Header: 'Type', + id: 'TYPE', + accessor: row => DatastoreModel.getType(row)?.name + }, + { + Header: 'Owner/Group', + accessor: row => `${row.UNAME}/${row.GNAME}` + }, + { Header: 'Cluster', accessor: 'CLUSTER' }, + { + Header: 'Allocated CPU', + accessor: row => { + const { percentOfUsed, percentLabel } = DatastoreModel.getCapacityInfo(row) + + return ( +
+ +
+ ) + } + } +] diff --git a/src/fireedge/src/client/components/Tables/Datastores/index.js b/src/fireedge/src/client/components/Tables/Datastores/index.js new file mode 100644 index 0000000000..c9768643f4 --- /dev/null +++ b/src/fireedge/src/client/components/Tables/Datastores/index.js @@ -0,0 +1,53 @@ +import React, { useEffect, useState } from 'react' + +import { useAuth } from 'client/features/Auth' +import { useFetch } from 'client/hooks' +import { useDatastore, useDatastoreApi } from 'client/features/One' + +import { VirtualizedTable } from 'client/components/Tables' +import Columns from 'client/components/Tables/Datastores/columns' + +const INITIAL_ELEMENT = 0 +const NUMBER_OF_INTERVAL = 20 + +const DatastoresTable = () => { + const [{ start, end }, setPage] = useState({ + start: INITIAL_ELEMENT, + end: -NUMBER_OF_INTERVAL + }) + + const columns = React.useMemo(() => Columns, []) + + const datastores = useDatastore() + const { getDatastores } = useDatastoreApi() + const { filterPool } = useAuth() + + const { data, fetchRequest, loading, reloading, error } = useFetch(getDatastores) + + useEffect(() => { fetchRequest({ start, end }) }, [filterPool]) + + const fetchMore = () => { + setPage(prevState => { + const newStart = prevState.start + NUMBER_OF_INTERVAL + const newEnd = prevState.end - NUMBER_OF_INTERVAL + + fetchRequest({ start: newStart, end: newEnd }) + + return { start: newStart, end: newEnd } + }) + } + + const canFetchMore = error || data?.datastores?.length < NUMBER_OF_INTERVAL + + return ( + + ) +} + +export default DatastoresTable diff --git a/src/fireedge/src/client/components/Tables/Hosts/columns.js b/src/fireedge/src/client/components/Tables/Hosts/columns.js new file mode 100644 index 0000000000..67dd9425eb --- /dev/null +++ b/src/fireedge/src/client/components/Tables/Hosts/columns.js @@ -0,0 +1,66 @@ +import * as React from 'react' + +import { LinearProgressWithLabel } from 'client/components/Status' +import * as HostModel from 'client/models/Host' + +export default [ + /* { + id: 'selection', + // The header can use the table's getToggleAllRowsSelectedProps method + // to render a checkbox. + // Pagination is a problem since this will select all rows even though + // not all rows are on the current page. + // The solution should be server side pagination. + // For one, the clients should not download all rows in most cases. + // The client should only download data for the current page. + // In that case, getToggleAllRowsSelectedProps works fine. + Header: ({ getToggleAllRowsSelectedProps }) => ( + + ), + // The cell can use the individual row's getToggleRowSelectedProps method + // to the render a checkbox + Cell: ({ row }) => ( + + ) + }, */ + { + Header: '#', accessor: 'ID' + }, + { Header: 'Name', accessor: 'NAME' }, + { + Header: 'State', + id: 'STATE', + accessor: row => HostModel.getState(row)?.name + }, + { + Header: 'IM/VM', + accessor: ({ IM_MAD, VM_MAD }) => + IM_MAD === VM_MAD ? IM_MAD : `${IM_MAD}/${VM_MAD}` + }, + { Header: 'Cluster', accessor: 'CLUSTER' }, + { Header: 'RVMs', accessor: 'HOST_SHARE.RUNNING_VMS' }, + { + Header: 'Allocated CPU', + accessor: row => { + const { percentCpuUsed, percentCpuLabel } = HostModel.getAllocatedInfo(row) + + return ( +
+ +
+ ) + } + }, + { + Header: 'Allocated MEM', + accessor: row => { + const { percentMemUsed, percentMemLabel } = HostModel.getAllocatedInfo(row) + + return ( +
+ +
+ ) + } + } +] diff --git a/src/fireedge/src/client/components/Tables/Hosts/index.js b/src/fireedge/src/client/components/Tables/Hosts/index.js new file mode 100644 index 0000000000..1cb26550ef --- /dev/null +++ b/src/fireedge/src/client/components/Tables/Hosts/index.js @@ -0,0 +1,53 @@ +import React, { useEffect, useState } from 'react' + +import { useAuth } from 'client/features/Auth' +import { useFetch } from 'client/hooks' +import { useHost, useHostApi } from 'client/features/One' + +import { VirtualizedTable } from 'client/components/Tables' +import Columns from 'client/components/Tables/Hosts/columns' + +const INITIAL_ELEMENT = 0 +const NUMBER_OF_INTERVAL = 20 + +const HostsTable = () => { + const [{ start, end }, setPage] = useState({ + start: INITIAL_ELEMENT, + end: -NUMBER_OF_INTERVAL + }) + + const columns = React.useMemo(() => Columns, []) + + const hosts = useHost() + const { getHosts } = useHostApi() + const { filterPool } = useAuth() + + const { data, fetchRequest, loading, reloading, error } = useFetch(getHosts) + + useEffect(() => { fetchRequest({ start, end }) }, [filterPool]) + + const fetchMore = () => { + setPage(prevState => { + const newStart = prevState.start + NUMBER_OF_INTERVAL + const newEnd = prevState.end - NUMBER_OF_INTERVAL + + fetchRequest({ start: newStart, end: newEnd }) + + return { start: newStart, end: newEnd } + }) + } + + const canFetchMore = error || data?.hosts?.length < NUMBER_OF_INTERVAL + + return ( + + ) +} + +export default HostsTable diff --git a/src/fireedge/src/client/components/Tables/VmTable/header.js b/src/fireedge/src/client/components/Tables/Virtualized/header.js similarity index 79% rename from src/fireedge/src/client/components/Tables/VmTable/header.js rename to src/fireedge/src/client/components/Tables/Virtualized/header.js index a730de7a1a..5c4e0be31f 100644 --- a/src/fireedge/src/client/components/Tables/VmTable/header.js +++ b/src/fireedge/src/client/components/Tables/Virtualized/header.js @@ -15,9 +15,15 @@ const useStyles = makeStyles(theme => ({ textAlign: 'start', padding: '1em', - color: '#4A5568', - backgroundColor: '#e6e8f7', - borderBlock: '0.5px solid #EDF2F7' + color: theme.palette.text.primary, + backgroundColor: theme.palette.action.hover, + border: `1px solid ${theme.palette.action.disabledBackground}`, + borderBottom: 'transparent', + + borderLeftWidth: 1, + borderRightWidth: 1, + borderTopLeftRadius: 6, + borderTopRightRadius: 6 } })) diff --git a/src/fireedge/src/client/components/Tables/Virtualized/index.js b/src/fireedge/src/client/components/Tables/Virtualized/index.js new file mode 100644 index 0000000000..b7cef9985c --- /dev/null +++ b/src/fireedge/src/client/components/Tables/Virtualized/index.js @@ -0,0 +1,122 @@ +/* eslint-disable react/prop-types */ +/* eslint-disable react/jsx-key */ +import * as React from 'react' + +import { makeStyles, Box, CircularProgress } from '@material-ui/core' +import { + useTable, + useGlobalFilter, + useRowSelect, + useFlexLayout +} from 'react-table' + +import { ListVirtualized } from 'client/components/List' +import Toolbar from 'client/components/Tables/Virtualized/toolbar' +import Header from 'client/components/Tables/Virtualized/header' +import Row from 'client/components/Tables/Virtualized/row' + +const useStyles = makeStyles(theme => ({ + root: { + height: '100%', + display: 'flex', + flexDirection: 'column' + }, + table: { + height: '100%', + overflow: 'hidden', + borderTop: 0, + border: `1px solid ${theme.palette.action.disabledBackground}`, + borderRadius: '0 0 6px 6px', + + '& *[role=row]': { + fontSize: '1em', + fontWeight: theme.typography.fontWeightMedium, + lineHeight: '1rem', + + overflowWrap: 'break-word', + textAlign: 'start', + padding: '1em', + alignItems: 'center', + + color: theme.palette.text.primary, + borderTop: `1px solid ${theme.palette.action.disabledBackground}`, + '&:hover': { + backgroundColor: theme.palette.action.hover + }, + '&:first-of-type': { + borderTopColor: 'transparent' + } + } + }, + header: { + ...theme.typography.body1, + color: theme.palette.text.hint, + marginBottom: 16, + + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + gap: '1em' + }, + total: { + display: 'flex', + alignItems: 'center', + gap: '1em', + transition: 200 + } +})) + +const DefaultCell = React.memo(({ value }) => value ?? '--') +DefaultCell.displayName = 'DefaultCell' + +const VirtualizedTable = ({ data, columns, isLoading, canFetchMore, fetchMore }) => { + const classes = useStyles() + + const defaultColumn = React.useMemo(() => ({ + // Filter: DefaultFilter, + Cell: DefaultCell + }), []) + + const useTableProps = useTable( + { columns, data, defaultColumn }, + useRowSelect, + useFlexLayout, + useGlobalFilter + ) + + const { getTableProps, getTableBodyProps, rows } = useTableProps + + return ( + + +
+ +
+ {isLoading && } + Total loaded: {useTableProps.rows.length} +
+
+ +
+
+ + {virtualItems => virtualItems?.map(virtualRow => ( + + )) + } + +
+ + ) +} + +export default VirtualizedTable diff --git a/src/fireedge/src/client/components/Tables/VmTable/row.js b/src/fireedge/src/client/components/Tables/Virtualized/row.js similarity index 80% rename from src/fireedge/src/client/components/Tables/VmTable/row.js rename to src/fireedge/src/client/components/Tables/Virtualized/row.js index 614663cd04..7b008b8f66 100644 --- a/src/fireedge/src/client/components/Tables/VmTable/row.js +++ b/src/fireedge/src/client/components/Tables/Virtualized/row.js @@ -4,24 +4,13 @@ import PropTypes from 'prop-types' import { makeStyles, Box } from '@material-ui/core' import clsx from 'clsx' -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles(() => ({ root: { // <-- it's needed to virtualize --> position: 'absolute', top: 0, left: 0, - width: '100%', - - fontSize: '1em', - fontWeight: theme.typography.fontWeightMedium, - lineHeight: '1rem', - - overflowWrap: 'break-word', - textAlign: 'start', - padding: '1em', - alignItems: 'center', - - boxShadow: '0 0 0 0.5px #e6e8f7' + width: '100%' }, virtual: ({ size, start }) => ({ height: size, diff --git a/src/fireedge/src/client/components/Tables/VmTable/toolbar.js b/src/fireedge/src/client/components/Tables/Virtualized/toolbar.js similarity index 56% rename from src/fireedge/src/client/components/Tables/VmTable/toolbar.js rename to src/fireedge/src/client/components/Tables/Virtualized/toolbar.js index 5ee666afb9..b5e97b6759 100644 --- a/src/fireedge/src/client/components/Tables/VmTable/toolbar.js +++ b/src/fireedge/src/client/components/Tables/Virtualized/toolbar.js @@ -1,18 +1,15 @@ import * as React from 'react' import PropTypes from 'prop-types' -import { makeStyles, Toolbar as MToolbar, Button } from '@material-ui/core' +import { makeStyles, Button } from '@material-ui/core' import { Filter as FilterIcon } from 'iconoir-react' -import GlobalFilter from 'client/components/Table/Filters/GlobalFilter' +// import GlobalFilter from 'client/components/Table/Filters/GlobalFilter' const useToolbarStyles = makeStyles(theme => ({ - root: { - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(1) - }, filterWrapper: { display: 'flex', + flexDirection: 'row', alignItems: 'center', gap: '1em' }, @@ -20,10 +17,6 @@ const useToolbarStyles = makeStyles(theme => ({ ...theme.typography.body1, fontWeight: theme.typography.fontWeightBold, textTransform: 'none' - }, - filters: { - ...theme.typography.body1, - color: theme.palette.grey[700] } })) @@ -31,39 +24,35 @@ const Toolbar = ({ useTableProps }) => { const classes = useToolbarStyles() /** @type {import('react-table').UseGlobalFiltersInstanceProps} */ - const { preGlobalFilteredRows, setGlobalFilter, state } = useTableProps + // const { preGlobalFilteredRows, setGlobalFilter, state } = useTableProps // const { selectedRowIds, globalFilter } = state // const numSelected = Object.keys(selectedRowIds).length return ( - -
- - - No filters selected - -
+ + No filters selected + + ) - {/* numSelected > 0 && ( + /* numSelected > 0 && ( {numSelected} selected - ) */} + ) */ - {/* */} -
- ) + /> */ } Toolbar.propTypes = { diff --git a/src/fireedge/src/client/components/Tables/VmTable/body.js b/src/fireedge/src/client/components/Tables/VmTable/body.js deleted file mode 100644 index 64d57d0b44..0000000000 --- a/src/fireedge/src/client/components/Tables/VmTable/body.js +++ /dev/null @@ -1,226 +0,0 @@ -/* eslint-disable react/prop-types */ -/* eslint-disable react/jsx-key */ -import * as React from 'react' -import PropTypes from 'prop-types' - -import clsx from 'clsx' -import { - makeStyles, - fade, - Table as MTable, - TableFooter, - TableContainer, - TablePagination, - TableSortLabel, - TableBody, - TableCell, - TableHead, - TableRow -} from '@material-ui/core' - -import { TableToolbar, TablePaginationActions } from 'client/components/Table' - -const useStyles = makeStyles(theme => ({ - table: { - // borderCollapse: 'collapse', - [theme.breakpoints.down('sm')]: { - borderCollapse: 'separate', - borderSpacing: theme.spacing(0, 4) - } - }, - head: { - [theme.breakpoints.down('sm')]: { - display: 'none' - } - }, - headRow: { - [theme.breakpoints.down('sm')]: { - display: 'block', - marginBottom: 5 - } - }, - bodyRow: { - '&:nth-of-type(odd)': { - // backgroundColor: theme.palette.action.hover - }, - '&$selected, &$selected:hover': { - backgroundColor: theme.palette.action.hover - // backgroundColor: fade(theme.palette.secondary.main, theme.palette.action.selectedOpacity) - } - }, - cell: { - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - '&:first-of-type': { - width: 45 - }, - [theme.breakpoints.down('sm')]: { - '&:first-of-type': { - display: 'none' - }, - '&:last-of-type': { - borderBottom: `1px solid ${theme.palette.primary.contrastText}` - }, - '&:nth-of-type(2)': { - borderTop: `1px solid ${theme.palette.primary.contrastText}` - }, - borderInline: `1px solid ${theme.palette.primary.contrastText}`, - display: 'block', - position: 'relative', - textAlign: 'left', - borderBottom: 'none' - // paddingLeft: 130, - // '&::before': { - // content: 'attr(data-heading)', - // position: 'absolute', - // top: 0, - // left: 0, - // width: 120, - // height: '100%', - // display: 'flex', - // alignItems: 'center', - // backgroundColor: theme.palette.primary.main, - // color: theme.palette.primary.contrastText, - // fontSize: '0.75rem', - // padding: theme.spacing(0, 1), - // justifyContent: 'center' - // } - } - }, - selected: {} -})) - -const TableBod = ({ - getTableProps, - headerGroups, - prepareRow, - page, - gotoPage, - setPageSize, - preGlobalFilteredRows, - setGlobalFilter, - state: { pageIndex, pageSize, selectedRowIds, globalFilter }, - data -}) => { - const classes = useStyles() - - const handleChangePage = (_, newPage) => { - gotoPage(newPage) - } - - const handleChangeRowsPerPage = event => { - setPageSize(parseInt(event.target.value, 10)) - } - - return ( - - - - - {headerGroups.map(headerGroup => ( - - {headerGroup.headers.map(column => ( - - {column.render('Header')} - {column.id !== 'selection' ? ( - - ) : null} - {/* Render the columns filter UI */} -
event.stopPropagation()}> - {column.canFilter ? column.render('Filter') : null} -
-
- ))} -
- ))} -
- - {page.map((row, i) => { - prepareRow(row) - const { onChange, checked } = row.getToggleRowSelectedProps() - - return ( - - {row.cells.map(cell => { - console.log({ cell }) - return ( - - {cell.render('Cell')} - - ) - })} - - ) - })} - - - - - - -
-
- ) - - /* return ( - - {rowVirtualizer.virtualItems.map(virtualRow => { - const row = rows[virtualRow.index] - prepareRow(row) - - return - })} - - ) */ -} - -TableBod.propTypes = { -} - -TableBod.defaultProps = { -} - -export default TableBod diff --git a/src/fireedge/src/client/components/Tables/VmTable/index.js b/src/fireedge/src/client/components/Tables/VmTable/index.js deleted file mode 100644 index 7543ae894f..0000000000 --- a/src/fireedge/src/client/components/Tables/VmTable/index.js +++ /dev/null @@ -1,81 +0,0 @@ -/* eslint-disable react/prop-types */ -/* eslint-disable react/jsx-key */ -import * as React from 'react' - -import { Box, CircularProgress } from '@material-ui/core' -import { - useTable, - useGlobalFilter, - useRowSelect, - useFlexLayout -} from 'react-table' - -import { ListVirtualized } from 'client/components/List' -import Toolbar from 'client/components/Tables/VmTable/toolbar' -import Header from 'client/components/Tables/VmTable/header' -import Row from 'client/components/Tables/VmTable/row' -import Columns from 'client/components/Tables/VmTable/columns' - -const VmTable = ({ data, isLoading, canFetchMore, fetchMore }) => { - const columns = React.useMemo(() => Columns, []) - - const defaultColumn = React.useMemo(() => ({ - // Filter: DefaultFilter, - Cell: React.memo(({ value }) => value ?? '--') - }), []) - - const useTableProps = useTable( - { columns, data, defaultColumn }, - useRowSelect, - useFlexLayout, - useGlobalFilter - ) - - const { getTableProps, getTableBodyProps, rows } = useTableProps - - return ( - - -
- - - {virtualItems => virtualItems?.map(virtualRow => ( - - )) - } - - -
- Total loaded: {useTableProps.rows.length} - {isLoading && } -
- - ) -} - -export default VmTable diff --git a/src/fireedge/src/client/components/Tables/VmTable/actions.js b/src/fireedge/src/client/components/Tables/Vms/actions.js similarity index 100% rename from src/fireedge/src/client/components/Tables/VmTable/actions.js rename to src/fireedge/src/client/components/Tables/Vms/actions.js diff --git a/src/fireedge/src/client/components/Tables/VmTable/columns.js b/src/fireedge/src/client/components/Tables/Vms/columns.js similarity index 52% rename from src/fireedge/src/client/components/Tables/VmTable/columns.js rename to src/fireedge/src/client/components/Tables/Vms/columns.js index f1c0dcc8a1..59b942159e 100644 --- a/src/fireedge/src/client/components/Tables/VmTable/columns.js +++ b/src/fireedge/src/client/components/Tables/Vms/columns.js @@ -1,9 +1,3 @@ -import * as React from 'react' - -import { SelectFilter } from 'client/components/Table' -import { StatusChip } from 'client/components/Status' - -import Colors from 'client/constants/color' import * as VirtualMachineModel from 'client/models/VirtualMachine' export default [ @@ -27,31 +21,33 @@ export default [ ) }, */ { - Header: '#', - accessor: 'ID', - Cell: ({ value }) => - + Header: '#', accessor: 'ID' + // Cell: ({ value }) => + // }, { Header: 'Name', accessor: 'NAME' }, { Header: 'State', id: 'STATE', - accessor: row => VirtualMachineModel.getState(row), - Cell: ({ value: { name, color } = {} }) => name && ( - - ), - Filter: ({ column }) => ( - - ), - filter: (rows, id, filterValue) => - rows.filter(row => row.values[id]?.name === filterValue) + accessor: row => VirtualMachineModel.getState(row)?.name + // Cell: ({ value: { name, color } = {} }) => name && ( + // + // ), + // Filter: ({ column }) => ( + // + // ), + // filter: (rows, id, filterValue) => + // rows.filter(row => row.values[id]?.name === filterValue) + }, + { + Header: 'Owner/Group', + accessor: row => `${row.UNAME}/${row.GNAME}` }, - { Header: 'Owner/Group', accessor: row => `${row.UNAME}/${row.GNAME}` }, { Header: 'Ips', - accessor: row => VirtualMachineModel.getIps(row), - Cell: ({ value }) => value.map(nic => ( - - )) + accessor: row => VirtualMachineModel.getIps(row).join(',') + // Cell: ({ value }) => value.map(nic => ( + // + // )) } ] diff --git a/src/fireedge/src/client/containers/VirtualMachines/index.js b/src/fireedge/src/client/components/Tables/Vms/index.js similarity index 56% rename from src/fireedge/src/client/containers/VirtualMachines/index.js rename to src/fireedge/src/client/components/Tables/Vms/index.js index 96ce976640..3914d0af9c 100644 --- a/src/fireedge/src/client/containers/VirtualMachines/index.js +++ b/src/fireedge/src/client/components/Tables/Vms/index.js @@ -1,22 +1,28 @@ import React, { useEffect, useState } from 'react' import { useAuth } from 'client/features/Auth' -import { useVm, useVmApi } from 'client/features/One' import { useFetch } from 'client/hooks' +import { useVm, useVmApi } from 'client/features/One' -import { VmTable } from 'client/components/Tables' +import { VirtualizedTable } from 'client/components/Tables' +import Columns from 'client/components/Tables/Vms/columns' const INITIAL_ELEMENT = 0 -const NUMBER_OF_INTERVAL = -100 +const NUMBER_OF_INTERVAL = 20 -function VirtualMachines () { - const [{ start, end }, setPage] = useState(({ start: INITIAL_ELEMENT, end: -NUMBER_OF_INTERVAL })) +const VmsTable = () => { + const [{ start, end }, setPage] = useState({ + start: INITIAL_ELEMENT, + end: -NUMBER_OF_INTERVAL + }) + + const columns = React.useMemo(() => Columns, []) const vms = useVm() const { getVms } = useVmApi() const { filterPool } = useAuth() - const { data, fetchRequest, loading, reloading } = useFetch(getVms) + const { data, fetchRequest, loading, reloading, error } = useFetch(getVms) useEffect(() => { fetchRequest({ start, end }) }, [filterPool]) @@ -31,16 +37,17 @@ function VirtualMachines () { }) } - const finish = data?.length < NUMBER_OF_INTERVAL + const canFetchMore = error || data?.vms?.length < NUMBER_OF_INTERVAL return ( - ) } -export default VirtualMachines +export default VmsTable diff --git a/src/fireedge/src/client/components/Tables/index.js b/src/fireedge/src/client/components/Tables/index.js index 84a50ec60f..4be3687b16 100644 --- a/src/fireedge/src/client/components/Tables/index.js +++ b/src/fireedge/src/client/components/Tables/index.js @@ -1,5 +1,11 @@ -import VmTable from 'client/components/Tables/VmTable' +import VirtualizedTable from 'client/components/Tables/Virtualized' +import VmsTable from 'client/components/Tables/Vms' +import DatastoresTable from 'client/components/Tables/Datastores' +import HostsTable from 'client/components/Tables/Hosts' export { - VmTable + VirtualizedTable, + VmsTable, + DatastoresTable, + HostsTable } diff --git a/src/fireedge/src/client/constants/datastore.js b/src/fireedge/src/client/constants/datastore.js index cf4154b6ee..97115e6725 100644 --- a/src/fireedge/src/client/constants/datastore.js +++ b/src/fireedge/src/client/constants/datastore.js @@ -1,7 +1,7 @@ import * as STATES from 'client/constants/states' import COLOR from 'client/constants/color' -const DATASTORE_TYPES = [ +export const DATASTORE_TYPES = [ { name: 'IMAGE', shortName: 'img' @@ -16,7 +16,7 @@ const DATASTORE_TYPES = [ } ] -const DATASTORE_STATES = [ +export const DATASTORE_STATES = [ { name: STATES.READY, shortName: 'on', @@ -28,8 +28,3 @@ const DATASTORE_STATES = [ color: COLOR.error.dark } ] - -export default { - TYPES: DATASTORE_TYPES, - STATES: DATASTORE_STATES -} diff --git a/src/fireedge/src/client/constants/host.js b/src/fireedge/src/client/constants/host.js index 13c3a690dc..b69eb132cb 100644 --- a/src/fireedge/src/client/constants/host.js +++ b/src/fireedge/src/client/constants/host.js @@ -1,7 +1,7 @@ import * as STATES from 'client/constants/states' import COLOR from 'client/constants/color' -const HOST_STATES = [ +export const HOST_STATES = [ { name: STATES.INIT, shortName: 'init', @@ -48,7 +48,3 @@ const HOST_STATES = [ color: COLOR.error.dark } ] - -export default { - STATES: HOST_STATES -} diff --git a/src/fireedge/src/client/constants/index.js b/src/fireedge/src/client/constants/index.js index 5bd6bce324..15e96c7daa 100644 --- a/src/fireedge/src/client/constants/index.js +++ b/src/fireedge/src/client/constants/index.js @@ -82,3 +82,5 @@ export * as STATES from 'client/constants/states' export * from 'client/constants/flow' export * from 'client/constants/provision' export * from 'client/constants/vm' +export * from 'client/constants/host' +export * from 'client/constants/datastore' diff --git a/src/fireedge/src/client/containers/Newstone/index.js b/src/fireedge/src/client/containers/Newstone/index.js new file mode 100644 index 0000000000..78e2c87939 --- /dev/null +++ b/src/fireedge/src/client/containers/Newstone/index.js @@ -0,0 +1,89 @@ +import * as React from 'react' + +import { useParams, useHistory } from 'react-router' +import { Redirect, Route, Switch, Link } from 'react-router-dom' + +import { withStyles, Container, Tabs, Tab, Box } from '@material-ui/core' + +import { + DatastoresTable, + HostsTable, + VmsTable +} from 'client/components/Tables' +import { PATH } from 'client/router/dev' + +const TABS = { + vms: PATH.NEWSTONE.replace(':resource', 'vms'), + datastores: PATH.NEWSTONE.replace(':resource', 'datastores'), + hosts: PATH.NEWSTONE.replace(':resource', 'hosts') +} + +const AntTabs = withStyles(theme => ({ + root: { + borderBottom: '1px solid #e8e8e8' + }, + indicator: { + backgroundColor: theme.palette.secondary.main + } +}))(Tabs) + +const AntTab = withStyles(theme => ({ + root: { + minWidth: 72, + fontWeight: theme.typography.fontWeightRegular, + marginRight: theme.spacing(4), + '&:hover': { + color: theme.palette.secondary.light, + opacity: 1 + }, + '&$selected': { + color: theme.palette.secondary.main, + fontWeight: theme.typography.fontWeightMedium + }, + '&:focus': { + color: theme.palette.secondary.light + } + }, + selected: {} +}))(props => ) + +const Newstone = () => { + const history = useHistory() + const { resource } = useParams() + + const renderTabs = React.useMemo(() => ( + + {Object.keys(TABS).map(tabName => + + )} + + ), [resource]) + + return ( + + {Object.values(TABS).includes(history.location.pathname) && renderTabs} + + + + + + + + } /> + + + + ) +} + +export default Newstone diff --git a/src/fireedge/src/client/containers/VirtualMachines/Sections/info.js b/src/fireedge/src/client/containers/VirtualMachines/Sections/info.js deleted file mode 100644 index 71b9001cf5..0000000000 --- a/src/fireedge/src/client/containers/VirtualMachines/Sections/info.js +++ /dev/null @@ -1,159 +0,0 @@ -import React, { memo, useState } from 'react' -import PropTypes from 'prop-types' - -import { List, ListItem, Typography, Grid, Paper, Divider } from '@material-ui/core' -import { CheckBox, CheckBoxOutlineBlank, Visibility } from '@material-ui/icons' -import clsx from 'clsx' - -import { useProviderApi } from 'client/features/One' -import { Action } from 'client/components/Cards/SelectCard' -import { Tr } from 'client/components/HOC' -import { T } from 'client/constants' - -import useStyles from 'client/containers/Providers/Sections/styles' - -const Info = memo(({ data }) => { - const classes = useStyles() - const { getProviderConnection } = useProviderApi() - - const [showConnection, setShowConnection] = useState(undefined) - - const { ID, NAME, GNAME, UNAME, PERMISSIONS, TEMPLATE } = data - const { - connection, - description, - provider: providerName, - registration_time: time - } = TEMPLATE?.PROVISION_BODY - - const hasConnection = connection && Object.keys(connection).length > 0 - - const isChecked = checked => - checked === '1' ? : - - const ConnectionButton = () => ( - } - cy='provider-connection' - handleClick={() => getProviderConnection(ID).then(setShowConnection)} - /> - ) - - return ( - - - - - - {Tr(T.Information)} - - - - {'ID'} - {ID} - - - {Tr(T.Name)} - {NAME} - - - {Tr(T.Description)} - {description} - - - {Tr(T.Provider)} - {providerName} - - - {Tr(T.RegistrationTime)} - - {new Date(time * 1000).toLocaleString()} - - - - - {hasConnection && ( - - - - {Tr(T.Credentials)} - - {!showConnection && } - - - - {Object.entries(connection)?.map(([key, value]) => - typeof value === 'string' && ( - - {key} - - {showConnection?.[key] ?? value} - - - ))} - - - )} - - - - - - {Tr(T.Permissions)} - {Tr(T.Use)} - {Tr(T.Manage)} - {Tr(T.Admin)} - - - - {Tr(T.Owner)} - {isChecked(PERMISSIONS.OWNER_U)} - {isChecked(PERMISSIONS.OWNER_M)} - {isChecked(PERMISSIONS.OWNER_A)} - - - {Tr(T.Group)} - {isChecked(PERMISSIONS.GROUP_U)} - {isChecked(PERMISSIONS.GROUP_M)} - {isChecked(PERMISSIONS.GROUP_A)} - - - {Tr(T.Other)} - {isChecked(PERMISSIONS.OTHER_U)} - {isChecked(PERMISSIONS.OTHER_M)} - {isChecked(PERMISSIONS.OTHER_A)} - - - - - - - {Tr(T.Ownership)} - - - - {Tr(T.Owner)} - {UNAME} - - - {Tr(T.Group)} - {GNAME} - - - - - - ) -}) - -Info.propTypes = { - data: PropTypes.object.isRequired -} - -Info.defaultProps = { - data: {} -} - -Info.displayName = 'Info' - -export default Info diff --git a/src/fireedge/src/client/containers/VirtualMachines/Sections/styles.js b/src/fireedge/src/client/containers/VirtualMachines/Sections/styles.js deleted file mode 100644 index 8462b9b88f..0000000000 --- a/src/fireedge/src/client/containers/VirtualMachines/Sections/styles.js +++ /dev/null @@ -1,32 +0,0 @@ -import { makeStyles } from '@material-ui/core' - -export default makeStyles(theme => ({ - marginBottom: { - marginBottom: theme.spacing(2) - }, - list: { - '& p': { - ...theme.typography.body2, - overflow: 'hidden', - textOverflow: 'ellipsis' - }, - '&.w-50 > *': { - '& > p, & > span': { - width: '50%' - } - }, - '&.w-25 > *': { - '& > p, & > span': { - width: '25%' - } - } - }, - title: { - '& p.bold': { - fontWeight: theme.typography.fontWeightBold - } - }, - alignToRight: { - textAlign: 'right' - } -})) diff --git a/src/fireedge/src/client/features/One/datastore/actions.js b/src/fireedge/src/client/features/One/datastore/actions.js index 0ebb9d0236..0e67b32edf 100644 --- a/src/fireedge/src/client/features/One/datastore/actions.js +++ b/src/fireedge/src/client/features/One/datastore/actions.js @@ -4,7 +4,7 @@ import { datastoreService } from 'client/features/One/datastore/services' export const getDatastore = createAction('datastore', datastoreService.getDatastore) export const getDatastores = createAction( - 'datastore', + 'datastore/pool', datastoreService.getDatastores, response => ({ datastores: response }) ) diff --git a/src/fireedge/src/client/features/One/datastore/hooks.js b/src/fireedge/src/client/features/One/datastore/hooks.js index b5a7bd8b9e..8a7968e429 100644 --- a/src/fireedge/src/client/features/One/datastore/hooks.js +++ b/src/fireedge/src/client/features/One/datastore/hooks.js @@ -18,6 +18,6 @@ export const useDatastoreApi = () => { return { getDatastore: id => unwrapDispatch(actions.getDatastore({ id })), - getDatastores: () => unwrapDispatch(actions.getDatastores()) + getDatastores: options => unwrapDispatch(actions.getDatastores(options)) } } diff --git a/src/fireedge/src/client/features/One/host/hooks.js b/src/fireedge/src/client/features/One/host/hooks.js index db989edbf5..be5e1711bf 100644 --- a/src/fireedge/src/client/features/One/host/hooks.js +++ b/src/fireedge/src/client/features/One/host/hooks.js @@ -18,6 +18,6 @@ export const useHostApi = () => { return { getHost: id => unwrapDispatch(actions.getHost({ id })), - getHosts: () => unwrapDispatch(actions.getHosts()) + getHosts: options => unwrapDispatch(actions.getHosts(options)) } } diff --git a/src/fireedge/src/client/hooks/useFetch.js b/src/fireedge/src/client/hooks/useFetch.js index 5a4c3f2763..75a2497b6b 100644 --- a/src/fireedge/src/client/hooks/useFetch.js +++ b/src/fireedge/src/client/hooks/useFetch.js @@ -1,6 +1,7 @@ import { useState, useCallback, useEffect, useRef } from 'react' import { debounce } from '@material-ui/core' import { fakeDelay } from 'client/utils' +import { console } from 'window-or-global' const useRequest = request => { const [data, setData] = useState(undefined) @@ -22,7 +23,8 @@ const useRequest = request => { } else setError(true) } }) - .catch(() => { + .catch(e => { + console.log('error', e) if (isMounted.current) { setData(undefined) setError(true) diff --git a/src/fireedge/src/client/models/Datastore.js b/src/fireedge/src/client/models/Datastore.js new file mode 100644 index 0000000000..2b5161e877 --- /dev/null +++ b/src/fireedge/src/client/models/Datastore.js @@ -0,0 +1,15 @@ +import { prettyBytes } from 'client/utils' +import { DATASTORE_STATES, DATASTORE_TYPES } from 'client/constants' + +export const getType = ({ TYPE } = {}) => DATASTORE_TYPES[TYPE] + +export const getState = ({ STATE } = {}) => DATASTORE_STATES[STATE] + +export const getCapacityInfo = ({ TOTAL_MB, USED_MB } = {}) => { + const percentOfUsed = +USED_MB * 100 / +TOTAL_MB || 0 + const usedBytes = prettyBytes(+USED_MB, 'MB') + const totalBytes = prettyBytes(+TOTAL_MB, 'MB') + const percentLabel = `${usedBytes} / ${totalBytes} (${Math.round(percentOfUsed)}%)` + + return { percentOfUsed, percentLabel } +} diff --git a/src/fireedge/src/client/models/Host.js b/src/fireedge/src/client/models/Host.js new file mode 100644 index 0000000000..61e9ccc418 --- /dev/null +++ b/src/fireedge/src/client/models/Host.js @@ -0,0 +1,23 @@ +import { prettyBytes } from 'client/utils' +import { HOST_STATES } from 'client/constants' + +export const getState = ({ STATE } = {}) => HOST_STATES[STATE] + +export const getAllocatedInfo = ({ HOST_SHARE } = {}) => { + const { CPU_USAGE, TOTAL_CPU, MEM_USAGE, TOTAL_MEM } = HOST_SHARE + + const percentCpuUsed = +CPU_USAGE * 100 / +TOTAL_CPU || 0 + const percentCpuLabel = `${CPU_USAGE} / ${TOTAL_CPU} (${Math.round(percentCpuUsed)}%)` + + const percentMemUsed = +MEM_USAGE * 100 / +TOTAL_MEM || 0 + const usedMemBytes = prettyBytes(+MEM_USAGE) + const totalMemBytes = prettyBytes(+TOTAL_MEM) + const percentMemLabel = `${usedMemBytes} / ${totalMemBytes} (${Math.round(percentMemUsed)}%)` + + return { + percentCpuUsed, + percentCpuLabel, + percentMemUsed, + percentMemLabel + } +} diff --git a/src/fireedge/src/client/router/dev.js b/src/fireedge/src/client/router/dev.js index 0d36acdfed..a8191c266c 100644 --- a/src/fireedge/src/client/router/dev.js +++ b/src/fireedge/src/client/router/dev.js @@ -1,27 +1,26 @@ import loadable from '@loadable/component' import { Code as DevIcon, - ViewGrid as VmIcon + ViewGrid as NewstoneIcon } from 'iconoir-react' -const VirtualMachines = loadable(() => import('client/containers/VirtualMachines'), { ssr: false }) +const Newstone = loadable(() => import('client/containers/Newstone'), { ssr: false }) const TestApi = loadable(() => import('client/containers/TestApi'), { ssr: false }) const WebConsole = loadable(() => import('client/containers/WebConsole'), { ssr: false }) export const PATH = { - VIRTUAL_MACHINES: '/vms', + NEWSTONE: '/newstone/:resource', TEST_API: '/test-api', WEB_CONSOLE: '/webconsole' } export const ENDPOINTS = [ { - label: 'VMs', - path: PATH.VIRTUAL_MACHINES, - devMode: true, + label: 'Newstone', + path: PATH.NEWSTONE, sidebar: true, - icon: VmIcon, - Component: VirtualMachines + icon: NewstoneIcon, + Component: Newstone }, { label: 'Test API', diff --git a/src/fireedge/src/client/theme/defaults.js b/src/fireedge/src/client/theme/defaults.js index a0fab647f9..1d51f9fac0 100644 --- a/src/fireedge/src/client/theme/defaults.js +++ b/src/fireedge/src/client/theme/defaults.js @@ -34,11 +34,17 @@ export default { }, typography: { fontFamily: [ - '"Ubuntu"', + 'Ubuntu', + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', 'Roboto', - 'Helvetica', + '"Helvetica Neue"', 'Arial', - 'sans-serif' + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"' ].join(',') }, mixins: {