feat(xo-web/Backup NG/logs): details are now dynamic (#3031)

This commit is contained in:
badrAZ
2018-06-06 14:25:31 +02:00
committed by Julien Fontanet
parent d732ee3ade
commit 0c23ca5b66
3 changed files with 58 additions and 55 deletions

View File

@@ -7,26 +7,22 @@ const call = fn => fn()
// callbacks have been correctly initialized when there are circular dependencies
const addSubscriptions = subscriptions => Component =>
class SubscriptionWrapper extends React.PureComponent {
constructor () {
super()
// provide all props since the beginning (better behavior with Freactal)
const state = (this.state = {})
Object.keys(subscriptions).forEach(key => {
state[key] = undefined
})
}
_unsubscribes = null
componentWillMount () {
const state = {}
this._unsubscribes = map(
typeof subscriptions === 'function'
? subscriptions(this.props)
: subscriptions,
(subscribe, prop) =>
subscribe(value => this.setState({ [prop]: value }))
(subscribe, prop) => {
state[prop] = undefined
return subscribe(value => this.setState({ [prop]: value }))
}
)
// provide all props since the beginning (better behavior with Freactal)
this.setState(state)
}
componentWillUnmount () {

View File

@@ -115,7 +115,7 @@ const showTasks = log =>
{log.id}
</span>
</span>,
<LogAlertBody log={log} />
<LogAlertBody id={log.id} />
)
const LOG_INDIVIDUAL_ACTIONS = [

View File

@@ -7,10 +7,10 @@ import renderXoItem, { renderXoItemFromId } from 'render-xo-item'
import Select from 'form/select'
import Tooltip from 'tooltip'
import { addSubscriptions, formatSize, formatSpeed } from 'utils'
import { filter, isEmpty, get, keyBy, map } from 'lodash'
import { countBy, filter, get, keyBy, map } from 'lodash'
import { FormattedDate } from 'react-intl'
import { injectState, provideState } from '@julien-f/freactal'
import { runBackupNgJob, subscribeRemotes } from 'xo'
import { runBackupNgJob, subscribeBackupNgLogs, subscribeRemotes } from 'xo'
const TASK_STATUS = {
failure: {
@@ -77,40 +77,20 @@ const TASK_FILTER_OPTIONS = [
{ label: 'taskSuccess', value: 'success' },
]
const getFilteredTaskLogs = (logs, filterValue) =>
filterValue === 'all'
? logs
: filter(logs, ({ status }) => status === filterValue)
const getInitialFilter = tasks => {
const isEmptyFilter = filterValue =>
isEmpty(getFilteredTaskLogs(tasks, filterValue))
if (!isEmptyFilter('pending')) {
return PENDING_FILTER_OPTION
}
if (!isEmptyFilter('failure')) {
return FAILURE_FILTER_OPTION
}
if (!isEmptyFilter('interrupted')) {
return INTERRUPTED_FILTER_OPTION
}
return ALL_FILTER_OPTION
}
export default [
addSubscriptions({
addSubscriptions(({ id }) => ({
remotes: cb =>
subscribeRemotes(remotes => {
cb(keyBy(remotes, 'id'))
}),
}),
log: cb =>
subscribeBackupNgLogs(logs => {
cb(logs[id])
}),
})),
provideState({
initialState: ({ log }) => ({
filter: getInitialFilter(log.tasks),
initialState: () => ({
filter: undefined,
}),
effects: {
setFilter: (_, filter) => state => ({
@@ -129,21 +109,47 @@ export default [
},
},
computed: {
filteredTaskLogs: ({ filter: { value } }, { log }) =>
getFilteredTaskLogs(log.tasks, value),
optionRenderer: (state, { log }) => ({ label, value }) => (
filteredTaskLogs: (
{ defaultFilter, filter: { value } = defaultFilter },
{ log = {} }
) =>
value === 'all'
? log.tasks
: filter(log.tasks, ({ status }) => status === value),
optionRenderer: ({ countByStatus }) => ({ label, value }) => (
<span>
{_(label)} ({getFilteredTaskLogs(log.tasks, value).length})
{_(label)} ({countByStatus[value] || 0})
</span>
),
countByStatus: (_, { log = {} }) => ({
all: get(log.tasks, 'length'),
...countBy(log.tasks, 'status'),
}),
defaultFilter: ({ countByStatus }) => {
if (countByStatus.pending > 0) {
return PENDING_FILTER_OPTION
}
if (countByStatus.failure > 0) {
return FAILURE_FILTER_OPTION
}
if (countByStatus.interrupted > 0) {
return INTERRUPTED_FILTER_OPTION
}
return ALL_FILTER_OPTION
},
},
}),
injectState,
({ log, remotes, state, effects }) =>
log.result !== undefined ? (
<span className={log.status === 'skipped' ? 'text-info' : 'text-danger'}>
<Copiable tagName='p' data={JSON.stringify(log.result, null, 2)}>
<Icon icon='alarm' /> {log.result.message}
({ log = {}, remotes, state, effects }) => {
const { status, result, scheduleId } = log
return (status === 'failure' || status === 'skipped') &&
result !== undefined ? (
<span className={status === 'skipped' ? 'text-info' : 'text-danger'}>
<Copiable tagName='p' data={JSON.stringify(result, null, 2)}>
<Icon icon='alarm' /> {result.message}
</Copiable>
</span>
) : (
@@ -154,7 +160,7 @@ export default [
optionRenderer={state.optionRenderer}
options={TASK_FILTER_OPTIONS}
required
value={state.filter}
value={state.filter || state.defaultFilter}
valueKey='value'
/>
<br />
@@ -165,7 +171,7 @@ export default [
4,
8
)}) <TaskStateInfos status={taskLog.status} />{' '}
{log.scheduleId !== undefined &&
{scheduleId !== undefined &&
taskLog.status === 'failure' && (
<ActionButton
handler={effects.restartVmJob}
@@ -385,5 +391,6 @@ export default [
))}
</ul>
</div>
),
)
},
].reduceRight((value, decorator) => decorator(value))