Change manner of assignment

Instead of having an input in which an assignee's email address can be
entered, show the gravatar of the assignee, or a question mark if no
assignee has been specified, and upon clicking it show a modal dialog
where this information can be changed.
This commit is contained in:
Tom Willemse 2013-07-24 23:46:16 +02:00
parent 108726efd4
commit ee2c58ffae
2 changed files with 94 additions and 24 deletions

View file

@ -221,3 +221,12 @@
(set-assignee 'story id assignee) (set-assignee 'story id assignee)
(encode-json-to-string '((status . "ok")))) (encode-json-to-string '((status . "ok"))))
403)) 403))
(define-route scrumli-task-set-assignee ("tasks/assignee"
:content-type "json"
:method :post)
(if (logged-in-p)
(with-post-parameters ("id" "assignee")
(set-assignee 'task id assignee)
(encode-json-to-string '((status . "ok"))))
403))

View file

@ -34,10 +34,8 @@ var AssigneeIcon = React.createClass({
title={this.props.assignee} title={this.props.assignee}
alt={this.props.assignee} />; alt={this.props.assignee} />;
else else
icon = (<span title="Unknown" class="icon-stack"> icon = <i title="Unknown"
<i class="icon-sign-blank icon-stack-base"></i> class="icon-question icon-border"></i>;
<i class="icon-question icon-light"></i>
</span>);
return icon; return icon;
} }
@ -68,6 +66,12 @@ var StoryTaskRow = React.createClass({
this.props.onMoved(-1); this.props.onMoved(-1);
}.bind(this)); }.bind(this));
}), }),
handleAssigneeClick: React.autoBind(function(event) {
this.props.onAssigneeClicked({url: "/tasks/assignee",
id: this.props.task.id,
assignee: this.props.task.assignee,
md5: this.props.task.md5});
}),
render: function() { render: function() {
return ( return (
<tr> <tr>
@ -78,8 +82,11 @@ var StoryTaskRow = React.createClass({
onClick={this.moveDown}></i> onClick={this.moveDown}></i>
</td> </td>
<td class="span1"> <td class="span1">
<button onClick={this.handleAssigneeClick}
class="nothing">
<AssigneeIcon assignee={this.props.task.assignee} <AssigneeIcon assignee={this.props.task.assignee}
md5={this.props.task.md5} /> md5={this.props.task.md5} />
</button>
</td> </td>
<td class="span2"> <td class="span2">
<span onClick={this.changeState} class="clickable"> <span onClick={this.changeState} class="clickable">
@ -100,7 +107,8 @@ var StoryTaskTable = React.createClass({
render: function() { render: function() {
var taskNodes = this.props.tasks.map(function (task) { var taskNodes = this.props.tasks.map(function (task) {
return <StoryTaskRow task={task} return <StoryTaskRow task={task}
onMoved={this.props.onTaskMoved} />; onMoved={this.props.onTaskMoved}
onAssigneeClicked={this.props.onAssigneeClicked} />;
}.bind(this)); }.bind(this));
return ( return (
@ -167,26 +175,16 @@ var StoryData = React.createClass({
handleTaskMoved: React.autoBind(function(direction) { handleTaskMoved: React.autoBind(function(direction) {
this.loadStoryFromServer(); this.loadStoryFromServer();
}), }),
handleAssigned: React.autoBind(function(event) {
$.post("/story/assignee", {id: this.state.data.id,
assignee: event.target.value})
.fail(function() {
event.target.value = "";
}.bind(this));
}),
render: function() { render: function() {
if (this.state.data) { if (this.state.data) {
return (<div> return (<div>
<h1>{this.state.data.title}</h1> <h1>{this.state.data.title}</h1>
Assignee:
<input type="text" ref="assignee"
value={this.state.data.assignee}
onChange={this.handleAssigned} />
<div class="well normalText"> <div class="well normalText">
{this.state.data.content} {this.state.data.content}
</div> </div>
<StoryTaskTable tasks={this.state.data.tasks || []} <StoryTaskTable tasks={this.state.data.tasks || []}
onTaskMoved={this.handleTaskMoved} /> onTaskMoved={this.handleTaskMoved}
onAssigneeClicked={this.props.onAssigneeClicked} />
<StoryTaskForm onTaskSubmit={this.handleTaskSubmit} /> <StoryTaskForm onTaskSubmit={this.handleTaskSubmit} />
</div>); </div>);
} }
@ -196,6 +194,12 @@ var StoryData = React.createClass({
}); });
var StoryRow = React.createClass({ var StoryRow = React.createClass({
handleAssigneeClick: React.autoBind(function(event) {
this.props.onAssigneeClicked({url: "/story/assignee",
id: this.props.story.id,
assignee: this.props.story.assignee,
md5: this.props.story.md5});
}),
render: function() { render: function() {
return ( return (
<tr> <tr>
@ -206,8 +210,11 @@ var StoryRow = React.createClass({
onClick={this.moveDown}></i> onClick={this.moveDown}></i>
</td> </td>
<td class="span1"> <td class="span1">
<button onClick={this.handleAssigneeClick}
class="nothing">
<AssigneeIcon assignee={this.props.story.assignee} <AssigneeIcon assignee={this.props.story.assignee}
md5={this.props.story.md5} /> md5={this.props.story.md5} />
</button>
</td> </td>
<td class="span2"> <td class="span2">
<span onClick={this.changeState} class="clickable"> <span onClick={this.changeState} class="clickable">
@ -265,7 +272,8 @@ var StoryTable = React.createClass({
render: function() { render: function() {
var storyNodes = this.props.data.map(function (story) { var storyNodes = this.props.data.map(function (story) {
return <StoryRow story={story} onMoved={this.handleMoved} return <StoryRow story={story} onMoved={this.handleMoved}
onTitleClicked={this.handleSelected} />; onTitleClicked={this.handleSelected}
onAssigneeClicked={this.props.onAssigneeClicked} />;
}.bind(this)); }.bind(this));
return ( return (
<table class="table table-striped"> <table class="table table-striped">
@ -275,6 +283,49 @@ var StoryTable = React.createClass({
} }
}); });
var AssignmentForm = React.createClass({
handleChanged: React.autoBind(function() {
var assignee = this.refs.assignee.getDOMNode().value;
$.post(this.state.url,
{id: this.state.id, assignee: assignee})
.done(function (data) {
if (data.status == "ok") {
this.refs.assignee.getDOMNode().value = '';
$(".assignModal").modal('hide');
}
}.bind(this));
}),
setInfo: React.autoBind(function(info) {
this.setState(info);
}),
getInitialState: function () {
return {id: 0, assignee: "", url: "", md5: ""};
},
render: function() {
return (
<div class="assignModal modal fade hide">
<div class="modal-header">
<button type="button" class="close"
data-dismiss="modal">
&times;
</button>
<h3 id="assignModalLabel">Assign</h3>
</div>
<div class="modal-body">
<AssigneeIcon assignee={this.state.assignee}
md5={this.state.md5} /> {" "}
<input type="text" ref="assignee"
value={this.state.assignee}
onChange={this.handleChanged} />
</div>
<div class="modal-footer">
</div>
</div>
);
}
});
var StoryForm = React.createClass({ var StoryForm = React.createClass({
handleSubmit: React.autoBind(function() { handleSubmit: React.autoBind(function() {
var role = this.refs.role.getDOMNode().value.trim(); var role = this.refs.role.getDOMNode().value.trim();
@ -383,18 +434,28 @@ var StoryPage = React.createClass({
this.refs.data.setData(data); this.refs.data.setData(data);
}.bind(this), 'json'); }.bind(this), 'json');
}), }),
handleAssigneeClicked: React.autoBind(function (info) {
var form = this.refs.assignmentForm;
form.setInfo(info);
$(".assignModal").modal();
}),
render: function() { render: function() {
return ( return (
<div class="row"> <div class="row">
<div class="span6"> <div class="span6">
<StoryTable data={this.state.data} <StoryTable data={this.state.data}
onStoryMoved={this.handleStoryMoved} onStoryMoved={this.handleStoryMoved}
onStorySelected={this.handleStorySelected} /> onStorySelected={this.handleStorySelected}
onAssigneeClicked={this.handleAssigneeClicked} />
<StoryForm onStorySubmit={this.handleStorySubmit} /> <StoryForm onStorySubmit={this.handleStorySubmit} />
<AssignmentForm ref="assignmentForm" />
</div> </div>
<div class="span6"> <div class="span6">
<StoryData ref="data" <StoryData ref="data"
pollInterval={this.props.pollInterval} /> pollInterval={this.props.pollInterval}
onAssigneeClicked={this.handleAssigneeClicked} />
</div> </div>
</div> </div>
); );