fix: display "loading" while fetching objects (#2294)

Fixes  #2285
This commit is contained in:
badrAZ
2017-08-11 17:47:12 +02:00
committed by Julien Fontanet
parent eaaf70e52e
commit 2ae4ed3999
5 changed files with 125 additions and 76 deletions

30
src/common/no-objects.js Normal file
View 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

View File

@@ -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)} />

View File

@@ -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>

View File

@@ -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>
)

View File

@@ -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>
}
}