1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-21 14:50:08 +03:00

F OpenNebula/One#6718: Add DataTable rows as lists functionality

This commit is contained in:
Jorge Miguel Lobo Escalona 2024-09-19 12:50:38 +02:00 committed by GitHub
parent cd4bcba0b9
commit 4afac0039c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
57 changed files with 1348 additions and 304 deletions

View File

@ -13,14 +13,15 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, Component, useState } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetAclsExtendedQuery } from 'client/features/OneApi/acl'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import ACLColumns from 'client/components/Tables/ACLs/columns'
import ACLRow from 'client/components/Tables/ACLs/row'
import { RESOURCE_NAMES, ACL_TABLE_VIEWS } from 'client/constants'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import { ACL_TABLE_VIEWS, RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetAclsExtendedQuery } from 'client/features/OneApi/acl'
import { sentenceCase } from 'client/utils'
import { Component, useMemo, useState } from 'react'
const DEFAULT_DATA_CY = 'acls'
@ -72,23 +73,55 @@ const ACLsTable = (props) => {
},
}
return (
data && (
<EnhancedTable
columns={columns}
data={data}
rootProps={rootProps}
searchProps={searchProps}
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={useMemo(() => ACLRow(viewType), [viewType])}
singleSelect={singleSelect}
tableViews={tableViews}
{...rest}
/>
)
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{
header: T.AppliesTo,
id: 'applies',
accessor: ({ USER }) =>
USER?.type ? sentenceCase(`${USER?.type} ${USER?.name ?? ''}`) : '',
},
{
header: T.AffectedResources,
id: 'affected-resources',
accessor: ({ RESOURCE }) =>
Array.isArray(RESOURCE?.resources)
? sentenceCase(RESOURCE.resources.join(', '))
: '',
},
{
header: T.AllowedOperations,
id: 'allowed-operations',
accessor: ({ RIGHTS }) => sentenceCase(RIGHTS?.string || ''),
},
{
header: T.Zone,
id: 'zone',
accessor: ({ ZONE }) => ZONE?.name || T.All,
},
]
const CardStyle = useMemo(() => ACLRow(viewType), [viewType])
const { component, header } = WrapperRow(CardStyle)
const EnhancedTableProps = {
columns,
data,
rootProps,
searchProps,
refetch,
isLoading: isFetching,
getRowId: (row) => String(row.ID),
singleSelect,
RowComponent: component,
headerList: header && listHeader,
...rest,
}
!header && (EnhancedTableProps.tableViews = tableViews)
return data && <EnhancedTable {...EnhancedTableProps} />
}
ACLsTable.propTypes = { ...EnhancedTable.propTypes }
ACLsTable.displayName = 'ACLsTable'
export default ACLsTable

View File

