mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-16 22:50:10 +03:00
F OpenNebula/one#5422: Add images table
This commit is contained in:
parent
aa83651919
commit
301da35182
11
src/fireedge/src/client/components/Tables/Images/columns.js
Normal file
11
src/fireedge/src/client/components/Tables/Images/columns.js
Normal file
@ -0,0 +1,11 @@
|
||||
export default [
|
||||
{ Header: '', accessor: 'ID' },
|
||||
{ Header: '', accessor: 'NAME' },
|
||||
{ Header: '', accessor: 'UNAME' },
|
||||
{ Header: '', accessor: 'GNAME' },
|
||||
{ Header: '', accessor: 'STATE' },
|
||||
{ Header: '', accessor: 'TYPE' },
|
||||
{ Header: '', accessor: 'DISK_TYPE' },
|
||||
{ Header: '', accessor: 'DATASTORE_ID' },
|
||||
{ Header: '', accessor: 'DATASTORE' }
|
||||
]
|
33
src/fireedge/src/client/components/Tables/Images/index.js
Normal file
33
src/fireedge/src/client/components/Tables/Images/index.js
Normal file
@ -0,0 +1,33 @@
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useFetch } from 'client/hooks'
|
||||
import { useImage, useImageApi } from 'client/features/One'
|
||||
|
||||
import { EnhancedTable } from 'client/components/Tables'
|
||||
import ImageColumns from 'client/components/Tables/Images/columns'
|
||||
import ImageRow from 'client/components/Tables/Images/row'
|
||||
|
||||
const ImagesTable = () => {
|
||||
const columns = React.useMemo(() => ImageColumns, [])
|
||||
|
||||
const images = useImage()
|
||||
const { getImages } = useImageApi()
|
||||
const { filterPool } = useAuth()
|
||||
|
||||
const { fetchRequest, loading, reloading } = useFetch(getImages)
|
||||
|
||||
useEffect(() => { fetchRequest() }, [filterPool])
|
||||
|
||||
return (
|
||||
<EnhancedTable
|
||||
columns={columns}
|
||||
data={images}
|
||||
isLoading={loading || reloading}
|
||||
getRowId={row => String(row.ID)}
|
||||
RowComponent={ImageRow}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default ImagesTable
|
77
src/fireedge/src/client/components/Tables/Images/row.js
Normal file
77
src/fireedge/src/client/components/Tables/Images/row.js
Normal file
@ -0,0 +1,77 @@
|
||||
import * as React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Lock, User, Group, Folder, ModernTv } from 'iconoir-react'
|
||||
import { Typography } from '@material-ui/core'
|
||||
|
||||
import { StatusCircle, StatusChip } from 'client/components/Status'
|
||||
import { rowStyles } from 'client/components/Tables/styles'
|
||||
|
||||
import * as ImageModel from 'client/models/Image'
|
||||
import * as Helper from 'client/models/Helper'
|
||||
|
||||
const Row = ({ value, ...props }) => {
|
||||
const classes = rowStyles()
|
||||
const { ID, NAME, UNAME, GNAME, REGTIME, PERSISTENT, LOCK, DATASTORE, VMS, RUNNING_VMS } = value
|
||||
|
||||
const state = ImageModel.getState(value)
|
||||
const type = ImageModel.getType(value)
|
||||
const diskType = ImageModel.getDiskType(value)
|
||||
const isPersistent = PERSISTENT && 'PERSISTENT'
|
||||
|
||||
const usedByVms = [VMS?.ID ?? []].flat().length || 0
|
||||
|
||||
const labels = [...new Set([isPersistent, type, diskType])].filter(Boolean)
|
||||
|
||||
const time = Helper.timeFromMilliseconds(+REGTIME)
|
||||
const timeAgo = `registered ${time.toRelative()}`
|
||||
|
||||
return (
|
||||
<div {...props}>
|
||||
<div>
|
||||
<StatusCircle color={state?.color} tooltip={state?.name} />
|
||||
</div>
|
||||
<div className={classes.main}>
|
||||
<Typography className={classes.title} component='span'>
|
||||
{NAME}
|
||||
{LOCK && <Lock size={20} />}
|
||||
<span className={classes.labels}>
|
||||
{labels.map(label => (
|
||||
<StatusChip key={label} stateColor={'#c6c6c6'} text={label} />
|
||||
))}
|
||||
</span>
|
||||
</Typography>
|
||||
<div className={classes.caption}>
|
||||
<span title={time.toFormat('ff')}>
|
||||
{`#${ID} ${timeAgo}`}
|
||||
</span>
|
||||
<span>
|
||||
<User size={16} />
|
||||
<span>{` ${UNAME}`}</span>
|
||||
</span>
|
||||
<span>
|
||||
<Group size={16} />
|
||||
<span>{` ${GNAME}`}</span>
|
||||
</span>
|
||||
<span>
|
||||
<Folder size={16} />
|
||||
<span>{` ${DATASTORE}`}</span>
|
||||
</span>
|
||||
<span>
|
||||
<ModernTv size={16} />
|
||||
<span>{` ${RUNNING_VMS} / ${usedByVms}`}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classes.secondary}></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Row.propTypes = {
|
||||
value: PropTypes.object,
|
||||
isSelected: PropTypes.bool,
|
||||
handleClick: PropTypes.func
|
||||
}
|
||||
|
||||
export default Row
|
@ -3,71 +3,41 @@ import * as React from 'react'
|
||||
import { useParams, useHistory } from 'react-router'
|
||||
import { Redirect, Route, Switch, Link } from 'react-router-dom'
|
||||
|
||||
import { withStyles, Container, Tabs, Tab, Box } from '@material-ui/core'
|
||||
import { Container, Tabs, Tab, Box } from '@material-ui/core'
|
||||
|
||||
import {
|
||||
DatastoresTable,
|
||||
HostsTable,
|
||||
VmsTable
|
||||
VmsTable,
|
||||
MarketplacesTable,
|
||||
MarketplaceAppsTable,
|
||||
ImagesTable
|
||||
} from 'client/components/Tables'
|
||||
|
||||
import { PATH } from 'client/router/dev'
|
||||
|
||||
const TABS = {
|
||||
vms: PATH.NEWSTONE.replace(':resource', 'vms'),
|
||||
datastores: PATH.NEWSTONE.replace(':resource', 'datastores'),
|
||||
hosts: PATH.NEWSTONE.replace(':resource', 'hosts')
|
||||
hosts: PATH.NEWSTONE.replace(':resource', 'hosts'),
|
||||
marketplaces: PATH.NEWSTONE.replace(':resource', 'marketplaces'),
|
||||
apps: PATH.NEWSTONE.replace(':resource', 'apps'),
|
||||
images: PATH.NEWSTONE.replace(':resource', 'images')
|
||||
}
|
||||
|
||||
const AntTabs = withStyles(theme => ({
|
||||
root: {
|
||||
borderBottom: '1px solid #e8e8e8'
|
||||
},
|
||||
indicator: {
|
||||
backgroundColor: theme.palette.secondary.main
|
||||
}
|
||||
}))(Tabs)
|
||||
|
||||
const AntTab = withStyles(theme => ({
|
||||
root: {
|
||||
minWidth: 72,
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
marginRight: theme.spacing(4),
|
||||
'&:hover': {
|
||||
color: theme.palette.secondary.light,
|
||||
opacity: 1
|
||||
},
|
||||
'&$selected': {
|
||||
color: theme.palette.secondary.main,
|
||||
fontWeight: theme.typography.fontWeightMedium
|
||||
},
|
||||
'&:focus': {
|
||||
color: theme.palette.secondary.light
|
||||
}
|
||||
},
|
||||
selected: {}
|
||||
}))(props => <Tab disableRipple {...props} />)
|
||||
|
||||
const AntContainer = withStyles({
|
||||
root: {
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
paddingInline: 0
|
||||
}
|
||||
})(Container)
|
||||
|
||||
const Newstone = () => {
|
||||
const history = useHistory()
|
||||
const { resource } = useParams()
|
||||
|
||||
const renderTabs = React.useMemo(() => (
|
||||
<AntTabs
|
||||
<Tabs
|
||||
style={{ borderBottom: '1px solid #e8e8e8' }}
|
||||
value={resource}
|
||||
variant='scrollable'
|
||||
scrollButtons='auto'
|
||||
>
|
||||
{Object.keys(TABS).map(tabName =>
|
||||
<AntTab
|
||||
<Tab
|
||||
key={`tab-${tabName}`}
|
||||
label={tabName}
|
||||
value={tabName}
|
||||
@ -75,23 +45,26 @@ const Newstone = () => {
|
||||
to={tabName}
|
||||
/>
|
||||
)}
|
||||
</AntTabs>
|
||||
</Tabs>
|
||||
), [resource])
|
||||
|
||||
return (
|
||||
<AntContainer>
|
||||
<Container>
|
||||
{Object.values(TABS).includes(history.location.pathname) && renderTabs}
|
||||
|
||||
<Box py={2} overflow='auto'>
|
||||
<Switch>
|
||||
<Route path={TABS.vms} component={VmsTable} />
|
||||
<Route path={TABS.datastores} component={DatastoresTable} />
|
||||
<Route path={TABS.hosts} component={HostsTable} />
|
||||
<Route exact path={TABS.vms} component={VmsTable} />
|
||||
<Route exact path={TABS.datastores} component={DatastoresTable} />
|
||||
<Route exact path={TABS.hosts} component={HostsTable} />
|
||||
<Route exact path={TABS.marketplaces} component={MarketplacesTable} />
|
||||
<Route exact path={TABS.apps} component={MarketplaceAppsTable} />
|
||||
<Route exact path={TABS.images} component={ImagesTable} />
|
||||
|
||||
<Route component={() => <Redirect to={TABS.vms} />} />
|
||||
</Switch>
|
||||
</Box>
|
||||
</AntContainer>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
|
71
src/fireedge/src/client/models/Image.js
Normal file
71
src/fireedge/src/client/models/Image.js
Normal file
@ -0,0 +1,71 @@
|
||||
import * as STATES from 'client/constants/states'
|
||||
import COLOR from 'client/constants/color'
|
||||
|
||||
const IMAGE_TYPES = [
|
||||
'OS',
|
||||
'CD ROM',
|
||||
'DATABLOCK',
|
||||
'KERNEL',
|
||||
'RAMDISK',
|
||||
'CONTEXT'
|
||||
]
|
||||
|
||||
const IMAGE_STATES = [
|
||||
{ // 0
|
||||
name: STATES.INIT,
|
||||
color: COLOR.debug.main
|
||||
},
|
||||
{ // 1
|
||||
name: STATES.READY,
|
||||
color: COLOR.success.main
|
||||
},
|
||||
{ // 2
|
||||
name: STATES.USED,
|
||||
color: COLOR.success.main
|
||||
},
|
||||
{ // 3
|
||||
name: STATES.DISABLED,
|
||||
color: COLOR.debug.light
|
||||
},
|
||||
{ // 4
|
||||
name: STATES.LOCKED,
|
||||
color: COLOR.warning.main
|
||||
},
|
||||
{ // 5
|
||||
name: STATES.ERROR,
|
||||
color: COLOR.error.main
|
||||
},
|
||||
{ // 6
|
||||
name: STATES.CLONE,
|
||||
color: COLOR.info.light
|
||||
},
|
||||
{ // 7
|
||||
name: STATES.DELETE,
|
||||
color: COLOR.error.main
|
||||
},
|
||||
{ // 8
|
||||
name: STATES.USED_PERS,
|
||||
color: COLOR.error.light
|
||||
},
|
||||
{ // 9
|
||||
name: STATES.LOCKED_USED,
|
||||
color: COLOR.warning.light
|
||||
},
|
||||
{ // 10
|
||||
name: STATES.LOCKED_USED_PERS,
|
||||
color: COLOR.error.light
|
||||
}
|
||||
]
|
||||
|
||||
const DISK_TYPES = [
|
||||
'FILE',
|
||||
'CD ROM',
|
||||
'BLOCK',
|
||||
'RBD'
|
||||
]
|
||||
|
||||
export const getType = ({ TYPE } = {}) => IMAGE_TYPES[+TYPE]
|
||||
|
||||
export const getDiskType = ({ DISK_TYPE } = {}) => DISK_TYPES[+DISK_TYPE]
|
||||
|
||||
export const getState = ({ STATE } = {}) => IMAGE_STATES[+STATE]
|
Loading…
x
Reference in New Issue
Block a user