VersionOne API 101: An Agile User Story Editor in 101 Lines

Build a simple, open-source agile user story editor in exactly 80 lines of CoffeeScript using Backbone.js, Backbone Forms, jQuery, and the VersionOne REST API! You could easily adapt this to save your user story into any tool of your choice. Note: the same thing takes 101 lines in JavaScript!

by JoshGough

HTML

<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
<script src="http://documentcloud.github.com/backbone/backbone-min.js"></script>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/backbone-forms.js"></script>
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/editors/list.js"></script>
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/adapters/backbone.bootstrap-modal.js"></script>
<script src="https://raw.github.com/powmedia/backbone-forms/master/distribution/templates/bootstrap.js"></script>
<html>
    <head>
        <title>Barebones Story Editor</title>
    </head>
    <body>
        <h1>Barebones Story Editor</h1>
        <div id="editor">
            <form id="editorForm">
                <h4>Story Details</h4>
                <hr />
              	<div id="editorFields"></div>
            </form>
            <button id="save">Save Story</button> <span id="message"></span><span id="error"></span>
        </div>
        <h2>Enter a Story ID</h2>
        <input type="text" id="storyId" value="1154" /> (Hint: use 1154 if don't know another...)
        <br />
        <button id="storyGet">Load Story</button>
        <hr/>
        Visit the <a href="http://community.versionone.com/default.aspx">VersionOne Community</a> for more open source tools and APIs. Download code and <b>get involved</b> at <a href="http://www.github.com/VersionOne" target="_blank">VersionOne on GitHub</a>!
        <br/>
    </body>
</html>

CSS

body 
{
    padding: 5px;
    font-family: sans-serif;
}  

#editor
{
    padding: 10px;
    border: 1px solid darkblue;
    background: whitesmoke;
    display: none;
}

h4 { 
    color: #666666;
    font-style: italic;
}


label 
{
    color: darkblue;
}

textarea 
{
  height:100px;
}

#message 
{
  margin-top: 5px;
  color: darkgreen;
}

#storyIdLabel {
    font-weight: bold;
}

#message {
    display: none;
    font-weight: bold;
    color: darkgreen;
}

#error {
    display: none;
    font-weight: bold;
    color: red;
}

CoffeeScript

StoryFormSchema =
  Name:
    validators: ['required']
  Description:
    type: 'TextArea'
  Benefits:
    type: 'TextArea'
  Estimate:
    type: 'Number'
  RequestedBy: {}
    
storyForm = null

urlRoot = 'http://eval.versionone.net/platformtest/rest-1.v1/Data/Story/'

headers =
  Authorization: 'Basic ' + btoa('admin:admin')
  Accept: 'haljson'

StoryModel = Backbone.Model.extend(
  urlRoot: urlRoot
  url: ->
    return @urlRoot + @id if @hasChanged() and not @isNew()
    @urlRoot + @id + '?sel=' + _.keys(storyForm.schema).join(',')
  fetch: (options) ->
    options or (options = {})
    _.defaults options, { dataType: 'json', headers: headers }
    Backbone.Model::fetch.call this, options
  save: (attributes, options) ->
    options or (options = {})
    _.defaults options, { contentType: 'haljson', patch: true, headers: headers }
    Backbone.Model::save.call this, attributes, options
)

Backbone.emulateHTTP = true
storyModel = new StoryModel
  
createForm = (model) ->
  finish = ->
    storyForm = new Backbone.Form(settings)
    $('#editorFields').empty()
    $('#editorFields').append storyForm.render().el
    $('#editor').fadeIn()  if model
  settings = schema: StoryFormSchema
  if model
    model.fetch().done (data) ->
      settings.model = model
      finish()
  else
    finish()
    
storyLoad = ->
  storyModel.id = $('#storyId').val()
  if storyModel.id is ''
    alert 'Please enter a story id first'
    return
  createForm storyModel

storySave = ->
  return if storyForm.validate()?
  storyForm.commit()
  storyModel.save(storyForm.getValue()).done((data) ->
    $('#error').hide()
    $('#message').text('Story saved!').fadeIn().delay(2500).fadeOut()
  ).fail (jqXHR) ->
    $('#message').hide()
    $('#error').text('Error during save! See console for details.').fadeIn().delay(5e3).fadeOut()
    console.log jqXHR

$ ->
  createForm()
  $('#storyGet').click storyLoad
  $('#save').click storySave