@ -13,20 +13,20 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import PropTypes from 'prop-types'
import {
ACLCardCLI,
ACLCardIcons,
ACLCardNames,
ACLCardCLI,
ACLCardReadableRule,
ACLCardResources,
ACLCardRule,
ACLCardReadableRule,
} from 'client/components/Cards'
import { ACL_TABLE_VIEWS } from 'client/constants'
/* eslint-disable jsdoc/require-jsdoc */
import PropTypes from 'prop-types'
const Row = (viewType) => {
const aclRow = ({ original, value, ...props }) => {
const aclRow = ({ original, value, headerList, ...props }) => {
// Check what view show in the table cards
if (viewType === ACL_TABLE_VIEWS.NAMES.type) {
return <ACLCardNames rootProps={props} acl={value} />

View File

@ -16,26 +16,26 @@
/* eslint-disable jsdoc/require-jsdoc */
import PropTypes from 'prop-types'
import { Typography } from '@mui/material'
import {
Lock,
User,
Group,
Db as DatastoreIcon,
Archive as DiskTypeIcon,
Group,
Lock,
ModernTv,
Pin as PersistentIcon,
Archive as DiskTypeIcon,
User,
} from 'iconoir-react'
import { Typography } from '@mui/material'
import Timer from 'client/components/Timer'
import { StatusCircle, StatusChip } from 'client/components/Status'
import { StatusChip, StatusCircle } from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
import Timer from 'client/components/Timer'
import { T } from 'client/constants'
import * as ImageModel from 'client/models/Image'
import * as Helper from 'client/models/Helper'
import * as ImageModel from 'client/models/Image'
const Row = ({ original, value, ...props }) => {
const Row = ({ original, value, headerList, ...props }) => {
const classes = rowStyles()
const {
ID,
@ -126,6 +126,7 @@ Row.propTypes = {
value: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export default Row

View File

@ -13,18 +13,25 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { ReactElement, useMemo } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetBackupJobsQuery } from 'client/features/OneApi/backupjobs'
import { StatusCircle } from 'client/components/Status'
import BackupJobsColumns from 'client/components/Tables/BackupJobs/columns'
import BackupJobsRow from 'client/components/Tables/BackupJobs/row'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import { RESOURCE_NAMES } from 'client/constants'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import Timer from 'client/components/Timer'
import { RESOURCE_NAMES, T } from 'client/constants'
import COLOR from 'client/constants/color'
import { useViews } from 'client/features/Auth'
import { useGetBackupJobsQuery } from 'client/features/OneApi/backupjobs'
import { timeFromMilliseconds } from 'client/models/Helper'
import { ReactElement, useMemo } from 'react'
const DEFAULT_DATA_CY = 'backupjobs'
const haveValues = function (object) {
return Object.values(object).length > 0
}
/**
* @param {object} props - Props
* @returns {ReactElement} Backup Jobs table
@ -46,6 +53,83 @@ const BackupJobsTable = (props) => {
[view]
)
const listHeader = [
{
header: '',
id: 'status-icon',
accessor: ({
OUTDATED_VMS,
BACKING_UP_VMS,
ERROR_VMS,
LAST_BACKUP_TIME,
}) => {
const status = useMemo(() => {
const completed = {
color: COLOR.success.main,
tooltip: T.Completed,
}
const noStarted = {
color: COLOR.warning.main,
tooltip: T.NotStartedYet,
}
const error = {
color: COLOR.error.main,
tooltip: T.Error,
}
const onGoing = {
color: COLOR.info.main,
tooltip: T.OnGoing,
}
if (haveValues(ERROR_VMS)) {
return error
}
if (!haveValues(OUTDATED_VMS) && !haveValues(BACKING_UP_VMS)) {
return LAST_BACKUP_TIME === '0' ? noStarted : completed
}
if (haveValues(OUTDATED_VMS)) {
return completed
}
if (haveValues(BACKING_UP_VMS)) {
return onGoing
}
}, [OUTDATED_VMS, BACKING_UP_VMS, ERROR_VMS, LAST_BACKUP_TIME])
return <StatusCircle color={status.color} tooltip={status.tooltip} />
},
},
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{ header: T.Priority, id: 'priority', accessor: 'PRIORITY' },
{
header: T.LastBackupTimeInfo,
id: 'last-time',
accessor: ({ LAST_BACKUP_TIME }) => {
const LastBackupTime = +LAST_BACKUP_TIME
if (LastBackupTime > 0) {
const timer = timeFromMilliseconds(LastBackupTime)
return (
<span title={timer.toFormat('ff')}>
<Timer translateWord={T.LastBackupTime} initial={timer} />
</span>
)
} else {
return ''
}
},
},
]
const { component, header } = WrapperRow(BackupJobsRow)
return (
<EnhancedTable
columns={columns}
@ -55,7 +139,8 @@ const BackupJobsTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={BackupJobsRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -23,7 +23,7 @@ import backupjobApi, {
import { jsonToXml } from 'client/models/Helper'
const Row = memo(
({ original, value, onClickLabel, ...props }) => {
({ original, value, onClickLabel, headerList, ...props }) => {
const [update] = useUpdateBackupJobMutation()
const state = backupjobApi.endpoints.getBackupJobs.useQueryState(
@ -67,6 +67,7 @@ Row.propTypes = {
className: PropTypes.string,
onClick: PropTypes.func,
onClickLabel: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'VirtualDataCenterRow'

View File

@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { StatusCircle } from 'client/components/Status'
import { useViews } from 'client/features/Auth'
import { useGetBackupsQuery } from 'client/features/OneApi/image'
import { ReactElement, useMemo } from 'react'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import backupColumns from 'client/components/Tables/Backups/columns'
import BackupRow from 'client/components/Tables/Backups/row'
import { RESOURCE_NAMES } from 'client/constants'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import { RESOURCE_NAMES, T } from 'client/constants'
import { getState, getType } from 'client/models/Image'
const DEFAULT_DATA_CY = 'backups'
@ -87,6 +89,26 @@ const BackupsTable = (props) => {
const isFetchingAll = () =>
isFetchingVm ? !!(isFetchingVm && isFetching) : isFetching
const listHeader = [
{
header: '',
id: 'status-icon',
accessor: (template) => {
const { color: stateColor, name: stateName } = getState(template)
return <StatusCircle color={stateColor} tooltip={stateName} />
},
},
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{ header: T.Datastore, id: 'datastore', accessor: 'DATASTORE' },
{ header: T.Type, id: 'type', accessor: (template) => getType(template) },
]
const { component, header } = WrapperRow(BackupRow)
return (
<EnhancedTable
columns={columns}
@ -96,7 +118,8 @@ const BackupsTable = (props) => {
refetch={refetchAll}
isLoading={isFetchingAll()}
getRowId={(row) => String(row.ID)}
RowComponent={BackupRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,39 +13,39 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useAuth } from 'client/features/Auth'
import imageApi, { useUpdateImageMutation } from 'client/features/OneApi/image'
/* eslint-disable jsdoc/require-jsdoc */
import PropTypes from 'prop-types'
import { useAuth } from 'client/features/Auth'
import { useMemo, useCallback } from 'react'
import imageApi, { useUpdateImageMutation } from 'client/features/OneApi/image'
import { useCallback, useMemo } from 'react'
import {
Lock,
User,
Group,
Db as DatastoreIcon,
ModernTv,
Pin as PersistentIcon,
Archive as DiskTypeIcon,
} from 'iconoir-react'
import { Typography } from '@mui/material'
import MultipleTags from 'client/components/MultipleTags'
import {
jsonToXml,
getUniqueLabels,
getColorFromString,
getUniqueLabels,
jsonToXml,
} from 'client/models/Helper'
import {
Db as DatastoreIcon,
Archive as DiskTypeIcon,
Group,
Lock,
ModernTv,
Pin as PersistentIcon,
User,
} from 'iconoir-react'
import Timer from 'client/components/Timer'
import { StatusCircle, StatusChip } from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
import { T } from 'client/constants'
import { Tr } from 'client/components/HOC'
import { StatusChip, StatusCircle } from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
import Timer from 'client/components/Timer'
import { T } from 'client/constants'
import * as ImageModel from 'client/models/Image'
import * as Helper from 'client/models/Helper'
import * as ImageModel from 'client/models/Image'
const Row = ({ original, value, onClickLabel, ...props }) => {
const Row = ({ original, value, onClickLabel, headerList, ...props }) => {
const [update] = useUpdateImageMutation()
const { labels: userLabels } = useAuth()
@ -187,6 +187,7 @@ Row.propTypes = {
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
onClickLabel: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export default Row

View File

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { ReactElement, useMemo } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetClustersQuery } from 'client/features/OneApi/cluster'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import ClusterColumns from 'client/components/Tables/Clusters/columns'
import ClusterRow from 'client/components/Tables/Clusters/row'
import { RESOURCE_NAMES } from 'client/constants'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetClustersQuery } from 'client/features/OneApi/cluster'
const DEFAULT_DATA_CY = 'clusters'
@ -74,6 +74,29 @@ const ClustersTable = (props) => {
[view]
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{
header: T.Hosts,
id: 'hosts',
accessor: ({ HOSTS }) => (Array.isArray(HOSTS.ID) ? HOSTS.ID.length : 1),
},
{
header: T.Vnets,
id: 'vnets',
accessor: ({ VNETS }) => (Array.isArray(VNETS.ID) ? VNETS.ID.length : 1),
},
{
header: T.Datastore,
id: 'datastores',
accessor: ({ DATASTORES }) =>
Array.isArray(DATASTORES.ID) ? DATASTORES.ID.length : 1,
},
]
const { component, header } = WrapperRow(ClusterRow)
return (
<EnhancedTable
columns={columns}
@ -83,7 +106,8 @@ const ClustersTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={ClusterRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -16,15 +16,15 @@
/* eslint-disable jsdoc/require-jsdoc */
import PropTypes from 'prop-types'
import { HardDrive, NetworkAlt, Folder, Cloud } from 'iconoir-react'
import { Typography } from '@mui/material'
import { Cloud, Folder, HardDrive, NetworkAlt } from 'iconoir-react'
import { rowStyles } from 'client/components/Tables/styles'
import { Tr } from 'client/components/HOC'
import { T } from 'client/constants'
const Row = ({ original, value, ...props }) => {
const Row = ({ original, value, headerList, ...props }) => {
const classes = rowStyles()
const { ID, NAME, HOSTS, DATASTORES, VNETS, PROVIDER_NAME } = value
@ -76,6 +76,7 @@ Row.propTypes = {
value: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export default Row

View File

@ -18,6 +18,7 @@ import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetDatastoresQuery } from 'client/features/OneApi/datastore'
import { LinearProgressWithLabel, StatusCircle } from 'client/components/Status'
import DatastoreColumns from 'client/components/Tables/Datastores/columns'
import DatastoreRow from 'client/components/Tables/Datastores/row'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
@ -25,7 +26,9 @@ import {
areArraysEqual,
sortStateTables,
} from 'client/components/Tables/Enhanced/Utils/DataTableUtils'
import { RESOURCE_NAMES } from 'client/constants'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import { DS_THRESHOLD, RESOURCE_NAMES, T } from 'client/constants'
import { getCapacityInfo, getState, getType } from 'client/models/Datastore'
import { useFormContext } from 'react-hook-form'
const DEFAULT_DATA_CY = 'datastores'
@ -135,6 +138,58 @@ const DatastoresTable = (props) => {
}
})
const listHeader = [
{
header: T.Status,
id: 'status',
accessor: (template) => {
const { color: stateColor, name: stateName } = getState(template)
return <StatusCircle color={stateColor} tooltip={stateName} />
},
},
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{
header: T.Capacity,
id: 'capacity',
accessor: (template) => {
const capacity = useMemo(() => getCapacityInfo(template), [template])
const { percentOfUsed, percentLabel } = capacity
return (
<LinearProgressWithLabel
value={percentOfUsed}
label={percentLabel}
high={DS_THRESHOLD.CAPACITY.high}
low={DS_THRESHOLD.CAPACITY.low}
title={T.UsedOfTotal}
/>
)
},
},
{
header: T.Cluster,
id: 'cluster',
accessor: ({ CLUSTERS }) => {
const clusters = useMemo(
() => [CLUSTERS?.ID ?? []].flat(),
[CLUSTERS?.ID]
)
return clusters.length && clusters[0]
},
},
{
header: T.Type,
id: 'type',
accessor: (template) => getType(template),
},
]
const { component, header } = WrapperRow(DatastoreRow)
return (
<EnhancedTable
columns={columns}
@ -144,8 +199,9 @@ const DatastoresTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={DatastoreRow}
dataDepend={values}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,27 +13,34 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useCallback, useMemo } from 'react'
import { jsonToXml } from 'client/models/Helper'
import PropTypes from 'prop-types'
import { memo, useCallback, useMemo } from 'react'
import {
useGetDatastoresQuery,
import { DatastoreCard } from 'client/components/Cards'
import api, {
useUpdateDatastoreMutation,
} from 'client/features/OneApi/datastore'
import { DatastoreCard } from 'client/components/Cards'
import { jsonToXml } from 'client/models/Helper'
const Row = memo(
({ original, onClickLabel, ...props }) => {
const { data: datastores } = useGetDatastoresQuery(undefined)
const selectedDatastore = datastores?.find(
(datastore) => +datastore.ID === +original.ID
)
({ original, value, onClickLabel, zone, headerList, ...props }) => {
const [update] = useUpdateDatastoreMutation()
const {
data: datastores,
error,
isLoading,
} = api.endpoints.getDatastores.useQueryState({ zone })
const selectedDatastore = useMemo(
() =>
datastores?.find((datastore) => +datastore.ID === +original.ID) ??
original,
[datastores, original]
)
const memoDs = useMemo(
() => selectedDatastore ?? original,
[datastores, original]
[selectedDatastore, original, update, isLoading, error, datastores]
)
const handleDeleteLabel = useCallback(
@ -62,7 +69,10 @@ const Row = memo(
Row.propTypes = {
original: PropTypes.object,
value: PropTypes.object,
onClickLabel: PropTypes.func,
zone: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'DatastoreRow'

View File

@ -0,0 +1,95 @@
/* ------------------------------------------------------------------------- *
* Copyright 2002-2024, OpenNebula Project, OpenNebula Systems *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
* not use this file except in compliance with the License. You may obtain *
* a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { TableCell, TableRow } from '@mui/material'
import makeStyles from '@mui/styles/makeStyles'
import { SERVER_CONFIG } from 'client/constants'
import { useAuth } from 'client/features/Auth'
import get from 'lodash.get'
import PropTypes from 'prop-types'
import { ReactElement, memo } from 'react'
const listStyles = makeStyles(({ palette }) => ({
row: {
'&.selected': {
boxShadow: `inset 0px -0.5px 0px 2px ${palette.secondary.main}`,
},
},
}))
/**
* @param {object} props - Props
* @returns {ReactElement} Generic Row
*/
const RowStyle = memo(
({
original,
value,
onClickLabel,
globalErrors,
headerList = [],
className,
...props
}) => {
const styles = listStyles()
return (
<TableRow {...props} className={`${styles.row} ${className}`}>
{headerList.map(({ id, accessor }) => {
switch (typeof accessor) {
case 'string':
return <TableCell key={id}>{get(original, accessor)}</TableCell>
case 'function':
return <TableCell key={id}>{accessor(original)}</TableCell>
default:
return ''
}
})}
</TableRow>
)
},
(prev, next) => prev.className === next.className
)
RowStyle.propTypes = {
original: PropTypes.object,
value: PropTypes.object,
onClickLabel: PropTypes.func,
globalErrors: PropTypes.array,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
className: PropTypes.string,
}
RowStyle.displayName = 'RowStyle'
/**
* @param {ReactElement} RowCardComponent - Standard row component (Card).
* @returns {ReactElement} Generic Row
*/
const WrapperRow = (RowCardComponent) => {
const { settings: { FIREEDGE: fireedge = {} } = {} } = useAuth()
const { ROW_STYLE } = fireedge
const { rowStyle } = SERVER_CONFIG
const data = ROW_STYLE || rowStyle
const header = data === 'list'
return {
component: header ? RowStyle : RowCardComponent,
header,
}
}
export default WrapperRow

View File

@ -17,7 +17,17 @@
import PropTypes from 'prop-types'
import { useEffect, useMemo, useState } from 'react'
import { Alert, Box, Chip, Grid } from '@mui/material'
import {
Alert,
Box,
Chip,
Grid,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
} from '@mui/material'
import clsx from 'clsx'
import InfoEmpty from 'iconoir-react/dist/InfoEmpty'
import RemoveIcon from 'iconoir-react/dist/RemoveSquare'
@ -83,6 +93,7 @@ const EnhancedTable = ({
readOnly = false,
tableViews,
zoneId,
headerList,
}) => {
const styles = EnhancedTableStyles({
readOnly: readOnly,
@ -301,6 +312,84 @@ const EnhancedTable = ({
)
}
const DataListPerPage = ({ page: internalPage = [] }) => {
if (!internalPage.length) {
return ''
}
const valuesPerPages = internalPage.map((row) => {
prepareRow(row)
/** @type {UseRowSelectRowProps} */
const { getRowProps, original, values, toggleRowSelected, isSelected } =
row
const { key, ...rowProps } = getRowProps()
return (
<RowComponent
{...rowProps}
headerList={headerList}
zone={zoneId}
key={key}
original={original}
value={values}
{...(messageValues.length && {
globalErrors: messageValues,
})}
className={isSelected ? 'selected' : ''}
{...(!cannotFilterByLabel && {
onClickLabel: (label) => {
const currentFilter =
state.filters
?.filter(({ id }) => id === LABEL_COLUMN_ID)
?.map(({ value }) => value)
?.flat() || []
const nextFilter = [...new Set([...currentFilter, label])]
setFilter(LABEL_COLUMN_ID, nextFilter)
},
})}
onClick={(e) => {
typeof onRowClick === 'function' && onRowClick(original)
if (!disableRowSelect && !readOnly) {
if (
singleSelect ||
(!singleSelect && !(e.ctrlKey || e.metaKey))
) {
toggleAllRowsSelected?.(false)
}
toggleRowSelected?.(!isSelected)
}
}}
/>
)
})
return headerList ? (
<Table stickyHeader>
<TableHead>
<TableRow>
{headerList.map(({ header = '', id = '' }) => (
<TableCell key={id} className={styles.cellHeaders}>
{header}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>{valuesPerPages}</TableBody>
</Table>
) : (
<>{valuesPerPages}</>
)
}
DataListPerPage.propTypes = {
page: PropTypes.any,
}
DataListPerPage.displayName = 'DataListPerPage'
return (
<Box
{...getTableProps()}
@ -376,7 +465,7 @@ const EnhancedTable = ({
}}
/>
<div className={clsx(styles.body, classes.body)}>
<div className={clsx(styles.body, !headerList ? classes.body : '')}>
{!!messages.length && <MessagesRowsAlerts />}
{/* NO DATA MESSAGE */}
{!isLoading &&
@ -390,58 +479,7 @@ const EnhancedTable = ({
))}
{/* DATALIST PER PAGE */}
{page.map((row) => {
prepareRow(row)
/** @type {UseRowSelectRowProps} */
const {
getRowProps,
original,
values,
toggleRowSelected,
isSelected,
} = row
const { key, ...rowProps } = getRowProps()
return (
<RowComponent
{...rowProps}
zone={zoneId}
key={key}
original={original}
value={values}
{...(messageValues.length && {
globalErrors: messageValues,
})}
className={isSelected ? 'selected' : ''}
{...(!cannotFilterByLabel && {
onClickLabel: (label) => {
const currentFilter =
state.filters
?.filter(({ id }) => id === LABEL_COLUMN_ID)
?.map(({ value }) => value)
?.flat() || []
const nextFilter = [...new Set([...currentFilter, label])]
setFilter(LABEL_COLUMN_ID, nextFilter)
},
})}
onClick={(e) => {
typeof onRowClick === 'function' && onRowClick(original)
if (!disableRowSelect && !readOnly) {
if (
singleSelect ||
(!singleSelect && !(e.ctrlKey || e.metaKey))
) {
toggleAllRowsSelected?.(false)
}
toggleRowSelected?.(!isSelected)
}
}}
/>
)
})}
<DataListPerPage page={page} />
</div>
</Box>
)
@ -490,6 +528,7 @@ EnhancedTable.propTypes = {
readOnly: PropTypes.bool,
tableViews: PropTypes.object,
zoneId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export * from 'client/components/Tables/Enhanced/Utils'

View File

@ -132,4 +132,7 @@ export default makeStyles(({ palette, typography, breakpoints }) => ({
gap: '0.8em',
padding: '1em',
},
cellHeaders: {
fontWeight: 'bold',
},
}))

View File

@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetFilesQuery } from 'client/features/OneApi/image'
import { ReactElement, useMemo } from 'react'
import { StatusCircle } from 'client/components/Status'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import ImageColumns from 'client/components/Tables/Images/columns'
import ImageRow from 'client/components/Tables/Images/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetFilesQuery } from 'client/features/OneApi/image'
import { getState, getType } from 'client/models/Image'
const DEFAULT_DATA_CY = 'images'
@ -61,6 +63,26 @@ const FilesTable = (props) => {
[view]
)
const listHeader = [
{
header: '',
id: 'status-icon',
accessor: (vm) => {
const { color: stateColor, name: stateName } = getState(vm)
return <StatusCircle color={stateColor} tooltip={stateName} />
},
},
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{ header: T.Datastore, id: 'datastore', accessor: 'DATASTORE' },
{ header: T.Type, id: 'type', accessor: (template) => getType(template) },
]
const { component, header } = WrapperRow(ImageRow)
return (
<EnhancedTable
columns={columns}
@ -70,7 +92,8 @@ const FilesTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={ImageRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, Component } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetGroupsQuery } from 'client/features/OneApi/group'
import { LinearProgressWithTooltip } from 'client/components/Status'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import GroupColumns from 'client/components/Tables/Groups/columns'
import GroupRow from 'client/components/Tables/Groups/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetGroupsQuery } from 'client/features/OneApi/group'
import { getQuotaUsage } from 'client/models/Group'
import { Component, useMemo } from 'react'
const DEFAULT_DATA_CY = 'groups'
@ -58,6 +61,74 @@ const GroupsTable = (props) => {
[view]
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{
header: T.Users,
id: 'users',
accessor: ({ USERS }) => (Array.isArray(USERS?.ID) ? USERS.ID.length : 0),
},
{
header: T.VMs,
id: 'vms',
accessor: ({ VM_QUOTA }) => {
const vmQuotaUsage = useMemo(
() => getQuotaUsage('VM', VM_QUOTA),
[VM_QUOTA]
)
return (
<LinearProgressWithTooltip
value={vmQuotaUsage.vms.percentOfUsed}
label={vmQuotaUsage.vms.percentLabel}
tooltipTitle={T.VMCount}
icon=""
/>
)
},
},
{
header: T.Datastores,
id: 'datastores',
accessor: ({ DATASTORE_QUOTA }) => {
const datastoreQuotaUsage = useMemo(
() => getQuotaUsage('DATASTORE', DATASTORE_QUOTA),
[DATASTORE_QUOTA]
)
return (
<LinearProgressWithTooltip
value={datastoreQuotaUsage.size.percentOfUsed}
label={datastoreQuotaUsage.size.percentLabel}
tooltipTitle={T.DatastoreSize}
icon=""
/>
)
},
},
{
header: T.Networks,
id: 'networks',
accessor: ({ NETWORK_QUOTA }) => {
const networkQuotaUsage = useMemo(
() => getQuotaUsage('NETWORK', NETWORK_QUOTA),
[NETWORK_QUOTA]
)
return (
<LinearProgressWithTooltip
value={networkQuotaUsage.leases.percentOfUsed}
label={networkQuotaUsage.leases.percentLabel}
tooltipTitle={T.NetworkLeases}
icon=""
/>
)
},
},
]
const { component, header } = WrapperRow(GroupRow)
return (
<EnhancedTable
columns={columns}
@ -67,11 +138,15 @@ const GroupsTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={GroupRow}
singleSelect={singleSelect}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)
}
GroupsTable.propTypes = { ...EnhancedTable.propTypes }
GroupsTable.displayName = 'GroupsTable'
export default GroupsTable

View File

@ -15,17 +15,20 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetHostsQuery } from 'client/features/OneApi/host'
import { Tr } from 'client/components/HOC'
import { LinearProgressWithLabel, StatusCircle } from 'client/components/Status'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import {
areArraysEqual,
sortStateTables,
} from 'client/components/Tables/Enhanced/Utils/DataTableUtils'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import HostColumns from 'client/components/Tables/Hosts/columns'
import HostRow from 'client/components/Tables/Hosts/row'
import { RESOURCE_NAMES } from 'client/constants'
import { HOST_THRESHOLD, RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetHostsQuery } from 'client/features/OneApi/host'
import { getAllocatedInfo, getState } from 'client/models/Host'
import { useFormContext } from 'react-hook-form'
const DEFAULT_DATA_CY = 'hosts'
@ -136,6 +139,65 @@ const HostsTable = (props) => {
})
useEffect(() => refetch(), [])
const listHeader = [
{
header: '',
id: 'status-icon',
accessor: (host) => {
const { color: stateColor, name: stateName } = getState(host)
return <StatusCircle color={stateColor} tooltip={stateName} />
},
},
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Cluster, id: 'cluster', accessor: 'CLUSTER' },
{
header: T.Rvms,
id: 'rvms',
accessor: ({ HOST_SHARE }) => HOST_SHARE?.RUNNING_VMS || 0,
},
{
header: T.AllocatedCpu,
id: 'cpu',
accessor: (host) => {
const { percentCpuUsed, percentCpuLabel, colorCpu } =
getAllocatedInfo(host)
return (
<LinearProgressWithLabel
value={percentCpuUsed}
high={HOST_THRESHOLD.CPU.high}
low={HOST_THRESHOLD.CPU.low}
label={percentCpuLabel}
title={`${Tr(T.AllocatedCpu)}`}
color={colorCpu}
/>
)
},
},
{
header: T.AllocatedMemory,
id: 'memory',
accessor: (host) => {
const { percentMemUsed, percentMemLabel, colorMem } =
getAllocatedInfo(host)
return (
<LinearProgressWithLabel
value={percentMemUsed}
high={HOST_THRESHOLD.MEMORY.high}
low={HOST_THRESHOLD.MEMORY.low}
label={percentMemLabel}
title={`${Tr(T.AllocatedMemory)}`}
color={colorMem}
/>
)
},
},
]
const { component, header } = WrapperRow(HostRow)
return (
<EnhancedTable
columns={columns}
@ -145,9 +207,10 @@ const HostsTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={HostRow}
dataDepend={values}
zoneId={zoneId}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -20,14 +20,14 @@ import PropTypes from 'prop-types'
import { memo, useCallback, useMemo } from 'react'
const Row = memo(
({ original, value, onClickLabel, zone, ...props }) => {
({ original, value, onClickLabel, zone, headerList, ...props }) => {
const [update] = useUpdateHostMutation()
const {
data: hosts,
error,
isLoading,
} = hostApi.endpoints.getHosts.useQuery({ zone })
} = hostApi.endpoints.getHosts.useQueryState({ zone })
const host = useMemo(
() => hosts?.find((h) => +h.ID === +original.ID) ?? original,
@ -71,6 +71,7 @@ Row.propTypes = {
handleClick: PropTypes.func,
onClickLabel: PropTypes.func,
zone: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'HostRow'

View File

@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetImagesQuery } from 'client/features/OneApi/image'
import { ReactElement, useMemo } from 'react'
import { StatusCircle } from 'client/components/Status'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import ImageColumns from 'client/components/Tables/Images/columns'
import ImageRow from 'client/components/Tables/Images/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetImagesQuery } from 'client/features/OneApi/image'
import { getState, getType } from 'client/models/Image'
const DEFAULT_DATA_CY = 'images'
@ -46,6 +48,27 @@ const ImagesTable = (props) => {
[view]
)
const listHeader = [
{
header: '',
id: 'status-icon',
accessor: (vm) => {
const { color: stateColor, name: stateName } = getState(vm)
return <StatusCircle color={stateColor} tooltip={stateName} />
},
},
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{ header: T.Datastore, id: 'datastore', accessor: 'DATASTORE' },
{ header: T.Type, id: 'type', accessor: (template) => getType(template) },
{ header: T.VMs, id: 'vms', accessor: 'RUNNING_VMS' },
]
const { component, header } = WrapperRow(ImageRow)
return (
<EnhancedTable
columns={columns}
@ -55,7 +78,8 @@ const ImagesTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={ImageRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -45,7 +45,7 @@ import { prettyBytes } from 'client/utils'
import * as Helper from 'client/models/Helper'
import * as ImageModel from 'client/models/Image'
const Row = ({ original, value, onClickLabel, ...props }) => {
const Row = ({ original, value, onClickLabel, headerList, ...props }) => {
const [update] = useUpdateImageMutation()
const { labels: userLabels } = useAuth()
@ -167,6 +167,7 @@ Row.propTypes = {
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
onClickLabel: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export default Row

View File

@ -16,22 +16,22 @@
/* eslint-disable jsdoc/require-jsdoc */
import PropTypes from 'prop-types'
import { Typography } from '@mui/material'
import {
HardDrive as SizeIcon,
RefreshCircular as FullIcon,
Refresh as IncrementIcon,
HardDrive as SizeIcon,
} from 'iconoir-react'
import { Typography } from '@mui/material'
import Timer from 'client/components/Timer'
import { StatusChip } from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
import Timer from 'client/components/Timer'
import { T } from 'client/constants'
import { prettyBytes } from 'client/utils'
import * as Helper from 'client/models/Helper'
const Row = ({ original, value, ...props }) => {
const Row = ({ original, value, headerList, ...props }) => {
const classes = rowStyles()
const { ID, TYPE, DATE, SIZE, SOURCE } = value
@ -78,6 +78,7 @@ Row.propTypes = {
value: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export default Row

View File

@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetMarketplaceAppsQuery } from 'client/features/OneApi/marketplaceApp'
import { StatusCircle } from 'client/components/Status'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import MarketplaceAppColumns from 'client/components/Tables/MarketplaceApps/columns'
import MarketplaceAppRow from 'client/components/Tables/MarketplaceApps/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetMarketplaceAppsQuery } from 'client/features/OneApi/marketplaceApp'
import { getState, getType } from 'client/models/MarketplaceApp'
import { prettyBytes } from 'client/utils'
import { ReactElement, useMemo } from 'react'
const DEFAULT_DATA_CY = 'apps'
@ -56,6 +58,41 @@ const MarketplaceAppsTable = (props) => {
[view]
)
const listHeader = [
{
header: '',
id: 'status-icon',
accessor: (vm) => {
const { color: stateColor, name: stateName } = getState(vm)
return <StatusCircle color={stateColor} tooltip={stateName} />
},
},
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{
header: T.Size,
id: 'Size',
accessor: ({ SIZE }) => prettyBytes(+SIZE, 'MB'),
},
{
header: T.Type,
id: 'type',
accessor: (template) =>
useMemo(() => getType(template), [template?.TYPE]),
},
{
header: T.Marketplace,
id: 'marketplace',
accessor: 'MARKETPLACE',
},
{ header: T.Zone, id: 'zone', accessor: 'ZONE_ID' },
]
const { component, header } = WrapperRow(MarketplaceAppRow)
return (
<EnhancedTable
columns={columns}
@ -65,7 +102,8 @@ const MarketplaceAppsTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={MarketplaceAppRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo, useCallback } from 'react'
import PropTypes from 'prop-types'
import { memo, useCallback, useMemo } from 'react'
import { MarketplaceAppCard } from 'client/components/Cards'
import api, {
useUpdateAppMutation,
} from 'client/features/OneApi/marketplaceApp'
import { MarketplaceAppCard } from 'client/components/Cards'
import { jsonToXml } from 'client/models/Helper'
const Row = memo(
({ original, value, onClickLabel, ...props }) => {
({ original, value, onClickLabel, headerList, ...props }) => {
const [update] = useUpdateAppMutation()
const state = api.endpoints.getMarketplaceApps.useQueryState(undefined, {
@ -64,6 +64,7 @@ Row.propTypes = {
className: PropTypes.string,
onClick: PropTypes.func,
onClickLabel: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'MarketplaceAppRow'

View File

@ -13,16 +13,19 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import PropTypes from 'prop-types'
import { ReactElement, useMemo } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetMarketplacesQuery } from 'client/features/OneApi/marketplace'
import { Tr } from 'client/components/HOC'
import { LinearProgressWithLabel, StatusCircle } from 'client/components/Status'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import MarketplaceColumns from 'client/components/Tables/Marketplaces/columns'
import MarketplaceRow from 'client/components/Tables/Marketplaces/row'
import { RESOURCE_NAMES } from 'client/constants'
import { MARKET_THRESHOLD, RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetMarketplacesQuery } from 'client/features/OneApi/marketplace'
import { getCapacityInfo, getState } from 'client/models/Datastore'
const DEFAULT_DATA_CY = 'marketplaces'
@ -53,6 +56,52 @@ const MarketplacesTable = ({ filter, ...props }) => {
[view]
)
const listHeader = [
{
header: '',
id: 'status-icon',
accessor: (vm) => {
const { color: stateColor, name: stateName } = getState(vm)
return <StatusCircle color={stateColor} tooltip={stateName} />
},
},
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{
header: T.Capacity,
id: 'capacity',
accessor: (template) => {
const capacity = useMemo(() => getCapacityInfo(template), [template])
const { percentOfUsed, percentLabel } = capacity
return (
<LinearProgressWithLabel
value={percentOfUsed}
label={percentLabel}
high={MARKET_THRESHOLD.CAPACITY.high}
low={MARKET_THRESHOLD.CAPACITY.low}
title={Tr(T.UsedOfTotal)}
/>
)
},
},
{
header: T.Apps,
id: 'apps',
accessor: ({ MARKETPLACEAPPS }) =>
useMemo(
() => [MARKETPLACEAPPS?.ID ?? []].flat().length || 0,
[MARKETPLACEAPPS?.ID]
),
},
{ header: T.Zone, id: 'zone', accessor: 'ZONE_ID' },
]
const { component, header } = WrapperRow(MarketplaceRow)
return (
<EnhancedTable
columns={columns}
@ -62,7 +111,8 @@ const MarketplacesTable = ({ filter, ...props }) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={MarketplaceRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import { memo, useMemo } from 'react'
import marketplaceApi from 'client/features/OneApi/marketplace'
import { MarketplaceCard } from 'client/components/Cards'
import marketplaceApi from 'client/features/OneApi/marketplace'
const Row = memo(
({ original, value, ...props }) => {
({ original, value, headerList, ...props }) => {
const state = marketplaceApi.endpoints.getMarketplaces.useQueryState(
undefined,
{
@ -42,6 +42,7 @@ Row.propTypes = {
isSelected: PropTypes.bool,
className: PropTypes.string,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'MarketplaceRow'

View File

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetSecGroupsQuery } from 'client/features/OneApi/securityGroup'
import { ReactElement, useMemo } from 'react'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import SecurityGroupColumns from 'client/components/Tables/SecurityGroups/columns'
import SecurityGroupsRow from 'client/components/Tables/SecurityGroups/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetSecGroupsQuery } from 'client/features/OneApi/securityGroup'
const DEFAULT_DATA_CY = 'secgroup'
@ -51,6 +51,15 @@ const SecurityGroupsTable = (props) => {
[view]
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
]
const { component, header } = WrapperRow(SecurityGroupsRow)
return (
<EnhancedTable
columns={columns}
@ -60,7 +69,8 @@ const SecurityGroupsTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={SecurityGroupsRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo, useCallback } from 'react'
import PropTypes from 'prop-types'
import { SecurityGroupCard } from 'client/components/Cards'
import secGroupApi, {
useUpdateSecGroupMutation,
} from 'client/features/OneApi/securityGroup'
import { SecurityGroupCard } from 'client/components/Cards'
import { jsonToXml } from 'client/models/Helper'
import PropTypes from 'prop-types'
import { memo, useCallback, useMemo } from 'react'
const Row = memo(
({ original, value, onClickLabel, ...props }) => {
({ original, value, onClickLabel, headerList, ...props }) => {
const [update] = useUpdateSecGroupMutation()
const {
@ -74,6 +74,7 @@ Row.propTypes = {
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
onClickLabel: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'SecurityGroupRow'

View File

@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo, useCallback } from 'react'
import PropTypes from 'prop-types'
import { memo, useCallback, useMemo } from 'react'
import { ServiceTemplateCard } from 'client/components/Cards'
import serviceTemplateApi, {
useUpdateServiceTemplateMutation,
} from 'client/features/OneApi/serviceTemplate'
import { ServiceTemplateCard } from 'client/components/Cards'
const Row = memo(
({ original, value, ...props }) => {
({ original, value, headerList, ...props }) => {
const [update] = useUpdateServiceTemplateMutation()
const state =
@ -63,6 +63,7 @@ Row.propTypes = {
isSelected: PropTypes.bool,
className: PropTypes.string,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'ServiceTemplateRow'

View File

@ -13,14 +13,14 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import { memo, useMemo } from 'react'
import serviceApi from 'client/features/OneApi/service'
import { ServiceCard } from 'client/components/Cards'
import serviceApi from 'client/features/OneApi/service'
const Row = memo(
({ original, value, ...props }) => {
({ original, value, headerList, ...props }) => {
const state = serviceApi.endpoints.getServices.useQueryState(undefined, {
selectFromResult: ({ data = [] }) =>
data.find((service) => +service.ID === +original.ID),
@ -39,6 +39,7 @@ Row.propTypes = {
isSelected: PropTypes.bool,
className: PropTypes.string,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'ServiceRow'

View File

@ -13,15 +13,14 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetTicketsQuery } from 'client/features/OneApi/support'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import SupportColumns from 'client/components/Tables/Support/columns'
import SupportRow from 'client/components/Tables/Support/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetTicketsQuery } from 'client/features/OneApi/support'
import { ReactElement, useMemo } from 'react'
const DEFAULT_DATA_CY = 'support'
@ -62,6 +61,13 @@ const SupportTable = (props) => {
[view]
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'id' },
{ header: T.Subject, id: 'subject', accessor: 'subject' },
{ header: T.Status, id: 'status', accessor: 'status' },
]
const { component, header } = WrapperRow(SupportRow)
return (
<EnhancedTable
columns={columns}
@ -71,8 +77,9 @@ const SupportTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.id)}
RowComponent={SupportRow}
initialState={initialState}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo } from 'react'
import PropTypes from 'prop-types'
import { memo } from 'react'
import { SupportCard } from 'client/components/Cards'
const Row = memo(
({ original, value, ...props }) => (
({ original, value, headerList, ...props }) => (
<SupportCard ticket={original} rootProps={props} />
),
(prev, next) => prev.className === next.className
@ -31,6 +31,7 @@ Row.propTypes = {
isSelected: PropTypes.bool,
className: PropTypes.string,
onClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'SupportRow'

View File

@ -13,15 +13,17 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetUsersQuery } from 'client/features/OneApi/user'
import { ReactElement, useMemo } from 'react'
import { LinearProgressWithTooltip } from 'client/components/Status'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import UserColumns from 'client/components/Tables/Users/columns'
import UserRow from 'client/components/Tables/Users/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetUsersQuery } from 'client/features/OneApi/user'
import { getQuotaUsage } from 'client/models/User'
const DEFAULT_DATA_CY = 'users'
@ -52,6 +54,76 @@ const UsersTable = (props) => {
[view]
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{
header: T.Enabled,
id: 'enabled',
accessor: ({ ENABLED }) => (+ENABLED ? T.Yes : T.No),
},
{ header: T.AuthDriver, id: 'auth-driver', accessor: 'AUTH_DRIVER' },
{
header: T.VMs,
id: 'vms',
accessor: ({ VM_QUOTA }) => {
const vmQuotaUsage = useMemo(
() => getQuotaUsage('VM', VM_QUOTA),
[VM_QUOTA]
)
return (
<LinearProgressWithTooltip
value={vmQuotaUsage.vms.percentOfUsed}
label={vmQuotaUsage.vms.percentLabel}
tooltipTitle={T.VMCount}
icon=""
/>
)
},
},
{
header: T.Datastores,
id: 'datastores',
accessor: ({ DATASTORE_QUOTA }) => {
const datastoreQuotaUsage = useMemo(
() => getQuotaUsage('DATASTORE', DATASTORE_QUOTA),
[DATASTORE_QUOTA]
)
return (
<LinearProgressWithTooltip
value={datastoreQuotaUsage.size.percentOfUsed}
label={datastoreQuotaUsage.size.percentLabel}
tooltipTitle={T.DatastoreSize}
icon=""
/>
)
},
},
{
header: T.Networks,
id: 'networks',
accessor: ({ NETWORK_QUOTA }) => {
const networkQuotaUsage = useMemo(
() => getQuotaUsage('NETWORK', NETWORK_QUOTA),
[NETWORK_QUOTA]
)
return (
<LinearProgressWithTooltip
value={networkQuotaUsage.leases.percentOfUsed}
label={networkQuotaUsage.leases.percentLabel}
tooltipTitle={T.NetworkLeases}
icon=""
/>
)
},
},
]
const { component, header } = WrapperRow(UserRow)
return (
<EnhancedTable
columns={columns}
@ -61,7 +133,8 @@ const UsersTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={UserRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -18,7 +18,7 @@ import PropTypes from 'prop-types'
import { UserCard } from 'client/components/Cards'
const Row = ({ original, value, ...props }) => (
const Row = ({ original, value, headerList, ...props }) => (
<UserCard rootProps={props} user={value} />
)
@ -27,6 +27,7 @@ Row.propTypes = {
value: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export default Row

View File

@ -14,15 +14,15 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetVNTemplatesQuery } from 'client/features/OneApi/networkTemplate'
import { ReactElement, useMemo } from 'react'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import VNetworkTemplateColumns from 'client/components/Tables/VNetworkTemplates/columns'
import VNetworkTemplateRow from 'client/components/Tables/VNetworkTemplates/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetVNTemplatesQuery } from 'client/features/OneApi/networkTemplate'
const DEFAULT_DATA_CY = 'vnet-templates'
@ -47,6 +47,15 @@ const VNetworkTemplatesTable = (props) => {
[view]
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
]
const { component, header } = WrapperRow(VNetworkTemplateRow)
return (
<EnhancedTable
columns={columns}
@ -56,7 +65,8 @@ const VNetworkTemplatesTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={VNetworkTemplateRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -19,13 +19,13 @@ import PropTypes from 'prop-types'
import { Typography } from '@mui/material'
import { Cloud, Group, Lock, User } from 'iconoir-react'
import { rowStyles } from 'client/components/Tables/styles'
import { Tr } from 'client/components/HOC'
import { rowStyles } from 'client/components/Tables/styles'
import { T } from 'client/constants'
import * as Helper from 'client/models/Helper'
const Row = ({ original, value, ...props }) => {
const Row = ({ original, value, headerList, ...props }) => {
const classes = rowStyles()
const { ID, NAME, UNAME, GNAME, LOCK, REGTIME, PROVISION_ID } = value
@ -68,6 +68,7 @@ Row.propTypes = {
value: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export default Row

View File

@ -13,19 +13,21 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetVNetworksQuery } from 'client/features/OneApi/network'
import { Tr } from 'client/components/HOC'
import { LinearProgressWithLabel, StatusCircle } from 'client/components/Status'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import {
areArraysEqual,
sortStateTables,
} from 'client/components/Tables/Enhanced/Utils/DataTableUtils'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import VNetworkColumns from 'client/components/Tables/VNetworks/columns'
import VNetworkRow from 'client/components/Tables/VNetworks/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T, VNET_THRESHOLD } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetVNetworksQuery } from 'client/features/OneApi/network'
import { getLeasesInfo, getState } from 'client/models/VirtualNetwork'
import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react'
import { useFormContext } from 'react-hook-form'
const DEFAULT_DATA_CY = 'vnets'
@ -137,6 +139,54 @@ const VNetworksTable = (props) => {
})
useEffect(() => refetch(), [])
const listHeader = [
{
header: '',
id: 'status-icon',
accessor: (template) => {
const { color: stateColor, name: stateName } = getState(template)
return <StatusCircle color={stateColor} tooltip={stateName} />
},
},
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{
header: T.Clusters,
id: 'clusters',
accessor: ({ CLUSTERS }) => {
const clusters = useMemo(
() => [CLUSTERS?.ID ?? []].flat(),
[CLUSTERS?.ID]
)
return clusters.length && clusters[0]
},
},
{
header: T.Leases,
id: 'leases',
accessor: (template) => {
const leasesInfo = useMemo(() => getLeasesInfo(template), [template])
const { percentOfUsed, percentLabel } = leasesInfo
return (
<LinearProgressWithLabel
value={percentOfUsed}
high={VNET_THRESHOLD.LEASES.high}
low={VNET_THRESHOLD.LEASES.low}
label={percentLabel}
title={`${Tr(T.Used)} / ${Tr(T.TotalLeases)}`}
/>
)
},
},
]
const { component, header } = WrapperRow(VNetworkRow)
return (
<EnhancedTable
columns={columns}
@ -146,8 +196,9 @@ const VNetworksTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={VNetworkRow}
dataDepend={values}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,21 +13,21 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo, useCallback } from 'react'
import PropTypes from 'prop-types'
import { jsonToXml } from 'client/models/Helper'
import PropTypes from 'prop-types'
import { memo, useCallback, useMemo } from 'react'
import api, { useUpdateVNetMutation } from 'client/features/OneApi/network'
import { NetworkCard } from 'client/components/Cards'
import api, { useUpdateVNetMutation } from 'client/features/OneApi/network'
const Row = memo(
({ original, value, onClickLabel, ...props }) => {
({ original, value, onClickLabel, headerList, ...props }) => {
const [update] = useUpdateVNetMutation()
const {
data: vnetworks,
error,
isLoading,
} = api.endpoints.getVNetworks.useQuery(undefined)
} = api.endpoints.getVNetworks.useQueryState(undefined)
const vnetwork = useMemo(
() => vnetworks?.find((vnet) => +vnet.ID === +original.ID) ?? original,
@ -69,6 +69,7 @@ Row.propTypes = {
className: PropTypes.string,
onClick: PropTypes.func,
onClickLabel: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'VirtualNetworkRow'

View File

@ -29,6 +29,7 @@ import { BoxIso as DownloadIcon } from 'iconoir-react'
import { Tr, Translate } from 'client/components/HOC'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import VRouterTemplateColumns from 'client/components/Tables/VRouterTemplates/columns'
import VRouterTemplateRow from 'client/components/Tables/VRouterTemplates/row'
import { useStyles } from 'client/components/Tabs/EmptyTab/styles'
@ -37,6 +38,7 @@ import {
useExportAppMutation,
useLazyGetMarketplaceAppsQuery,
} from 'client/features/OneApi/marketplaceApp'
import { timeToString } from 'client/models/Helper'
import InfoEmpty from 'iconoir-react/dist/InfoEmpty'
import { debounce } from 'lodash'
@ -262,6 +264,20 @@ const VRouterTemplatesTable = (props) => {
</>
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{
header: T.RegistrationTime,
id: 'registration-time',
accessor: (template) => timeToString(template.REGTIME),
},
]
const { component, header } = WrapperRow(VRouterTemplateRow)
return (
<EnhancedTable
columns={columns}
@ -271,8 +287,9 @@ const VRouterTemplatesTable = (props) => {
refetch={refetch}
isLoading={loading}
getRowId={(row) => String(row.ID)}
RowComponent={VRouterTemplateRow}
noDataCustomRenderer={<NoDataAvailable />}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo, useCallback } from 'react'
import PropTypes from 'prop-types'
import { memo, useCallback, useMemo } from 'react'
import { VmTemplateCard as VRouterTemplateCard } from 'client/components/Cards'
import vrouterTemplatesApi, {
useUpdateTemplateMutation,
} from 'client/features/OneApi/vmTemplate'
import { VmTemplateCard as VRouterTemplateCard } from 'client/components/Cards'
import { jsonToXml } from 'client/models/Helper'
const Row = memo(
({ original, value, onClickLabel, ...props }) => {
({ original, value, onClickLabel, headerList, ...props }) => {
const [update] = useUpdateTemplateMutation()
const state =
@ -68,6 +68,7 @@ Row.propTypes = {
className: PropTypes.string,
onClick: PropTypes.func,
onClickLabel: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'VRouterTemplateRow'

View File

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetVRoutersQuery } from 'client/features/OneApi/vrouter'
import { ReactElement, useMemo } from 'react'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import VRouterColumns from 'client/components/Tables/VRouters/columns'
import VRouterRow from 'client/components/Tables/VRouters/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetVRoutersQuery } from 'client/features/OneApi/vrouter'
const DEFAULT_DATA_CY = 'vrouters'
@ -51,6 +51,15 @@ const VRoutersTable = (props) => {
[view]
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
]
const { component, header } = WrapperRow(VRouterRow)
return (
<EnhancedTable
columns={columns}
@ -60,7 +69,8 @@ const VRoutersTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={VRouterRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -16,15 +16,15 @@
/* eslint-disable jsdoc/require-jsdoc */
import PropTypes from 'prop-types'
import { User, Group, EmptyPage, ModernTv } from 'iconoir-react'
import { Typography } from '@mui/material'
import { EmptyPage, Group, ModernTv, User } from 'iconoir-react'
import { rowStyles } from 'client/components/Tables/styles'
import { Tr } from 'client/components/HOC'
import { T } from 'client/constants'
const Row = ({ original, value, ...props }) => {
const Row = ({ original, value, headerList, ...props }) => {
const classes = rowStyles()
const { ID, NAME, UNAME, GNAME, VMS, TEMPLATE_ID } = value
@ -65,6 +65,7 @@ Row.propTypes = {
value: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export default Row

View File

@ -13,18 +13,21 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { ReactElement, useMemo } from 'react'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import VDCColumns from 'client/components/Tables/VirtualDataCenters/columns'
import VDCRow from 'client/components/Tables/VirtualDataCenters/row'
import { ALL_SELECTED, RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetVDCsQuery } from 'client/features/OneApi/vdc'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import VDCColumns from 'client/components/Tables/VirtualDataCenters/columns'
import VDCRow from 'client/components/Tables/VirtualDataCenters/row'
import { RESOURCE_NAMES } from 'client/constants'
const DEFAULT_DATA_CY = 'vdcs'
const isAllSelected = (resourceArray) =>
resourceArray.length === 1 && resourceArray[0] === ALL_SELECTED
/**
* @param {object} props - Props
* @returns {ReactElement} VM Templates table
@ -46,6 +49,75 @@ const VDCsTable = (props) => {
[view]
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{
header: T.Groups,
id: 'group',
accessor: ({ GROUPS }) =>
useMemo(() => {
const { ID: groupsIds = [] } = GROUPS
const groupsArray = Array.isArray(groupsIds) ? groupsIds : [groupsIds]
return groupsArray.length
}, [GROUPS.ID]),
},
{
header: T.Clusters,
id: 'clusters',
accessor: ({ CLUSTERS }) =>
useMemo(() => {
const { CLUSTER: clustersInfo = [] } = CLUSTERS
const clustersArray = (
Array.isArray(clustersInfo) ? clustersInfo : [clustersInfo]
).map((cluster) => cluster.CLUSTER_ID)
return isAllSelected(clustersArray) ? T.All : clustersArray.length
}, [CLUSTERS.CLUSTER]),
},
{
header: T.Hosts,
id: 'hosts',
accessor: ({ HOSTS }) =>
useMemo(() => {
const { HOST: hostsInfo = [] } = HOSTS
const hostsArray = (
Array.isArray(hostsInfo) ? hostsInfo : [hostsInfo]
).map((host) => host.HOST_ID)
return isAllSelected(hostsArray) ? T.All : hostsArray.length
}, [HOSTS.HOST]),
},
{
header: T.Vnets,
id: 'vnets',
accessor: ({ VNETS }) =>
useMemo(() => {
const { VNET: vnetsInfo = [] } = VNETS
const vnetsArray = (
Array.isArray(vnetsInfo) ? vnetsInfo : [vnetsInfo]
).map((vnet) => vnet.VNET_ID)
return isAllSelected(vnetsArray) ? T.All : vnetsArray.length
}, [VNETS.VNET]),
},
{
header: T.Datastores,
id: 'datastores',
accessor: ({ DATASTORES }) =>
useMemo(() => {
const { DATASTORE: datastoresInfo = [] } = DATASTORES
const datastoresArray = (
Array.isArray(datastoresInfo) ? datastoresInfo : [datastoresInfo]
).map((ds) => ds.DATASTORE_ID)
return isAllSelected(datastoresArray) ? T.All : datastoresArray.length
}, [DATASTORES.DATASTORE]),
},
]
const { component, header } = WrapperRow(VDCRow)
return (
<EnhancedTable
columns={columns}
@ -55,7 +127,8 @@ const VDCsTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={VDCRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo, useCallback } from 'react'
import PropTypes from 'prop-types'
import { memo, useCallback, useMemo } from 'react'
import vdcApi, { useUpdateVDCMutation } from 'client/features/OneApi/vdc'
import { VirtualDataCenterCard } from 'client/components/Cards'
import vdcApi, { useUpdateVDCMutation } from 'client/features/OneApi/vdc'
import { jsonToXml } from 'client/models/Helper'
const Row = memo(
({ original, value, onClickLabel, ...props }) => {
({ original, value, onClickLabel, headerList, ...props }) => {
const [update] = useUpdateVDCMutation()
const state = vdcApi.endpoints.getVDCs.useQueryState(undefined, {
@ -62,6 +62,7 @@ Row.propTypes = {
className: PropTypes.string,
onClick: PropTypes.func,
onClickLabel: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'VirtualDataCenterRow'

View File

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import { memo, useMemo } from 'react'
import { DiskCard } from 'client/components/Cards'
const Row = memo(
({ original, value, ...props }) => {
({ original, value, headerList, ...props }) => {
const memoDisk = useMemo(() => original, [original])
return <DiskCard disk={memoDisk} rootProps={props} />
@ -33,6 +33,7 @@ Row.propTypes = {
isSelected: PropTypes.bool,
className: PropTypes.string,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'VmDiskRow'

View File

@ -15,13 +15,13 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useEffect, useMemo } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetVMGroupsQuery } from 'client/features/OneApi/vmGroup'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import VmGroupColumns from 'client/components/Tables/VmGroups/columns'
import VmGroupRow from 'client/components/Tables/VmGroups/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetVMGroupsQuery } from 'client/features/OneApi/vmGroup'
const DEFAULT_DATA_CY = 'vmgroups'
@ -48,6 +48,15 @@ const VmGroupsTable = (props) => {
useEffect(() => refetch(), [])
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
]
const { component, header } = WrapperRow(VmGroupRow)
return (
<EnhancedTable
columns={columns}
@ -57,7 +66,8 @@ const VmGroupsTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={VmGroupRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -18,7 +18,7 @@ import PropTypes from 'prop-types'
import { VmGroupCard } from 'client/components/Cards'
const Row = ({ original, value, ...props }) => (
const Row = ({ original, value, headerList, ...props }) => (
<VmGroupCard rootProps={props} vmgroup={value} />
)
@ -27,6 +27,7 @@ Row.propTypes = {
value: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export default Row

View File

@ -15,13 +15,14 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useEffect, useMemo } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetTemplatesQuery } from 'client/features/OneApi/vmTemplate'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import VmTemplateColumns from 'client/components/Tables/VmTemplates/columns'
import VmTemplateRow from 'client/components/Tables/VmTemplates/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetTemplatesQuery } from 'client/features/OneApi/vmTemplate'
import { timeToString } from 'client/models/Helper'
const DEFAULT_DATA_CY = 'vm-templates'
@ -47,6 +48,20 @@ const VmTemplatesTable = (props) => {
)
useEffect(() => refetch(), [])
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{
header: T.RegistrationTime,
id: 'registration-time',
accessor: (vm) => timeToString(vm.REGTIME),
},
]
const { component, header } = WrapperRow(VmTemplateRow)
return (
<EnhancedTable
columns={columns}
@ -56,7 +71,8 @@ const VmTemplatesTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={VmTemplateRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, useMemo, useCallback } from 'react'
import PropTypes from 'prop-types'
import { memo, useCallback, useMemo } from 'react'
import { VmTemplateCard } from 'client/components/Cards'
import vmTemplateApi, {
useUpdateTemplateMutation,
} from 'client/features/OneApi/vmTemplate'
import { VmTemplateCard } from 'client/components/Cards'
import { jsonToXml } from 'client/models/Helper'
const Row = memo(
({ original, value, onClickLabel, ...props }) => {
({ original, value, onClickLabel, headerList, ...props }) => {
const [update] = useUpdateTemplateMutation()
const state = vmTemplateApi.endpoints.getTemplates.useQueryState(
@ -67,6 +67,7 @@ Row.propTypes = {
className: PropTypes.string,
onClick: PropTypes.func,
onClickLabel: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'VmTemplateRow'

View File

@ -15,21 +15,29 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useMemo } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetVmsQuery } from 'client/features/OneApi/vm'
import { ConsoleButton } from 'client/components/Buttons'
import { StatusCircle } from 'client/components/Status'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import VmColumns from 'client/components/Tables/Vms/columns'
import VmRow from 'client/components/Tables/Vms/row'
import {
RESOURCE_NAMES,
STATES,
T,
VM_ACTIONS,
VM_EXTENDED_POOL,
VM_STATES,
} from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGeneral } from 'client/features/General'
import { useGetVmsQuery } from 'client/features/OneApi/vm'
import { getIps, getLastHistory, getState } from 'client/models/VirtualMachine'
const DEFAULT_DATA_CY = 'vms'
const { VNC, RDP, SSH, VMRC } = VM_ACTIONS
const CONNECTION_TYPES = [VNC, RDP, SSH, VMRC]
/**
* @param {object} props - Props
* @returns {ReactElement} Virtual Machines table
@ -121,6 +129,66 @@ const VmsTable = (props) => {
[view]
)
const { zone, defaultZone } = useGeneral()
const listHeader = [
{
header: '',
id: 'status-icon',
accessor: (vm) => {
const {
color: stateColor,
name: stateName,
displayName: stateDisplayName,
} = getState(vm)
return (
<StatusCircle
color={stateColor}
tooltip={stateDisplayName ?? stateName}
/>
)
},
},
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Owner, id: 'owner', accessor: 'UNAME' },
{ header: T.Group, id: 'group', accessor: 'GNAME' },
{
header: T.State,
id: 'state',
accessor: (vm) => getState(vm)?.name,
},
{
header: T.Hostname,
id: 'hostname',
accessor: (vm) => getLastHistory(vm)?.HOSTNAME,
},
{
header: T.IP,
id: 'ips',
accessor: (vm) => getIps(vm).join(),
},
]
zone === defaultZone &&
listHeader.push({
header: '',
id: 'consoles',
accessor: (vm) => (
<>
{CONNECTION_TYPES.map((connectionType) => (
<ConsoleButton
key={`${vm}-${connectionType}`}
connectionType={connectionType}
vm={vm}
/>
))}
</>
),
})
const { component, header } = WrapperRow(VmRow)
return (
<EnhancedTable
columns={columns}
@ -130,8 +198,9 @@ const VmsTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={VmRow}
initialState={initialState}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -27,7 +27,7 @@ const { VNC, RDP, SSH, VMRC } = VM_ACTIONS
const CONNECTION_TYPES = [VNC, RDP, SSH, VMRC]
const Row = memo(
({ original, value, onClickLabel, globalErrors, ...props }) => {
({ original, value, onClickLabel, globalErrors, headerList, ...props }) => {
// This is for not showing VNC coneections when the user use other zone.
const { zone, defaultZone } = useGeneral()
@ -88,6 +88,7 @@ Row.propTypes = {
onClick: PropTypes.func,
onClickLabel: PropTypes.func,
globalErrors: PropTypes.array,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'VirtualMachineRow'

View File

@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, ReactElement } from 'react'
import PropTypes from 'prop-types'
import { memo, ReactElement } from 'react'
import { Typography } from '@mui/material'
@ -28,7 +28,7 @@ import { Row as RowType } from 'react-table'
* @param {Function} props.handleClick - Action by click
* @returns {ReactElement} - Table row
*/
const Row = memo(({ original, ...props }) => {
const Row = memo(({ original, headerList, ...props }) => {
const classes = rowStyles()
const { DEPLOY_ID, VM_NAME } = original
@ -52,6 +52,7 @@ Row.propTypes = {
original: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'WildsRow'

View File

@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { memo, ReactElement } from 'react'
import PropTypes from 'prop-types'
import { memo, ReactElement } from 'react'
import { Typography } from '@mui/material'
@ -28,7 +28,7 @@ import { Row as RowType } from 'react-table'
* @param {Function} props.handleClick - Action by click
* @returns {ReactElement} - Table row
*/
const Row = memo(({ original, ...props }) => {
const Row = memo(({ original, headerList, ...props }) => {
const classes = rowStyles()
const { DEPLOY_ID, VM_NAME } = original
@ -49,6 +49,7 @@ Row.propTypes = {
original: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
Row.displayName = 'ZombiesRow'

View File

@ -13,15 +13,15 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { useMemo, ReactElement } from 'react'
import { useViews } from 'client/features/Auth'
import { useGetZonesQuery } from 'client/features/OneApi/zone'
import { ReactElement, useMemo } from 'react'
import EnhancedTable, { createColumns } from 'client/components/Tables/Enhanced'
import WrapperRow from 'client/components/Tables/Enhanced/WrapperRow'
import ZoneColumns from 'client/components/Tables/Zones/columns'
import ZoneRow from 'client/components/Tables/Zones/row'
import { RESOURCE_NAMES } from 'client/constants'
import { RESOURCE_NAMES, T } from 'client/constants'
import { useViews } from 'client/features/Auth'
import { useGetZonesQuery } from 'client/features/OneApi/zone'
const DEFAULT_DATA_CY = 'zones'
@ -46,6 +46,13 @@ const ZonesTable = (props) => {
[view]
)
const listHeader = [
{ header: T.ID, id: 'id', accessor: 'ID' },
{ header: T.Name, id: 'name', accessor: 'NAME' },
{ header: T.Endpoint, id: 'endpoint', accessor: 'TEMPLATE.ENDPOINT' },
]
const { component, header } = WrapperRow(ZoneRow)
return (
<EnhancedTable
columns={columns}
@ -55,7 +62,8 @@ const ZonesTable = (props) => {
refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={ZoneRow}
RowComponent={component}
headerList={header && listHeader}
{...rest}
/>
)

View File

@ -17,15 +17,15 @@
import PropTypes from 'prop-types'
import { Typography } from '@mui/material'
import { HomeShield } from 'iconoir-react'
import { Tr } from 'client/components/HOC'
import { T } from 'client/constants'
import { StatusCircle } from 'client/components/Status'
import { rowStyles } from 'client/components/Tables/styles'
import { T } from 'client/constants'
import { HomeShield } from 'iconoir-react'
import * as ZoneModel from 'client/models/Zone'
const Row = ({ original, value, ...props }) => {
const Row = ({ original, value, headerList, ...props }) => {
const classes = rowStyles()
const { ID, NAME, ENDPOINT } = value
@ -57,6 +57,7 @@ Row.propTypes = {
value: PropTypes.object,
isSelected: PropTypes.bool,
handleClick: PropTypes.func,
headerList: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
}
export default Row

View File

@ -427,6 +427,9 @@ module.exports = {
/* sections - settings */
Settings: 'Settings',
AppliesTo: 'Applies To',
AllowedOperations: 'Allowed operations',
AffectedResources: 'Affected resources',
Schema: 'Schema',
Dark: 'Dark',
Light: 'Light',
@ -591,6 +594,7 @@ module.exports = {
Zone: 'Zone',
Zones: 'Zones',
Vnet: 'Vnet',
Vnets: 'Vnets',
'cluster.form.create.general.help.title': 'Cluster',
'cluster.form.create.general.help.paragraph.1.1':
'Clusters group together hosts, datastores and virtual networks that are configured to work together. A cluster is used to:',
@ -929,6 +933,7 @@ module.exports = {
Reconnect: 'Reconnect',
FullScreen: 'Full screen',
FullScreenInfo: 'Full screen information in datatables',
RowStyle: 'DataTables Row Style',
Screenshot: 'Screenshot',
LastConnection: 'Last connection',
PartOf: 'Part of',
@ -1485,6 +1490,7 @@ module.exports = {
AutomaticDeletion: 'Automatic deletion',
Role: 'Role',
Roles: 'Roles',
Card: 'Card',
Cardinality: 'Cardinality',
Parents: 'Parents',
ParentRoles: 'Parent roles',

View File

@ -119,6 +119,22 @@ const FULL_SCREEN_INFO_FIELD = {
grid: { md: 12 },
}
const ROW_STYLE_FIELD = {
name: 'ROW_STYLE',
label: T.RowStyle,
type: INPUT_TYPES.AUTOCOMPLETE,
optionsOnly: true,
values: [
{ text: T.Card, value: 'card' },
{ text: T.List, value: 'list' },
],
validation: string()
.trim()
.required()
.default(() => 'card'),
grid: { md: 12 },
}
/**
* @param {object} props - Props
* @param {object} props.views - views.
@ -131,6 +147,7 @@ export const FIELDS = (props) => [
LANG_FIELD,
VIEW_FIELD(props),
ZONE_ENDPOINT_FIELD(props),
ROW_STYLE_FIELD,
DISABLE_ANIMATIONS_FIELD,
FULL_SCREEN_INFO_FIELD,
]

View File

@ -43,7 +43,7 @@ const Settings = () => {
<Box
display="grid"
gridTemplateColumns={{ sm: '1fr', md: 'repeat(2, minmax(49%, 1fr))' }}
gridTemplateRows="minmax(0, 33em)"
gridTemplateRows="minmax(0, 36em)"
gap="1em"
>
<ConfigurationUISection />