diff options
author | Tom Willemse | 2013-06-19 23:51:54 +0200 |
---|---|---|
committer | Tom Willemse | 2013-06-19 23:51:54 +0200 |
commit | 07fbe5ece2d326dd2027e297f3fb37f1ae9d18c8 (patch) | |
tree | 910e3cdb620f1ba36f41321042329ce954e4c709 /js | |
parent | 024b66d4c7e2e0ffea6c14b4343eb8f89b074efd (diff) | |
download | scrumelo-07fbe5ece2d326dd2027e297f3fb37f1ae9d18c8.tar.gz scrumelo-07fbe5ece2d326dd2027e297f3fb37f1ae9d18c8.zip |
Replace front-end with React
Diffstat (limited to 'js')
-rw-r--r-- | js/main.js | 162 | ||||
-rw-r--r-- | js/scrumelo.js | 24 |
2 files changed, 162 insertions, 24 deletions
diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..a600ccf --- /dev/null +++ b/js/main.js @@ -0,0 +1,162 @@ +/** @jsx React.DOM */ +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>; + } +}); + +var StoryData = React.createClass({ + render: function() { + if (this.props.data) { + return (<div> + Assignee: {this.props.data.Assignee} + <pre>{this.props.data.content}</pre> + </div>); + } + + return null; + } +}); + +var StoryRow = React.createClass({ + render: function() { + // A little ugly to get a space, but I don't know of any other + // way. + var state = " " + this.props.story.state; + var sdata = null; + + if (this.state.content) + sdata = <StoryData data={this.state.content} />; + + return ( + <tr> + <td> + <StateIcon state={this.props.story.state} /> + {state} + </td> + <td> + <a id={this.props.story.id} onClick={this.handleClick}> + As a {this.props.story.role}, I + {this.props.story.necessity} to + {this.props.story.title} + </a> + <br /> + {sdata} + </td> + </tr> + ); + }, + getInitialState: function() { + return {content: null}; + }, + handleClick: React.autoBind(function(event) { + if (!!this.state.content) { + this.setState({content: null}); + return; + } + var self = this; + + $.get('/stories/' + this.props.story.id, null, + function (data, textStatus, jqXHR) { + self.setState({content: data}); + }, 'json'); + }) +}); + +var StoryTable = React.createClass({ + loadStoriesFromServer: function() { + $.ajax({ + url: this.props.url, + mimeType: 'textPlain', + success: function(data) { + this.setState({data: eval(data)}); + }.bind(this) + }); + }, + getInitialState: function() { + return {data: []}; + }, + componentWillMount: function() { + this.loadStoriesFromServer(); + setInterval( + this.loadStoriesFromServer.bind(this), + this.props.pollInterval + ); + }, + render: function() { + var storyNodes = this.state.data.map(function (story) { + return <StoryRow story={story} />; + }); + return ( + <table class="table table-striped"> + {storyNodes} + </table> + ); + } +}); + +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(); + + this.props.onStorySubmit({role: role, + necessity: necessity, + headline: headline}); + + this.refs.role.getDOMNode().value = ''; + this.refs.necessity.getDOMNode().value = ''; + this.refs.headline.getDOMNode().value = ''; + + return false; + }), + render: function() { + return ( + <form onSubmit={this.handleSubmit}> + <fieldset> + <legend class="toggle">New story</legend> + <div id="new-story"> + <div class="input-prepend input-append"> + <span class="add-on">As a </span> + <input type="text" class="input-medium" ref="role" /> + <span class="add-on"> I </span> + <input type="text" class="input-mini" ref="necessity" /> + <span class="add-on"> to </span> + <input type="text" class="input-xxlarge" ref="headline" /> + <button class="btn" type="submit">!</button> + </div> + </div> + </fieldset> + </form> + ); + } +}); + +var StoryPage = React.createClass({ + handleStorySubmit: React.autoBind(function (story) { + $.ajax({ + url: "/stories/new/", + type: "POST", + data: story, + dataType: 'json', + mimeType: 'textPlain' + }); + }), + render: function() { + return ( + <div> + <StoryTable url="/stories/" pollInterval={5000} /> + <StoryForm onStorySubmit={this.handleStorySubmit} /> + </div> + ); + } +}); + +React.renderComponent( + <StoryPage />, + document.getElementById('content') +); diff --git a/js/scrumelo.js b/js/scrumelo.js deleted file mode 100644 index e408aa7..0000000 --- a/js/scrumelo.js +++ /dev/null @@ -1,24 +0,0 @@ -(function ($) { - $(document).ready(function () { - $(".hide").hide(); - $(".toggle").click(function () { - $(document.getElementById($(this).data("show"))).toggle(); - }); - }); -})(jQuery); - -function get_story_info(element) { - var id = element.id; - var data_element = $(element).parent().find(".data"); - - if (data_element.length > 0) - data_element.remove(); - else - $.get('/stories/' + id, null, - function (data, textStatus, jqXHR) { - $(element).after("<div class=\"data\">" + - "Assignee: " + data.Assignee + - "<pre>" + data.content + "</pre>" + - "</div>"); - }, 'json'); -} |