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.
This commit is contained in:
parent
bdd07b68ca
commit
84ae7a5fd4
3 changed files with 54 additions and 15 deletions
|
@ -46,11 +46,13 @@
|
||||||
(defmethod datastore-get-story ((datastore pg-datastore) id)
|
(defmethod datastore-get-story ((datastore pg-datastore) id)
|
||||||
(with-connection (connection-spec datastore)
|
(with-connection (connection-spec datastore)
|
||||||
(append (query (:select :* :from 'story :where (:= 'id id)) :alist)
|
(append (query (:select :* :from 'story :where (:= 'id id)) :alist)
|
||||||
`((tasks . ,(query (:order-by
|
`((tasks . ,(datastore-get-tasks-for-story datastore id))))))
|
||||||
(:select :* :from 'task
|
|
||||||
:where (:= 'story-id 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)
|
'priority)
|
||||||
:alists))))))
|
:alists)))
|
||||||
|
|
||||||
(defmethod datastore-post-story
|
(defmethod datastore-post-story
|
||||||
((datastore pg-datastore) role necessity title content reporter)
|
((datastore pg-datastore) role necessity title content reporter)
|
||||||
|
|
16
scrumli.lisp
16
scrumli.lisp
|
@ -105,12 +105,13 @@
|
||||||
(encode-json-to-string '((status . "ok"))))
|
(encode-json-to-string '((status . "ok"))))
|
||||||
403))
|
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)
|
(if (logged-in-p)
|
||||||
(with-post-parameters ("storyId" "description")
|
(with-post-parameters ("storyId" "description")
|
||||||
(post-task storyid description
|
(post-task storyid description
|
||||||
(hunchentoot:session-value :username))
|
(hunchentoot:session-value :username))
|
||||||
200)
|
(encode-json-to-string '((status . "ok"))))
|
||||||
403))
|
403))
|
||||||
|
|
||||||
(define-route stories-state ("stories/state" :method :post
|
(define-route stories-state ("stories/state" :method :post
|
||||||
|
@ -148,12 +149,13 @@
|
||||||
(encode-json-to-string '((status . "ok"))))
|
(encode-json-to-string '((status . "ok"))))
|
||||||
403))
|
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)
|
(if (logged-in-p)
|
||||||
(let* ((id (hunchentoot:post-parameter "id")))
|
(let* ((id (hunchentoot:post-parameter "id")))
|
||||||
(story-change-priority
|
(story-change-priority
|
||||||
'task id (intern (string-upcase dir) :keyword))
|
'task id (intern (string-upcase dir) :keyword))
|
||||||
200)
|
(encode-json-to-string '((status . "ok"))))
|
||||||
403))
|
403))
|
||||||
|
|
||||||
(define-route login-page ("login")
|
(define-route login-page ("login")
|
||||||
|
@ -217,3 +219,9 @@
|
||||||
(if (logged-in-p)
|
(if (logged-in-p)
|
||||||
(encode-json-to-string (get-story id))
|
(encode-json-to-string (get-story id))
|
||||||
403))
|
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))
|
||||||
|
|
|
@ -20,10 +20,18 @@ var StoryTaskRow = React.createClass({
|
||||||
return {state: this.props.task.state};
|
return {state: this.props.task.state};
|
||||||
},
|
},
|
||||||
moveUp: React.autoBind(function(event) {
|
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) {
|
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() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
|
@ -50,7 +58,8 @@ var StoryTaskRow = React.createClass({
|
||||||
var StoryTaskTable = React.createClass({
|
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} />;
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -91,14 +100,32 @@ var StoryTaskForm = React.createClass({
|
||||||
var StoryData = React.createClass({
|
var StoryData = React.createClass({
|
||||||
handleTaskSubmit: React.autoBind(function (task) {
|
handleTaskSubmit: React.autoBind(function (task) {
|
||||||
task.storyId = this.state.data.id;
|
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() {
|
getInitialState: function() {
|
||||||
return {data: null};
|
return {data: null};
|
||||||
},
|
},
|
||||||
setData: function(data) {
|
setData: function(data) {
|
||||||
|
this.setState({data: null});
|
||||||
this.setState({data: data});
|
this.setState({data: data});
|
||||||
},
|
},
|
||||||
|
handleTaskMoved: React.autoBind(function(direction) {
|
||||||
|
this.loadStoryFromServer();
|
||||||
|
}),
|
||||||
render: function() {
|
render: function() {
|
||||||
if (this.state.data) {
|
if (this.state.data) {
|
||||||
return (<div>
|
return (<div>
|
||||||
|
@ -107,7 +134,8 @@ var StoryData = React.createClass({
|
||||||
<div class="well">
|
<div class="well">
|
||||||
{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} />
|
||||||
<StoryTaskForm onTaskSubmit={this.handleTaskSubmit} />
|
<StoryTaskForm onTaskSubmit={this.handleTaskSubmit} />
|
||||||
</div>);
|
</div>);
|
||||||
}
|
}
|
||||||
|
@ -283,7 +311,8 @@ var StoryPage = React.createClass({
|
||||||
<StoryForm onStorySubmit={this.handleStorySubmit} />
|
<StoryForm onStorySubmit={this.handleStorySubmit} />
|
||||||
</div>
|
</div>
|
||||||
<div class="span6">
|
<div class="span6">
|
||||||
<StoryData ref="data" />
|
<StoryData ref="data"
|
||||||
|
pollInterval={this.props.pollInterval} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue