New: Show Series title and season number after task name when applicable
Closes #6601
This commit is contained in:
parent
4d4d63921b
commit
6d552f2a60
|
@ -13,6 +13,8 @@ export interface CommandBody {
|
||||||
trigger: string;
|
trigger: string;
|
||||||
suppressMessages: boolean;
|
suppressMessages: boolean;
|
||||||
seriesId?: number;
|
seriesId?: number;
|
||||||
|
seriesIds?: number[];
|
||||||
|
seasonNumber?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Command extends ModelBase {
|
interface Command extends ModelBase {
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
|
|
||||||
|
function createMultiSeriesSelector(seriesIds: number[]) {
|
||||||
|
return createSelector(
|
||||||
|
(state: AppState) => state.series.itemMap,
|
||||||
|
(state: AppState) => state.series.items,
|
||||||
|
(itemMap, allSeries) => {
|
||||||
|
return seriesIds.map((seriesId) => allSeries[itemMap[seriesId]]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default createMultiSeriesSelector;
|
|
@ -10,15 +10,6 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.commandName {
|
|
||||||
display: inline-block;
|
|
||||||
min-width: 220px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.userAgent {
|
|
||||||
color: #b0b0b0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queued,
|
.queued,
|
||||||
.started,
|
.started,
|
||||||
.ended {
|
.ended {
|
||||||
|
|
|
@ -2,14 +2,12 @@
|
||||||
// Please do not change this file!
|
// Please do not change this file!
|
||||||
interface CssExports {
|
interface CssExports {
|
||||||
'actions': string;
|
'actions': string;
|
||||||
'commandName': string;
|
|
||||||
'duration': string;
|
'duration': string;
|
||||||
'ended': string;
|
'ended': string;
|
||||||
'queued': string;
|
'queued': string;
|
||||||
'started': string;
|
'started': string;
|
||||||
'trigger': string;
|
'trigger': string;
|
||||||
'triggerContent': string;
|
'triggerContent': string;
|
||||||
'userAgent': string;
|
|
||||||
}
|
}
|
||||||
export const cssExports: CssExports;
|
export const cssExports: CssExports;
|
||||||
export default cssExports;
|
export default cssExports;
|
||||||
|
|
|
@ -1,279 +0,0 @@
|
||||||
import moment from 'moment';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Icon from 'Components/Icon';
|
|
||||||
import IconButton from 'Components/Link/IconButton';
|
|
||||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
|
||||||
import TableRow from 'Components/Table/TableRow';
|
|
||||||
import { icons, kinds } from 'Helpers/Props';
|
|
||||||
import formatDate from 'Utilities/Date/formatDate';
|
|
||||||
import formatDateTime from 'Utilities/Date/formatDateTime';
|
|
||||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
|
||||||
import titleCase from 'Utilities/String/titleCase';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
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.DANGER,
|
|
||||||
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,
|
|
||||||
clientUserAgent,
|
|
||||||
longDateFormat,
|
|
||||||
timeFormat,
|
|
||||||
onCancelPress
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const {
|
|
||||||
queuedAt,
|
|
||||||
startedAt,
|
|
||||||
endedAt,
|
|
||||||
isCancelConfirmModalOpen
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
let triggerIcon = icons.QUICK;
|
|
||||||
|
|
||||||
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>
|
|
||||||
<span className={styles.commandName}>
|
|
||||||
{commandName}
|
|
||||||
</span>
|
|
||||||
{
|
|
||||||
clientUserAgent ?
|
|
||||||
<span className={styles.userAgent} title={translate('TaskUserAgentTooltip')}>
|
|
||||||
{translate('From')}: {clientUserAgent}
|
|
||||||
</span> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</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
|
|
||||||
title={translate('RemovedFromTaskQueue')}
|
|
||||||
name={icons.REMOVE}
|
|
||||||
onPress={this.onCancelPress}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
isOpen={isCancelConfirmModalOpen}
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
title={translate('Cancel')}
|
|
||||||
message={translate('CancelPendingTask')}
|
|
||||||
confirmLabel={translate('YesCancel')}
|
|
||||||
cancelLabel={translate('NoLeaveIt')}
|
|
||||||
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,
|
|
||||||
clientUserAgent: 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,238 @@
|
||||||
|
import moment from 'moment';
|
||||||
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { CommandBody } from 'Commands/Command';
|
||||||
|
import Icon from 'Components/Icon';
|
||||||
|
import IconButton from 'Components/Link/IconButton';
|
||||||
|
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||||
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
|
import TableRow from 'Components/Table/TableRow';
|
||||||
|
import useModalOpenState from 'Helpers/Hooks/useModalOpenState';
|
||||||
|
import { icons, kinds } from 'Helpers/Props';
|
||||||
|
import { cancelCommand } from 'Store/Actions/commandActions';
|
||||||
|
import createUISettingsSelector from 'Store/Selectors/createUISettingsSelector';
|
||||||
|
import formatDate from 'Utilities/Date/formatDate';
|
||||||
|
import formatDateTime from 'Utilities/Date/formatDateTime';
|
||||||
|
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||||
|
import titleCase from 'Utilities/String/titleCase';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import QueuedTaskRowNameCell from './QueuedTaskRowNameCell';
|
||||||
|
import styles from './QueuedTaskRow.css';
|
||||||
|
|
||||||
|
function getStatusIconProps(status: string, message: string | undefined) {
|
||||||
|
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.DANGER,
|
||||||
|
title: `${title}: ${message}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
name: icons.UNKNOWN,
|
||||||
|
title,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFormattedDates(
|
||||||
|
queued: string,
|
||||||
|
started: string | undefined,
|
||||||
|
ended: string | undefined,
|
||||||
|
showRelativeDates: boolean,
|
||||||
|
shortDateFormat: string
|
||||||
|
) {
|
||||||
|
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) : '-',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QueuedTimes {
|
||||||
|
queuedAt: string;
|
||||||
|
startedAt: string;
|
||||||
|
endedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueuedTaskRowProps {
|
||||||
|
id: number;
|
||||||
|
trigger: string;
|
||||||
|
commandName: string;
|
||||||
|
queued: string;
|
||||||
|
started?: string;
|
||||||
|
ended?: string;
|
||||||
|
status: string;
|
||||||
|
duration?: string;
|
||||||
|
message?: string;
|
||||||
|
body: CommandBody;
|
||||||
|
clientUserAgent?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function QueuedTaskRow(props: QueuedTaskRowProps) {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
trigger,
|
||||||
|
commandName,
|
||||||
|
queued,
|
||||||
|
started,
|
||||||
|
ended,
|
||||||
|
status,
|
||||||
|
duration,
|
||||||
|
message,
|
||||||
|
body,
|
||||||
|
clientUserAgent,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { longDateFormat, shortDateFormat, showRelativeDates, timeFormat } =
|
||||||
|
useSelector(createUISettingsSelector());
|
||||||
|
|
||||||
|
const updateTimeTimeoutId = useRef<ReturnType<typeof setTimeout> | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [times, setTimes] = useState<QueuedTimes>(
|
||||||
|
getFormattedDates(
|
||||||
|
queued,
|
||||||
|
started,
|
||||||
|
ended,
|
||||||
|
showRelativeDates,
|
||||||
|
shortDateFormat
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const [
|
||||||
|
isCancelConfirmModalOpen,
|
||||||
|
openCancelConfirmModal,
|
||||||
|
closeCancelConfirmModal,
|
||||||
|
] = useModalOpenState(false);
|
||||||
|
|
||||||
|
const handleCancelPress = useCallback(() => {
|
||||||
|
dispatch(cancelCommand({ id }));
|
||||||
|
}, [id, dispatch]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateTimeTimeoutId.current = setTimeout(() => {
|
||||||
|
setTimes(
|
||||||
|
getFormattedDates(
|
||||||
|
queued,
|
||||||
|
started,
|
||||||
|
ended,
|
||||||
|
showRelativeDates,
|
||||||
|
shortDateFormat
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, 30000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (updateTimeTimeoutId.current) {
|
||||||
|
clearTimeout(updateTimeTimeoutId.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [queued, started, ended, showRelativeDates, shortDateFormat, setTimes]);
|
||||||
|
|
||||||
|
const { queuedAt, startedAt, endedAt } = times;
|
||||||
|
|
||||||
|
let triggerIcon = icons.QUICK;
|
||||||
|
|
||||||
|
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>
|
||||||
|
|
||||||
|
<QueuedTaskRowNameCell
|
||||||
|
commandName={commandName}
|
||||||
|
body={body}
|
||||||
|
clientUserAgent={clientUserAgent}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<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
|
||||||
|
title={translate('RemovedFromTaskQueue')}
|
||||||
|
name={icons.REMOVE}
|
||||||
|
onPress={openCancelConfirmModal}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</TableRowCell>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
isOpen={isCancelConfirmModalOpen}
|
||||||
|
kind={kinds.DANGER}
|
||||||
|
title={translate('Cancel')}
|
||||||
|
message={translate('CancelPendingTask')}
|
||||||
|
confirmLabel={translate('YesCancel')}
|
||||||
|
cancelLabel={translate('NoLeaveIt')}
|
||||||
|
onConfirm={handleCancelPress}
|
||||||
|
onCancel={closeCancelConfirmModal}
|
||||||
|
/>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
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,8 @@
|
||||||
|
.commandName {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userAgent {
|
||||||
|
color: #b0b0b0;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
// This file is automatically generated.
|
||||||
|
// Please do not change this file!
|
||||||
|
interface CssExports {
|
||||||
|
'commandName': string;
|
||||||
|
'userAgent': string;
|
||||||
|
}
|
||||||
|
export const cssExports: CssExports;
|
||||||
|
export default cssExports;
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { CommandBody } from 'Commands/Command';
|
||||||
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
|
import createMultiSeriesSelector from 'Store/Selectors/createMultiSeriesSelector';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import styles from './QueuedTaskRowNameCell.css';
|
||||||
|
|
||||||
|
export interface QueuedTaskRowNameCellProps {
|
||||||
|
commandName: string;
|
||||||
|
body: CommandBody;
|
||||||
|
clientUserAgent?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function QueuedTaskRowNameCell(
|
||||||
|
props: QueuedTaskRowNameCellProps
|
||||||
|
) {
|
||||||
|
const { commandName, body, clientUserAgent } = props;
|
||||||
|
const seriesIds = [...(body.seriesIds ?? [])];
|
||||||
|
|
||||||
|
if (body.seriesId) {
|
||||||
|
seriesIds.push(body.seriesId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const series = useSelector(createMultiSeriesSelector(seriesIds));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRowCell>
|
||||||
|
<span className={styles.commandName}>
|
||||||
|
{commandName}
|
||||||
|
{series.length ? (
|
||||||
|
<span> - {series.map((s) => s.title).join(', ')}</span>
|
||||||
|
) : null}
|
||||||
|
{body.seasonNumber ? (
|
||||||
|
<span>
|
||||||
|
{' '}
|
||||||
|
{translate('SeasonNumberToken', {
|
||||||
|
seasonNumber: body.seasonNumber,
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{clientUserAgent ? (
|
||||||
|
<span
|
||||||
|
className={styles.userAgent}
|
||||||
|
title={translate('TaskUserAgentTooltip')}
|
||||||
|
>
|
||||||
|
{translate('From')}: {clientUserAgent}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,90 +0,0 @@
|
||||||
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 translate from 'Utilities/String/translate';
|
|
||||||
import QueuedTaskRowConnector from './QueuedTaskRowConnector';
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
name: 'trigger',
|
|
||||||
label: '',
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'commandName',
|
|
||||||
label: () => translate('Name'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'queued',
|
|
||||||
label: () => translate('Queued'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'started',
|
|
||||||
label: () => translate('Started'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ended',
|
|
||||||
label: () => translate('Ended'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'duration',
|
|
||||||
label: () => translate('Duration'),
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'actions',
|
|
||||||
isVisible: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
function QueuedTasks(props) {
|
|
||||||
const {
|
|
||||||
isFetching,
|
|
||||||
isPopulated,
|
|
||||||
items
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FieldSet legend={translate('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,74 @@
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import AppState from 'App/State/AppState';
|
||||||
|
import FieldSet from 'Components/FieldSet';
|
||||||
|
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||||
|
import Table from 'Components/Table/Table';
|
||||||
|
import TableBody from 'Components/Table/TableBody';
|
||||||
|
import { fetchCommands } from 'Store/Actions/commandActions';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import QueuedTaskRow from './QueuedTaskRow';
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
name: 'trigger',
|
||||||
|
label: '',
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'commandName',
|
||||||
|
label: () => translate('Name'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'queued',
|
||||||
|
label: () => translate('Queued'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'started',
|
||||||
|
label: () => translate('Started'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ended',
|
||||||
|
label: () => translate('Ended'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'duration',
|
||||||
|
label: () => translate('Duration'),
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'actions',
|
||||||
|
isVisible: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function QueuedTasks() {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { isFetching, isPopulated, items } = useSelector(
|
||||||
|
(state: AppState) => state.commands
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchCommands());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FieldSet legend={translate('Queue')}>
|
||||||
|
{isFetching && !isPopulated && <LoadingIndicator />}
|
||||||
|
|
||||||
|
{isPopulated && (
|
||||||
|
<Table columns={columns}>
|
||||||
|
<TableBody>
|
||||||
|
{items.map((item) => {
|
||||||
|
return <QueuedTaskRow key={item.id} {...item} />;
|
||||||
|
})}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
)}
|
||||||
|
</FieldSet>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,46 +0,0 @@
|
||||||
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);
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import PageContent from 'Components/Page/PageContent';
|
import PageContent from 'Components/Page/PageContent';
|
||||||
import PageContentBody from 'Components/Page/PageContentBody';
|
import PageContentBody from 'Components/Page/PageContentBody';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import QueuedTasksConnector from './Queued/QueuedTasksConnector';
|
import QueuedTasks from './Queued/QueuedTasks';
|
||||||
import ScheduledTasksConnector from './Scheduled/ScheduledTasksConnector';
|
import ScheduledTasksConnector from './Scheduled/ScheduledTasksConnector';
|
||||||
|
|
||||||
function Tasks() {
|
function Tasks() {
|
||||||
|
@ -10,7 +10,7 @@ function Tasks() {
|
||||||
<PageContent title={translate('Tasks')}>
|
<PageContent title={translate('Tasks')}>
|
||||||
<PageContentBody>
|
<PageContentBody>
|
||||||
<ScheduledTasksConnector />
|
<ScheduledTasksConnector />
|
||||||
<QueuedTasksConnector />
|
<QueuedTasks />
|
||||||
</PageContentBody>
|
</PageContentBody>
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue