Add tasks to stories

This commit is contained in:
Tom Willemse 2013-07-04 21:51:31 +02:00
parent b8fad18047
commit c65cde9e8e
4 changed files with 123 additions and 2 deletions

View file

@ -12,9 +12,15 @@
(define-method get-story (id) (define-method get-story (id)
"Get a story from the datastore.") "Get a story from the datastore.")
(define-method get-tasks-for-story (id)
"Get the tasks associated with a story.")
(define-method post-story (role necessity title content reporter) (define-method post-story (role necessity title content reporter)
"Post a new story.") "Post a new story.")
(define-method post-task (story-id description reporter)
"Post a new task for a story.")
(define-method story-get-state (id) (define-method story-get-state (id)
"Get the state of a story.") "Get the state of a story.")

View file

@ -17,10 +17,27 @@
(:metaclass dao-class) (:metaclass dao-class)
(:keys id)) (:keys id))
(defclass task ()
((id :col-type serial :reader story-id)
(state :col-type string :reader state :initform "TODO")
(description :col-type string :reader description :initarg :description)
(priority :col-type integer :reader priority :initarg :priority)
(reporter :col-type string :reader reporter :initarg :reporter)
(assignee :col-type string :reader assignee :initarg :assignee)
(story-id :col-type integer :reader story-id :initarg :story-id))
(:metaclass dao-class)
(:keys id))
(deftable task
(!dao-def)
(!foreign 'story 'story-id 'id))
(defmethod datastore-init ((datastore pg-datastore)) (defmethod datastore-init ((datastore pg-datastore))
(with-connection (connection-spec datastore) (with-connection (connection-spec datastore)
(unless (table-exists-p 'story) (unless (table-exists-p 'story)
(execute (dao-table-definition 'story))))) (execute (dao-table-definition 'story)))
(unless (table-exists-p 'task)
(execute (dao-table-definition 'task)))))
(defmethod datastore-get-all-stories ((datastore pg-datastore)) (defmethod datastore-get-all-stories ((datastore pg-datastore))
(with-connection (connection-spec datastore) (with-connection (connection-spec datastore)
@ -28,7 +45,10 @@
(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)
(query (:select :* :from 'story :where (:= 'id id)) :alist))) (append (query (:select :* :from 'story :where (:= 'id id)) :alist)
`((tasks . ,(query (:select :* :from 'task
:where (:= 'story-id id))
: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)
@ -42,6 +62,18 @@
:content content :assignee "" :reporter reporter))) :content content :assignee "" :reporter reporter)))
(save-dao obj)))) (save-dao obj))))
(defmethod datastore-post-task
((datastore pg-datastore) story-id description reporter)
(with-connection (connection-spec datastore)
(let ((obj (make-instance
'task :description description
:priority (+ 1 (query (:select
(:coalesce (:max 'priority) 0)
:from 'task) :single))
:reporter reporter :story-id (parse-integer story-id)
:assignee "")))
(save-dao obj))))
(defmethod datastore-story-get-state ((datastore pg-datastore) id) (defmethod datastore-story-get-state ((datastore pg-datastore) id)
(with-connection (connection-spec datastore) (with-connection (connection-spec datastore)
(query (:select 'state :from 'story :where (:= 'id id)) :single))) (query (:select 'state :from 'story :where (:= 'id id)) :single)))

View file

@ -80,6 +80,13 @@
200) 200)
403)) 403))
(define-route tasks-new ("stories/:id/tasks/new" :method :post)
(if (logged-in-p)
(let ((description (hunchentoot:post-parameter "description")))
(post-task id description (hunchentoot:session-value :username))
200)
403))
(define-route stories-state ("stories/state" :method :post) (define-route stories-state ("stories/state" :method :post)
(if (logged-in-p) (if (logged-in-p)
(let* ((id (hunchentoot:post-parameter "id")) (let* ((id (hunchentoot:post-parameter "id"))

View file

@ -8,12 +8,88 @@ var StateIcon = React.createClass({
} }
}); });
var StoryTaskRow = React.createClass({
render: function() {
var state = " " + this.props.task.state;
return (
<tr>
<td class="span1">
<i class="icon-arrow-up"></i>
<i class="icon-arrow-down"></i>
</td>
<td class="span2">
<span>
<StateIcon state={this.props.task.state} />
{state}
</span>
</td>
<td>
{this.props.task.description}
</td>
</tr>
);
}
});
var StoryTaskTable = React.createClass({
render: function() {
var taskNodes = this.props.tasks.map(function (task) {
return <StoryTaskRow task={task} />;
});
return (
<table class="table table-striped">
{taskNodes}
</table>
);
}
});
var StoryTaskForm = React.createClass({
handleSubmit: React.autoBind(function() {
var text = this.refs.text.getDOMNode().value.trim();
this.props.onTaskSubmit({description: text});
this.refs.text.getDOMNode().value = '';
return false;
}),
render: function() {
return (
<form onSubmit={this.handleSubmit}>
<fieldset>
<legend>New task</legend>
<div class="input-append">
<input type="text" ref="text" class="input-medium" />
<button type="submit" class="btn btn-primary">
Send
</button>
</div>
</fieldset>
</form>
);
}
});
var StoryData = React.createClass({ var StoryData = React.createClass({
handleTaskSubmit: React.autoBind(function (task) {
$.ajax({
url: "/stories/" + this.props.data.id + "/tasks/new",
type: "POST",
data: task,
dataType: 'json',
mimeType: 'textPlain'
});
}),
render: function() { render: function() {
if (this.props.data) { if (this.props.data) {
return (<div> return (<div>
Assignee: {this.props.data.assignee} Assignee: {this.props.data.assignee}
<pre>{this.props.data.content}</pre> <pre>{this.props.data.content}</pre>
<StoryTaskTable tasks={this.props.data.tasks} />
<StoryTaskForm onTaskSubmit={this.handleTaskSubmit} />
</div>); </div>);
} }