mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
parent
a40167e9b4
commit
440ed864a4
11
src/fireedge/package-lock.json
generated
11
src/fireedge/package-lock.json
generated
@ -92,6 +92,7 @@
|
||||
"socket.io": "4.4.1",
|
||||
"socket.io-client": "4.4.1",
|
||||
"speakeasy": "2.0.0",
|
||||
"split-grid": "1.0.11",
|
||||
"sprintf-js": "1.1.2",
|
||||
"style-loader": "3.2.1",
|
||||
"terser-webpack-plugin": "5.1.4",
|
||||
@ -11544,6 +11545,11 @@
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split-grid": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/split-grid/-/split-grid-1.0.11.tgz",
|
||||
"integrity": "sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ=="
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
|
||||
@ -21752,6 +21758,11 @@
|
||||
"base32.js": "0.0.1"
|
||||
}
|
||||
},
|
||||
"split-grid": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/split-grid/-/split-grid-1.0.11.tgz",
|
||||
"integrity": "sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ=="
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
|
||||
|
@ -126,6 +126,7 @@
|
||||
"socket.io": "4.4.1",
|
||||
"socket.io-client": "4.4.1",
|
||||
"speakeasy": "2.0.0",
|
||||
"split-grid": "1.0.11",
|
||||
"sprintf-js": "1.1.2",
|
||||
"style-loader": "3.2.1",
|
||||
"terser-webpack-plugin": "5.1.4",
|
||||
|
@ -13,130 +13,117 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState, createRef, useRef, useEffect } from 'react'
|
||||
import { useRef, useEffect, useCallback, Children, ReactElement } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Divider } from '@mui/material'
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
import Split, { SplitOptions } from 'split-grid'
|
||||
import { styled, Divider } from '@mui/material'
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
splitPane: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
},
|
||||
topPane: {
|
||||
flex: 1,
|
||||
},
|
||||
separator: {
|
||||
position: 'relative',
|
||||
cursor: 'row-resize',
|
||||
const Gutter = styled(Divider)(({ theme, direction = 'row' }) => ({
|
||||
position: 'relative',
|
||||
cursor: `${direction}-resize`,
|
||||
height: 8,
|
||||
marginBlock: '1em',
|
||||
background: `linear-gradient(
|
||||
to right,
|
||||
transparent,
|
||||
${theme.palette.divider},
|
||||
transparent
|
||||
)`,
|
||||
'&:after': {
|
||||
content: "''",
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
left: '50%',
|
||||
width: 8,
|
||||
height: 8,
|
||||
marginBlock: '1em',
|
||||
background: `linear-gradient(
|
||||
to right,
|
||||
transparent,
|
||||
${theme.palette.divider},
|
||||
transparent
|
||||
)`,
|
||||
'&:after': {
|
||||
content: "''",
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
left: '50%',
|
||||
width: 8,
|
||||
height: 8,
|
||||
transform: 'rotate(45deg)',
|
||||
backgroundColor: theme.palette.action.active,
|
||||
},
|
||||
transform: 'rotate(45deg)',
|
||||
backgroundColor: theme.palette.action.active,
|
||||
},
|
||||
}))
|
||||
|
||||
const SplitPane = ({ children, containerProps }) => {
|
||||
const classes = useStyles()
|
||||
const [topHeight, setTopHeight] = useState(null)
|
||||
/**
|
||||
* @typedef SplitGridHook
|
||||
* @property {function():object} getGridProps - Function to get grid props
|
||||
* @property {ReactElement} GutterComponent - Gutter component
|
||||
*/
|
||||
|
||||
const splitPaneRef = createRef()
|
||||
const topRef = createRef()
|
||||
const separatorYPosition = useRef(null)
|
||||
/**
|
||||
* Hook to create a split pane with a divider between the two panes.
|
||||
*
|
||||
* @param {SplitOptions} options - Options to configure the split pane
|
||||
* @returns {SplitGridHook} Hook functions to element grid and gutter
|
||||
*/
|
||||
const useSplitGrid = (options) => {
|
||||
const {
|
||||
columnMinSizes,
|
||||
rowMinSizes,
|
||||
columnMaxSizes,
|
||||
rowMaxSizes,
|
||||
gridTemplateColumns,
|
||||
gridTemplateRows,
|
||||
} = options
|
||||
|
||||
const onMouseDown = (event) => {
|
||||
separatorYPosition.current = event?.touches?.[0]?.clientY ?? event.clientY
|
||||
}
|
||||
|
||||
const onMouseMove = (event) => {
|
||||
if (!separatorYPosition.current) return
|
||||
|
||||
const clientY = event?.touches?.[0]?.clientY || event.clientY
|
||||
|
||||
const newTopHeight = topHeight + clientY - separatorYPosition.current
|
||||
separatorYPosition.current = clientY
|
||||
|
||||
if (newTopHeight <= 0) {
|
||||
return topHeight !== 0 && setTopHeight(0)
|
||||
}
|
||||
|
||||
const splitPaneHeight = splitPaneRef.current?.clientHeight
|
||||
|
||||
if (newTopHeight >= splitPaneHeight) {
|
||||
return topHeight !== splitPaneHeight && setTopHeight(splitPaneHeight)
|
||||
}
|
||||
|
||||
setTopHeight(newTopHeight)
|
||||
}
|
||||
|
||||
const onMouseUp = () => {
|
||||
separatorYPosition.current = null
|
||||
}
|
||||
const split = useRef({})
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('mousemove', onMouseMove)
|
||||
document.addEventListener('mouseup', onMouseUp)
|
||||
// on mobile device
|
||||
document.addEventListener('touchmove', onMouseMove)
|
||||
document.addEventListener('touchend', onMouseUp)
|
||||
split.current = Split(options)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', onMouseMove)
|
||||
document.removeEventListener('mouseup', onMouseUp)
|
||||
// on mobile device
|
||||
document.removeEventListener('touchmove', onMouseMove)
|
||||
document.removeEventListener('touchend', onMouseUp)
|
||||
}
|
||||
})
|
||||
return () => split.current.destroy()
|
||||
}, [columnMinSizes, rowMinSizes, columnMaxSizes, rowMaxSizes])
|
||||
|
||||
useEffect(() => {
|
||||
if (!topHeight && children[1]) {
|
||||
setTopHeight(document.body.clientHeight / 2)
|
||||
topRef.current.style.flex = 'none'
|
||||
}
|
||||
|
||||
topRef.current.style.height = children[1]
|
||||
? `${topHeight}px`
|
||||
: `${splitPaneRef.current?.clientHeight}px`
|
||||
}, [topHeight, children[1]])
|
||||
|
||||
return (
|
||||
<div {...containerProps} className={classes.splitPane} ref={splitPaneRef}>
|
||||
<div className={classes.topPane} ref={topRef}>
|
||||
{children[0]}
|
||||
</div>
|
||||
{!!children[1] && (
|
||||
<Divider
|
||||
className={classes.separator}
|
||||
onTouchStart={onMouseDown}
|
||||
onMouseDown={onMouseDown}
|
||||
/>
|
||||
)}
|
||||
{children[1]}
|
||||
</div>
|
||||
const getGridProps = useCallback(
|
||||
() => ({
|
||||
display: 'grid',
|
||||
height: 1, // 100%
|
||||
gridTemplateColumns,
|
||||
gridTemplateRows,
|
||||
}),
|
||||
[gridTemplateColumns, gridTemplateRows]
|
||||
)
|
||||
|
||||
const GutterComponent = useCallback(
|
||||
({ direction, track }) => {
|
||||
const handleDragStart = (e) => {
|
||||
split.current?.handleDragStart(e, direction, track)
|
||||
}
|
||||
|
||||
return (
|
||||
<Gutter
|
||||
key={`gutter-${direction}-${track}`}
|
||||
className="gutter"
|
||||
direction={direction}
|
||||
track={track}
|
||||
onMouseDown={handleDragStart}
|
||||
onTouchStart={handleDragStart}
|
||||
/>
|
||||
)
|
||||
},
|
||||
[split.current?.handleDragStart]
|
||||
)
|
||||
|
||||
return { getGridProps, GutterComponent }
|
||||
}
|
||||
|
||||
SplitPane.propTypes = {
|
||||
children: PropTypes.array.isRequired,
|
||||
containerProps: PropTypes.object,
|
||||
/**
|
||||
* @param {SplitOptions} props - Component props
|
||||
* @param {Function|ReactElement} [props.children] - Child components
|
||||
* @returns {ReactElement|function(SplitGridHook):ReactElement} Split pane component
|
||||
*/
|
||||
const SplitGrid = ({ children, ...options }) => {
|
||||
const hook = useSplitGrid(options)
|
||||
|
||||
if (!children) return null
|
||||
if (typeof children === 'function') return children(hook)
|
||||
|
||||
return !(Children.count(children) === 0) ? Children.only(children) : null
|
||||
}
|
||||
|
||||
export default SplitPane
|
||||
SplitGrid.propTypes = {
|
||||
children: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
|
||||
options: PropTypes.object,
|
||||
}
|
||||
|
||||
export { Gutter, useSplitGrid }
|
||||
|
||||
export default SplitGrid
|
||||
|
@ -13,47 +13,90 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { ClustersTable } from 'client/components/Tables'
|
||||
import ClusterTabs from 'client/components/Tabs/Cluster'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of Clusters with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Clusters list and selected row(s)
|
||||
*/
|
||||
function Clusters() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<ClustersTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto">
|
||||
{selectedRows?.length === 1 ? (
|
||||
<ClusterTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<ClustersTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs cluster={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a Cluster.
|
||||
*
|
||||
* @param {object} cluster - Cluster to display
|
||||
* @returns {ReactElement} Cluster details
|
||||
*/
|
||||
const InfoTabs = memo(({ cluster }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${cluster.ID} | ${cluster.NAME}`}
|
||||
</Typography>
|
||||
<ClusterTabs id={cluster.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { cluster: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default Clusters
|
||||
|
@ -13,47 +13,90 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { DatastoresTable } from 'client/components/Tables'
|
||||
import DatastoreTabs from 'client/components/Tabs/Datastore'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of Datastores with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Datastores list and selected row(s)
|
||||
*/
|
||||
function Datastores() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<DatastoresTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto">
|
||||
{selectedRows?.length === 1 ? (
|
||||
<DatastoreTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<DatastoresTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs datastore={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a Datastore.
|
||||
*
|
||||
* @param {object} datastore - Datastore to display
|
||||
* @returns {ReactElement} Datastore details
|
||||
*/
|
||||
const InfoTabs = memo(({ datastore }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${datastore.ID} | ${datastore.NAME}`}
|
||||
</Typography>
|
||||
<DatastoreTabs id={datastore.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { datastore: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default Datastores
|
||||
|
@ -13,47 +13,90 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { GroupsTable } from 'client/components/Tables'
|
||||
import GroupTabs from 'client/components/Tabs/Group'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of Groups with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Groups list and selected row(s)
|
||||
*/
|
||||
function Groups() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<GroupsTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto" data-cy={'detail'}>
|
||||
{selectedRows?.length === 1 ? (
|
||||
<GroupTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<GroupsTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs group={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a Group.
|
||||
*
|
||||
* @param {object} group - Group to display
|
||||
* @returns {ReactElement} Group details
|
||||
*/
|
||||
const InfoTabs = memo(({ group }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${group.ID} | ${group.NAME}`}
|
||||
</Typography>
|
||||
<GroupTabs id={group.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { group: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default Groups
|
||||
|
@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { HostsTable } from 'client/components/Tables'
|
||||
import HostTabs from 'client/components/Tabs/Host'
|
||||
@ -23,42 +24,84 @@ import HostActions from 'client/components/Tables/Hosts/actions'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of Hosts with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Hosts list and selected row(s)
|
||||
*/
|
||||
function Hosts() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
const actions = HostActions()
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<HostsTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
/>
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto">
|
||||
{selectedRows?.length === 1 ? (
|
||||
<HostTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<HostsTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
/>
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs host={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a Host.
|
||||
*
|
||||
* @param {object} host - Host to display
|
||||
* @returns {ReactElement} Host details
|
||||
*/
|
||||
const InfoTabs = memo(({ host }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${host.ID} | ${host.NAME}`}
|
||||
</Typography>
|
||||
<HostTabs id={host.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { host: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default Hosts
|
||||
|
@ -13,47 +13,90 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { ImagesTable } from 'client/components/Tables'
|
||||
import ImageTabs from 'client/components/Tabs/Image'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of Images with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Images list and selected row(s)
|
||||
*/
|
||||
function Images() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<ImagesTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto">
|
||||
{selectedRows?.length === 1 ? (
|
||||
<ImageTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<ImagesTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs image={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of an Image.
|
||||
*
|
||||
* @param {object} image - Image to display
|
||||
* @returns {ReactElement} Image details
|
||||
*/
|
||||
const InfoTabs = memo(({ image }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${image.ID} | ${image.NAME}`}
|
||||
</Typography>
|
||||
<ImageTabs id={image.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { image: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default Images
|
||||
|
@ -13,8 +13,10 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { useState, JSXElementConstructor } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { MarketplaceAppsTable } from 'client/components/Tables'
|
||||
import MarketplaceAppActions from 'client/components/Tables/MarketplaceApps/actions'
|
||||
@ -23,46 +25,83 @@ import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a Marketplace Apps list.
|
||||
* Displays a list of Marketplace Apps with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {JSXElementConstructor} List of Marketplace Apps
|
||||
* @returns {ReactElement} Marketplace Apps list and selected row(s)
|
||||
*/
|
||||
function MarketplaceApps() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
const actions = MarketplaceAppActions()
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<MarketplaceAppsTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
/>
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto">
|
||||
{selectedRows?.length === 1 ? (
|
||||
<MarketplaceAppsTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<MarketplaceAppsTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
/>
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs app={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a Marketplace App.
|
||||
*
|
||||
* @param {object} app - Marketplace App to display
|
||||
* @returns {ReactElement} Marketplace App details
|
||||
*/
|
||||
const InfoTabs = memo(({ app }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${app.ID} | ${app.NAME}`}
|
||||
</Typography>
|
||||
<MarketplaceAppsTabs id={app.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { app: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default MarketplaceApps
|
||||
|
@ -13,47 +13,90 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { MarketplacesTable } from 'client/components/Tables'
|
||||
import MarketplaceTabs from 'client/components/Tabs/Marketplace'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of Marketplaces with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Marketplaces list and selected row(s)
|
||||
*/
|
||||
function Marketplaces() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<MarketplacesTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto" data-cy={'detail'}>
|
||||
{selectedRows?.length === 1 ? (
|
||||
<MarketplaceTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<MarketplacesTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs market={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a Marketplace.
|
||||
*
|
||||
* @param {object} market - Marketplace to display
|
||||
* @returns {ReactElement} Marketplace details
|
||||
*/
|
||||
const InfoTabs = memo(({ market }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${market.ID} | ${market.NAME}`}
|
||||
</Typography>
|
||||
<MarketplaceTabs id={market.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { market: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default Marketplaces
|
||||
|
@ -13,47 +13,90 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { UsersTable } from 'client/components/Tables'
|
||||
import UserTabs from 'client/components/Tabs/User'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of Users with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Users list and selected row(s)
|
||||
*/
|
||||
function Users() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<UsersTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto" data-cy={'detail'}>
|
||||
{selectedRows?.length === 1 ? (
|
||||
<UserTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<UsersTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs user={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of an User.
|
||||
*
|
||||
* @param {object} user - User to display
|
||||
* @returns {ReactElement} User details
|
||||
*/
|
||||
const InfoTabs = memo(({ user }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${user.ID} | ${user.NAME}`}
|
||||
</Typography>
|
||||
<UserTabs id={user.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { user: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default Users
|
||||
|
@ -13,47 +13,90 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { VNetworkTemplatesTable } from 'client/components/Tables'
|
||||
import VNetworkTemplateTabs from 'client/components/Tabs/VNetworkTemplate'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of VNet Templates with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} VNet Templates list and selected row(s)
|
||||
*/
|
||||
function VNetworkTemplates() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<VNetworkTemplatesTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto" data-cy={'detail'}>
|
||||
{selectedRows?.length === 1 ? (
|
||||
<VNetworkTemplateTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<VNetworkTemplatesTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs vnTemplate={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a VNet Template.
|
||||
*
|
||||
* @param {object} vnTemplate - VNet Template to display
|
||||
* @returns {ReactElement} VNet Template details
|
||||
*/
|
||||
const InfoTabs = memo(({ vnTemplate }) => (
|
||||
<Stack overflow="auto" data-cy="detail">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${vnTemplate.ID} | ${vnTemplate.NAME}`}
|
||||
</Typography>
|
||||
<VNetworkTemplateTabs id={vnTemplate.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { vnTemplate: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default VNetworkTemplates
|
||||
|
@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Typography, Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { VmsTable } from 'client/components/Tables'
|
||||
import VmActions from 'client/components/Tables/Vms/actions'
|
||||
@ -23,46 +24,84 @@ import VmTabs from 'client/components/Tabs/Vm'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of VMs with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} VMs list and selected row(s)
|
||||
*/
|
||||
function VirtualMachines() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
const actions = VmActions()
|
||||
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<VmsTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
/>
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto" data-cy={'detail'}>
|
||||
{selectedRows?.length === 1 ? (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<VmsTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
/>
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${selectedRows[0]?.original.ID} | ${selectedRows[0]?.original.NAME}`}
|
||||
</Typography>
|
||||
<VmTabs id={selectedRows[0]?.original.ID} />
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs vm={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="outlined"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a VM.
|
||||
*
|
||||
* @param {object} vm - VM to display
|
||||
* @returns {ReactElement} VM details
|
||||
*/
|
||||
const InfoTabs = memo(({ vm }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${vm.ID} | ${vm.NAME}`}
|
||||
</Typography>
|
||||
<VmTabs id={vm.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { vm: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default VirtualMachines
|
||||
|
@ -13,47 +13,90 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { VNetworksTable } from 'client/components/Tables'
|
||||
import VNetworkTabs from 'client/components/Tabs/VNetwork'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of Virtual Networks with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Virtual Networks list and selected row(s)
|
||||
*/
|
||||
function VirtualNetworks() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<VNetworksTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto" data-cy={'detail'}>
|
||||
{selectedRows?.length === 1 ? (
|
||||
<VNetworkTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<VNetworksTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs vnet={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a Virtual Network.
|
||||
*
|
||||
* @param {object} vnet - Virtual Network to display
|
||||
* @returns {ReactElement} Virtual Network details
|
||||
*/
|
||||
const InfoTabs = memo(({ vnet }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${vnet.ID} | ${vnet.NAME}`}
|
||||
</Typography>
|
||||
<VNetworkTabs id={vnet.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { vnet: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default VirtualNetworks
|
||||
|
@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Typography, Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { VmTemplatesTable } from 'client/components/Tables'
|
||||
import VmTemplateActions from 'client/components/Tables/VmTemplates/actions'
|
||||
@ -23,46 +24,84 @@ import VmTemplateTabs from 'client/components/Tabs/VmTemplate'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of VM Templates with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} VM Templates list and selected row(s)
|
||||
*/
|
||||
function VmTemplates() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
const actions = VmTemplateActions()
|
||||
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<VmTemplatesTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
/>
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto">
|
||||
{selectedRows?.length === 1 ? (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<VmTemplatesTable
|
||||
onSelectedRowsChange={onSelectedRowsChange}
|
||||
globalActions={actions}
|
||||
/>
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${selectedRows[0]?.original.ID} | ${selectedRows[0]?.original.NAME}`}
|
||||
</Typography>
|
||||
<VmTemplateTabs id={selectedRows[0]?.original.ID} />
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs vmTemplate={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a VM Template.
|
||||
*
|
||||
* @param {object} vmTemplate - VM Template to display
|
||||
* @returns {ReactElement} VM Template details
|
||||
*/
|
||||
const InfoTabs = memo(({ vmTemplate }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${vmTemplate.ID} | ${vmTemplate.NAME}`}
|
||||
</Typography>
|
||||
<VmTemplateTabs id={vmTemplate.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { vmTemplate: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default VmTemplates
|
||||
|
@ -13,47 +13,90 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useState } from 'react'
|
||||
import { Stack, Chip } from '@mui/material'
|
||||
import { ReactElement, useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Typography, Box, Stack, Chip } from '@mui/material'
|
||||
import { Row } from 'react-table'
|
||||
|
||||
import { ZonesTable } from 'client/components/Tables'
|
||||
import ZoneTabs from 'client/components/Tabs/Zone'
|
||||
import SplitPane from 'client/components/SplitPane'
|
||||
import MultipleTags from 'client/components/MultipleTags'
|
||||
|
||||
/**
|
||||
* Displays a list of Zones with a split pane between the list and selected row(s).
|
||||
*
|
||||
* @returns {ReactElement} Zones list and selected row(s)
|
||||
*/
|
||||
function Zones() {
|
||||
const [selectedRows, onSelectedRowsChange] = useState(() => [])
|
||||
|
||||
return (
|
||||
<SplitPane>
|
||||
<ZonesTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
const hasSelectedRows = selectedRows?.length > 0
|
||||
const moreThanOneSelected = selectedRows?.length > 1
|
||||
const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr'
|
||||
|
||||
{selectedRows?.length > 0 && (
|
||||
<Stack overflow="auto" data-cy={'detail'}>
|
||||
{selectedRows?.length === 1 ? (
|
||||
<ZoneTabs id={selectedRows[0]?.original.ID} />
|
||||
) : (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1} alignItems="center">
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={selectedRows?.map(
|
||||
({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
variant="text"
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
return (
|
||||
<SplitPane gridTemplateRows={gridTemplateRows}>
|
||||
{({ getGridProps, GutterComponent }) => (
|
||||
<Box {...getGridProps()}>
|
||||
<ZonesTable onSelectedRowsChange={onSelectedRowsChange} />
|
||||
|
||||
{hasSelectedRows && (
|
||||
<>
|
||||
<GutterComponent direction="row" track={1} />
|
||||
{moreThanOneSelected ? (
|
||||
<GroupedTags tags={selectedRows} />
|
||||
) : (
|
||||
<InfoTabs zone={selectedRows[0]?.original} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</SplitPane>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays details of a Zone.
|
||||
*
|
||||
* @param {object} zone - Zone to display
|
||||
* @returns {ReactElement} Zone details
|
||||
*/
|
||||
const InfoTabs = memo(({ zone }) => (
|
||||
<Stack overflow="auto">
|
||||
<Typography color="text.primary" noWrap mb={1}>
|
||||
{`#${zone.ID} | ${zone.NAME}`}
|
||||
</Typography>
|
||||
<ZoneTabs id={zone.ID} />
|
||||
</Stack>
|
||||
))
|
||||
|
||||
InfoTabs.propTypes = { zone: PropTypes.object.isRequired }
|
||||
InfoTabs.displayName = 'InfoTabs'
|
||||
|
||||
/**
|
||||
* Displays a list of tags that represent the selected rows.
|
||||
*
|
||||
* @param {Row[]} tags - Row(s) to display as tags
|
||||
* @returns {ReactElement} List of tags
|
||||
*/
|
||||
const GroupedTags = memo(({ tags = [] }) => (
|
||||
<Stack direction="row" flexWrap="wrap" gap={1}>
|
||||
<MultipleTags
|
||||
limitTags={10}
|
||||
tags={tags?.map(({ original, id, toggleRowSelected }) => (
|
||||
<Chip
|
||||
key={id}
|
||||
label={original?.NAME ?? id}
|
||||
onDelete={() => toggleRowSelected(false)}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
</Stack>
|
||||
))
|
||||
|
||||
GroupedTags.propTypes = { tags: PropTypes.array }
|
||||
GroupedTags.displayName = 'GroupedTags'
|
||||
|
||||
export default Zones
|
||||
|
Loading…
x
Reference in New Issue
Block a user