30
src/common/no-objects.js
Normal file
30
src/common/no-objects.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from 'react'
|
||||
import { isEmpty } from 'lodash'
|
||||
|
||||
import propTypes from './prop-types-decorator'
|
||||
|
||||
// This component returns :
|
||||
// - A loading icon when the objects are not fetched
|
||||
// - A default message if the objects are fetched and the collection is empty
|
||||
// - The children if the objects are fetched and the collection is not empty
|
||||
//
|
||||
// ```js
|
||||
// <NoObjects collection={collection} emptyMessage={message}>
|
||||
// {children}
|
||||
// </NoObjects>
|
||||
// ````
|
||||
const NoObjects = ({ children, collection, emptyMessage }) => collection == null
|
||||
? <img src='assets/loading.svg' alt='loading' />
|
||||
: isEmpty(collection)
|
||||
? <p>{emptyMessage}</p>
|
||||
: <div>{children}</div>
|
||||
|
||||
propTypes(NoObjects)({
|
||||
children: propTypes.node.isRequired,
|
||||
collection: propTypes.oneOfType([
|
||||
propTypes.array,
|
||||
propTypes.object
|
||||
]).isRequired,
|
||||
emptyMessage: propTypes.node.isRequired
|
||||
})
|
||||
export default NoObjects
|
||||
@@ -2,15 +2,10 @@ import _ from 'intl'
|
||||
import ActionRowButton from 'action-row-button'
|
||||
import ButtonGroup from 'button-group'
|
||||
import Component from 'base-component'
|
||||
import filter from 'lodash/filter'
|
||||
import find from 'lodash/find'
|
||||
import forEach from 'lodash/forEach'
|
||||
import get from 'lodash/get'
|
||||
import Icon from 'icon'
|
||||
import Link from 'link'
|
||||
import LogList from '../../logs'
|
||||
import map from 'lodash/map'
|
||||
import orderBy from 'lodash/orderBy'
|
||||
import NoObjects from 'no-objects'
|
||||
import React from 'react'
|
||||
import SortedTable from 'sorted-table'
|
||||
import StateButton from 'state-button'
|
||||
@@ -22,6 +17,14 @@ import {
|
||||
CardHeader,
|
||||
CardBlock
|
||||
} from 'card'
|
||||
import {
|
||||
filter,
|
||||
find,
|
||||
forEach,
|
||||
get,
|
||||
map,
|
||||
orderBy
|
||||
} from 'lodash'
|
||||
import {
|
||||
deleteBackupSchedule,
|
||||
disableSchedule,
|
||||
@@ -122,7 +125,6 @@ export default class Overview extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
schedules: [],
|
||||
scheduleTable: {}
|
||||
}
|
||||
}
|
||||
@@ -215,9 +217,9 @@ export default class Overview extends Component {
|
||||
<Icon icon='schedule' /> {_('backupSchedules')}
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
{schedules.length ? (
|
||||
<NoObjects collection={schedules} emptyMessage={_('noScheduledJobs')}>
|
||||
<SortedTable columns={JOB_COLUMNS} collection={this._getScheduleCollection()} userData={isScheduleUserMissing} />
|
||||
) : <p>{_('noScheduledJobs')}</p>}
|
||||
</NoObjects>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
<LogList jobKeys={Object.keys(jobKeyToLabel)} />
|
||||
|
||||
@@ -3,17 +3,18 @@ import ActionRowButton from 'action-row-button'
|
||||
import Component from 'base-component'
|
||||
import Icon from 'icon'
|
||||
import Link from 'link'
|
||||
import NoObjects from 'no-objects'
|
||||
import React from 'react'
|
||||
import SortedTable from 'sorted-table'
|
||||
import TabButton from 'tab-button'
|
||||
import Tooltip from 'tooltip'
|
||||
import Upgrade from 'xoa-upgrade'
|
||||
import React from 'react'
|
||||
import xml2js from 'xml2js'
|
||||
import { Card, CardHeader, CardBlock } from 'card'
|
||||
import { confirm } from 'modal'
|
||||
import { Container, Row, Col } from 'grid'
|
||||
import { FormattedRelative, FormattedTime } from 'react-intl'
|
||||
import { fromCallback } from 'promise-toolbox'
|
||||
import { Container, Row, Col } from 'grid'
|
||||
import {
|
||||
deleteMessage,
|
||||
deleteOrphanedVdis,
|
||||
@@ -23,18 +24,18 @@ import {
|
||||
isSrWritable
|
||||
} from 'xo'
|
||||
import {
|
||||
flatten,
|
||||
get,
|
||||
isEmpty,
|
||||
map,
|
||||
mapValues
|
||||
} from 'lodash'
|
||||
import {
|
||||
areObjectsFetched,
|
||||
createCollectionWrapper,
|
||||
createGetObject,
|
||||
createGetObjectsOfType,
|
||||
createSelector
|
||||
} from 'selectors'
|
||||
import {
|
||||
flatten,
|
||||
get,
|
||||
map,
|
||||
mapValues
|
||||
} from 'lodash'
|
||||
import {
|
||||
connectStore,
|
||||
formatSize,
|
||||
@@ -352,6 +353,7 @@ const ALARM_COLUMNS = [
|
||||
.filter([ message => message.name === 'ALARM' ])
|
||||
|
||||
return {
|
||||
areObjectsFetched,
|
||||
alertMessages: getAlertMessages,
|
||||
controlDomainVdis: getControlDomainVdis,
|
||||
userSrs: getUserSrs,
|
||||
@@ -428,6 +430,8 @@ export default class Health extends Component {
|
||||
_getSrUrl = sr => `srs/${sr.id}`
|
||||
|
||||
render () {
|
||||
const { props } = this
|
||||
|
||||
return process.env.XOA_PLAN > 3
|
||||
? <Container>
|
||||
<Row>
|
||||
@@ -437,18 +441,20 @@ export default class Health extends Component {
|
||||
<Icon icon='disk' /> {_('srStatePanel')}
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
{isEmpty(this.props.userSrs)
|
||||
? <p className='text-xs-center'>{_('noSrs')}</p>
|
||||
: <Row>
|
||||
<NoObjects
|
||||
collection={props.areObjectsFetched ? props.userSrs : null}
|
||||
emptyMessage={_('noSrs')}
|
||||
>
|
||||
<Row>
|
||||
<Col>
|
||||
<SortedTable
|
||||
collection={this.props.userSrs}
|
||||
collection={props.userSrs}
|
||||
columns={SR_COLUMNS}
|
||||
rowLink={this._getSrUrl}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
}
|
||||
</NoObjects>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
@@ -460,9 +466,11 @@ export default class Health extends Component {
|
||||
<Icon icon='disk' /> {_('orphanedVdis')}
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
{isEmpty(this.props.vdiOrphaned)
|
||||
? <p className='text-xs-center'>{_('noOrphanedObject')}</p>
|
||||
: <div>
|
||||
<NoObjects
|
||||
collection={props.areObjectsFetched ? props.vdiOrphaned : null}
|
||||
emptyMessage={_('noOrphanedObject')}
|
||||
>
|
||||
<div>
|
||||
<Row>
|
||||
<Col className='text-xs-right'>
|
||||
<TabButton
|
||||
@@ -479,7 +487,7 @@ export default class Health extends Component {
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
}
|
||||
</NoObjects>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
@@ -491,10 +499,12 @@ export default class Health extends Component {
|
||||
<Icon icon='disk' /> {_('vdisOnControlDomain')}
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
{isEmpty(this.props.controlDomainVdis)
|
||||
? <p className='text-xs-center'>{_('noControlDomainVdis')}</p>
|
||||
: <SortedTable collection={this.props.controlDomainVdis} columns={CONTROL_DOMAIN_VDI_COLUMNS} />
|
||||
}
|
||||
<NoObjects
|
||||
collection={props.areObjectsFetched ? props.controlDomainVdis : null}
|
||||
emptyMessage={_('noControlDomainVdis')}
|
||||
>
|
||||
<SortedTable collection={props.controlDomainVdis} columns={CONTROL_DOMAIN_VDI_COLUMNS} />
|
||||
</NoObjects>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
@@ -506,10 +516,12 @@ export default class Health extends Component {
|
||||
<Icon icon='vm' /> {_('orphanedVms')}
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
{isEmpty(this.props.vmOrphaned)
|
||||
? <p className='text-xs-center'>{_('noOrphanedObject')}</p>
|
||||
: <SortedTable collection={this.props.vmOrphaned} columns={VM_COLUMNS} />
|
||||
}
|
||||
<NoObjects
|
||||
collection={props.areObjectsFetched ? props.vmOrphaned : null}
|
||||
emptyMessage={_('noOrphanedObject')}
|
||||
>
|
||||
<SortedTable collection={props.vmOrphaned} columns={VM_COLUMNS} />
|
||||
</NoObjects>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
@@ -521,9 +533,11 @@ export default class Health extends Component {
|
||||
<Icon icon='alarm' /> {_('alarmMessage')}
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
{isEmpty(this.props.alertMessages)
|
||||
? <p className='text-xs-center'>{_('noAlarms')}</p>
|
||||
: <div>
|
||||
<NoObjects
|
||||
collection={props.areObjectsFetched ? props.alertMessages : null}
|
||||
emptyMessage={_('noAlarms')}
|
||||
>
|
||||
<div>
|
||||
<Row>
|
||||
<Col className='text-xs-right'>
|
||||
<TabButton
|
||||
@@ -540,7 +554,7 @@ export default class Health extends Component {
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
}
|
||||
</NoObjects>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
@@ -4,12 +4,8 @@ import ActionRowButton from 'action-row-button'
|
||||
import BaseComponent from 'base-component'
|
||||
import ButtonGroup from 'button-group'
|
||||
import classnames from 'classnames'
|
||||
import forEach from 'lodash/forEach'
|
||||
import get from 'lodash/get'
|
||||
import Icon from 'icon'
|
||||
import includes from 'lodash/includes'
|
||||
import map from 'lodash/map'
|
||||
import orderBy from 'lodash/orderBy'
|
||||
import NoObjects from 'no-objects'
|
||||
import propTypes from 'prop-types-decorator'
|
||||
import React, { Component } from 'react'
|
||||
import renderXoItem from 'render-xo-item'
|
||||
@@ -28,7 +24,14 @@ import {
|
||||
CardHeader,
|
||||
CardBlock
|
||||
} from 'card'
|
||||
|
||||
import {
|
||||
forEach,
|
||||
get,
|
||||
includes,
|
||||
isEmpty,
|
||||
map,
|
||||
orderBy
|
||||
} from 'lodash'
|
||||
import {
|
||||
deleteJobsLog,
|
||||
subscribeJobsLogs
|
||||
@@ -234,7 +237,6 @@ export default class LogList extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
logs: [],
|
||||
logsToClear: []
|
||||
}
|
||||
this.filters = {
|
||||
@@ -318,18 +320,20 @@ export default class LogList extends Component {
|
||||
}).then(() => deleteJobsLog(this.state.logsToClear))
|
||||
}
|
||||
|
||||
_getPredicate = logs => logs != null
|
||||
|
||||
render () {
|
||||
const { logs } = this.state
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Icon icon='log' /> Logs<span className='pull-right'><ActionButton disabled={!logs.length} btnStyle='danger' handler={this._deleteAllLogs} icon='delete' /></span>
|
||||
<Icon icon='log' /> Logs<span className='pull-right'><ActionButton disabled={isEmpty(logs)} btnStyle='danger' handler={this._deleteAllLogs} icon='delete' /></span>
|
||||
</CardHeader>
|
||||
<CardBlock>
|
||||
{logs.length
|
||||
? <SortedTable collection={logs} columns={LOG_COLUMNS} filters={this.filters} />
|
||||
: <p>{_('noLogs')}</p>}
|
||||
<NoObjects collection={logs} emptyMessage={_('noLogs')}>
|
||||
<SortedTable collection={logs} columns={LOG_COLUMNS} filters={this.filters} />
|
||||
</NoObjects>
|
||||
</CardBlock>
|
||||
</Card>
|
||||
)
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import React from 'react'
|
||||
import { FormattedDate } from 'react-intl'
|
||||
import { find, map } from 'lodash'
|
||||
|
||||
import _ from 'intl'
|
||||
import ActionRowButton from 'action-row-button'
|
||||
import BaseComponent from 'base-component'
|
||||
import ButtonGroup from 'button-group'
|
||||
import Copiable from 'copiable'
|
||||
import find from 'lodash/find'
|
||||
import isEmpty from 'lodash/isEmpty'
|
||||
import map from 'lodash/map'
|
||||
import React from 'react'
|
||||
import NoObjects from 'no-objects'
|
||||
import SortedTable from 'sorted-table'
|
||||
import styles from './index.css'
|
||||
import TabButton from 'tab-button'
|
||||
import { addSubscriptions } from 'utils'
|
||||
import { alert, confirm } from 'modal'
|
||||
import { createSelector } from 'selectors'
|
||||
import { FormattedDate } from 'react-intl'
|
||||
import { subscribeApiLogs, subscribeUsers, deleteApiLog } from 'xo'
|
||||
|
||||
const CAN_REPORT_BUG = process.env.XOA_PLAN > 1
|
||||
@@ -99,7 +99,7 @@ export default class Logs extends BaseComponent {
|
||||
|
||||
_getLogs = createSelector(
|
||||
() => this.props.logs,
|
||||
logs => map(logs, (log, id) => ({ ...log, id }))
|
||||
logs => logs && map(logs, (log, id) => ({ ...log, id }))
|
||||
)
|
||||
|
||||
_showError = log => alert(
|
||||
@@ -115,29 +115,28 @@ export default class Logs extends BaseComponent {
|
||||
(users, showError) => ({ users, showError })
|
||||
)
|
||||
|
||||
_getPredicate = logs => logs != null
|
||||
|
||||
render () {
|
||||
const logs = this._getLogs()
|
||||
|
||||
return <div>
|
||||
{isEmpty(logs)
|
||||
? <p>{_('noLogs')}</p>
|
||||
: <div>
|
||||
<span className='pull-right'>
|
||||
<TabButton
|
||||
btnStyle='danger'
|
||||
handler={this._deleteAllLogs}
|
||||
icon='delete'
|
||||
labelId='logDeleteAll'
|
||||
/>
|
||||
</span>
|
||||
{' '}
|
||||
<SortedTable
|
||||
collection={logs}
|
||||
columns={COLUMNS}
|
||||
userData={this._getData()}
|
||||
return <NoObjects collection={logs} message={_('noLogs')} predicate={this._getPredicate}>
|
||||
<div>
|
||||
<span className='pull-right'>
|
||||
<TabButton
|
||||
btnStyle='danger'
|
||||
handler={this._deleteAllLogs}
|
||||
icon='delete'
|
||||
labelId='logDeleteAll'
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</span>
|
||||
{' '}
|
||||
<SortedTable
|
||||
collection={logs}
|
||||
columns={COLUMNS}
|
||||
userData={this._getData()}
|
||||
/>
|
||||
</div>
|
||||
</NoObjects>
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user