diff options
Diffstat (limited to 'js/main.js')
-rw-r--r-- | js/main.js | 162 |
1 files changed, 162 insertions, 0 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') +); |