Skip to content

Commit

Permalink
Improve task-graph-inspector's UX (#130)
Browse files Browse the repository at this point in the history
* task-graph in table

* removed items from summary

* add review suggestions

* added arrows
  • Loading branch information
andreadelrio authored and eliperelman committed Aug 18, 2016
1 parent 518970c commit a8c7820
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 52 deletions.
93 changes: 93 additions & 0 deletions lib/ui/tasksummary.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const React = require('react');
const _ = require('lodash');
const bs = require('react-bootstrap');
const format = require('../format');
const path = require('path');
const taskcluster = require('taskcluster-client');
const utils = require('../utils');

/** Displays information about a task in a tab page */
var TaskSummary = React.createClass({
mixins: [
// Calls load() initially and on reload()
utils.createTaskClusterMixin({
// Need updated clients for Queue
clients: {
queue: taskcluster.Queue
},
// Reload when props.status.taskId changes, ignore credential changes
reloadOnProps: ['status.taskId'],
reloadOnLogin: false
})
],

// Validate properties
propTypes: {
status: React.PropTypes.object.isRequired
},

/** Get initial state */
getInitialState() {
return {
// task definition
taskLoaded: false,
taskError: null,
task: null
};
},

/** Load task definition */
load() {
return {
task: this.queue.task(this.props.status.taskId)
};
},

render() {
// Easy references to values
const { status } = this.props;
const { task } = this.state;

var taskStateLabel = {
unscheduled: 'label label-default',
pending: 'label label-info',
running: 'label label-primary',
completed: 'label label-success',
failed: 'label label-danger',
exception: 'label label-warning'
};

return this.renderWaitFor('task') || (
<div>
<dl className="dl-horizontal">
<dt>Name</dt>
<dd>
<format.Markdown>
{task.metadata.name}
</format.Markdown>
</dd>
<dt>Description</dt>
<dd>
<format.Markdown>
{task.metadata.description}
</format.Markdown>
</dd>
<dt>State</dt>
<dd>
<span className={taskStateLabel[status.state]}>
{status.state}
</span>
</dd>
<dt>Task Inspector</dt>
<dd>
<a target="_blank" href={`../task-inspector/#${status.taskId}`}>
{status.taskId} <i className="fa fa-external-link" />
</a>
</dd>
</dl>
</div>
);
}
});

module.exports = TaskSummary;
90 changes: 43 additions & 47 deletions task-graph-inspector/taskgraphinspector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ var bs = require('react-bootstrap');
var utils = require('../lib/utils');
var taskcluster = require('taskcluster-client');
var _ = require('lodash');
var TaskView = require('../lib/ui/taskview');
var TaskSummary = require('../lib/ui/tasksummary');
var format = require('../lib/format');
var PreviousTasks = require('../lib/ui/previoustasks');

