router loading projects from local storage
This commit is contained in:
parent
1cee7fe269
commit
be2acdd564
|
@ -37,7 +37,7 @@ module.exports = (grunt) ->
|
|||
'vendor/firebase/firebase.js'
|
||||
'vendor/firebase-simple-login/firebase-simple-login.js'
|
||||
'vendor/superagent/superagent.js'
|
||||
'vendor/localforage/dist/localforage.js'
|
||||
'vendor/lscache/lscache.js'
|
||||
'vendor/async/lib/async.js'
|
||||
'vendor/moment/moment.js'
|
||||
'vendor/d3/d3.js'
|
||||
|
|
3
TODO.md
3
TODO.md
|
@ -4,8 +4,7 @@
|
|||
|
||||
###Main
|
||||
|
||||
1. Load projects from `localStorage` (use sync). If we are on *Project* or *Chart* page, add it behind the scenes.
|
||||
1. Now we fetch all milestones for our repo if we don't have any cached already. This calculates the points for each milestone. *The first two steps can happen on a Router level*
|
||||
1. Now we fetch all milestones for our repo if we don't have any cached already. This calculates the points for each milestone.
|
||||
1. Continue with page-specific actions.
|
||||
|
||||
###GitHub
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
"ractive-ractive": "rstacruz/ractive-ractive",
|
||||
"firebase": "~1.0.21",
|
||||
"firebase-simple-login": "~1.6.3",
|
||||
"localforage": "~0.9.2",
|
||||
"superagent": "~0.19.0",
|
||||
"async": "~0.9.0",
|
||||
"moment": "~2.8.3",
|
||||
|
@ -17,6 +16,7 @@
|
|||
"d3-tip": "~0.6.5",
|
||||
"marked": "~0.3.2",
|
||||
"director": "~1.2.2",
|
||||
"ractive-transitions-fade": "~0.1.2"
|
||||
"ractive-transitions-fade": "~0.1.2",
|
||||
"lscache": "~1.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -442,7 +442,9 @@ table {
|
|||
.d3-tip:after{width:100%;color:rgba(0,0,0,0.8);content:"\25BC";position:absolute}
|
||||
.d3-tip.n:after{margin:-3px 0 0 0;top:100%;left:0}
|
||||
|
||||
html,body{margin:0;padding:0;height:100%}
|
||||
body{color:#3e4457;font-family:'MuseoSans500Regular',sans-serif}
|
||||
#app{position:relative;height:auto !important;min-height:100%}
|
||||
a{text-decoration:none;color:#aaafbf;cursor:pointer}
|
||||
h1,h2,h3,p{margin:0}
|
||||
ul{list-style-type:none;margin:0;padding:0;}
|
||||
|
@ -519,4 +521,4 @@ ul li{display:inline-block}
|
|||
#content #projects .header a{font-family:'MuseoSlab500Regular',serif}
|
||||
#content #projects .footer{background:#f9fafb;color:#aaafbf;-webkit-box-shadow:inset 0 1px 2px rgba(221,225,237,0.2);box-shadow:inset 0 1px 2px rgba(221,225,237,0.2);border-top:1px solid #dde1ed;text-align:right;font-family:'MuseoSlab500Regular',serif;}
|
||||
#content #projects .footer .icon{color:#aaafbf}
|
||||
#footer{border-top:1px solid #f3f4f8;text-align:center;padding:30px;margin-top:30px;font-family:'MuseoSlab500Regular',serif}
|
||||
#footer{position:absolute;width:100%;bottom:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border-top:1px solid #f3f4f8;text-align:center;padding:30px;margin-top:30px;font-family:'MuseoSlab500Regular',serif}
|
||||
|
|
|
@ -35,7 +35,9 @@
|
|||
.d3-tip:after{width:100%;color:rgba(0,0,0,0.8);content:"\25BC";position:absolute}
|
||||
.d3-tip.n:after{margin:-3px 0 0 0;top:100%;left:0}
|
||||
|
||||
html,body{margin:0;padding:0;height:100%}
|
||||
body{color:#3e4457;font-family:'MuseoSans500Regular',sans-serif}
|
||||
#app{position:relative;height:auto !important;min-height:100%}
|
||||
a{text-decoration:none;color:#aaafbf;cursor:pointer}
|
||||
h1,h2,h3,p{margin:0}
|
||||
ul{list-style-type:none;margin:0;padding:0;}
|
||||
|
@ -112,4 +114,4 @@ ul li{display:inline-block}
|
|||
#content #projects .header a{font-family:'MuseoSlab500Regular',serif}
|
||||
#content #projects .footer{background:#f9fafb;color:#aaafbf;-webkit-box-shadow:inset 0 1px 2px rgba(221,225,237,0.2);box-shadow:inset 0 1px 2px rgba(221,225,237,0.2);border-top:1px solid #dde1ed;text-align:right;font-family:'MuseoSlab500Regular',serif;}
|
||||
#content #projects .footer .icon{color:#aaafbf}
|
||||
#footer{border-top:1px solid #f3f4f8;text-align:center;padding:30px;margin-top:30px;font-family:'MuseoSlab500Regular',serif}
|
||||
#footer{position:absolute;width:100%;bottom:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;border-top:1px solid #f3f4f8;text-align:center;padding:30px;margin-top:30px;font-family:'MuseoSlab500Regular',serif}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
226
public/js/app.js
226
public/js/app.js
|
@ -22,7 +22,7 @@
|
|||
router = require('./modules/router');
|
||||
|
||||
App = Ractive.extend({
|
||||
'template': require('./templates/layout'),
|
||||
'template': require('./templates/app'),
|
||||
'components': {
|
||||
Header: Header,
|
||||
Notify: Notify
|
||||
|
@ -44,6 +44,7 @@
|
|||
Model = require('../utils/model');
|
||||
|
||||
module.exports = new Model({
|
||||
'name': 'models/config',
|
||||
"data": {
|
||||
"firebase": "burnchart",
|
||||
"provider": "github",
|
||||
|
@ -80,46 +81,31 @@
|
|||
user = require('./user');
|
||||
|
||||
module.exports = new Model({
|
||||
'data': {
|
||||
'list': []
|
||||
'name': 'models/projects',
|
||||
find: function(project) {
|
||||
return _.find(this.data.list, project);
|
||||
},
|
||||
load: function(projects) {
|
||||
if (projects == null) {
|
||||
projects = [];
|
||||
exists: function() {
|
||||
return !!this.find.apply(this, arguments);
|
||||
},
|
||||
add: function(project) {
|
||||
if (!this.exists(project)) {
|
||||
return this.push('list', project);
|
||||
}
|
||||
return async.each(projects, function(project, cb) {
|
||||
return mediator.fire('!projects/add', project);
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
},
|
||||
add: function(repo, done) {
|
||||
var _this = this;
|
||||
return request.allMilestones(repo, function(err, res) {
|
||||
var milestones;
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
milestones = _.pluckMany(res, config.get('fields.milestone'));
|
||||
_this.push('list', _.merge(repo, {
|
||||
milestones: milestones
|
||||
}));
|
||||
return done();
|
||||
});
|
||||
},
|
||||
clear: function() {
|
||||
return this.set('list', []);
|
||||
},
|
||||
onconstruct: function() {
|
||||
localforage.getItem('projects', _.bind(this.load, this));
|
||||
mediator.on('!projects/add', _.bind(this.add, this));
|
||||
return mediator.on('!projects/clear', _.bind(this.clear, this));
|
||||
},
|
||||
onrender: function() {
|
||||
this.set('list', lscache.get('projects') || []);
|
||||
return this.observe('list', function(projects) {
|
||||
return localforage.setItem('projects', projects);
|
||||
return lscache.set('projects', _.pluckMany(projects, ['owner', 'name']));
|
||||
}, {
|
||||
'init': false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -136,6 +122,7 @@
|
|||
Model = require('../utils/model');
|
||||
|
||||
system = new Model({
|
||||
'name': 'models/system',
|
||||
'data': {
|
||||
'loading': false
|
||||
}
|
||||
|
@ -169,6 +156,7 @@
|
|||
Model = require('../utils/model');
|
||||
|
||||
module.exports = new Model({
|
||||
'name': 'models/user',
|
||||
'data': {
|
||||
'provider': "local",
|
||||
'id': "0",
|
||||
|
@ -500,7 +488,11 @@
|
|||
// mediator.coffee
|
||||
root.require.register('burnchart/src/modules/mediator.js', function(exports, require, module) {
|
||||
|
||||
module.exports = new Ractive();
|
||||
var Mediator;
|
||||
|
||||
Mediator = Ractive.extend({});
|
||||
|
||||
module.exports = new Mediator();
|
||||
|
||||
});
|
||||
|
||||
|
@ -523,26 +515,12 @@
|
|||
}
|
||||
return cb(null, null, m);
|
||||
});
|
||||
} else {
|
||||
return request.allMilestones(repo, function(err, data) {
|
||||
var m;
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
if (!data.length) {
|
||||
return cb(null, "No open milestones for repo " + repo.path);
|
||||
}
|
||||
m = data[0];
|
||||
m = _.rest(data, {
|
||||
'due_on': null
|
||||
});
|
||||
m = m[0] ? m[0] : data[0];
|
||||
if (m.open_issues + m.closed_issues === 0) {
|
||||
return cb(null, "No issues for milestone `" + m.title + "`");
|
||||
}
|
||||
return cb(null, null, m);
|
||||
});
|
||||
}
|
||||
},
|
||||
getAll: function(repo, cb) {
|
||||
return request.allMilestones(repo, function(err, data) {
|
||||
return cb(err, null, data);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -764,7 +742,7 @@
|
|||
// router.coffee
|
||||
root.require.register('burnchart/src/modules/router.js', function(exports, require, module) {
|
||||
|
||||
var el, mediator, route, routes, system,
|
||||
var addProject, c, el, mediator, route, routes, system, view,
|
||||
__slice = [].slice;
|
||||
|
||||
mediator = require('./mediator');
|
||||
|
@ -773,11 +751,37 @@
|
|||
|
||||
el = '#page';
|
||||
|
||||
addProject = function(page, owner, name) {
|
||||
return mediator.fire('!projects/add', {
|
||||
owner: owner,
|
||||
name: name
|
||||
});
|
||||
};
|
||||
|
||||
c = function(name, fns) {
|
||||
var fn, _i, _len, _results;
|
||||
if (fns == null) {
|
||||
fns = [];
|
||||
}
|
||||
_results = [];
|
||||
for (_i = 0, _len = fns.length; _i < _len; _i++) {
|
||||
fn = fns[_i];
|
||||
_results.push(_.partial(fn, name));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
view = null;
|
||||
|
||||
route = function() {
|
||||
var Page, args, page;
|
||||
page = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
if (view != null) {
|
||||
view.teardown();
|
||||
}
|
||||
mediator.fire('!app/notify/hide');
|
||||
Page = require("../views/pages/" + page);
|
||||
return new Page({
|
||||
return view = new Page({
|
||||
el: el,
|
||||
'data': {
|
||||
'route': args
|
||||
|
@ -786,10 +790,10 @@
|
|||
};
|
||||
|
||||
routes = {
|
||||
'/': _.partial(route, 'index'),
|
||||
'/new/project': _.partial(route, 'new'),
|
||||
'/:owner/:name': _.partial(route, 'project'),
|
||||
'/:owner/:name/:milestone': _.partial(route, 'chart'),
|
||||
'/': c('index', [route]),
|
||||
'/new/project': c('new', [route]),
|
||||
'/:owner/:name': c('project', [addProject, route]),
|
||||
'/:owner/:name/:milestone': c('chart', [addProject, route]),
|
||||
'/reset': function() {
|
||||
mediator.fire('!projects/clear');
|
||||
return window.location.hash = '#';
|
||||
|
@ -807,11 +811,20 @@
|
|||
};
|
||||
|
||||
module.exports = Router(routes).configure({
|
||||
'strict': false
|
||||
'strict': false,
|
||||
notfound: function() {
|
||||
throw 404;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// app.mustache
|
||||
root.require.register('burnchart/src/templates/app.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"app\">"," <Notify/>"," <Header/>",""," <div id=\"page\">"," <!-- content loaded from a router -->"," </div>",""," <div id=\"footer\">"," <div class=\"wrap\">"," © 2012-2014 <a href=\"http://cloudfi.re\">Cloudfire Systems</a>"," </div>"," </div>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// header.mustache
|
||||
root.require.register('burnchart/src/templates/header.js', function(exports, require, module) {
|
||||
|
||||
|
@ -830,16 +843,10 @@
|
|||
module.exports = ["{{#code}}"," <span class=\"icon {{icon}}\">{{{ '&#' + code + ';' }}}</span>","{{/code}}"].join("\n");
|
||||
});
|
||||
|
||||
// layout.mustache
|
||||
root.require.register('burnchart/src/templates/layout.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<Notify/>","<Header/>","","<div id=\"page\">"," <!-- content loaded from a router -->","</div>","","<div id=\"footer\">"," <div class=\"wrap\">"," © 2012-2014 <a href=\"http://cloudfi.re\">Cloudfire Systems</a>"," </div>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// milestones.mustache
|
||||
root.require.register('burnchart/src/templates/milestones.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["{{#milestones.length}}"," <div id=\"projects\">"," <div class=\"header\">"," <a href=\"#\" class=\"sort\"><Icons icon=\"sort-alphabet\"/> Sorted by priority</a>"," <h2>Milestones</h2>"," </div>",""," <table>"," {{#milestones}}"," <tr>"," <td>"," <a class=\"milestone\" href=\"#{{owner}}/{{name}}/{{number}}\">{{ title }}</a>"," </td>"," <td style=\"width:1%\">"," <div class=\"progress\">"," <span class=\"percent\">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>"," <span class=\"due\">{{{ format.due(due_on) }}}</span>"," <div class=\"outer bar\">"," <div class=\"inner bar {{format.onTime(this)}}\" style=\"width:{{format.progress(closed_issues, open_issues)}}%\"></div>"," </div>"," </div>"," </td>"," </tr>"," {{/milestones}}"," </table>",""," <div class=\"footer\">"," <a href=\"#\"><Icons icon=\"cog\"/> Edit</a>"," </div>"," </div>","{{/milestones.length}}"].join("\n");
|
||||
module.exports = ["<div id=\"projects\">"," <div class=\"header\">"," <a href=\"#\" class=\"sort\"><Icons icon=\"sort-alphabet\"/> Sorted by priority</a>"," <h2>Milestones</h2>"," </div>",""," <table>"," {{#project.milestones}}"," <tr>"," <td>"," <a class=\"milestone\" href=\"#{{project.owner}}/{{project.name}}/{{number}}\">{{ title }}</a>"," </td>"," <td style=\"width:1%\">"," <div class=\"progress\">"," <span class=\"percent\">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>"," <span class=\"due\">{{{ format.due(due_on) }}}</span>"," <div class=\"outer bar\">"," <div class=\"inner bar {{format.onTime(this)}}\" style=\"width:{{format.progress(closed_issues, open_issues)}}%\"></div>"," </div>"," </div>"," </td>"," </tr>"," {{/project.milestones}}"," </table>",""," <div class=\"footer\">"," <a href=\"#\"><Icons icon=\"cog\"/> Edit</a>"," </div>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// notify.mustache
|
||||
|
@ -869,7 +876,7 @@
|
|||
// project.mustache
|
||||
root.require.register('burnchart/src/templates/pages/project.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"title\">"," <div class=\"wrap\">"," <h2 class=\"title\">{{route.join('/')}}</h2>"," </div>","</div>","","<div id=\"content\" class=\"wrap\">"," <Milestones owner=\"{{route[0]}}\" name=\"{{route[1]}}\"/>","</div>"].join("\n");
|
||||
module.exports = ["{{#ready}}"," <div id=\"title\">"," <div class=\"wrap\">"," <h2 class=\"title\">{{route.join('/')}}</h2>"," </div>"," </div>",""," <div id=\"content\" class=\"wrap\">"," <Milestones project=\"{{project}}\"/>"," </div>","{{/ready}}"].join("\n");
|
||||
});
|
||||
|
||||
// projects.mustache
|
||||
|
@ -1003,11 +1010,16 @@
|
|||
Icons = require('./icons');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'name': 'views/header',
|
||||
'template': require('../templates/header'),
|
||||
'data': {
|
||||
'user': user,
|
||||
'icon': 'fire-station'
|
||||
},
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive],
|
||||
onconstruct: function() {
|
||||
return this.on('!login', function() {
|
||||
return firebase.login(function(err) {
|
||||
|
@ -1022,11 +1034,7 @@
|
|||
return system.observe('loading', function(ya) {
|
||||
return _this.set('icon', ya ? 'spinner1' : 'fire-station');
|
||||
});
|
||||
},
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive]
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1043,6 +1051,7 @@
|
|||
Icons = require('./icons');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'name': 'views/hero',
|
||||
'template': require('../templates/hero'),
|
||||
'data': {
|
||||
projects: projects
|
||||
|
@ -1078,6 +1087,7 @@
|
|||
};
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'name': 'views/icons',
|
||||
'template': require('../templates/icons'),
|
||||
'isolated': true,
|
||||
onrender: function() {
|
||||
|
@ -1106,17 +1116,12 @@
|
|||
Icons = require('./icons');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'name': 'views/milestones',
|
||||
'template': require('../templates/milestones'),
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive],
|
||||
onconstruct: function() {
|
||||
return this.set('milestones', _.filter(projects.get('list'), {
|
||||
'owner': this.get('owner'),
|
||||
'name': this.get('name')
|
||||
}));
|
||||
}
|
||||
'adapt': [Ractive.adaptors.Ractive]
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1133,6 +1138,7 @@
|
|||
HEIGHT = 68;
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'name': 'views/notify',
|
||||
'template': require('../templates/notify'),
|
||||
'data': {
|
||||
'top': HEIGHT,
|
||||
|
@ -1145,6 +1151,10 @@
|
|||
'ttl': 5e3
|
||||
}
|
||||
},
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive],
|
||||
show: function(opts) {
|
||||
var pos;
|
||||
this.set('hidden', false);
|
||||
|
@ -1176,11 +1186,7 @@
|
|||
mediator.on('!app/notify', _.bind(this.show, this));
|
||||
mediator.on('!app/notify/hide', _.bind(this.hide, this));
|
||||
return this.on('close', this.hide);
|
||||
},
|
||||
'components': {
|
||||
Icons: Icons
|
||||
},
|
||||
'adapt': [Ractive.adaptors.Ractive]
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1199,6 +1205,7 @@
|
|||
format = require('../../utils/format');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'name': 'views/pages/chart',
|
||||
'template': require('../../templates/pages/chart'),
|
||||
'adapt': [Ractive.adaptors.Ractive],
|
||||
'data': {
|
||||
|
@ -1207,6 +1214,7 @@
|
|||
onrender: function() {
|
||||
var name, owner, route, _ref,
|
||||
_this = this;
|
||||
return;
|
||||
_ref = this.get('route'), owner = _ref[0], name = _ref[1], milestone = _ref[2];
|
||||
route = {
|
||||
owner: owner,
|
||||
|
@ -1246,6 +1254,7 @@
|
|||
format = require('../../utils/format');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'name': 'views/pages/index',
|
||||
'template': require('../../templates/pages/index'),
|
||||
'components': {
|
||||
Hero: Hero,
|
||||
|
@ -1275,6 +1284,7 @@
|
|||
key = require('../../utils/key');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'name': 'views/pages/new',
|
||||
'template': require('../../templates/pages/new'),
|
||||
'data': {
|
||||
'value': 'radekstepan/disposable',
|
||||
|
@ -1317,19 +1327,66 @@
|
|||
// project.coffee
|
||||
root.require.register('burnchart/src/views/pages/project.js', function(exports, require, module) {
|
||||
|
||||
var Milestones;
|
||||
var Milestones, mediator, milestone, projects, system;
|
||||
|
||||
Milestones = require('../milestones');
|
||||
|
||||
projects = require('../../models/projects');
|
||||
|
||||
system = require('../../models/system');
|
||||
|
||||
milestone = require('../../modules/milestone');
|
||||
|
||||
mediator = require('../../modules/mediator');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'name': 'views/pages/project',
|
||||
'template': require('../../templates/pages/project'),
|
||||
'components': {
|
||||
Milestones: Milestones
|
||||
},
|
||||
'data': {
|
||||
'ready': false
|
||||
},
|
||||
onrender: function() {
|
||||
var name, owner, _ref;
|
||||
var done, name, owner, project, _ref,
|
||||
_this = this;
|
||||
_ref = this.get('route'), owner = _ref[0], name = _ref[1];
|
||||
return document.title = "" + owner + "/" + name;
|
||||
document.title = "" + owner + "/" + name;
|
||||
this.set('project', project = projects.find({
|
||||
owner: owner,
|
||||
name: name
|
||||
}));
|
||||
if (!project) {
|
||||
throw 500;
|
||||
}
|
||||
if (project.milestones) {
|
||||
return this.set('ready', true);
|
||||
}
|
||||
done = system.async();
|
||||
return milestone.getAll(project, function(err, warn, list) {
|
||||
done();
|
||||
if (err) {
|
||||
return mediator.fire('!app/notify', {
|
||||
'text': err.toString(),
|
||||
'type': 'alert',
|
||||
'system': true,
|
||||
'ttl': null
|
||||
});
|
||||
}
|
||||
if (warn) {
|
||||
return mediator.fire('!app/notify', {
|
||||
'text': warn.toString(),
|
||||
'type': 'warn',
|
||||
'system': true,
|
||||
'ttl': null
|
||||
});
|
||||
}
|
||||
return _this.set({
|
||||
'project.milestones': list,
|
||||
'ready': true
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1347,6 +1404,7 @@
|
|||
Icons = require('./icons');
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'name': 'views/projects',
|
||||
'template': require('../templates/projects'),
|
||||
'data': {
|
||||
projects: projects
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
( require "./#{key}" for key in [
|
||||
'utils/mixins'
|
||||
'models/projects'
|
||||
'models/projects' # will load projects from localStorage
|
||||
] )
|
||||
|
||||
|
||||
Header = require './views/header'
|
||||
Notify = require './views/notify'
|
||||
router = require './modules/router'
|
||||
|
||||
App = Ractive.extend
|
||||
|
||||
'template': require './templates/layout'
|
||||
'template': require './templates/app'
|
||||
|
||||
'components': { Header, Notify }
|
||||
|
||||
onrender: ->
|
||||
# Start the router.
|
||||
router.init '/'
|
||||
|
||||
module.exports = new App()
|
|
@ -2,6 +2,8 @@ Model = require '../utils/model'
|
|||
|
||||
module.exports = new Model
|
||||
|
||||
'name': 'models/config'
|
||||
|
||||
"data":
|
||||
# Firebase app name.
|
||||
"firebase": "burnchart"
|
||||
|
|
|
@ -7,44 +7,30 @@ user = require './user'
|
|||
|
||||
module.exports = new Model
|
||||
|
||||
'data':
|
||||
'list': []
|
||||
'name': 'models/projects'
|
||||
|
||||
load: (projects=[]) ->
|
||||
# Fetch milestones for each of these projects.
|
||||
async.each projects, (project, cb) ->
|
||||
mediator.fire '!projects/add', project
|
||||
, (err) ->
|
||||
throw err if err
|
||||
find: (project) ->
|
||||
_.find @data.list, project
|
||||
|
||||
add: (repo, done) ->
|
||||
# TODO: warn when we are adding an existing repo (or
|
||||
# silently go to index again).
|
||||
exists: ->
|
||||
!!@find.apply @, arguments
|
||||
|
||||
# Fetch milestones (which validates repo too).
|
||||
request.allMilestones repo, (err, res) =>
|
||||
return done err if err
|
||||
|
||||
# Pluck these fields for milestones.
|
||||
milestones = _.pluckMany res, config.get('fields.milestone')
|
||||
|
||||
# Push to the stack.
|
||||
@push 'list', _.merge repo, { milestones }
|
||||
|
||||
# Call back.
|
||||
do done
|
||||
# Push to the stack unless it exists already.
|
||||
add: (project) ->
|
||||
@push 'list', project unless @exists project
|
||||
|
||||
clear: ->
|
||||
@set 'list', []
|
||||
|
||||
onconstruct: ->
|
||||
# Initialize with items stored locally.
|
||||
localforage.getItem 'projects', _.bind @load, @
|
||||
|
||||
mediator.on '!projects/add', _.bind @add, @
|
||||
mediator.on '!projects/clear', _.bind @clear, @
|
||||
mediator.on '!projects/add', _.bind @add, @
|
||||
mediator.on '!projects/clear', _.bind @clear, @
|
||||
|
||||
onrender: ->
|
||||
# Persist projects in local storage.
|
||||
# Init the projects.
|
||||
@set 'list', lscache.get('projects') or []
|
||||
|
||||
# Persist projects in local storage (sans milestones).
|
||||
@observe 'list', (projects) ->
|
||||
localforage.setItem 'projects', projects
|
||||
lscache.set 'projects', _.pluckMany projects, [ 'owner', 'name' ]
|
||||
, 'init': no
|
|
@ -3,6 +3,9 @@ Model = require '../utils/model'
|
|||
|
||||
# System state.
|
||||
system = new Model
|
||||
|
||||
'name': 'models/system'
|
||||
|
||||
'data':
|
||||
'loading': no
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ Model = require '../utils/model'
|
|||
# Currently logged-in user.
|
||||
module.exports = new Model
|
||||
|
||||
'name': 'models/user'
|
||||
|
||||
# Default to a local user.
|
||||
'data':
|
||||
'provider': "local"
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
module.exports = new Ractive()
|
||||
Mediator = Ractive.extend {}
|
||||
|
||||
module.exports = new Mediator()
|
|
@ -2,7 +2,8 @@
|
|||
request = require './request'
|
||||
|
||||
module.exports =
|
||||
# Get current/specified milestone for a repo.
|
||||
|
||||
# Get a specific milestone for a repo.
|
||||
get: (repo, cb) ->
|
||||
# Get a specific milestone.
|
||||
if repo.milestone
|
||||
|
@ -15,21 +16,26 @@ module.exports =
|
|||
|
||||
cb null, null, m
|
||||
|
||||
# Get the current milestone out of many.
|
||||
else
|
||||
request.allMilestones repo, (err, data) ->
|
||||
# Errors?
|
||||
return cb err if err
|
||||
# Empty warning?
|
||||
return cb null, "No open milestones for repo #{repo.path}" unless data.length
|
||||
# The first milestone should be ending soonest.
|
||||
m = data[0]
|
||||
# Filter milestones without due date.
|
||||
m = _.rest data, { 'due_on' : null }
|
||||
# The first milestone should be ending soonest. Prefer milestones with due dates.
|
||||
m = if m[0] then m[0] else data[0]
|
||||
# Empty milestone?
|
||||
if m.open_issues + m.closed_issues is 0
|
||||
return cb null, "No issues for milestone `#{m.title}`"
|
||||
# Get all milestones.
|
||||
getAll: (repo, cb) ->
|
||||
request.allMilestones repo, (err, data) ->
|
||||
cb err, null, data
|
||||
|
||||
cb null, null, m
|
||||
# # Get the current milestone out of many.
|
||||
# else
|
||||
# request.allMilestones repo, (err, data) ->
|
||||
# # Errors?
|
||||
# return cb err if err
|
||||
# # Empty warning?
|
||||
# return cb null, "No open milestones for repo #{repo.path}" unless data.length
|
||||
# # The first milestone should be ending soonest.
|
||||
# m = data[0]
|
||||
# # Filter milestones without due date.
|
||||
# m = _.rest data, { 'due_on' : null }
|
||||
# # The first milestone should be ending soonest. Prefer milestones with due dates.
|
||||
# m = if m[0] then m[0] else data[0]
|
||||
# # Empty milestone?
|
||||
# if m.open_issues + m.closed_issues is 0
|
||||
# return cb null, "No issues for milestone `#{m.title}`"
|
||||
|
||||
# cb null, null, m
|
|
@ -3,15 +3,31 @@ system = require '../models/system'
|
|||
|
||||
el = '#page'
|
||||
|
||||
# Add a project from a route.
|
||||
addProject = (page, owner, name) ->
|
||||
mediator.fire '!projects/add', { owner, name }
|
||||
|
||||
# Preapply all functions with our page name/context.
|
||||
c = (name, fns=[]) ->
|
||||
( _.partial fn, name for fn in fns )
|
||||
|
||||
view = null
|
||||
route = (page, args...) ->
|
||||
# Unrender the previous one.
|
||||
do view?.teardown
|
||||
# Hide any notifications.
|
||||
mediator.fire '!app/notify/hide'
|
||||
# Require the new one.
|
||||
Page = require "../views/pages/#{page}"
|
||||
new Page { el, 'data': { 'route': args } }
|
||||
# Render it.
|
||||
view = new Page { el, 'data': { 'route': args } }
|
||||
|
||||
routes =
|
||||
'/': _.partial route, 'index'
|
||||
'/new/project': _.partial route, 'new'
|
||||
'/:owner/:name': _.partial route, 'project'
|
||||
'/:owner/:name/:milestone': _.partial route, 'chart'
|
||||
'/': c 'index', [ route ]
|
||||
'/new/project': c 'new', [ route ]
|
||||
# The following two routes add a project in the background.
|
||||
'/:owner/:name': c 'project', [ addProject, route ]
|
||||
'/:owner/:name/:milestone': c 'chart', [ addProject, route ]
|
||||
# TODO: remove in production.
|
||||
'/reset': ->
|
||||
mediator.fire '!projects/clear'
|
||||
|
@ -29,4 +45,6 @@ routes =
|
|||
|
||||
# Flatiron Director router.
|
||||
module.exports = Router(routes).configure
|
||||
'strict': no # allow trailing slashes
|
||||
'strict': no # allow trailing slashes
|
||||
notfound: ->
|
||||
throw 404
|
|
@ -6,10 +6,20 @@ $sans_serif_font = 'MuseoSans500Regular', sans-serif
|
|||
$strong_color = #C1041C
|
||||
$highlight_color = #FFBB2A
|
||||
|
||||
html, body
|
||||
margin: 0
|
||||
padding: 0
|
||||
height: 100%
|
||||
|
||||
body
|
||||
color: #3E4457
|
||||
font-family: $sans_serif_font
|
||||
|
||||
#app
|
||||
position: relative
|
||||
height: auto !important
|
||||
min-height: 100%
|
||||
|
||||
a
|
||||
text-decoration: none
|
||||
color: #AAAFBF
|
||||
|
@ -373,6 +383,10 @@ ul
|
|||
color: #AAAFBF
|
||||
|
||||
#footer
|
||||
position: absolute
|
||||
width: 100%
|
||||
bottom: 0
|
||||
box-sizing: border-box
|
||||
border-top: 1px solid #F3F4F8
|
||||
text-align: center
|
||||
padding: 30px
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<div id="app">
|
||||
<Notify/>
|
||||
<Header/>
|
||||
|
||||
<div id="page">
|
||||
<!-- content loaded from a router -->
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<div class="wrap">
|
||||
© 2012-2014 <a href="http://cloudfi.re">Cloudfire Systems</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,12 +0,0 @@
|
|||
<Notify/>
|
||||
<Header/>
|
||||
|
||||
<div id="page">
|
||||
<!-- content loaded from a router -->
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
<div class="wrap">
|
||||
© 2012-2014 <a href="http://cloudfi.re">Cloudfire Systems</a>
|
||||
</div>
|
||||
</div>
|
|
@ -1,31 +1,29 @@
|
|||
{{#milestones.length}}
|
||||
<div id="projects">
|
||||
<div class="header">
|
||||
<a href="#" class="sort"><Icons icon="sort-alphabet"/> Sorted by priority</a>
|
||||
<h2>Milestones</h2>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
{{#milestones}}
|
||||
<tr>
|
||||
<td>
|
||||
<a class="milestone" href="#{{owner}}/{{name}}/{{number}}">{{ title }}</a>
|
||||
</td>
|
||||
<td style="width:1%">
|
||||
<div class="progress">
|
||||
<span class="percent">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>
|
||||
<span class="due">{{{ format.due(due_on) }}}</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar {{format.onTime(this)}}" style="width:{{format.progress(closed_issues, open_issues)}}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/milestones}}
|
||||
</table>
|
||||
|
||||
<div class="footer">
|
||||
<a href="#"><Icons icon="cog"/> Edit</a>
|
||||
</div>
|
||||
<div id="projects">
|
||||
<div class="header">
|
||||
<a href="#" class="sort"><Icons icon="sort-alphabet"/> Sorted by priority</a>
|
||||
<h2>Milestones</h2>
|
||||
</div>
|
||||
{{/milestones.length}}
|
||||
|
||||
<table>
|
||||
{{#project.milestones}}
|
||||
<tr>
|
||||
<td>
|
||||
<a class="milestone" href="#{{project.owner}}/{{project.name}}/{{number}}">{{ title }}</a>
|
||||
</td>
|
||||
<td style="width:1%">
|
||||
<div class="progress">
|
||||
<span class="percent">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>
|
||||
<span class="due">{{{ format.due(due_on) }}}</span>
|
||||
<div class="outer bar">
|
||||
<div class="inner bar {{format.onTime(this)}}" style="width:{{format.progress(closed_issues, open_issues)}}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/project.milestones}}
|
||||
</table>
|
||||
|
||||
<div class="footer">
|
||||
<a href="#"><Icons icon="cog"/> Edit</a>
|
||||
</div>
|
||||
</div>
|
|
@ -1,9 +1,11 @@
|
|||
<div id="title">
|
||||
<div class="wrap">
|
||||
<h2 class="title">{{route.join('/')}}</h2>
|
||||
{{#ready}}
|
||||
<div id="title">
|
||||
<div class="wrap">
|
||||
<h2 class="title">{{route.join('/')}}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="content" class="wrap">
|
||||
<Milestones owner="{{route[0]}}" name="{{route[1]}}"/>
|
||||
</div>
|
||||
<div id="content" class="wrap">
|
||||
<Milestones project="{{project}}"/>
|
||||
</div>
|
||||
{{/ready}}
|
|
@ -5,6 +5,8 @@ Icons = require './icons'
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'name': 'views/header'
|
||||
|
||||
'template': require '../templates/header'
|
||||
|
||||
'data':
|
||||
|
@ -12,6 +14,10 @@ module.exports = Ractive.extend
|
|||
# Default app icon.
|
||||
'icon': 'fire-station'
|
||||
|
||||
'components': { Icons }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
|
||||
onconstruct: ->
|
||||
# Login user.
|
||||
@on '!login', ->
|
||||
|
@ -21,8 +27,4 @@ module.exports = Ractive.extend
|
|||
onrender: ->
|
||||
# Switch loading icon with app icon.
|
||||
system.observe 'loading', (ya) =>
|
||||
@set 'icon', if ya then 'spinner1' else 'fire-station'
|
||||
|
||||
'components': { Icons }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
@set 'icon', if ya then 'spinner1' else 'fire-station'
|
|
@ -4,6 +4,8 @@ Icons = require './icons'
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'name': 'views/hero'
|
||||
|
||||
'template': require '../templates/hero'
|
||||
|
||||
'data': { projects }
|
||||
|
|
|
@ -17,6 +17,8 @@ codes =
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'name': 'views/icons'
|
||||
|
||||
'template': require '../templates/icons'
|
||||
|
||||
'isolated': yes
|
||||
|
|
|
@ -4,13 +4,10 @@ Icons = require './icons'
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'name': 'views/milestones'
|
||||
|
||||
'template': require '../templates/milestones'
|
||||
|
||||
'components': { Icons }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
|
||||
onconstruct: ->
|
||||
@set 'milestones', _.filter projects.get('list'),
|
||||
'owner': @get 'owner'
|
||||
'name': @get 'name'
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
|
@ -5,6 +5,8 @@ HEIGHT = 68 # height of div in px
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'name': 'views/notify'
|
||||
|
||||
'template': require '../templates/notify'
|
||||
|
||||
'data':
|
||||
|
@ -17,6 +19,10 @@ module.exports = Ractive.extend
|
|||
'icon': 'megaphone'
|
||||
'ttl': 5e3
|
||||
|
||||
'components': { Icons }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
|
||||
# Show a notification.
|
||||
show: (opts) ->
|
||||
@set 'hidden', no
|
||||
|
@ -52,8 +58,4 @@ module.exports = Ractive.extend
|
|||
mediator.on '!app/notify/hide', _.bind @hide, @
|
||||
|
||||
# Close us prematurely...
|
||||
@on 'close', @hide
|
||||
|
||||
'components': { Icons }
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
@on 'close', @hide
|
|
@ -5,6 +5,8 @@ format = require '../../utils/format'
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'name': 'views/pages/chart'
|
||||
|
||||
'template': require '../../templates/pages/chart'
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
|
@ -12,9 +14,11 @@ module.exports = Ractive.extend
|
|||
'data': { format }
|
||||
|
||||
onrender: ->
|
||||
return
|
||||
|
||||
[ owner, name, milestone ] = @get 'route'
|
||||
route = { owner, name, milestone }
|
||||
|
||||
|
||||
document.title = "#{owner}/#{name}/#{milestone}"
|
||||
|
||||
milestone.get route, (err, warn, obj) =>
|
||||
|
|
|
@ -4,6 +4,8 @@ format = require '../../utils/format'
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'name': 'views/pages/index'
|
||||
|
||||
'template': require '../../templates/pages/index'
|
||||
|
||||
'components': { Hero, Projects }
|
||||
|
|
|
@ -5,6 +5,8 @@ key = require '../../utils/key'
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'name': 'views/pages/new'
|
||||
|
||||
'template': require '../../templates/pages/new'
|
||||
|
||||
'data': { 'value': 'radekstepan/disposable', user }
|
||||
|
|
|
@ -1,12 +1,56 @@
|
|||
Milestones = require '../milestones'
|
||||
|
||||
projects = require '../../models/projects'
|
||||
system = require '../../models/system'
|
||||
milestone = require '../../modules/milestone'
|
||||
mediator = require '../../modules/mediator'
|
||||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'name': 'views/pages/project'
|
||||
|
||||
'template': require '../../templates/pages/project'
|
||||
|
||||
'components': { Milestones }
|
||||
|
||||
'data':
|
||||
'ready': no
|
||||
|
||||
onrender: ->
|
||||
[ owner, name ] = @get 'route'
|
||||
|
||||
document.title = "#{owner}/#{name}"
|
||||
|
||||
document.title = "#{owner}/#{name}"
|
||||
|
||||
# Get the associated project.
|
||||
@set 'project', project = projects.find { owner, name }
|
||||
|
||||
# Should not happen...
|
||||
throw 500 unless project
|
||||
|
||||
# Does it have milestones already?
|
||||
return @set('ready', yes) if project.milestones
|
||||
|
||||
# We are loading the milestones then.
|
||||
done = do system.async
|
||||
|
||||
milestone.getAll project, (err, warn, list) =>
|
||||
do done
|
||||
|
||||
return mediator.fire '!app/notify', {
|
||||
'text': do err.toString
|
||||
'type': 'alert'
|
||||
'system': yes
|
||||
'ttl': null
|
||||
} if err
|
||||
|
||||
return mediator.fire '!app/notify', {
|
||||
'text': do warn.toString
|
||||
'type': 'warn'
|
||||
'system': yes
|
||||
'ttl': null
|
||||
} if warn
|
||||
|
||||
# Save the milestones.
|
||||
@set
|
||||
'project.milestones': list
|
||||
'ready': yes
|
|
@ -4,6 +4,8 @@ Icons = require './icons'
|
|||
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'name': 'views/projects'
|
||||
|
||||
'template': require '../templates/projects'
|
||||
|
||||
'data': { projects }
|
||||
|
|
Loading…
Reference in New Issue