From ee2c58ffae301f4040aeff15131dd60ae98d7463 Mon Sep 17 00:00:00 2001 From: Tom Willemse Date: Wed, 24 Jul 2013 23:46:16 +0200 Subject: [PATCH] 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. --- scrumli.lisp | 9 ++++ static/js/main.js | 109 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 94 insertions(+), 24 deletions(-) diff --git a/scrumli.lisp b/scrumli.lisp index 362f4a5..df6b442 100644 --- a/scrumli.lisp +++ b/scrumli.lisp @@ -221,3 +221,12 @@ (set-assignee 'story id assignee) (encode-json-to-string '((status . "ok")))) 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)) diff --git a/static/js/main.js b/static/js/main.js index 169448d..201c332 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -34,10 +34,8 @@ var AssigneeIcon = React.createClass({ title={this.props.assignee} alt={this.props.assignee} />; else - icon = ( - - - ); + icon = ; return icon; } @@ -68,6 +66,12 @@ var StoryTaskRow = React.createClass({ this.props.onMoved(-1); }.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() { return ( @@ -78,8 +82,11 @@ var StoryTaskRow = React.createClass({ onClick={this.moveDown}> - + @@ -100,7 +107,8 @@ var StoryTaskTable = React.createClass({ render: function() { var taskNodes = this.props.tasks.map(function (task) { return ; + onMoved={this.props.onTaskMoved} + onAssigneeClicked={this.props.onAssigneeClicked} />; }.bind(this)); return ( @@ -167,26 +175,16 @@ var StoryData = React.createClass({ handleTaskMoved: React.autoBind(function(direction) { 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() { if (this.state.data) { return (

{this.state.data.title}

- Assignee: -
{this.state.data.content}
+ onTaskMoved={this.handleTaskMoved} + onAssigneeClicked={this.props.onAssigneeClicked} />
); } @@ -196,6 +194,12 @@ var StoryData = 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() { return ( @@ -206,8 +210,11 @@ var StoryRow = React.createClass({ onClick={this.moveDown}> - + @@ -265,7 +272,8 @@ var StoryTable = React.createClass({ render: function() { var storyNodes = this.props.data.map(function (story) { return ; + onTitleClicked={this.handleSelected} + onAssigneeClicked={this.props.onAssigneeClicked} />; }.bind(this)); return ( @@ -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 ( + + ); + } +}); + var StoryForm = React.createClass({ handleSubmit: React.autoBind(function() { var role = this.refs.role.getDOMNode().value.trim(); @@ -383,18 +434,28 @@ var StoryPage = React.createClass({ this.refs.data.setData(data); }.bind(this), 'json'); }), + handleAssigneeClicked: React.autoBind(function (info) { + var form = this.refs.assignmentForm; + + form.setInfo(info); + + $(".assignModal").modal(); + }), render: function() { return (
+ onStorySelected={this.handleStorySelected} + onAssigneeClicked={this.handleAssigneeClicked} /> +
+ pollInterval={this.props.pollInterval} + onAssigneeClicked={this.handleAssigneeClicked} />
);