Expand All @@ -14,42 +14,42 @@ var TaskGraphInspector = React.createClass({
utils.createTaskClusterMixin({
// Need updated clients for Queue, Scheduler and associated events
clients: {
scheduler: taskcluster.Scheduler,
schedulerEvents: taskcluster.SchedulerEvents,
queue: taskcluster.Queue,
queueEvents: taskcluster.QueueEvents
scheduler: taskcluster.Scheduler,
schedulerEvents: taskcluster.SchedulerEvents,
queue: taskcluster.Queue,
queueEvents: taskcluster.QueueEvents
},
// Reload when state.taskGraphId changes, ignore credential changes
reloadOnKeys: ['taskGraphId'],
reloadOnLogin: false
reloadOnKeys: ['taskGraphId'],
reloadOnLogin: false
}),
// Called handler when state.taskGraphId and state.taskId changes
utils.createWatchStateMixin({
onKeys: {
updateTaskGraphIdInput: ['taskGraphId'],
loadTaskStatus: ['taskId']
updateTaskGraphIdInput: ['taskGraphId'],
loadTaskStatus: ['taskId']
}
}),
// Listen for messages, reload bindings() when state.taskGraphId changes
utils.createWebListenerMixin({
reloadOnKeys: ['taskGraphId']
reloadOnKeys: ['taskGraphId']
}),
// Serialize state.taskGraphId to location.hash as string
utils.createLocationHashMixin({
keys: ['taskGraphId', 'taskId'],
type: 'string'
keys: ['taskGraphId', 'taskId'],
type: 'string'
})
],

/** Get initial state */
getInitialState: function() {
return {
taskGraphId: '',
taskId: null,
taskGraphLoaded: true,
taskGraphError: undefined,
taskGraph: null,
taskGraphIdInput: ''
taskGraphId: '',
taskId: null,
taskGraphLoaded: true,
taskGraphError: null,
taskGraph: null,
taskGraphIdInput: ''
// Remark we'll cache task status structures under
// 'task/<taskId>/status' as arrive from messages or when we load a
// a task...
Expand All @@ -61,14 +61,14 @@ var TaskGraphInspector = React.createClass({
// Skip loading empty-strings
if (this.state.taskGraphId === '') {
return {
taskGraph: null
taskGraph: null
};
}

// Construct promised state
return {
// Load task status and take the `status` key from the response
taskGraph: this.scheduler.inspect(this.state.taskGraphId)
taskGraph: this.scheduler.inspect(this.state.taskGraphId)
};
},

Expand Down Expand Up @@ -182,7 +182,7 @@ var TaskGraphInspector = React.createClass({

// Create updated state
var state = {
taskGraph: taskGraph
taskGraph: taskGraph
};

// Cache status structure, we don't have to reload it if we select the task
Expand Down Expand Up @@ -292,9 +292,9 @@ var TaskGraphInspector = React.createClass({

// Mapping from state to labels
var taskGraphStateLabel = {
running: 'label label-primary',
finished: 'label label-success',
blocked: 'label label-danger'
running: 'label label-primary',
finished: 'label label-success',
blocked: 'label label-danger'
};

// Shorten source if necessary
Expand Down Expand Up @@ -355,22 +355,20 @@ var TaskGraphInspector = React.createClass({
<dd><code>{taskGraph.status.taskGraphId}</code></dd>
</dl>
{this.renderTaskTable()}
<hr/>
{this.renderTaskView()}
</span>
);
},

/** Render table of tasks in a task-graph */
renderTaskTable: function() {
var taskStateLabel = {
unscheduled: 'label label-default',
scheduled: 'label label-info',
pending: 'label label-info',
running: 'label label-primary',
completed: 'label label-success',
failed: 'label label-danger',
exception: 'label label-warning'
unscheduled: 'label label-default',
scheduled: 'label label-info',
pending: 'label label-info',
running: 'label label-primary',
completed: 'label label-success',
failed: 'label label-danger',
exception: 'label label-warning'
};

var requiredTasks = [];
Expand All @@ -389,6 +387,7 @@ var TaskGraphInspector = React.createClass({
<table className="table table-condensed task-graph-inspector-tasks">
<thead>
<tr>
<th>&nbsp;</th>
<th>TaskId</th>
<th>Name</th>
<th>State</th>
Expand All @@ -413,13 +412,15 @@ var TaskGraphInspector = React.createClass({
}
return (
<tr key={task.taskId}
className={this.state.taskId == task.taskId ? 'info' : null}
className={this.state.taskId === task.taskId ? 'info' : null}
onClick={this.handleSelectTask.bind(this, task.taskId)}>
<td><bs.Glyphicon glyph={this.state.taskId === task.taskId ? 'chevron-down' : 'chevron-right'} /></td>
<td><code>{task.taskId}</code></td>
<td>
<format.Markdown>
{task.name}
</format.Markdown>
{this.state.taskId === task.taskId ?
this.renderTaskSummary() :
<format.Markdown>{task.name}</format.Markdown>
}
</td>
<td>
<span className={stateLabel}>
Expand Down Expand Up @@ -451,8 +452,8 @@ var TaskGraphInspector = React.createClass({
);
},

/** Render taskview for currently selected task */
renderTaskView: function() {
/** Render taskSummary for currently selected task */
renderTaskSummary: function() {
// If nothing is selected don't try to load
if (!this.state.taskId || this.state.taskId === '') {
return undefined;
Expand All @@ -466,24 +467,19 @@ var TaskGraphInspector = React.createClass({
// Find status structure from state
var status = this.state['task/' + this.state.taskId + '/status'];

return (
<TaskView ref="taskView"
hashEntry={this.nextHashEntry()}
status={status}
queue={this.props.queue}/>
);
return <TaskSummary status={status} />;
},

/** Update TaskGraphIdInput to reflect input */
handleTaskGraphIdInputChange: function() {
this.setState({
taskGraphIdInput: this.refs.taskGraphId.getInputDOMNode().value.trim()
taskGraphIdInput: this.refs.taskGraphId.getInputDOMNode().value.trim()
});
},

/** Handle selection of a task */
handleSelectTask: function(taskId) {
this.setState({taskId: taskId});
this.setState({ taskId: this.state.taskId === taskId ? null : taskId });
},

/** Handle form submission */
Expand Down
7 changes: 2 additions & 5 deletions task-graph-inspector/taskgraphinspector.less
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
@import "../lib/ui/taskview.less";

.task-graph-inspector-tasks > tbody > tr:not(.info) {
.task-graph-inspector-tasks > tbody > tr {
cursor: pointer;

}

.table > tbody > tr > td > span.markdown-view > p:last-of-type {
margin: 0px;

&:hover {
text-decoration: underline;
}
}



0 comments on commit a8c7820

Please sign in to comment.