blob: 4513715e284325f3ff72f5e2e7e6c1c0aaa1cffa [file] [log] [blame]
import React from 'react';
import Icon from 'components/Icon';
import Pagination from 'components/Pagination';
import TaskListItem from 'components/TaskListItem';
import { isNully, pluralize } from 'utils/Common';
import { getClassForScheduleStatus, getLastEventTime } from 'utils/Task';
import { SCHEDULE_STATUS } from 'utils/Thrift';
// VisibleForTesting
export function searchTask(task, userQuery) {
const query = userQuery.toLowerCase();
return (task.assignedTask.instanceId.toString().startsWith(query) ||
(task.assignedTask.slaveHost && task.assignedTask.slaveHost.toLowerCase().includes(query)) ||
SCHEDULE_STATUS[task.status].toLowerCase().startsWith(query));
}
export function TaskListFilter({ numberPerPage, onChange, tasks }) {
if (tasks.length > numberPerPage) {
return (<div className='table-input-wrapper'>
<Icon name='search' />
<input
autoFocus
onChange={(e) => onChange(e)}
placeholder='Search tasks by instance-id, host or current status'
type='text' />
</div>);
}
return null;
}
export function TaskListStatus({ status }) {
return [
<span className={`img-circle ${getClassForScheduleStatus(ScheduleStatus[status])}`} />,
<span>{status}</span>
];
}
export function TaskListStatusFilter({ onClick, tasks }) {
const statuses = Object.keys(tasks.reduce((seen, task) => {
seen[SCHEDULE_STATUS[task.status]] = true;
return seen;
}, {}));
if (statuses.length <= 1) {
return (<div>
{pluralize(tasks, 'One task is ', `All ${tasks.length} tasks are `)}
<TaskListStatus status={statuses[0]} />
</div>);
}
return (<ul className='task-list-status-filter'>
<li>Filter by:</li>
<li onClick={(e) => onClick(null)}>all</li>
{statuses.map((status) => (<li key={status} onClick={(e) => onClick(status)}>
<TaskListStatus status={status} />
</li>))}
</ul>);
}
export function TaskListControls({ currentSort, onFilter, onSort, tasks }) {
return (<div className='task-list-controls'>
<ul className='task-list-main-sort'>
<li>Sort by:</li>
<li className={currentSort === 'default' ? 'active' : ''} onClick={(e) => onSort('default')}>
instance
</li>
<li className={currentSort === 'latest' ? 'active' : ''} onClick={(e) => onSort('latest')}>
updated
</li>
</ul>
<TaskListStatusFilter onClick={onFilter} tasks={tasks} />
</div>);
}
export default class TaskList extends React.Component {
constructor(props) {
super(props);
this.state = {
filter: props.filter,
reverseSort: props.reverse || false,
sortBy: props.sortBy || 'default'
};
}
setFilter(filter) {
this.setState({filter});
}
setSort(sortBy) {
if (sortBy === this.state.sortBy) {
this.setState({reverseSort: !this.state.reverseSort});
} else {
this.setState({sortBy});
}
}
render() {
const that = this;
const tasksPerPage = 25;
const filterFn = (t) => that.state.filter ? searchTask(t, that.state.filter) : true;
const sortFn = this.state.sortBy === 'latest'
? (t) => getLastEventTime(t) * -1
: (t) => t.assignedTask.instanceId;
const reasons = isNully(this.props.pendingReasons) ? {} : this.props.pendingReasons;
this.props.tasks.forEach((t) => {
if (t.status === ScheduleStatus.PENDING && reasons[t.assignedTask.taskId]) {
t.taskEvents[t.taskEvents.length - 1].message = reasons[t.assignedTask.taskId];
}
});
return (<div>
<TaskListFilter
numberPerPage={tasksPerPage}
onChange={(e) => that.setFilter(e.target.value)}
tasks={this.props.tasks} />
<TaskListControls
currentSort={this.state.sortBy}
onFilter={(query) => that.setFilter(query)}
onSort={(key) => that.setSort(key)}
tasks={this.props.tasks} />
<div className='task-list'>
<table className='psuedo-table'>
<Pagination
data={this.props.tasks}
filter={filterFn}
hideIfSinglePage
isTable
numberPerPage={tasksPerPage}
renderer={(t) => <TaskListItem key={t.assignedTask.taskId} task={t} />}
reverseSort={this.state.reverseSort}
sortBy={sortFn} />
</table>
</div>
</div>);
}
}