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

F #5422: Add button to goto row page on table (#2056)

This commit is contained in:
Sergio Betanzos 2022-05-18 14:55:28 +02:00 committed by GitHub
parent cd023c29e6
commit 1bda1e37af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 412 additions and 145 deletions

View File

@ -16,7 +16,7 @@
import { JSXElementConstructor } from 'react'
import PropTypes from 'prop-types'
import { TableProps } from 'react-table'
import { TableProps, Row } from 'react-table'
import { styled, Chip, Alert, Button, alertClasses } from '@mui/material'
import { Translate } from 'client/components/HOC'
@ -39,10 +39,15 @@ const MessageStyled = styled(Alert)({
*
* @param {object} props - Props
* @param {boolean} props.withAlert - If `true`, the list of selected rows will be an alert
* @param {function(Row)} [props.gotoRowPage] - Function to navigate to a page of the row
* @param {TableProps} props.useTableProps - Table props
* @returns {JSXElementConstructor} Component JSX
*/
const GlobalSelectedRows = ({ withAlert = false, useTableProps }) => {
const GlobalSelectedRows = ({
withAlert = false,
useTableProps,
gotoRowPage,
}) => {
const {
preFilteredRows,
toggleAllRowsSelected,
@ -78,11 +83,12 @@ const GlobalSelectedRows = ({ withAlert = false, useTableProps }) => {
</MessageStyled>
) : (
<div>
{selectedRows?.map(({ original, id, toggleRowSelected }) => (
{selectedRows?.map((row) => (
<Chip
key={id}
label={original?.NAME ?? id}
onDelete={() => toggleRowSelected(false)}
key={row.id}
label={row.original?.NAME ?? row.id}
onDelete={() => row.toggleRowSelected(false)}
{...(gotoRowPage && { onClick: () => gotoRowPage(row) })}
/>
))}
</div>
@ -92,6 +98,7 @@ const GlobalSelectedRows = ({ withAlert = false, useTableProps }) => {
GlobalSelectedRows.propTypes = {
withAlert: PropTypes.bool,
useTableProps: PropTypes.object.isRequired,
gotoRowPage: PropTypes.func,
}
GlobalSelectedRows.displayName = ' GlobalSelectedRows'

View File

@ -104,11 +104,9 @@ const EnhancedTable = ({
autoResetSelectedRow: false,
autoResetSelectedRows: false,
autoResetSortBy: false,
autoResetPage: false,
// -------------------------------------
initialState: {
pageSize,
...initialState,
},
initialState: { pageSize, ...initialState },
},
useGlobalFilter,
useFilters,
@ -126,13 +124,24 @@ const EnhancedTable = ({
page,
gotoPage,
pageCount,
state: { pageIndex, selectedRowIds },
state: { pageIndex, selectedRowIds, ...state },
} = useTableProps
const gotoRowPage = async (row) => {
const pageIdx = Math.floor(row.index / state.pageSize)
await gotoPage(pageIdx)
// scroll to the row in the table view (if it's visible)
document
?.querySelector(`.selected[role='row'][data-cy$='-${row.id}']`)
?.scrollIntoView({ behavior: 'smooth', block: 'center' })
}
useMountedLayoutEffect(() => {
const selectedRows = preFilteredRows.filter(
(row) => !!selectedRowIds[row.id]
)
const selectedRows = preFilteredRows
.filter((row) => !!selectedRowIds[row.id])
.map((row) => ({ ...row, gotoPage: () => gotoRowPage(row) }))
onSelectedRowsChange?.(selectedRows)
}, [selectedRowIds])
@ -189,7 +198,10 @@ const EnhancedTable = ({
{/* SELECTED ROWS */}
{displaySelectedRows && (
<div>
<GlobalSelectedRows useTableProps={useTableProps} />
<GlobalSelectedRows
useTableProps={useTableProps}
gotoRowPage={gotoRowPage}
/>
</div>
)}
</div>

View File

@ -85,6 +85,7 @@ module.exports = {
Info: 'Info',
Instantiate: 'Instantiate',
InstantiateVmTemplate: 'Instantiate VM Template',
LocateOnTable: 'Locate on table',
Lock: 'Lock',
Migrate: 'Migrate',
MigrateLive: 'Migrate live',

View File

@ -15,13 +15,16 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } 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'
import { Tr } from 'client/components/HOC'
import { T, Cluster } from 'client/constants'
/**
* Displays a list of Clusters with a split pane between the list and selected row(s).
@ -47,7 +50,10 @@ function Clusters() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs cluster={selectedRows[0]?.original} />
<InfoTabs
cluster={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -60,19 +66,31 @@ function Clusters() {
/**
* Displays details of a Cluster.
*
* @param {object} cluster - Cluster to display
* @param {Cluster} cluster - Cluster to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Cluster
* @returns {ReactElement} Cluster details
*/
const InfoTabs = memo(({ cluster }) => (
const InfoTabs = memo(({ cluster, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${cluster.ID} | ${cluster.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${cluster.ID} | ${cluster.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<ClusterTabs id={cluster.ID} />
</Stack>
))
InfoTabs.propTypes = { cluster: PropTypes.object.isRequired }
InfoTabs.propTypes = {
cluster: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -82,13 +100,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,13 +15,16 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } 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'
import { Tr } from 'client/components/HOC'
import { T, Datastore } from 'client/constants'
/**
* Displays a list of Datastores with a split pane between the list and selected row(s).
@ -47,7 +50,10 @@ function Datastores() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs datastore={selectedRows[0]?.original} />
<InfoTabs
datastore={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -60,19 +66,31 @@ function Datastores() {
/**
* Displays details of a Datastore.
*
* @param {object} datastore - Datastore to display
* @param {Datastore} datastore - Datastore to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Datastore
* @returns {ReactElement} Datastore details
*/
const InfoTabs = memo(({ datastore }) => (
const InfoTabs = memo(({ datastore, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${datastore.ID} | ${datastore.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${datastore.ID} | ${datastore.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<DatastoreTabs id={datastore.ID} />
</Stack>
))
InfoTabs.propTypes = { datastore: PropTypes.object.isRequired }
InfoTabs.propTypes = {
datastore: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -82,13 +100,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,13 +15,16 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } 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'
import { Tr } from 'client/components/HOC'
import { T, Group } from 'client/constants'
/**
* Displays a list of Groups with a split pane between the list and selected row(s).
@ -47,7 +50,10 @@ function Groups() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs group={selectedRows[0]?.original} />
<InfoTabs
group={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -60,19 +66,31 @@ function Groups() {
/**
* Displays details of a Group.
*
* @param {object} group - Group to display
* @param {Group} group - Group to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Group
* @returns {ReactElement} Group details
*/
const InfoTabs = memo(({ group }) => (
const InfoTabs = memo(({ group, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${group.ID} | ${group.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${group.ID} | ${group.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<GroupTabs id={group.ID} />
</Stack>
))
InfoTabs.propTypes = { group: PropTypes.object.isRequired }
InfoTabs.propTypes = {
group: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -82,13 +100,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,7 +15,8 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import { Row } from 'react-table'
import { HostsTable } from 'client/components/Tables'
@ -23,6 +24,8 @@ import HostTabs from 'client/components/Tabs/Host'
import HostActions from 'client/components/Tables/Hosts/actions'
import SplitPane from 'client/components/SplitPane'
import MultipleTags from 'client/components/MultipleTags'
import { Tr } from 'client/components/HOC'
import { T, Host } from 'client/constants'
/**
* Displays a list of Hosts with a split pane between the list and selected row(s).
@ -52,7 +55,10 @@ function Hosts() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs host={selectedRows[0]?.original} />
<InfoTabs
host={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -65,19 +71,31 @@ function Hosts() {
/**
* Displays details of a Host.
*
* @param {object} host - Host to display
* @param {Host} host - Host to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Host
* @returns {ReactElement} Host details
*/
const InfoTabs = memo(({ host }) => (
const InfoTabs = memo(({ host, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${host.ID} | ${host.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${host.ID} | ${host.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<HostTabs id={host.ID} />
</Stack>
))
InfoTabs.propTypes = { host: PropTypes.object.isRequired }
InfoTabs.propTypes = {
host: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -87,13 +105,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,13 +15,16 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } 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'
import { Tr } from 'client/components/HOC'
import { T, Image } from 'client/constants'
/**
* Displays a list of Images with a split pane between the list and selected row(s).
@ -47,7 +50,10 @@ function Images() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs image={selectedRows[0]?.original} />
<InfoTabs
image={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -60,19 +66,31 @@ function Images() {
/**
* Displays details of an Image.
*
* @param {object} image - Image to display
* @param {Image} image - Image to display
* @param {Function} [gotoPage] - Function to navigate to a page of an Image
* @returns {ReactElement} Image details
*/
const InfoTabs = memo(({ image }) => (
const InfoTabs = memo(({ image, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${image.ID} | ${image.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${image.ID} | ${image.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<ImageTabs id={image.ID} />
</Stack>
))
InfoTabs.propTypes = { image: PropTypes.object.isRequired }
InfoTabs.propTypes = {
image: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -82,13 +100,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,7 +15,8 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import { Row } from 'react-table'
import { MarketplaceAppsTable } from 'client/components/Tables'
@ -23,6 +24,8 @@ import MarketplaceAppActions from 'client/components/Tables/MarketplaceApps/acti
import MarketplaceAppsTabs from 'client/components/Tabs/MarketplaceApp'
import SplitPane from 'client/components/SplitPane'
import MultipleTags from 'client/components/MultipleTags'
import { Tr } from 'client/components/HOC'
import { T, MarketplaceApp } from 'client/constants'
/**
* Displays a list of Marketplace Apps with a split pane between the list and selected row(s).
@ -52,7 +55,10 @@ function MarketplaceApps() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs app={selectedRows[0]?.original} />
<InfoTabs
app={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -65,19 +71,31 @@ function MarketplaceApps() {
/**
* Displays details of a Marketplace App.
*
* @param {object} app - Marketplace App to display
* @param {MarketplaceApp} app - Marketplace App to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Marketplace App
* @returns {ReactElement} Marketplace App details
*/
const InfoTabs = memo(({ app }) => (
const InfoTabs = memo(({ app, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${app.ID} | ${app.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${app.ID} | ${app.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<MarketplaceAppsTabs id={app.ID} />
</Stack>
))
InfoTabs.propTypes = { app: PropTypes.object.isRequired }
InfoTabs.propTypes = {
app: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -87,13 +105,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,13 +15,16 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } 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'
import { Tr } from 'client/components/HOC'
import { T, Marketplace } from 'client/constants'
/**
* Displays a list of Marketplaces with a split pane between the list and selected row(s).
@ -47,7 +50,10 @@ function Marketplaces() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs market={selectedRows[0]?.original} />
<InfoTabs
market={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -60,19 +66,31 @@ function Marketplaces() {
/**
* Displays details of a Marketplace.
*
* @param {object} market - Marketplace to display
* @param {Marketplace} market - Marketplace to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Marketplace
* @returns {ReactElement} Marketplace details
*/
const InfoTabs = memo(({ market }) => (
const InfoTabs = memo(({ market, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${market.ID} | ${market.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${market.ID} | ${market.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<MarketplaceTabs id={market.ID} />
</Stack>
))
InfoTabs.propTypes = { market: PropTypes.object.isRequired }
InfoTabs.propTypes = {
market: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -82,13 +100,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,13 +15,16 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } 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'
import { Tr } from 'client/components/HOC'
import { T, User } from 'client/constants'
/**
* Displays a list of Users with a split pane between the list and selected row(s).
@ -47,7 +50,10 @@ function Users() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs user={selectedRows[0]?.original} />
<InfoTabs
user={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -60,19 +66,31 @@ function Users() {
/**
* Displays details of an User.
*
* @param {object} user - User to display
* @param {User} user - User to display
* @param {Function} [gotoPage] - Function to navigate to a page of an User
* @returns {ReactElement} User details
*/
const InfoTabs = memo(({ user }) => (
const InfoTabs = memo(({ user, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${user.ID} | ${user.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${user.ID} | ${user.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<UserTabs id={user.ID} />
</Stack>
))
InfoTabs.propTypes = { user: PropTypes.object.isRequired }
InfoTabs.propTypes = {
user: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -82,13 +100,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,13 +15,16 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } 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'
import { Tr } from 'client/components/HOC'
import { T, VNetworkTemplate } from 'client/constants'
/**
* Displays a list of VNet Templates with a split pane between the list and selected row(s).
@ -47,7 +50,10 @@ function VNetworkTemplates() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs vnTemplate={selectedRows[0]?.original} />
<InfoTabs
vnTemplate={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -60,19 +66,31 @@ function VNetworkTemplates() {
/**
* Displays details of a VNet Template.
*
* @param {object} vnTemplate - VNet Template to display
* @param {VNetworkTemplate} vnTemplate - VNet Template to display
* @param {Function} [gotoPage] - Function to navigate to a page of a VNet Template
* @returns {ReactElement} VNet Template details
*/
const InfoTabs = memo(({ vnTemplate }) => (
const InfoTabs = memo(({ vnTemplate, gotoPage }) => (
<Stack overflow="auto" data-cy="detail">
<Typography color="text.primary" noWrap mb={1}>
{`#${vnTemplate.ID} | ${vnTemplate.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${vnTemplate.ID} | ${vnTemplate.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<VNetworkTemplateTabs id={vnTemplate.ID} />
</Stack>
))
InfoTabs.propTypes = { vnTemplate: PropTypes.object.isRequired }
InfoTabs.propTypes = {
vnTemplate: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -82,13 +100,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,7 +15,8 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import { Row } from 'react-table'
import { VmsTable } from 'client/components/Tables'
@ -23,6 +24,8 @@ import VmActions from 'client/components/Tables/Vms/actions'
import VmTabs from 'client/components/Tabs/Vm'
import SplitPane from 'client/components/SplitPane'
import MultipleTags from 'client/components/MultipleTags'
import { Tr } from 'client/components/HOC'
import { T, VM } from 'client/constants'
/**
* Displays a list of VMs with a split pane between the list and selected row(s).
@ -52,7 +55,10 @@ function VirtualMachines() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs vm={selectedRows[0]?.original} />
<InfoTabs
vm={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -65,19 +71,31 @@ function VirtualMachines() {
/**
* Displays details of a VM.
*
* @param {object} vm - VM to display
* @param {VM} vm - VM to display
* @param {Function} [gotoPage] - Function to navigate to a page of a VM
* @returns {ReactElement} VM details
*/
const InfoTabs = memo(({ vm }) => (
const InfoTabs = memo(({ vm, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${vm.ID} | ${vm.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${vm.ID} | ${vm.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<VmTabs id={vm.ID} />
</Stack>
))
InfoTabs.propTypes = { vm: PropTypes.object.isRequired }
InfoTabs.propTypes = {
vm: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -87,13 +105,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,13 +15,16 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } 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'
import { Tr } from 'client/components/HOC'
import { T, VirtualNetwork } from 'client/constants'
/**
* Displays a list of Virtual Networks with a split pane between the list and selected row(s).
@ -47,7 +50,10 @@ function VirtualNetworks() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs vnet={selectedRows[0]?.original} />
<InfoTabs
vnet={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -60,19 +66,31 @@ function VirtualNetworks() {
/**
* Displays details of a Virtual Network.
*
* @param {object} vnet - Virtual Network to display
* @param {VirtualNetwork} vnet - Virtual Network to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Virtual Network
* @returns {ReactElement} Virtual Network details
*/
const InfoTabs = memo(({ vnet }) => (
const InfoTabs = memo(({ vnet, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${vnet.ID} | ${vnet.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${vnet.ID} | ${vnet.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<VNetworkTabs id={vnet.ID} />
</Stack>
))
InfoTabs.propTypes = { vnet: PropTypes.object.isRequired }
InfoTabs.propTypes = {
vnet: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -82,13 +100,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,7 +15,8 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import { Row } from 'react-table'
import { VmTemplatesTable } from 'client/components/Tables'
@ -23,6 +24,8 @@ import VmTemplateActions from 'client/components/Tables/VmTemplates/actions'
import VmTemplateTabs from 'client/components/Tabs/VmTemplate'
import SplitPane from 'client/components/SplitPane'
import MultipleTags from 'client/components/MultipleTags'
import { Tr } from 'client/components/HOC'
import { T, VmTemplate } from 'client/constants'
/**
* Displays a list of VM Templates with a split pane between the list and selected row(s).
@ -52,7 +55,10 @@ function VmTemplates() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs vmTemplate={selectedRows[0]?.original} />
<InfoTabs
vmTemplate={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -65,19 +71,31 @@ function VmTemplates() {
/**
* Displays details of a VM Template.
*
* @param {object} vmTemplate - VM Template to display
* @param {VmTemplate} vmTemplate - VM Template to display
* @param {Function} [gotoPage] - Function to navigate to a page of a VM Template
* @returns {ReactElement} VM Template details
*/
const InfoTabs = memo(({ vmTemplate }) => (
const InfoTabs = memo(({ vmTemplate, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${vmTemplate.ID} | ${vmTemplate.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${vmTemplate.ID} | ${vmTemplate.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<VmTemplateTabs id={vmTemplate.ID} />
</Stack>
))
InfoTabs.propTypes = { vmTemplate: PropTypes.object.isRequired }
InfoTabs.propTypes = {
vmTemplate: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -87,13 +105,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}

View File

@ -15,13 +15,16 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } 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'
import { Tr } from 'client/components/HOC'
import { T, Zone } from 'client/constants'
/**
* Displays a list of Zones with a split pane between the list and selected row(s).
@ -47,7 +50,10 @@ function Zones() {
{moreThanOneSelected ? (
<GroupedTags tags={selectedRows} />
) : (
<InfoTabs zone={selectedRows[0]?.original} />
<InfoTabs
zone={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
/>
)}
</>
)}
@ -60,19 +66,31 @@ function Zones() {
/**
* Displays details of a Zone.
*
* @param {object} zone - Zone to display
* @param {Zone} zone - Zone to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Zone
* @returns {ReactElement} Zone details
*/
const InfoTabs = memo(({ zone }) => (
const InfoTabs = memo(({ zone, gotoPage }) => (
<Stack overflow="auto">
<Typography color="text.primary" noWrap mb={1}>
{`#${zone.ID} | ${zone.NAME}`}
</Typography>
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${zone.ID} | ${zone.NAME}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
)}
</Stack>
<ZoneTabs id={zone.ID} />
</Stack>
))
InfoTabs.propTypes = { zone: PropTypes.object.isRequired }
InfoTabs.propTypes = {
zone: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'
/**
@ -82,13 +100,14 @@ InfoTabs.displayName = 'InfoTabs'
* @returns {ReactElement} List of tags
*/
const GroupedTags = memo(({ tags = [] }) => (
<Stack direction="row" flexWrap="wrap" gap={1}>
<Stack direction="row" flexWrap="wrap" gap={1} alignContent="flex-start">
<MultipleTags
limitTags={10}
tags={tags?.map(({ original, id, toggleRowSelected }) => (
tags={tags?.map(({ original, id, toggleRowSelected, gotoPage }) => (
<Chip
key={id}
label={original?.NAME ?? id}
onClick={gotoPage}
onDelete={() => toggleRowSelected(false)}
/>
))}