Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>pull/469/head
parent
9a1660da51
commit
60bb0ac063
@ -0,0 +1,31 @@
|
|||||||
|
.trigger {
|
||||||
|
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.triggerContent {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queued,
|
||||||
|
.started,
|
||||||
|
.ended {
|
||||||
|
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.duration {
|
||||||
|
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
|
width: 20px;
|
||||||
|
}
|
@ -0,0 +1,264 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import titleCase from 'Utilities/String/titleCase';
|
||||||
|
import formatDate from 'Utilities/Date/formatDate';
|
||||||
|
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||||
|
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||||
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import IconButton from 'Components/Link/IconButton';
|
||||||
|
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||||
|
import TableRow from 'Components/Table/TableRow';
|
||||||
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
|
import styles from './QueuedTaskRow.css';
|
||||||
|
|
||||||
|
function getStatusIconProps(status, message) {
|
||||||
|
const title = titleCase(status);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 'queued':
|
||||||
|
return {
|
||||||
|
name: icons.PENDING,
|
||||||
|
title
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'started':
|
||||||
|
return {
|
||||||
|
name: icons.REFRESH,
|
||||||
|
isSpinning: true,
|
||||||
|
title
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'completed':
|
||||||
|
return {
|
||||||
|
name: icons.CHECK,
|
||||||
|
kind: kinds.SUCCESS,
|
||||||
|
title: message === 'Completed' ? title : `${title}: ${message}`
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'failed':
|
||||||
|
return {
|
||||||
|
name: icons.FATAL,
|
||||||
|
kind: kinds.ERROR,
|
||||||
|
title: `${title}: ${message}`
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
name: icons.UNKNOWN,
|
||||||
|
title
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFormattedDates(props) {
|
||||||
|
const {
|
||||||
|
queued,
|
||||||
|
started,
|
||||||
|
ended,
|
||||||
|
showRelativeDates,
|
||||||
|
shortDateFormat
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
if (showRelativeDates) {
|
||||||
|
return {
|
||||||
|
queuedAt: moment(queued).fromNow(),
|
||||||
|
startedAt: started ? moment(started).fromNow() : '-',
|
||||||
|
endedAt: ended ? moment(ended).fromNow() : '-'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
queuedAt: formatDate(queued, shortDateFormat),
|
||||||
|
startedAt: started ? formatDate(started, shortDateFormat) : '-',
|
||||||
|
endedAt: ended ? formatDate(ended, shortDateFormat) : '-'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class QueuedTaskRow extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
...getFormattedDates(props),
|
||||||
|
isCancelConfirmModalOpen: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this._updateTimeoutId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.setUpdateTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
const {
|
||||||
|
queued,
|
||||||
|
started,
|
||||||
|
ended
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (
|
||||||
|
queued !== prevProps.queued ||
|
||||||
|
started !== prevProps.started ||
|
||||||
|
ended !== prevProps.ended
|
||||||
|
) {
|
||||||
|
this.setState(getFormattedDates(this.props));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this._updateTimeoutId) {
|
||||||
|
this._updateTimeoutId = clearTimeout(this._updateTimeoutId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Control
|
||||||
|
|
||||||
|
setUpdateTimer() {
|
||||||
|
this._updateTimeoutId = setTimeout(() => {
|
||||||
|
this.setState(getFormattedDates(this.props));
|
||||||
|
this.setUpdateTimer();
|
||||||
|
}, 30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onCancelPress = () => {
|
||||||
|
this.setState({
|
||||||
|
isCancelConfirmModalOpen: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onAbortCancel = () => {
|
||||||
|
this.setState({
|
||||||
|
isCancelConfirmModalOpen: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
trigger,
|
||||||
|
commandName,
|
||||||
|
queued,
|
||||||
|
started,
|
||||||
|
ended,
|
||||||
|
status,
|
||||||
|
duration,
|
||||||
|
message,
|
||||||
|
longDateFormat,
|
||||||
|
timeFormat,
|
||||||
|
onCancelPress
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
queuedAt,
|
||||||
|
startedAt,
|
||||||
|
endedAt,
|
||||||
|
isCancelConfirmModalOpen
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
let triggerIcon = icons.UNKNOWN;
|
||||||
|
|
||||||
|
if (trigger === 'manual') {
|
||||||
|
triggerIcon = icons.INTERACTIVE;
|
||||||
|
} else if (trigger === 'scheduled') {
|
||||||
|
triggerIcon = icons.SCHEDULED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow>
|
||||||
|
<TableRowCell className={styles.trigger}>
|
||||||
|
<span className={styles.triggerContent}>
|
||||||
|
<Icon
|
||||||
|
name={triggerIcon}
|
||||||
|
title={titleCase(trigger)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Icon
|
||||||
|
{...getStatusIconProps(status, message)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell>{commandName}</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell
|
||||||
|
className={styles.queued}
|
||||||
|
title={formatDateTime(queued, longDateFormat, timeFormat)}
|
||||||
|
>
|
||||||
|
{queuedAt}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell
|
||||||
|
className={styles.started}
|
||||||
|
title={formatDateTime(started, longDateFormat, timeFormat)}
|
||||||
|
>
|
||||||
|
{startedAt}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell
|
||||||
|
className={styles.ended}
|
||||||
|
title={formatDateTime(ended, longDateFormat, timeFormat)}
|
||||||
|
>
|
||||||
|
{endedAt}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell className={styles.duration}>
|
||||||
|
{formatTimeSpan(duration)}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell
|
||||||
|
className={styles.actions}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
status === 'queued' &&
|
||||||
|
<IconButton
|
||||||
|
name={icons.REMOVE}
|
||||||
|
onPress={this.onCancelPress}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
isOpen={isCancelConfirmModalOpen}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
title="Cancel"
|
||||||
|
message={'Are you sure you want to cancel this pending task?'}
|
||||||
|
confirmLabel="Yes, Cancel"
|
||||||
|
cancelLabel="No, Leave It"
|
||||||
|
onConfirm={onCancelPress}
|
||||||
|
onCancel={this.onAbortCancel}
|
||||||
|
/>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QueuedTaskRow.propTypes = {
|
||||||
|
trigger: PropTypes.string.isRequired,
|
||||||
|
commandName: PropTypes.string.isRequired,
|
||||||
|
queued: PropTypes.string.isRequired,
|
||||||
|
started: PropTypes.string,
|
||||||
|
ended: PropTypes.string,
|
||||||
|
status: PropTypes.string.isRequired,
|
||||||
|
duration: PropTypes.string,
|
||||||
|
message: PropTypes.string,
|
||||||
|
showRelativeDates: PropTypes.bool.isRequired,
|
||||||
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
|
longDateFormat: PropTypes.string.isRequired,
|
||||||
|
timeFormat: PropTypes.string.isRequired,
|
||||||
|
onCancelPress: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default QueuedTaskRow;
|
@ -0,0 +1,31 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { cancelCommand } from 'Store/Actions/commandActions';
|
||||||
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
|
import QueuedTaskRow from './QueuedTaskRow';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
createUISettingsSelector(),
|
||||||
|
(uiSettings) => {
|
||||||
|
return {
|
||||||
|
showRelativeDates: uiSettings.showRelativeDates,
|
||||||
|
shortDateFormat: uiSettings.shortDateFormat,
|
||||||
|
longDateFormat: uiSettings.longDateFormat,
|
||||||
|
timeFormat: uiSettings.timeFormat
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
|
return {
|
||||||
|
onCancelPress() {
|
||||||
|
dispatch(cancelCommand({
|
||||||
|
id: props.id
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, createMapDispatchToProps)(QueuedTaskRow);
|
@ -0,0 +1,89 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import FieldSet from 'Components/FieldSet';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
import Table from 'Components/Table/Table';
|
||||||
|
import TableBody from 'Components/Table/TableBody';
|
||||||
|
import QueuedTaskRowConnector from './QueuedTaskRowConnector';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'trigger',
|
||||||
|
label: '',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'commandName',
|
||||||
|
label: 'Name',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'queued',
|
||||||
|
label: 'Queued',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'started',
|
||||||
|
label: 'Started',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ended',
|
||||||
|
label: 'Ended',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'duration',
|
||||||
|
label: 'Duration',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
isVisible: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function QueuedTasks(props) {
|
||||||
|
const {
|
||||||
|
isFetching,
|
||||||
|
isPopulated,
|
||||||
|
items
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FieldSet legend="Queue">
|
||||||
|
{
|
||||||
|
isFetching && !isPopulated &&
|
||||||
|
<LoadingIndicator />
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isPopulated &&
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
>
|
||||||
|
<TableBody>
|
||||||
|
{
|
||||||
|
items.map((item) => {
|
||||||
|
return (
|
||||||
|
<QueuedTaskRowConnector
|
||||||
|
key={item.id}
|
||||||
|
{...item}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
}
|
||||||
|
</FieldSet>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
QueuedTasks.propTypes = {
|
||||||
|
isFetching: PropTypes.bool.isRequired,
|
||||||
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
|
items: PropTypes.array.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default QueuedTasks;
|
@ -0,0 +1,46 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { fetchCommands } from 'Store/Actions/commandActions';
|
||||||
|
import QueuedTasks from './QueuedTasks';
|
||||||
|
|
||||||
|
function createMapStateToProps() {
|
||||||
|
return createSelector(
|
||||||
|
(state) => state.commands,
|
||||||
|
(commands) => {
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
dispatchFetchCommands: fetchCommands
|
||||||
|
};
|
||||||
|
|
||||||
|
class QueuedTasksConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.dispatchFetchCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<QueuedTasks
|
||||||
|
{...this.props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QueuedTasksConnector.propTypes = {
|
||||||
|
dispatchFetchCommands: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(createMapStateToProps, mapDispatchToProps)(QueuedTasksConnector);
|
@ -0,0 +1,182 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import formatDate from 'Utilities/Date/formatDate';
|
||||||
|
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||||
|
import { icons } from 'Helpers/Props';
|
||||||
|
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
||||||
|
import TableRow from 'Components/Table/TableRow';
|
||||||
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
|
import styles from './ScheduledTaskRow.css';
|
||||||
|
|
||||||
|
function getFormattedDates(props) {
|
||||||
|
const {
|
||||||
|
lastExecution,
|
||||||
|
nextExecution,
|
||||||
|
interval,
|
||||||
|
showRelativeDates,
|
||||||
|
shortDateFormat
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const isDisabled = interval === 0;
|
||||||
|
|
||||||
|
if (showRelativeDates) {
|
||||||
|
return {
|
||||||
|
lastExecutionTime: moment(lastExecution).fromNow(),
|
||||||
|
nextExecutionTime: isDisabled ? '-' : moment(nextExecution).fromNow()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
lastExecutionTime: formatDate(lastExecution, shortDateFormat),
|
||||||
|
nextExecutionTime: isDisabled ? '-' : formatDate(nextExecution, shortDateFormat)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScheduledTaskRow extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this.state = getFormattedDates(props);
|
||||||
|
|
||||||
|
this._updateTimeoutId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.setUpdateTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
const {
|
||||||
|
lastExecution,
|
||||||
|
nextExecution
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
if (
|
||||||
|
lastExecution !== prevProps.lastExecution ||
|
||||||
|
nextExecution !== prevProps.nextExecution
|
||||||
|
) {
|
||||||
|
this.setState(getFormattedDates(this.props));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this._updateTimeoutId) {
|
||||||
|
this._updateTimeoutId = clearTimeout(this._updateTimeoutId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
setUpdateTimer() {
|
||||||
|
const { interval } = this.props;
|
||||||
|
const timeout = interval < 60 ? 10000 : 60000;
|
||||||
|
|
||||||
|
this._updateTimeoutId = setTimeout(() => {
|
||||||
|
this.setState(getFormattedDates(this.props));
|
||||||
|
this.setUpdateTimer();
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
interval,
|
||||||
|
lastExecution,
|
||||||
|
nextExecution,
|
||||||
|
isQueued,
|
||||||
|
isExecuting,
|
||||||
|
longDateFormat,
|
||||||
|
timeFormat,
|
||||||
|
onExecutePress
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
lastExecutionTime,
|
||||||
|
nextExecutionTime
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
const isDisabled = interval === 0;
|
||||||
|
const executeNow = !isDisabled && moment().isAfter(nextExecution);
|
||||||
|
const hasNextExecutionTime = !isDisabled && !executeNow;
|
||||||
|
const duration = moment.duration(interval, 'minutes').humanize().replace(/an?(?=\s)/, '1');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow>
|
||||||
|
<TableRowCell>{name}</TableRowCell>
|
||||||
|
<TableRowCell
|
||||||
|
className={styles.interval}
|
||||||
|
>
|
||||||
|
{isDisabled ? 'disabled' : duration}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<TableRowCell
|
||||||
|
className={styles.lastExecution}
|
||||||
|
title={formatDateTime(lastExecution, longDateFormat, timeFormat)}
|
||||||
|
>
|
||||||
|
{lastExecutionTime}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
{
|
||||||
|
isDisabled &&
|
||||||
|
<TableRowCell className={styles.nextExecution}>-</TableRowCell>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
executeNow && isQueued &&
|
||||||
|
<TableRowCell className={styles.nextExecution}>queued</TableRowCell>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
executeNow && !isQueued &&
|
||||||
|
<TableRowCell className={styles.nextExecution}>now</TableRowCell>
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
hasNextExecutionTime &&
|
||||||
|
<TableRowCell
|
||||||
|
className={styles.nextExecution}
|
||||||
|
title={formatDateTime(nextExecution, longDateFormat, timeFormat, { includeSeconds: true })}
|
||||||
|
>
|
||||||
|
{nextExecutionTime}
|
||||||
|
</TableRowCell>
|
||||||
|
}
|
||||||
|
|
||||||
|
<TableRowCell
|
||||||
|
className={styles.actions}
|
||||||
|
>
|
||||||
|
<SpinnerIconButton
|
||||||
|
name={icons.REFRESH}
|
||||||
|
spinningName={icons.REFRESH}
|
||||||
|
isSpinning={isExecuting}
|
||||||
|
onPress={onExecutePress}
|
||||||
|
/>
|
||||||
|
</TableRowCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScheduledTaskRow.propTypes = {
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
interval: PropTypes.number.isRequired,
|
||||||
|
lastExecution: PropTypes.string.isRequired,
|
||||||
|
nextExecution: PropTypes.string.isRequired,
|
||||||
|
isQueued: PropTypes.bool.isRequired,
|
||||||
|
isExecuting: PropTypes.bool.isRequired,
|
||||||
|
showRelativeDates: PropTypes.bool.isRequired,
|
||||||
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
|
longDateFormat: PropTypes.string.isRequired,
|
||||||
|
timeFormat: PropTypes.string.isRequired,
|
||||||
|
onExecutePress: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScheduledTaskRow;
|
@ -0,0 +1,79 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React from 'react';
|
||||||
|
import FieldSet from 'Components/FieldSet';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
import Table from 'Components/Table/Table';
|
||||||
|
import TableBody from 'Components/Table/TableBody';
|
||||||
|
import ScheduledTaskRowConnector from './ScheduledTaskRowConnector';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Name',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'interval',
|
||||||
|
label: 'Interval',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lastExecution',
|
||||||
|
label: 'Last Execution',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nextExecution',
|
||||||
|
label: 'Next Execution',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
isVisible: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function ScheduledTasks(props) {
|
||||||
|
const {
|
||||||
|
isFetching,
|
||||||
|
isPopulated,
|
||||||
|
items
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FieldSet legend="Scheduled">
|
||||||
|
{
|
||||||
|
isFetching && !isPopulated &&
|
||||||
|
<LoadingIndicator />
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isPopulated &&
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
>
|
||||||
|
<TableBody>
|
||||||
|
{
|
||||||
|
items.map((item) => {
|
||||||
|
return (
|
||||||
|
<ScheduledTaskRowConnector
|
||||||
|
key={item.id}
|
||||||
|
{...item}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
}
|
||||||
|
</FieldSet>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScheduledTasks.propTypes = {
|
||||||
|
isFetching: PropTypes.bool.isRequired,
|
||||||
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
|
items: PropTypes.array.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScheduledTasks;
|
@ -1,94 +0,0 @@
|
|||||||
import moment from 'moment';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import formatDate from 'Utilities/Date/formatDate';
|
|
||||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
|
||||||
import { icons } from 'Helpers/Props';
|
|
||||||
import SpinnerIconButton from 'Components/Link/SpinnerIconButton';
|
|
||||||
import TableRow from 'Components/Table/TableRow';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
|
||||||
import styles from './TaskRow.css';
|
|
||||||
|
|
||||||
function TaskRow(props) {
|
|
||||||
const {
|
|
||||||
name,
|
|
||||||
interval,
|
|
||||||
lastExecution,
|
|
||||||
nextExecution,
|
|
||||||
isExecuting,
|
|
||||||
showRelativeDates,
|
|
||||||
shortDateFormat,
|
|
||||||
longDateFormat,
|
|
||||||
timeFormat,
|
|
||||||
onExecutePress
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const disabled = interval === 0;
|
|
||||||
const executeNow = !disabled && moment().isAfter(nextExecution);
|
|
||||||
const hasNextExecutionTime = !disabled && !executeNow;
|
|
||||||
const duration = moment.duration(interval, 'minutes').humanize().replace(/an?(?=\s)/, '1');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow>
|
|
||||||
<TableRowCell>{name}</TableRowCell>
|
|
||||||
<TableRowCell
|
|
||||||
className={styles.interval}
|
|
||||||
>
|
|
||||||
{disabled ? 'disabled' : duration}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell
|
|
||||||
className={styles.lastExecution}
|
|
||||||
title={formatDateTime(lastExecution, longDateFormat, timeFormat)}
|
|
||||||
>
|
|
||||||
{showRelativeDates ? moment(lastExecution).fromNow() : formatDate(lastExecution, shortDateFormat)}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
{
|
|
||||||
disabled &&
|
|
||||||
<TableRowCell className={styles.nextExecution}>-</TableRowCell>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
executeNow &&
|
|
||||||
<TableRowCell className={styles.nextExecution}>now</TableRowCell>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
hasNextExecutionTime &&
|
|
||||||
<TableRowCell
|
|
||||||
className={styles.nextExecution}
|
|
||||||
title={formatDateTime(nextExecution, longDateFormat, timeFormat, { includeSeconds: true })}
|
|
||||||
>
|
|
||||||
{showRelativeDates ? moment(nextExecution).fromNow() : formatDate(nextExecution, shortDateFormat)}
|
|
||||||
</TableRowCell>
|
|
||||||
}
|
|
||||||
|
|
||||||
<TableRowCell
|
|
||||||
className={styles.actions}
|
|
||||||
>
|
|
||||||
<SpinnerIconButton
|
|
||||||
name={icons.REFRESH}
|
|
||||||
spinningName={icons.REFRESH}
|
|
||||||
isSpinning={isExecuting}
|
|
||||||
onPress={onExecutePress}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskRow.propTypes = {
|
|
||||||
name: PropTypes.string.isRequired,
|
|
||||||
interval: PropTypes.number.isRequired,
|
|
||||||
lastExecution: PropTypes.string.isRequired,
|
|
||||||
nextExecution: PropTypes.string.isRequired,
|
|
||||||
isExecuting: PropTypes.bool.isRequired,
|
|
||||||
showRelativeDates: PropTypes.bool.isRequired,
|
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
|
||||||
longDateFormat: PropTypes.string.isRequired,
|
|
||||||
timeFormat: PropTypes.string.isRequired,
|
|
||||||
onExecutePress: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TaskRow;
|
|
@ -1,89 +1,18 @@
|
|||||||
import PropTypes from 'prop-types';
|
import React from 'react';
|
||||||
import React, { Component } from 'react';
|
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
|
import PageContentBodyConnector from 'Components/Page/PageContentBodyConnector';
|
||||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
import ScheduledTasksConnector from './Scheduled/ScheduledTasksConnector';
|
||||||
import Table from 'Components/Table/Table';
|
import QueuedTasksConnector from './Queued/QueuedTasksConnector';
|
||||||
import TableBody from 'Components/Table/TableBody';
|
|
||||||
import TaskRowConnector from './TaskRowConnector';
|
function Tasks() {
|
||||||
|
return (
|
||||||
const columns = [
|
<PageContent title="Tasks">
|
||||||
{
|
<PageContentBodyConnector>
|
||||||
name: 'name',
|
<ScheduledTasksConnector />
|
||||||
label: 'Name',
|
<QueuedTasksConnector />
|
||||||
isVisible: true
|
</PageContentBodyConnector>
|
||||||
},
|
</PageContent>
|
||||||
{
|
);
|
||||||
name: 'interval',
|
|
||||||
label: 'Interval',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'lastExecution',
|
|
||||||
label: 'Last Execution',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'nextExecution',
|
|
||||||
label: 'Next Execution',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'actions',
|
|
||||||
isVisible: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
class Tasks extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
isPopulated,
|
|
||||||
items
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PageContent title="Tasks">
|
|
||||||
<PageContentBodyConnector>
|
|
||||||
{
|
|
||||||
isFetching && !isPopulated &&
|
|
||||||
<LoadingIndicator />
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
isPopulated &&
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
>
|
|
||||||
<TableBody>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<TaskRowConnector
|
|
||||||
key={item.id}
|
|
||||||
{...item}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
}
|
|
||||||
</PageContentBodyConnector>
|
|
||||||
</PageContent>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tasks.propTypes = {
|
|
||||||
isFetching: PropTypes.bool.isRequired,
|
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
|
||||||
items: PropTypes.array.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Tasks;
|
export default Tasks;
|
||||||
|
Loading…
Reference in new issue