2013-06-30 22:38:05 +02:00
|
|
|
/** @jsx React.DOM */
|
2013-07-23 23:40:09 +02:00
|
|
|
/* scrumli --- A simple scrum web application
|
|
|
|
Copyright (C) 2013 Tom Willemse
|
|
|
|
|
|
|
|
scrumli is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
scrumli is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
|
|
along with scrumli. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
|
2013-06-30 22:38:05 +02:00
|
|
|
var StateIcon = React.createClass({
|
|
|
|
render: function() {
|
|
|
|
var icon_names = {"TODO": "icon-check-empty",
|
|
|
|
"DOING": "icon-sign-blank",
|
|
|
|
"DONE": "icon-check"};
|
|
|
|
return <i class={icon_names[this.props.state]}></i>;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-07-24 20:42:13 +02:00
|
|
|
var AssigneeIcon = React.createClass({
|
|
|
|
render: function() {
|
|
|
|
var icon;
|
|
|
|
|
|
|
|
if (this.props.assignee)
|
|
|
|
icon = <img src={"https://secure.gravatar.com/avatar/" +
|
|
|
|
this.props.md5 + '?s=24'}
|
|
|
|
title={this.props.assignee}
|
|
|
|
alt={this.props.assignee} />;
|
|
|
|
else
|
2013-07-24 23:46:16 +02:00
|
|
|
icon = <i title="Unknown"
|
|
|
|
class="icon-question icon-border"></i>;
|
2013-07-24 20:42:13 +02:00
|
|
|
|
|
|
|
return icon;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-07-04 21:51:31 +02:00
|
|
|
var StoryTaskRow = React.createClass({
|
2013-07-04 23:55:43 +02:00
|
|
|
changeState: React.autoBind(function(event) {
|
2013-07-29 02:09:34 +02:00
|
|
|
$.post(baseUrl + "tasks/state", {'id': this.props.task.id})
|
2013-07-13 02:05:46 +02:00
|
|
|
.done(function (data, textStatus, jqXHR) {
|
2013-07-07 23:46:15 +02:00
|
|
|
if (data.status == "ok")
|
|
|
|
this.setState({state: data.state});
|
2013-07-13 02:05:46 +02:00
|
|
|
}.bind(this));
|
2013-07-04 23:55:43 +02:00
|
|
|
}),
|
2013-07-07 23:46:15 +02:00
|
|
|
getInitialState: function () {
|
|
|
|
return {state: this.props.task.state};
|
|
|
|
},
|
2013-07-04 23:55:43 +02:00
|
|
|
moveUp: React.autoBind(function(event) {
|
2013-07-29 02:09:34 +02:00
|
|
|
$.post(baseUrl + "tasks/up", {'id': this.props.task.id})
|
2013-07-13 21:09:40 +02:00
|
|
|
.done(function (data) {
|
|
|
|
if (data.status == "ok")
|
|
|
|
this.props.onMoved(1);
|
|
|
|
}.bind(this));
|
2013-07-04 23:55:43 +02:00
|
|
|
}),
|
|
|
|
moveDown: React.autoBind(function(event) {
|
2013-07-29 02:09:34 +02:00
|
|
|
$.post(baseUrl + "tasks/down", {'id': this.props.task.id})
|
2013-07-13 21:09:40 +02:00
|
|
|
.done(function (data) {
|
|
|
|
if (data.status == "ok")
|
|
|
|
this.props.onMoved(-1);
|
|
|
|
}.bind(this));
|
2013-07-04 23:55:43 +02:00
|
|
|
}),
|
2013-07-24 23:46:16 +02:00
|
|
|
handleAssigneeClick: React.autoBind(function(event) {
|
2013-07-29 02:09:34 +02:00
|
|
|
this.props.onAssigneeClicked({url: baseUrl + "task/assignee",
|
2013-07-24 23:46:16 +02:00
|
|
|
id: this.props.task.id,
|
|
|
|
assignee: this.props.task.assignee,
|
|
|
|
md5: this.props.task.md5});
|
|
|
|
}),
|
2013-07-04 21:51:31 +02:00
|
|
|
render: function() {
|
|
|
|
return (
|
|
|
|
<tr>
|
|
|
|
<td class="span1">
|
2013-07-13 21:23:46 +02:00
|
|
|
<i class="icon-arrow-up clickable"
|
|
|
|
onClick={this.moveUp}></i>
|
|
|
|
<i class="icon-arrow-down clickable"
|
|
|
|
onClick={this.moveDown}></i>
|
2013-07-04 21:51:31 +02:00
|
|
|
</td>
|
2013-07-24 20:42:13 +02:00
|
|
|
<td class="span1">
|
2013-07-24 23:46:16 +02:00
|
|
|
<button onClick={this.handleAssigneeClick}
|
|
|
|
class="nothing">
|
|
|
|
<AssigneeIcon assignee={this.props.task.assignee}
|
|
|
|
md5={this.props.task.md5} />
|
|
|
|
</button>
|
2013-07-24 20:42:13 +02:00
|
|
|
</td>
|
2013-07-04 21:51:31 +02:00
|
|
|
<td class="span2">
|
2013-07-13 21:23:46 +02:00
|
|
|
<span onClick={this.changeState} class="clickable">
|
2013-07-07 23:46:15 +02:00
|
|
|
<StateIcon state={this.state.state} /> {" "}
|
|
|
|
{this.state.state}
|
2013-07-04 21:51:31 +02:00
|
|
|
</span>
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
{this.props.task.description}
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
var StoryTaskTable = React.createClass({
|
|
|
|
render: function() {
|
|
|
|
var taskNodes = this.props.tasks.map(function (task) {
|
2013-07-13 21:09:40 +02:00
|
|
|
return <StoryTaskRow task={task}
|
2013-07-24 23:46:16 +02:00
|
|
|
onMoved={this.props.onTaskMoved}
|
|
|
|
onAssigneeClicked={this.props.onAssigneeClicked} />;
|
2013-07-05 00:02:10 +02:00
|
|
|
}.bind(this));
|
|
|
|
|
2013-07-04 21:51:31 +02:00
|
|
|
return (
|
|
|
|
<table class="table table-striped">
|
|
|
|
{taskNodes}
|
|
|
|
</table>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
var StoryTaskForm = React.createClass({
|
|
|
|
handleSubmit: React.autoBind(function() {
|
|
|
|
var text = this.refs.text.getDOMNode().value.trim();
|
|
|
|
|
2013-08-11 14:21:39 +02:00
|
|
|
this.props.onTaskSubmit({description: text,
|
|
|
|
storyId: this.state.storyId});
|
2013-07-04 21:51:31 +02:00
|
|
|
|
2013-08-11 14:21:39 +02:00
|
|
|
$(".taskModal").modal('hide');
|
2013-07-04 21:51:31 +02:00
|
|
|
this.refs.text.getDOMNode().value = '';
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}),
|
2013-08-11 14:21:39 +02:00
|
|
|
getInitialState: function() {
|
|
|
|
return {storyId: null};
|
|
|
|
},
|
|
|
|
setStoryId: React.autoBind(function(id) {
|
|
|
|
this.setState({storyId: id});
|
|
|
|
}),
|
2013-07-04 21:51:31 +02:00
|
|
|
render: function() {
|
|
|
|
return (
|
2013-08-11 14:21:39 +02:00
|
|
|
<div class="taskModal modal fade hide">
|
|
|
|
<div class="modal-header">
|
|
|
|
<button type="button" class="close" data-dismiss="modal">
|
|
|
|
×
|
|
|
|
</button>
|
|
|
|
<h3 id="taskModalLabel">New task</h3>
|
|
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
|
|
<input type="text" ref="text" class="input-medium" />
|
|
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
|
|
<button type="button" class="btn btn-primary"
|
|
|
|
onClick={this.handleSubmit}>
|
|
|
|
Send
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
2013-07-04 21:51:31 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-06-30 22:38:05 +02:00
|
|
|
var StoryData = React.createClass({
|
2013-07-13 21:09:40 +02:00
|
|
|
loadStoryFromServer: function() {
|
2013-07-29 02:09:34 +02:00
|
|
|
$.get(baseUrl + "stories/" + this.state.data.id)
|
2013-07-13 21:09:40 +02:00
|
|
|
.done(this.setData.bind(this));
|
|
|
|
},
|
|
|
|
componentWillMount: function() {
|
|
|
|
setInterval(
|
|
|
|
this.loadStoryFromServer.bind(this),
|
|
|
|
this.props.pollInterval
|
|
|
|
);
|
|
|
|
},
|
2013-07-13 19:57:22 +02:00
|
|
|
getInitialState: function() {
|
|
|
|
return {data: null};
|
|
|
|
},
|
|
|
|
setData: function(data) {
|
2013-07-13 21:09:40 +02:00
|
|
|
this.setState({data: null});
|
2013-07-13 19:57:22 +02:00
|
|
|
this.setState({data: data});
|
|
|
|
},
|
2013-07-13 21:09:40 +02:00
|
|
|
handleTaskMoved: React.autoBind(function(direction) {
|
|
|
|
this.loadStoryFromServer();
|
|
|
|
}),
|
2013-06-30 22:38:05 +02:00
|
|
|
render: function() {
|
2013-07-13 19:57:22 +02:00
|
|
|
if (this.state.data) {
|
2013-06-30 22:38:05 +02:00
|
|
|
return (<div>
|
2013-08-11 14:21:39 +02:00
|
|
|
<h1>
|
|
|
|
<button data-target=".taskModal" role="button"
|
|
|
|
data-toggle="modal" class="nothing">
|
|
|
|
<i class="icon-plus-sign icon-2x"></i>
|
|
|
|
</button>
|
|
|
|
{this.state.data.title}
|
|
|
|
</h1>
|
2013-07-16 20:53:18 +02:00
|
|
|
<div class="well normalText">
|
2013-07-13 19:57:22 +02:00
|
|
|
{this.state.data.content}
|
|
|
|
</div>
|
2013-07-13 21:09:40 +02:00
|
|
|
<StoryTaskTable tasks={this.state.data.tasks || []}
|
2013-07-24 23:46:16 +02:00
|
|
|
onTaskMoved={this.handleTaskMoved}
|
|
|
|
onAssigneeClicked={this.props.onAssigneeClicked} />
|
2013-06-30 22:38:05 +02:00
|
|
|
</div>);
|
|
|
|
}
|
|
|
|
|
2013-07-13 19:57:22 +02:00
|
|
|
return <div></div>;
|
2013-06-30 22:38:05 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
var StoryRow = React.createClass({
|
2013-07-24 23:46:16 +02:00
|
|
|
handleAssigneeClick: React.autoBind(function(event) {
|
2013-07-29 02:09:34 +02:00
|
|
|
this.props.onAssigneeClicked({url: baseUrl + "story/assignee",
|
2013-07-24 23:46:16 +02:00
|
|
|
id: this.props.story.id,
|
|
|
|
assignee: this.props.story.assignee,
|
|
|
|
md5: this.props.story.md5});
|
|
|
|
}),
|
2013-06-30 22:38:05 +02:00
|
|
|
render: function() {
|
|
|
|
return (
|
|
|
|
<tr>
|
2013-07-04 23:45:07 +02:00
|
|
|
<td class="span1">
|
2013-07-13 21:23:46 +02:00
|
|
|
<i class="icon-arrow-up clickable"
|
|
|
|
onClick={this.moveUp}></i>
|
|
|
|
<i class="icon-arrow-down clickable"
|
|
|
|
onClick={this.moveDown}></i>
|
2013-06-30 22:38:05 +02:00
|
|
|
</td>
|
2013-07-24 20:42:13 +02:00
|
|
|
<td class="span1">
|
2013-07-24 23:46:16 +02:00
|
|
|
<button onClick={this.handleAssigneeClick}
|
|
|
|
class="nothing">
|
|
|
|
<AssigneeIcon assignee={this.props.story.assignee}
|
|
|
|
md5={this.props.story.md5} />
|
|
|
|
</button>
|
2013-07-24 20:42:13 +02:00
|
|
|
</td>
|
2013-07-04 23:45:07 +02:00
|
|
|
<td class="span2">
|
2013-07-13 21:23:46 +02:00
|
|
|
<span onClick={this.changeState} class="clickable">
|
2013-07-07 23:46:15 +02:00
|
|
|
<StateIcon state={this.state.state} /> {" "}
|
|
|
|
{this.state.state}
|
2013-06-30 22:38:05 +02:00
|
|
|
</span>
|
|
|
|
</td>
|
|
|
|
<td>
|
2013-07-13 21:23:46 +02:00
|
|
|
<a onClick={this.handleClick} class="clickable">
|
2013-06-30 22:38:05 +02:00
|
|
|
As a {this.props.story.role}, I
|
|
|
|
{this.props.story.necessity} to
|
|
|
|
{this.props.story.title}
|
|
|
|
</a>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
getInitialState: function() {
|
|
|
|
return {state: this.props.story.state,
|
|
|
|
content: null};
|
|
|
|
},
|
|
|
|
handleClick: React.autoBind(function(event) {
|
2013-07-13 19:57:22 +02:00
|
|
|
this.props.onTitleClicked(this.props.story.id);
|
2013-06-30 22:38:05 +02:00
|
|
|
}),
|
|
|
|
changeState: React.autoBind(function(event) {
|
2013-07-29 02:09:34 +02:00
|
|
|
$.post(baseUrl + "stories/state", {'id': this.props.story.id})
|
2013-07-13 02:05:46 +02:00
|
|
|
.done(function(data, textStatus, jqXHR) {
|
2013-07-07 23:46:15 +02:00
|
|
|
if (data.status == "ok")
|
|
|
|
this.setState({state: data.state});
|
2013-07-13 02:05:46 +02:00
|
|
|
}.bind(this));
|
2013-06-30 22:38:05 +02:00
|
|
|
}),
|
|
|
|
moveUp: React.autoBind(function(event) {
|
2013-07-29 02:09:34 +02:00
|
|
|
$.post(baseUrl + "stories/up", {'id': this.props.story.id})
|
2013-07-13 02:05:46 +02:00
|
|
|
.done(function (data, textStatus, jqXHR) {
|
2013-07-07 23:46:15 +02:00
|
|
|
if (data.status == "ok")
|
|
|
|
this.props.onMoved(1);
|
2013-07-13 02:05:46 +02:00
|
|
|
}.bind(this));
|
2013-06-30 22:38:05 +02:00
|
|
|
}),
|
|
|
|
moveDown: React.autoBind(function(event) {
|
2013-07-29 02:09:34 +02:00
|
|
|
$.post(baseUrl + "stories/down", {'id': this.props.story.id})
|
2013-07-13 02:05:46 +02:00
|
|
|
.done(function (data) {
|
|
|
|
if (data.status == "ok")
|
|
|
|
this.props.onMoved(-1);
|
|
|
|
}.bind(this));
|
2013-06-30 22:38:05 +02:00
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
var StoryTable = React.createClass({
|
|
|
|
render: function() {
|
2013-07-07 22:36:51 +02:00
|
|
|
var storyNodes = this.props.data.map(function (story) {
|
2013-07-24 23:49:29 +02:00
|
|
|
return <StoryRow story={story} onMoved={this.props.onStoryMoved}
|
|
|
|
onTitleClicked={this.props.onStorySelected}
|
2013-07-24 23:46:16 +02:00
|
|
|
onAssigneeClicked={this.props.onAssigneeClicked} />;
|
2013-06-30 22:38:05 +02:00
|
|
|
}.bind(this));
|
|
|
|
return (
|
|
|
|
<table class="table table-striped">
|
|
|
|
{storyNodes}
|
|
|
|
</table>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-07-24 23:46:16 +02:00
|
|
|
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">
|
2013-08-11 14:21:39 +02:00
|
|
|
<div class="modal-header">
|
|
|
|
<button type="button" class="close"
|
|
|
|
data-dismiss="modal">
|
|
|
|
×
|
|
|
|
</button>
|
2013-07-24 23:46:16 +02:00
|
|
|
<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>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-06-30 22:38:05 +02:00
|
|
|
var StoryForm = React.createClass({
|
|
|
|
handleSubmit: React.autoBind(function() {
|
|
|
|
var role = this.refs.role.getDOMNode().value.trim();
|
|
|
|
var necessity = this.refs.necessity.getDOMNode().value.trim();
|
|
|
|
var headline = this.refs.headline.getDOMNode().value.trim();
|
|
|
|
var content = this.refs.content.getDOMNode().value.trim();
|
|
|
|
|
2013-07-24 20:45:43 +02:00
|
|
|
$(".newTaskModal").modal('hide');
|
2013-06-30 22:38:05 +02:00
|
|
|
this.props.onStorySubmit({role: role,
|
|
|
|
necessity: necessity,
|
|
|
|
headline: headline,
|
|
|
|
content: content});
|
|
|
|
|
|
|
|
this.refs.role.getDOMNode().value = '';
|
|
|
|
this.refs.necessity.getDOMNode().value = '';
|
|
|
|
this.refs.headline.getDOMNode().value = '';
|
|
|
|
this.refs.content.getDOMNode().value = '';
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}),
|
|
|
|
render: function() {
|
|
|
|
return (
|
2013-07-24 20:45:43 +02:00
|
|
|
<div class="newTaskModal modal fade hide">
|
2013-07-15 19:51:45 +02:00
|
|
|
<form onSubmit={this.handleSubmit} class="form-horizontal">
|
|
|
|
<div class="modal-header">
|
|
|
|
<button type="button" class="close" data-dismiss="modal">
|
|
|
|
×
|
|
|
|
</button>
|
2013-07-24 20:45:43 +02:00
|
|
|
<h3 id="newTaskModalLabel">New story</h3>
|
2013-07-15 19:51:45 +02:00
|
|
|
</div>
|
|
|
|
<div class="modal-body">
|
2013-06-30 22:38:05 +02:00
|
|
|
<div id="new-story">
|
2013-07-15 19:51:45 +02:00
|
|
|
<div class="control-group">
|
|
|
|
<label class="control-label">As a</label>
|
|
|
|
<div class="controls">
|
|
|
|
<input type="text" ref="role" placeholder="person" />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
|
|
<label class="control-label">I</label>
|
|
|
|
<div class="controls">
|
|
|
|
<input type="text" ref="necessity"
|
|
|
|
placeholder="would like" />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
|
|
<label class="control-label">to</label>
|
|
|
|
<div class="controls">
|
|
|
|
<input type="text" ref="headline"
|
|
|
|
placeholder="fill in this form..." />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="control-group">
|
|
|
|
<div class="controls">
|
|
|
|
<textarea ref="content"></textarea>
|
|
|
|
</div>
|
2013-06-30 22:38:05 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
2013-07-15 19:51:45 +02:00
|
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
|
|
<button class="btn" data-dismiss="modal" aria-hidden="true">
|
|
|
|
Close
|
|
|
|
</button>
|
|
|
|
<button class="btn btn-primary" type="submit">Save</button>
|
|
|
|
</div>
|
2013-06-30 22:38:05 +02:00
|
|
|
</form>
|
2013-07-15 19:51:45 +02:00
|
|
|
</div>
|
2013-06-30 22:38:05 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
var StoryPage = React.createClass({
|
2013-07-07 22:36:51 +02:00
|
|
|
loadStoriesFromServer: function() {
|
2013-07-25 22:45:04 +02:00
|
|
|
$.get(this.state.url)
|
2013-07-13 02:05:46 +02:00
|
|
|
.done(function(data) {
|
2013-07-13 19:19:36 +02:00
|
|
|
this.setState({data: []});
|
2013-07-13 02:05:46 +02:00
|
|
|
this.setState({data: data});
|
|
|
|
}.bind(this));
|
2013-07-07 22:36:51 +02:00
|
|
|
},
|
|
|
|
getInitialState: function() {
|
2013-07-25 22:45:04 +02:00
|
|
|
return {data: [], url: this.props.url};
|
2013-07-07 22:36:51 +02:00
|
|
|
},
|
2013-07-25 22:45:04 +02:00
|
|
|
setUrl: React.autoBind(function(url) {
|
|
|
|
this.setState({url: url});
|
|
|
|
}),
|
2013-07-07 22:36:51 +02:00
|
|
|
componentWillMount: function() {
|
|
|
|
this.loadStoriesFromServer();
|
|
|
|
setInterval(
|
|
|
|
this.loadStoriesFromServer.bind(this),
|
|
|
|
this.props.pollInterval
|
|
|
|
);
|
|
|
|
},
|
2013-07-07 23:46:15 +02:00
|
|
|
handleStoryMoved: React.autoBind(function (direction) {
|
|
|
|
this.loadStoriesFromServer();
|
|
|
|
}),
|
2013-06-30 22:38:05 +02:00
|
|
|
handleStorySubmit: React.autoBind(function (story) {
|
2013-07-29 02:09:34 +02:00
|
|
|
$.post(baseUrl + "stories/new", story)
|
2013-07-13 02:05:46 +02:00
|
|
|
.done(function (data, textStatus, jqXHR) {
|
2013-07-07 22:36:51 +02:00
|
|
|
if (data.status == "ok")
|
|
|
|
this.loadStoriesFromServer();
|
2013-07-13 02:05:46 +02:00
|
|
|
}.bind(this))
|
|
|
|
.fail(function (jqXHR, textStatus, errorThrown) {
|
2013-07-07 22:36:51 +02:00
|
|
|
alert("error: " + errorThrown);
|
2013-07-13 02:05:46 +02:00
|
|
|
}.bind(this));
|
2013-06-30 22:38:05 +02:00
|
|
|
}),
|
2013-07-13 19:57:22 +02:00
|
|
|
handleStorySelected: React.autoBind(function (storyId) {
|
2013-07-29 02:09:34 +02:00
|
|
|
$.get(baseUrl + 'stories/' + storyId)
|
2013-07-13 19:57:22 +02:00
|
|
|
.done(function (data, textStatus, jqXHR) {
|
|
|
|
this.refs.data.setData(data);
|
2013-08-11 14:21:39 +02:00
|
|
|
this.refs.taskForm.setStoryId(data.id);
|
2013-07-13 19:57:22 +02:00
|
|
|
}.bind(this), 'json');
|
|
|
|
}),
|
2013-07-24 23:46:16 +02:00
|
|
|
handleAssigneeClicked: React.autoBind(function (info) {
|
|
|
|
var form = this.refs.assignmentForm;
|
|
|
|
|
|
|
|
form.setInfo(info);
|
|
|
|
|
|
|
|
$(".assignModal").modal();
|
|
|
|
}),
|
2013-08-11 14:21:39 +02:00
|
|
|
handleTaskSubmit: React.autoBind(function (task) {
|
|
|
|
$.post(baseUrl + "stories/tasks/new", task)
|
|
|
|
.done(function(data) {
|
|
|
|
if (data.status == "ok")
|
|
|
|
this.refs.data.loadStoryFromServer();
|
|
|
|
}.bind(this));
|
|
|
|
}),
|
2013-06-30 22:38:05 +02:00
|
|
|
render: function() {
|
|
|
|
return (
|
2013-07-13 19:57:22 +02:00
|
|
|
<div class="row">
|
|
|
|
<div class="span6">
|
|
|
|
<StoryTable data={this.state.data}
|
|
|
|
onStoryMoved={this.handleStoryMoved}
|
2013-07-24 23:46:16 +02:00
|
|
|
onStorySelected={this.handleStorySelected}
|
|
|
|
onAssigneeClicked={this.handleAssigneeClicked} />
|
2013-07-13 19:57:22 +02:00
|
|
|
<StoryForm onStorySubmit={this.handleStorySubmit} />
|
2013-07-24 23:46:16 +02:00
|
|
|
<AssignmentForm ref="assignmentForm" />
|
2013-07-13 19:57:22 +02:00
|
|
|
</div>
|
|
|
|
<div class="span6">
|
2013-07-13 21:09:40 +02:00
|
|
|
<StoryData ref="data"
|
2013-07-24 23:46:16 +02:00
|
|
|
pollInterval={this.props.pollInterval}
|
|
|
|
onAssigneeClicked={this.handleAssigneeClicked} />
|
2013-08-11 14:21:39 +02:00
|
|
|
<StoryTaskForm onTaskSubmit={this.handleTaskSubmit}
|
|
|
|
ref="taskForm"/>
|
2013-07-13 19:57:22 +02:00
|
|
|
</div>
|
2013-06-30 22:38:05 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-07-25 22:45:04 +02:00
|
|
|
var StoryFilter = React.createClass({
|
|
|
|
getInitialState: function() {
|
|
|
|
return {filter: 'all'};
|
|
|
|
},
|
|
|
|
handleClick: React.autoBind(function(event) {
|
|
|
|
this.setState({filter: (this.state.filter != 'all'
|
|
|
|
? 'all' : 'user')});
|
2013-07-29 02:09:34 +02:00
|
|
|
scrumli_page.setUrl(baseUrl + (this.state.filter == "all"
|
|
|
|
? "stories" : "stories/mine"));
|
2013-07-25 22:45:04 +02:00
|
|
|
}),
|
|
|
|
render: function() {
|
|
|
|
var classes = {all: ['icon-group', 'All'],
|
|
|
|
user: ['icon-user', 'Mine']};
|
|
|
|
|
|
|
|
return (<a onClick={this.handleClick}>
|
|
|
|
<i class={classes[this.state.filter][0] + ' icon-light'}></i>
|
|
|
|
{" "} {classes[this.state.filter][1]}
|
|
|
|
</a>);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2013-07-29 02:09:34 +02:00
|
|
|
var scrumli_page = <StoryPage url={baseUrl + "stories"} pollInterval={5000} />;
|
2013-07-25 22:45:04 +02:00
|
|
|
|
2013-06-30 22:38:05 +02:00
|
|
|
React.renderComponent(
|
2013-07-25 22:45:04 +02:00
|
|
|
scrumli_page,
|
2013-06-30 22:38:05 +02:00
|
|
|
document.getElementById('content')
|
|
|
|
);
|
2013-07-25 22:45:04 +02:00
|
|
|
|
|
|
|
React.renderComponent(
|
|
|
|
<StoryFilter />,
|
|
|
|
document.getElementById('filter')
|
|
|
|
);
|