aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Tom Willemse2013-07-13 21:09:40 +0200
committerGravatar Tom Willemse2013-07-13 21:14:37 +0200
commit84ae7a5fd4524cc06def467efd62019d810781f4 (patch)
treea46c26d2b90445d0d46021d47992ba1f97c72672
parentbdd07b68ca1f70b53bfcc8e54fc7ec489e4b52e8 (diff)
downloadscrumli-84ae7a5fd4524cc06def467efd62019d810781f4.tar.gz
scrumli-84ae7a5fd4524cc06def467efd62019d810781f4.zip
Handle task interaction like story interaction
When moving or adding tasks, instead of waiting for the user to reselect the task reload the task each time.
-rw-r--r--pg-datastore.lisp12
-rw-r--r--scrumli.lisp16
-rw-r--r--static/js/main.js41
3 files changed, 54 insertions, 15 deletions
diff --git a/pg-datastore.lisp b/pg-datastore.lisp
index 0952ab0..805e748 100644
--- a/pg-datastore.lisp
+++ b/pg-datastore.lisp
@@ -46,11 +46,13 @@
(defmethod datastore-get-story ((datastore pg-datastore) id)
(with-connection (connection-spec datastore)
(append (query (:select :* :from 'story :where (:= 'id id)) :alist)
- `((tasks . ,(query (:order-by
- (:select :* :from 'task
- :where (:= 'story-id id))
- 'priority)
- :alists))))))
+ `((tasks . ,(datastore-get-tasks-for-story datastore id))))))
+
+(defmethod datastore-get-tasks-for-story ((datastore pg-datastore) id)
+ (with-connection (connection-spec datastore)
+ (query (:order-by (:select :* :from 'task :where (:= 'story-id id))
+ 'priority)
+ :alists)))
(defmethod datastore-post-story
((datastore pg-datastore) role necessity title content reporter)
diff --git a/scrumli.lisp b/scrumli.lisp
index f52fff5..33a9ae9 100644
--- a/scrumli.lisp
+++ b/scrumli.lisp
@@ -105,12 +105,13 @@
(encode-json-to-string '((status . "ok"))))
403))
-(define-route tasks-new ("stories/tasks/new" :method :post)
+(define-route tasks-new ("stories/tasks/new" :method :post
+ :content-type "text/json")
(if (logged-in-p)
(with-post-parameters ("storyId" "description")
(post-task storyid description
(hunchentoot:session-value :username))
- 200)
+ (encode-json-to-string '((status . "ok"))))
403))
(define-route stories-state ("stories/state" :method :post
@@ -148,12 +149,13 @@
(encode-json-to-string '((status . "ok"))))
403))
-(define-route task-priority ("tasks/:dir" :method :post)
+(define-route task-priority ("tasks/:dir" :method :post
+ :content-type "text/json")
(if (logged-in-p)
(let* ((id (hunchentoot:post-parameter "id")))
(story-change-priority
'task id (intern (string-upcase dir) :keyword))
- 200)
+ (encode-json-to-string '((status . "ok"))))
403))
(define-route login-page ("login")
@@ -217,3 +219,9 @@
(if (logged-in-p)
(encode-json-to-string (get-story id))
403))
+
+(define-route scrumli-story-tasks ("stories/:id/tasks"
+ :content-type "json")
+ (if (logged-in-p)
+ (encode-json-to-string (get-tasks-for-story id))
+ 403))
diff --git a/static/js/main.js b/static/js/main.js
index 28abd54..3e5ae07 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -20,10 +20,18 @@ var StoryTaskRow = React.createClass({
return {state: this.props.task.state};
},
moveUp: React.autoBind(function(event) {
- $.post("/tasks/up", {'id': this.props.task.id});
+ $.post("/tasks/up", {'id': this.props.task.id})
+ .done(function (data) {
+ if (data.status == "ok")
+ this.props.onMoved(1);
+ }.bind(this));
}),
moveDown: React.autoBind(function(event) {
- $.post("/tasks/down", {'id': this.props.task.id});
+ $.post("/tasks/down", {'id': this.props.task.id})
+ .done(function (data) {
+ if (data.status == "ok")
+ this.props.onMoved(-1);
+ }.bind(this));
}),
render: function() {
return (
@@ -50,7 +58,8 @@ var StoryTaskRow = React.createClass({
var StoryTaskTable = React.createClass({
render: function() {
var taskNodes = this.props.tasks.map(function (task) {
- return <StoryTaskRow task={task} />;
+ return <StoryTaskRow task={task}
+ onMoved={this.props.onTaskMoved} />;
}.bind(this));
return (
@@ -91,14 +100,32 @@ var StoryTaskForm = React.createClass({
var StoryData = React.createClass({
handleTaskSubmit: React.autoBind(function (task) {
task.storyId = this.state.data.id;
- $.post("/stories/tasks/new", task);
+ $.post("/stories/tasks/new", task)
+ .done(function(data) {
+ if (data.status == "ok")
+ this.loadStoryFromServer();
+ }.bind(this));
}),
+ loadStoryFromServer: function() {
+ $.get("/stories/" + this.state.data.id)
+ .done(this.setData.bind(this));
+ },
+ componentWillMount: function() {
+ setInterval(
+ this.loadStoryFromServer.bind(this),
+ this.props.pollInterval
+ );
+ },
getInitialState: function() {
return {data: null};
},
setData: function(data) {
+ this.setState({data: null});
this.setState({data: data});
},
+ handleTaskMoved: React.autoBind(function(direction) {
+ this.loadStoryFromServer();
+ }),
render: function() {
if (this.state.data) {
return (<div>
@@ -107,7 +134,8 @@ var StoryData = React.createClass({
<div class="well">
{this.state.data.content}
</div>
- <StoryTaskTable tasks={this.state.data.tasks || []} />
+ <StoryTaskTable tasks={this.state.data.tasks || []}
+ onTaskMoved={this.handleTaskMoved} />
<StoryTaskForm onTaskSubmit={this.handleTaskSubmit} />
</div>);
}
@@ -283,7 +311,8 @@ var StoryPage = React.createClass({
<StoryForm onStorySubmit={this.handleStorySubmit} />
</div>
<div class="span6">
- <StoryData ref="data" />
+ <StoryData ref="data"
+ pollInterval={this.props.pollInterval} />
</div>
</div>
);