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

M #~: Minor fixes to Fireedge Sunstone (#2224)

(cherry picked from commit 75c5af9c7bf0c6a19444503916cf161da0764b7e)
This commit is contained in:
Sergio Betanzos 2022-07-07 17:35:15 +02:00 committed by Tino Vazquez
parent 11dbb1e418
commit 7b9b4711b0
24 changed files with 640 additions and 240 deletions

View File

@ -32,10 +32,12 @@ import { Translate } from 'client/components/HOC'
import { T, SCHEMES } from 'client/constants'
const StepperStyled = styled(Stepper)(({ theme }) => ({
backdropFilter: 'blur(3px)',
position: 'sticky',
top: -15,
minHeight: 100,
zIndex: theme.zIndex.mobileStepper,
backgroundColor: theme.palette.action.hover,
}))
const ConnectorStyled = styled(StepConnector)(({ theme }) => ({
@ -75,6 +77,16 @@ const StepIconStyled = styled(StepIcon)(({ theme }) => ({
},
}))
const ButtonsWrapper = styled(Box)(({ theme }) => ({
padding: '1em 0.5em',
textAlign: 'end',
backdropFilter: 'blur(3px)',
position: 'sticky',
top: 85,
zIndex: theme.zIndex.mobileStepper + 1,
backgroundColor: theme.palette.action.hover,
}))
const CustomStepper = ({
steps,
activeStep,
@ -115,7 +127,7 @@ const CustomStepper = ({
</Step>
))}
</StepperStyled>
<Box marginY={2} textAlign="end">
<ButtonsWrapper>
<Button
data-cy="stepper-back-button"
disabled={disabledBack || isSubmitting}
@ -132,7 +144,7 @@ const CustomStepper = ({
size="small"
label={<Translate word={activeStep === lastStep ? T.Finish : T.Next} />}
/>
</Box>
</ButtonsWrapper>
</>
)

View File

@ -28,10 +28,10 @@ const ENFORCE = {
const MEMORY = {
name: 'MEMORY',
label: T.Memory,
label: [T.MemoryWithUnit, '(MB)'],
tooltip: T.MemoryConcept,
type: INPUT_TYPES.TEXT,
htmlType: 'number',
tooltip: T.MemoryConcept,
validation: number()
.required()
.positive()
@ -40,10 +40,10 @@ const MEMORY = {
const PHYSICAL_CPU = {
name: 'CPU',
label: T.PhysicalCpu,
label: T.PhysicalCpuWithPercent,
tooltip: T.CpuConcept,
type: INPUT_TYPES.TEXT,
htmlType: 'number',
tooltip: T.CpuConcept,
validation: number()
.required()
.positive()
@ -52,10 +52,10 @@ const PHYSICAL_CPU = {
const VIRTUAL_CPU = {
name: 'VCPU',
label: T.VirtualCpu,
label: T.VirtualCpuWithPercent,
tooltip: T.VirtualCpuConcept,
type: INPUT_TYPES.TEXT,
htmlType: 'number',
tooltip: T.VirtualCpuConcept,
validation: number().default(() => undefined),
}

View File

@ -37,7 +37,7 @@ const commonValidation = number()
/** @type {Field} Memory field */
export const MEMORY = generateCapacityInput({
name: 'MEMORY',
label: T.Memory,
label: [T.MemoryWithUnit, '(MB)'],
tooltip: T.MemoryConcept,
validation: commonValidation
.integer()
@ -70,7 +70,7 @@ export const MEMORY_FIELDS = [MEMORY, ...HR_MEMORY_FIELDS, ...MOD_MEMORY_FIELDS]
/** @type {Field} Physical CPU field */
export const PHYSICAL_CPU = generateCapacityInput({
name: 'CPU',
label: T.PhysicalCpu,
label: T.PhysicalCpuWithPercent,
tooltip: T.CpuConcept,
validation: commonValidation.required(),
})
@ -88,7 +88,7 @@ export const CPU_FIELDS = [PHYSICAL_CPU, ...MOD_CPU_FIELDS]
/** @type {Field} Virtual CPU field */
export const VIRTUAL_CPU = generateCapacityInput({
name: 'VCPU',
label: T.VirtualCpu,
label: T.VirtualCpuWithPercent,
tooltip: T.VirtualCpuConcept,
validation: commonValidation,
})

View File

@ -267,6 +267,7 @@ export const generateCapacityInput = ({ validation, ...field }) => ({
modificationType === list ? schema.oneOf(options) : schema
),
grid: { md: 3 },
fieldProps: { min: 0 },
})
/**

View File

@ -34,9 +34,17 @@ import {
const { number, numberFloat, range, rangeFloat } = USER_INPUT_TYPES
const TRANSLATES = {
MEMORY: { name: 'MEMORY', label: T.Memory, tooltip: T.MemoryConcept },
CPU: { name: 'CPU', label: T.PhysicalCpu, tooltip: T.CpuConcept },
VCPU: { name: 'VCPU', label: T.VirtualCpu, tooltip: T.VirtualCpuConcept },
MEMORY: {
name: 'MEMORY',
label: [T.MemoryWithUnit, '(MB)'],
tooltip: T.MemoryConcept,
},
CPU: { name: 'CPU', label: T.PhysicalCpuWithPercent, tooltip: T.CpuConcept },
VCPU: {
name: 'VCPU',
label: T.VirtualCpuWithPercent,
tooltip: T.VirtualCpuConcept,
},
}
const valueLabelFormat = (value) => prettyBytes(value, 'MB')

View File

@ -20,7 +20,7 @@ import PropTypes from 'prop-types'
import clsx from 'clsx'
import InfoEmpty from 'iconoir-react/dist/InfoEmpty'
import RemoveIcon from 'iconoir-react/dist/RemoveSquare'
import { Box } from '@mui/material'
import { Box, Chip } from '@mui/material'
import {
useGlobalFilter,
useFilters,
@ -184,6 +184,8 @@ const EnhancedTable = ({
[disableGlobalLabel]
)
const canResetFilter = state.filters?.length > 0 || state.sortBy?.length > 0
return (
<Box
{...getTableProps()}
@ -244,18 +246,17 @@ const EnhancedTable = ({
</div>
{/* RESET FILTERS */}
<Box
visibility={
state.filters?.length > 0 || state.sortBy?.length > 0
? 'visible'
: 'hidden'
}
>
<span className={styles.resetFilters} onClick={handleResetFilters}>
<RemoveIcon />
<Translate word={T.ResetFilters} />
</span>
</Box>
<Chip
label={<Translate word={T.ResetFilters} />}
onClick={canResetFilter ? handleResetFilters : undefined}
icon={<RemoveIcon />}
sx={{
visibility: canResetFilter ? 'visible' : 'hidden',
width: 'fit-content',
padding: '0.75em',
marginBottom: '0.5em',
}}
/>
<div className={clsx(styles.body, classes.body)}>
{/* NO DATA MESSAGE */}

View File

@ -55,16 +55,6 @@ export default makeStyles(({ palette, typography, breakpoints }) => ({
justifySelf: 'end',
gap: '1em',
},
resetFilters: {
display: 'flex',
alignItems: 'center',
gap: '0.5em',
cursor: 'pointer',
marginBottom: '1em',
'&:hover': {
color: palette.secondary.dark,
},
},
body: {
overflow: 'auto',
display: 'grid',

View File

@ -16,14 +16,12 @@
import { memo } from 'react'
import PropTypes from 'prop-types'
import {
Trash,
Edit,
UndoAction,
SaveActionFloppy,
Camera,
Expand,
} from 'iconoir-react'
import Trash from 'iconoir-react/dist/Trash'
import Edit from 'iconoir-react/dist/Edit'
import UndoAction from 'iconoir-react/dist/UndoAction'
import SaveActionFloppy from 'iconoir-react/dist/SaveActionFloppy'
import Camera from 'iconoir-react/dist/Camera'
import Expand from 'iconoir-react/dist/ExpandLines'
import {
useAttachDiskMutation,

View File

@ -465,11 +465,14 @@ module.exports = {
/* VM schema - capacity */
Capacity: 'Capacity',
PhysicalCpu: 'Physical CPU',
PhysicalCpuWithPercent: 'Physical CPU (%)',
VirtualCpu: 'Virtual CPU',
VirtualCpuWithPercent: 'Virtual CPU (%)',
VirtualCores: 'Virtual Cores',
Cores: 'Cores',
Sockets: 'Sockets',
Memory: 'Memory',
MemoryWithUnit: 'Memory %s',
Cost: 'Cost',
CostEachMonth: '%s / month',
CostCpu: 'Cost / CPU',

View File

@ -15,14 +15,21 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import {
useLazyGetClustersQuery,
useUpdateClusterMutation,
} from 'client/features/OneApi/cluster'
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 { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T, Cluster } from 'client/constants'
@ -41,7 +48,10 @@ function Clusters() {
<SplitPane gridTemplateRows="1fr auto 1fr">
{({ getGridProps, GutterComponent }) => (
<Box height={1} {...(hasSelectedRows && getGridProps())}>
<ClustersTable onSelectedRowsChange={onSelectedRowsChange} />
<ClustersTable
onSelectedRowsChange={onSelectedRowsChange}
useUpdateMutation={useUpdateClusterMutation}
/>
{hasSelectedRows && (
<>
@ -52,6 +62,7 @@ function Clusters() {
<InfoTabs
cluster={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
/>
)}
</>
@ -67,27 +78,56 @@ function Clusters() {
*
* @param {Cluster} cluster - Cluster to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Cluster
* @param {Function} [unselect] - Function to unselect a Cluster
* @returns {ReactElement} Cluster details
*/
const InfoTabs = memo(({ cluster, gotoPage }) => (
<Stack overflow="auto">
<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>
)}
const InfoTabs = memo(({ cluster, gotoPage, unselect }) => {
const [get, { data: lazyData, isFetching }] = useLazyGetClustersQuery()
const id = lazyData?.ID ?? cluster.ID
const name = lazyData?.NAME ?? cluster.NAME
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
data-cy="locate-on-table"
icon={<GotoIcon />}
tooltip={Tr(T.LocateOnTable)}
onClick={() => gotoPage()}
/>
)}
{typeof unselect === 'function' && (
<SubmitButton
data-cy="unselect"
icon={<Cancel />}
tooltip={Tr(T.Close)}
onClick={() => unselect()}
/>
)}
{/* -- END ACTIONS -- */}
</Stack>
<ClusterTabs id={id} />
</Stack>
<ClusterTabs id={cluster.ID} />
</Stack>
))
)
})
InfoTabs.propTypes = {
cluster: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
unselect: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'

View File

@ -15,14 +15,21 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import {
useLazyGetDatastoreQuery,
useUpdateDatastoreMutation,
} from 'client/features/OneApi/datastore'
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 { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T, Datastore } from 'client/constants'
@ -41,7 +48,10 @@ function Datastores() {
<SplitPane gridTemplateRows="1fr auto 1fr">
{({ getGridProps, GutterComponent }) => (
<Box height={1} {...(hasSelectedRows && getGridProps())}>
<DatastoresTable onSelectedRowsChange={onSelectedRowsChange} />
<DatastoresTable
onSelectedRowsChange={onSelectedRowsChange}
useUpdateMutation={useUpdateDatastoreMutation}
/>
{hasSelectedRows && (
<>
@ -52,6 +62,7 @@ function Datastores() {
<InfoTabs
datastore={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
/>
)}
</>
@ -67,27 +78,56 @@ function Datastores() {
*
* @param {Datastore} datastore - Datastore to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Datastore
* @param {Function} [unselect] - Function to unselect a Datastore
* @returns {ReactElement} Datastore details
*/
const InfoTabs = memo(({ datastore, gotoPage }) => (
<Stack overflow="auto">
<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>
)}
const InfoTabs = memo(({ datastore, gotoPage, unselect }) => {
const [get, { data: lazyData, isFetching }] = useLazyGetDatastoreQuery()
const id = lazyData?.ID ?? datastore.ID
const name = lazyData?.NAME ?? datastore.NAME
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
data-cy="locate-on-table"
icon={<GotoIcon />}
tooltip={Tr(T.LocateOnTable)}
onClick={() => gotoPage()}
/>
)}
{typeof unselect === 'function' && (
<SubmitButton
data-cy="unselect"
icon={<Cancel />}
tooltip={Tr(T.Close)}
onClick={() => unselect()}
/>
)}
{/* -- END ACTIONS -- */}
</Stack>
<DatastoreTabs id={id} />
</Stack>
<DatastoreTabs id={datastore.ID} />
</Stack>
))
)
})
InfoTabs.propTypes = {
datastore: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
unselect: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'

View File

@ -15,14 +15,21 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import {
useLazyGetGroupQuery,
useUpdateGroupMutation,
} from 'client/features/OneApi/group'
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 { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T, Group } from 'client/constants'
@ -41,7 +48,10 @@ function Groups() {
<SplitPane gridTemplateRows="1fr auto 1fr">
{({ getGridProps, GutterComponent }) => (
<Box height={1} {...(hasSelectedRows && getGridProps())}>
<GroupsTable onSelectedRowsChange={onSelectedRowsChange} />
<GroupsTable
onSelectedRowsChange={onSelectedRowsChange}
useUpdateMutation={useUpdateGroupMutation}
/>
{hasSelectedRows && (
<>
@ -52,6 +62,7 @@ function Groups() {
<InfoTabs
group={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
/>
)}
</>
@ -67,27 +78,56 @@ function Groups() {
*
* @param {Group} group - Group to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Group
* @param {Function} [unselect] - Function to unselect a Group
* @returns {ReactElement} Group details
*/
const InfoTabs = memo(({ group, gotoPage }) => (
<Stack overflow="auto">
<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>
)}
const InfoTabs = memo(({ group, gotoPage, unselect }) => {
const [get, { data: lazyData, isFetching }] = useLazyGetGroupQuery()
const id = lazyData?.ID ?? group.ID
const name = lazyData?.NAME ?? group.NAME
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
data-cy="locate-on-table"
icon={<GotoIcon />}
tooltip={Tr(T.LocateOnTable)}
onClick={() => gotoPage()}
/>
)}
{typeof unselect === 'function' && (
<SubmitButton
data-cy="unselect"
icon={<Cancel />}
tooltip={Tr(T.Close)}
onClick={() => unselect()}
/>
)}
{/* -- END ACTIONS -- */}
</Stack>
<GroupTabs id={id} />
</Stack>
<GroupTabs id={group.ID} />
</Stack>
))
)
})
InfoTabs.propTypes = {
group: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
unselect: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'

View File

@ -91,7 +91,12 @@ const InfoTabs = memo(({ host, gotoPage, unselect }) => {
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
@ -115,9 +120,7 @@ const InfoTabs = memo(({ host, gotoPage, unselect }) => {
onClick={() => unselect()}
/>
)}
<Typography color="text.primary" noWrap>
{`#${id} | ${name}`}
</Typography>
{/* -- END ACTIONS -- */}
</Stack>
<HostTabs id={id} />
</Stack>

View File

@ -15,14 +15,21 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import {
useLazyGetImageQuery,
useUpdateImageMutation,
} from 'client/features/OneApi/image'
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 { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T, Image } from 'client/constants'
@ -41,7 +48,10 @@ function Images() {
<SplitPane gridTemplateRows="1fr auto 1fr">
{({ getGridProps, GutterComponent }) => (
<Box height={1} {...(hasSelectedRows && getGridProps())}>
<ImagesTable onSelectedRowsChange={onSelectedRowsChange} />
<ImagesTable
onSelectedRowsChange={onSelectedRowsChange}
useUpdateMutation={useUpdateImageMutation}
/>
{hasSelectedRows && (
<>
@ -52,6 +62,7 @@ function Images() {
<InfoTabs
image={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
/>
)}
</>
@ -67,27 +78,56 @@ function Images() {
*
* @param {Image} image - Image to display
* @param {Function} [gotoPage] - Function to navigate to a page of an Image
* @param {Function} [unselect] - Function to unselect a Image
* @returns {ReactElement} Image details
*/
const InfoTabs = memo(({ image, gotoPage }) => (
<Stack overflow="auto">
<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>
)}
const InfoTabs = memo(({ image, gotoPage, unselect }) => {
const [get, { data: lazyData, isFetching }] = useLazyGetImageQuery()
const id = lazyData?.ID ?? image.ID
const name = lazyData?.NAME ?? image.NAME
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
data-cy="locate-on-table"
icon={<GotoIcon />}
tooltip={Tr(T.LocateOnTable)}
onClick={() => gotoPage()}
/>
)}
{typeof unselect === 'function' && (
<SubmitButton
data-cy="unselect"
icon={<Cancel />}
tooltip={Tr(T.Close)}
onClick={() => unselect()}
/>
)}
{/* -- END ACTIONS -- */}
</Stack>
<ImageTabs id={id} />
</Stack>
<ImageTabs id={image.ID} />
</Stack>
))
)
})
InfoTabs.propTypes = {
image: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
unselect: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'

View File

@ -85,7 +85,7 @@ function MarketplaceApps() {
* @returns {ReactElement} Marketplace App details
*/
const InfoTabs = memo(({ app, gotoPage, unselect }) => {
const [getApp, queryState] = useLazyGetMarketplaceAppQuery()
const [get, queryState] = useLazyGetMarketplaceAppQuery()
const { data: lazyData, isFetching } = queryState
const id = lazyData?.ID ?? app.ID
@ -93,13 +93,18 @@ const InfoTabs = memo(({ app, gotoPage, unselect }) => {
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => getApp({ id })}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
@ -117,9 +122,7 @@ const InfoTabs = memo(({ app, gotoPage, unselect }) => {
onClick={() => unselect()}
/>
)}
<Typography color="text.primary" noWrap>
{`#${id} | ${name}`}
</Typography>
{/* -- END ACTIONS -- */}
</Stack>
<MarketplaceAppsTabs id={id} />
</Stack>

View File

@ -15,14 +15,21 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import {
useLazyGetMarketplaceQuery,
useUpdateMarketplaceMutation,
} from 'client/features/OneApi/marketplace'
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 { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T, Marketplace } from 'client/constants'
@ -41,7 +48,10 @@ function Marketplaces() {
<SplitPane gridTemplateRows="1fr auto 1fr">
{({ getGridProps, GutterComponent }) => (
<Box height={1} {...(hasSelectedRows && getGridProps())}>
<MarketplacesTable onSelectedRowsChange={onSelectedRowsChange} />
<MarketplacesTable
onSelectedRowsChange={onSelectedRowsChange}
useUpdateMutation={useUpdateMarketplaceMutation}
/>
{hasSelectedRows && (
<>
@ -52,6 +62,7 @@ function Marketplaces() {
<InfoTabs
market={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
/>
)}
</>
@ -67,27 +78,56 @@ function Marketplaces() {
*
* @param {Marketplace} market - Marketplace to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Marketplace
* @param {Function} [unselect] - Function to unselect a Marketplace
* @returns {ReactElement} Marketplace details
*/
const InfoTabs = memo(({ market, gotoPage }) => (
<Stack overflow="auto">
<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>
)}
const InfoTabs = memo(({ market, gotoPage, unselect }) => {
const [get, { data: lazyData, isFetching }] = useLazyGetMarketplaceQuery()
const id = lazyData?.ID ?? market.ID
const name = lazyData?.NAME ?? market.NAME
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
data-cy="locate-on-table"
icon={<GotoIcon />}
tooltip={Tr(T.LocateOnTable)}
onClick={() => gotoPage()}
/>
)}
{typeof unselect === 'function' && (
<SubmitButton
data-cy="unselect"
icon={<Cancel />}
tooltip={Tr(T.Close)}
onClick={() => unselect()}
/>
)}
{/* -- END ACTIONS -- */}
</Stack>
<MarketplaceTabs id={id} />
</Stack>
<MarketplaceTabs id={market.ID} />
</Stack>
))
)
})
InfoTabs.propTypes = {
market: PropTypes.object.isRequired,
gotoPage: PropTypes.func,
unselect: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'

View File

@ -15,15 +15,18 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import serviceTemplateApi from 'client/features/OneApi/serviceTemplate'
import { useLazyGetServiceTemplateQuery } from 'client/features/OneApi/serviceTemplate'
import { ServiceTemplatesTable } from 'client/components/Tables'
import ServiceTemplateTabs from 'client/components/Tabs/ServiceTemplate'
import SplitPane from 'client/components/SplitPane'
import MultipleTags from 'client/components/MultipleTags'
import { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T } from 'client/constants'
@ -54,6 +57,7 @@ function ServiceTemplates() {
<InfoTabs
id={selectedRows[0]?.original?.ID}
gotoPage={selectedRows[0]?.gotoPage}
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
/>
)}
</>
@ -67,28 +71,48 @@ function ServiceTemplates() {
/**
* Displays details of a Service Template.
*
* @param {string} id - Service Template id to display
* @param {object} template - Service Template to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Service Template
* @param {Function} [unselect] - Function to unselect a Service Template
* @returns {ReactElement} Service Template details
*/
const InfoTabs = memo(({ id, gotoPage }) => {
const template =
serviceTemplateApi.endpoints.getServiceTemplates.useQueryState(undefined, {
selectFromResult: ({ data = [] }) =>
data.find((item) => +item.ID === +id),
})
const InfoTabs = memo(({ template, gotoPage, unselect }) => {
const [get, { data: lazyData, isFetching }] = useLazyGetServiceTemplateQuery()
const id = lazyData?.ID ?? template.ID
const name = lazyData?.NAME ?? template.NAME
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${id} | ${template.NAME}`}
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
data-cy="locate-on-table"
icon={<GotoIcon />}
tooltip={Tr(T.LocateOnTable)}
onClick={() => gotoPage()}
/>
)}
{typeof unselect === 'function' && (
<SubmitButton
data-cy="unselect"
icon={<Cancel />}
tooltip={Tr(T.Close)}
onClick={() => unselect()}
/>
)}
{/* -- END ACTIONS -- */}
</Stack>
<ServiceTemplateTabs id={id} />
</Stack>
@ -96,8 +120,9 @@ const InfoTabs = memo(({ id, gotoPage }) => {
})
InfoTabs.propTypes = {
id: PropTypes.string.isRequired,
template: PropTypes.object,
gotoPage: PropTypes.func,
unselect: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'

View File

@ -15,15 +15,18 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import serviceApi from 'client/features/OneApi/service'
import { useLazyGetServiceQuery } from 'client/features/OneApi/service'
import { ServicesTable } from 'client/components/Tables'
import ServiceTabs from 'client/components/Tabs/Service'
import SplitPane from 'client/components/SplitPane'
import MultipleTags from 'client/components/MultipleTags'
import { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T } from 'client/constants'
@ -54,6 +57,7 @@ function Services() {
<InfoTabs
id={selectedRows[0]?.original?.ID}
gotoPage={selectedRows[0]?.gotoPage}
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
/>
)}
</>
@ -67,26 +71,48 @@ function Services() {
/**
* Displays details of a Service.
*
* @param {string} id - Service id to display
* @param {object} service - Service to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Service
* @param {Function} [unselect] - Function to unselect a Service
* @returns {ReactElement} Service details
*/
const InfoTabs = memo(({ id, gotoPage }) => {
const template = serviceApi.endpoints.getServices.useQueryState(undefined, {
selectFromResult: ({ data = [] }) => data.find((item) => +item.ID === +id),
})
const InfoTabs = memo(({ service, gotoPage, unselect }) => {
const [get, { data: lazyData, isFetching }] = useLazyGetServiceQuery()
const id = lazyData?.ID ?? service.ID
const name = lazyData?.NAME ?? service.NAME
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Typography color="text.primary" noWrap>
{`#${id} | ${template.NAME}`}
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{gotoPage && (
<IconButton title={Tr(T.LocateOnTable)} onClick={gotoPage}>
<BookmarkEmpty />
</IconButton>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
data-cy="locate-on-table"
icon={<GotoIcon />}
tooltip={Tr(T.LocateOnTable)}
onClick={() => gotoPage()}
/>
)}
{typeof unselect === 'function' && (
<SubmitButton
data-cy="unselect"
icon={<Cancel />}
tooltip={Tr(T.Close)}
onClick={() => unselect()}
/>
)}
{/* -- END ACTIONS -- */}
</Stack>
<ServiceTabs id={id} />
</Stack>
@ -94,8 +120,9 @@ const InfoTabs = memo(({ id, gotoPage }) => {
})
InfoTabs.propTypes = {
id: PropTypes.string.isRequired,
service: PropTypes.object,
gotoPage: PropTypes.func,
unselect: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'

View File

@ -15,14 +15,21 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import {
useLazyGetUserQuery,
useUpdateUserMutation,
} from 'client/features/OneApi/user'
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 { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T, User } from 'client/constants'
@ -41,7 +48,10 @@ function Users() {
<SplitPane gridTemplateRows="1fr auto 1fr">
{({ getGridProps, GutterComponent }) => (
<Box height={1} {...(hasSelectedRows && getGridProps())}>
<UsersTable onSelectedRowsChange={onSelectedRowsChange} />
<UsersTable
onSelectedRowsChange={onSelectedRowsChange}
useUpdateMutation={useUpdateUserMutation}
/>
{hasSelectedRows && (
<>
@ -52,6 +62,7 @@ function Users() {
<InfoTabs
user={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
/>
)}
</>
@ -67,27 +78,56 @@ function Users() {
*
* @param {User} user - User to display
* @param {Function} [gotoPage] - Function to navigate to a page of an User
* @param {Function} [unselect] - Function to unselect a User
* @returns {ReactElement} User details
*/
const InfoTabs = memo(({ user, gotoPage }) => (
<Stack overflow="auto">
<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>
)}
const InfoTabs = memo(({ user, gotoPage, unselect }) => {
const [get, { data: lazyData, isFetching }] = useLazyGetUserQuery()
const id = lazyData?.ID ?? user.ID
const name = lazyData?.NAME ?? user.NAME
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
data-cy="locate-on-table"
icon={<GotoIcon />}
tooltip={Tr(T.LocateOnTable)}
onClick={() => gotoPage()}
/>
)}
{typeof unselect === 'function' && (
<SubmitButton
data-cy="unselect"
icon={<Cancel />}
tooltip={Tr(T.Close)}
onClick={() => unselect()}
/>
)}
{/* -- END ACTIONS -- */}
</Stack>
<UserTabs id={id} />
</Stack>
<UserTabs id={user.ID} />
</Stack>
))
)
})
InfoTabs.propTypes = {
user: PropTypes.object.isRequired,
user: PropTypes.object,
gotoPage: PropTypes.func,
unselect: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'

View File

@ -15,14 +15,21 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import {
useLazyGetVNTemplateQuery,
useUpdateVNTemplateMutation,
} from 'client/features/OneApi/networkTemplate'
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 { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T, VNetworkTemplate } from 'client/constants'
@ -41,7 +48,10 @@ function VNetworkTemplates() {
<SplitPane gridTemplateRows="1fr auto 1fr">
{({ getGridProps, GutterComponent }) => (
<Box height={1} {...(hasSelectedRows && getGridProps())}>
<VNetworkTemplatesTable onSelectedRowsChange={onSelectedRowsChange} />
<VNetworkTemplatesTable
onSelectedRowsChange={onSelectedRowsChange}
useUpdateMutation={useUpdateVNTemplateMutation}
/>
{hasSelectedRows && (
<>
@ -52,6 +62,7 @@ function VNetworkTemplates() {
<InfoTabs
vnTemplate={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
/>
)}
</>
@ -67,27 +78,56 @@ function VNetworkTemplates() {
*
* @param {VNetworkTemplate} vnTemplate - VNet Template to display
* @param {Function} [gotoPage] - Function to navigate to a page of a VNet Template
* @param {Function} [unselect] - Function to unselect
* @returns {ReactElement} VNet Template details
*/
const InfoTabs = memo(({ vnTemplate, gotoPage }) => (
<Stack overflow="auto" data-cy="detail">
<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>
)}
const InfoTabs = memo(({ vnTemplate, gotoPage, unselect }) => {
const [get, { data: lazyData, isFetching }] = useLazyGetVNTemplateQuery()
const id = lazyData?.ID ?? vnTemplate.ID
const name = lazyData?.NAME ?? vnTemplate.NAME
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
data-cy="locate-on-table"
icon={<GotoIcon />}
tooltip={Tr(T.LocateOnTable)}
onClick={() => gotoPage()}
/>
)}
{typeof unselect === 'function' && (
<SubmitButton
data-cy="unselect"
icon={<Cancel />}
tooltip={Tr(T.Close)}
onClick={() => unselect()}
/>
)}
{/* -- END ACTIONS -- */}
</Stack>
<VNetworkTemplateTabs id={id} />
</Stack>
<VNetworkTemplateTabs id={vnTemplate.ID} />
</Stack>
))
)
})
InfoTabs.propTypes = {
vnTemplate: PropTypes.object.isRequired,
vnTemplate: PropTypes.object,
gotoPage: PropTypes.func,
unselect: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'

View File

@ -91,7 +91,12 @@ const InfoTabs = memo(({ vm, gotoPage, unselect }) => {
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
@ -115,9 +120,7 @@ const InfoTabs = memo(({ vm, gotoPage, unselect }) => {
onClick={() => unselect()}
/>
)}
<Typography color="text.primary" noWrap>
{`#${id} | ${name}`}
</Typography>
{/* -- END ACTIONS -- */}
</Stack>
<VmTabs id={id} />
</Stack>

View File

@ -91,7 +91,12 @@ const InfoTabs = memo(({ vnet, gotoPage, unselect }) => {
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
@ -115,9 +120,7 @@ const InfoTabs = memo(({ vnet, gotoPage, unselect }) => {
onClick={() => unselect()}
/>
)}
<Typography color="text.primary" noWrap>
{`#${id} | ${name}`}
</Typography>
{/* -- END ACTIONS -- */}
</Stack>
<VNetworkTabs id={id} />
</Stack>

View File

@ -89,7 +89,12 @@ const InfoTabs = memo(({ template, gotoPage, unselect }) => {
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mb={1}>
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
@ -113,9 +118,7 @@ const InfoTabs = memo(({ template, gotoPage, unselect }) => {
onClick={() => unselect()}
/>
)}
<Typography color="text.primary" noWrap>
{`#${id} | ${name}`}
</Typography>
{/* -- END ACTIONS -- */}
</Stack>
<VmTemplateTabs id={id} />
</Stack>

View File

@ -15,14 +15,21 @@
* ------------------------------------------------------------------------- */
import { ReactElement, useState, memo } from 'react'
import PropTypes from 'prop-types'
import { BookmarkEmpty } from 'iconoir-react'
import { Typography, Box, Stack, Chip, IconButton } from '@mui/material'
import GotoIcon from 'iconoir-react/dist/Pin'
import RefreshDouble from 'iconoir-react/dist/RefreshDouble'
import Cancel from 'iconoir-react/dist/Cancel'
import { Typography, Box, Stack, Chip } from '@mui/material'
import { Row } from 'react-table'
import {
useLazyGetZoneQuery,
useUpdateZoneMutation,
} from 'client/features/OneApi/zone'
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 { SubmitButton } from 'client/components/FormControl'
import { Tr } from 'client/components/HOC'
import { T, Zone } from 'client/constants'
@ -41,7 +48,10 @@ function Zones() {
<SplitPane gridTemplateRows="1fr auto 1fr">
{({ getGridProps, GutterComponent }) => (
<Box height={1} {...(hasSelectedRows && getGridProps())}>
<ZonesTable onSelectedRowsChange={onSelectedRowsChange} />
<ZonesTable
onSelectedRowsChange={onSelectedRowsChange}
useUpdateMutation={useUpdateZoneMutation}
/>
{hasSelectedRows && (
<>
@ -52,6 +62,7 @@ function Zones() {
<InfoTabs
zone={selectedRows[0]?.original}
gotoPage={selectedRows[0]?.gotoPage}
unselect={() => selectedRows[0]?.toggleRowSelected(false)}
/>
)}
</>
@ -67,27 +78,56 @@ function Zones() {
*
* @param {Zone} zone - Zone to display
* @param {Function} [gotoPage] - Function to navigate to a page of a Zone
* @param {Function} [unselect] - Function to unselect
* @returns {ReactElement} Zone details
*/
const InfoTabs = memo(({ zone, gotoPage }) => (
<Stack overflow="auto">
<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>
)}
const InfoTabs = memo(({ zone, gotoPage, unselect }) => {
const [get, { data: lazyData, isFetching }] = useLazyGetZoneQuery()
const id = lazyData?.ID ?? zone.ID
const name = lazyData?.NAME ?? zone.NAME
return (
<Stack overflow="auto">
<Stack direction="row" alignItems="center" gap={1} mx={1} mb={1}>
<Typography color="text.primary" noWrap flexGrow={1}>
{`#${id} | ${name}`}
</Typography>
{/* -- ACTIONS -- */}
<SubmitButton
data-cy="detail-refresh"
icon={<RefreshDouble />}
tooltip={Tr(T.Refresh)}
isSubmitting={isFetching}
onClick={() => get({ id })}
/>
{typeof gotoPage === 'function' && (
<SubmitButton
data-cy="locate-on-table"
icon={<GotoIcon />}
tooltip={Tr(T.LocateOnTable)}
onClick={() => gotoPage()}
/>
)}
{typeof unselect === 'function' && (
<SubmitButton
data-cy="unselect"
icon={<Cancel />}
tooltip={Tr(T.Close)}
onClick={() => unselect()}
/>
)}
{/* -- END ACTIONS -- */}
</Stack>
<ZoneTabs id={id} />
</Stack>
<ZoneTabs id={zone.ID} />
</Stack>
))
)
})
InfoTabs.propTypes = {
zone: PropTypes.object.isRequired,
zone: PropTypes.object,
gotoPage: PropTypes.func,
unselect: PropTypes.func,
}
InfoTabs.displayName = 'InfoTabs'