mirror of
https://github.com/status-im/burnchart.git
synced 2025-01-11 19:24:28 +00:00
show current milestone
This commit is contained in:
parent
ec99547712
commit
0823078eb5
@ -34,9 +34,10 @@ module.exports = (grunt) ->
|
|||||||
'vendor/firebase/firebase.js'
|
'vendor/firebase/firebase.js'
|
||||||
'vendor/firebase-simple-login/firebase-simple-login.js'
|
'vendor/firebase-simple-login/firebase-simple-login.js'
|
||||||
'vendor/grapnel/src/grapnel.js'
|
'vendor/grapnel/src/grapnel.js'
|
||||||
'vendor/github/lib/base64.js'
|
'vendor/superagent/superagent.js'
|
||||||
'vendor/github/github.js'
|
|
||||||
'vendor/localforage/dist/localforage.js'
|
'vendor/localforage/dist/localforage.js'
|
||||||
|
'vendor/async/lib/async.js'
|
||||||
|
'vendor/moment/moment.js'
|
||||||
# Our app.
|
# Our app.
|
||||||
'public/js/app.js'
|
'public/js/app.js'
|
||||||
]
|
]
|
||||||
|
@ -8,12 +8,12 @@ GitHub Burndown Chart as a service. Public repos are free, for private access au
|
|||||||
|
|
||||||
### MVP
|
### MVP
|
||||||
|
|
||||||
- [ ] landing page allows you to immediately jump into action
|
- [x] landing page allows you to immediately jump into action
|
||||||
- [ ] show chart for the current milestone, default to the first one returned and allow to choose a custom one
|
- [ ] show chart for the current milestone, default to the first one returned and allow to choose a custom one
|
||||||
- [ ] sort projects based on their closest due dates
|
- [ ] sort projects based on their closest due dates
|
||||||
- [ ] show only repo name if all projects are under our name
|
- [ ] show only repo name if all projects are under our name
|
||||||
- [ ] show all issues as [one size](https://github.com/radekstepan/github-burndown-chart/issues/46)
|
- [ ] show all issues as [one size](https://github.com/radekstepan/github-burndown-chart/issues/46)
|
||||||
- [ ] use local storage to save information about us, but keep the API open for Firebase
|
- [x] use local storage to save information about us, but keep the API open for Firebase
|
||||||
|
|
||||||
### The 20%
|
### The 20%
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
"firebase-simple-login": "~1.6.3",
|
"firebase-simple-login": "~1.6.3",
|
||||||
"grapnel": "~0.4.2",
|
"grapnel": "~0.4.2",
|
||||||
"github": "~0.9.0",
|
"github": "~0.9.0",
|
||||||
"localforage": "~0.9.2"
|
"localforage": "~0.9.2",
|
||||||
|
"superagent": "~0.19.0",
|
||||||
|
"async": "~0.9.0",
|
||||||
|
"moment": "~2.8.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
357
public/js/app.js
357
public/js/app.js
@ -7,16 +7,18 @@
|
|||||||
// app.coffee
|
// app.coffee
|
||||||
root.require.register('burnchart/src/app.js', function(exports, require, module) {
|
root.require.register('burnchart/src/app.js', function(exports, require, module) {
|
||||||
|
|
||||||
var App, Header, el, key, route, router, _i, _len, _ref;
|
var App, Header, el, key, mediator, route, router, _i, _len, _ref;
|
||||||
|
|
||||||
_ref = ['projects'];
|
_ref = ['utils/mixins', 'models/projects'];
|
||||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
key = _ref[_i];
|
key = _ref[_i];
|
||||||
require("./models/" + key);
|
require("./" + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
Header = require('./views/header');
|
Header = require('./views/header');
|
||||||
|
|
||||||
|
mediator = require('./modules/mediator');
|
||||||
|
|
||||||
el = '#page';
|
el = '#page';
|
||||||
|
|
||||||
route = function(page, req, evt) {
|
route = function(page, req, evt) {
|
||||||
@ -30,7 +32,11 @@
|
|||||||
|
|
||||||
router = {
|
router = {
|
||||||
'': _.partial(route, 'index'),
|
'': _.partial(route, 'index'),
|
||||||
'project/add': _.partial(route, 'addProject')
|
'project/add': _.partial(route, 'addProject'),
|
||||||
|
'reset': function() {
|
||||||
|
mediator.fire('!projects/clear');
|
||||||
|
return window.location.hash = '#';
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
App = Ractive.extend({
|
App = Ractive.extend({
|
||||||
@ -52,42 +58,81 @@
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"firebase": "burnchart",
|
"firebase": "burnchart",
|
||||||
"provider": "github"
|
"provider": "github",
|
||||||
|
"fields": {
|
||||||
|
"milestone": [
|
||||||
|
"closed_issues",
|
||||||
|
"created_at",
|
||||||
|
"description",
|
||||||
|
"due_on",
|
||||||
|
"number",
|
||||||
|
"open_issues",
|
||||||
|
"title",
|
||||||
|
"updated_at"
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// projects.coffee
|
// projects.coffee
|
||||||
root.require.register('burnchart/src/models/projects.js', function(exports, require, module) {
|
root.require.register('burnchart/src/models/projects.js', function(exports, require, module) {
|
||||||
|
|
||||||
var Model, mediator, user;
|
var Model, config, date, mediator, request, user;
|
||||||
|
|
||||||
mediator = require('../modules/mediator');
|
mediator = require('../modules/mediator');
|
||||||
|
|
||||||
|
request = require('../modules/request');
|
||||||
|
|
||||||
Model = require('../utils/model');
|
Model = require('../utils/model');
|
||||||
|
|
||||||
|
date = require('../utils/date');
|
||||||
|
|
||||||
|
config = require('./config');
|
||||||
|
|
||||||
user = require('./user');
|
user = require('./user');
|
||||||
|
|
||||||
module.exports = new Model({
|
module.exports = new Model({
|
||||||
'data': {
|
'data': {
|
||||||
'items': []
|
'list': []
|
||||||
},
|
},
|
||||||
init: function() {
|
init: function() {
|
||||||
var _this = this;
|
var getMilestones,
|
||||||
localforage.getItem('projects', function(items) {
|
_this = this;
|
||||||
if (items == null) {
|
getMilestones = function() {};
|
||||||
items = [];
|
localforage.getItem('projects', function(projects) {
|
||||||
|
if (projects == null) {
|
||||||
|
projects = [];
|
||||||
}
|
}
|
||||||
return _this.set('items', items);
|
return _this.set('list', projects);
|
||||||
});
|
});
|
||||||
this.observe('items', function() {
|
this.observe('list', function(projects) {
|
||||||
return localforage.setItem('projects', this.get('items'));
|
return localforage.setItem('projects', projects);
|
||||||
});
|
});
|
||||||
return mediator.on('!projects/add', function(repo) {
|
mediator.on('!projects/add', function(repo, done) {
|
||||||
return _this.push('items', {
|
return request.allMilestones(repo, function(err, res) {
|
||||||
'owner': repo.owner.login,
|
var active, milestones;
|
||||||
'name': repo.name
|
if (err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
milestones = _.pluckMany(res, config.fields.milestone);
|
||||||
|
active = _.find(milestones, function(m) {
|
||||||
|
return 0 < m.open_issues + m.closed_issues;
|
||||||
|
});
|
||||||
|
if (active != null) {
|
||||||
|
active.active = true;
|
||||||
|
}
|
||||||
|
_this.push('list', _.merge(repo, {
|
||||||
|
'milestones': {
|
||||||
|
'list': milestones,
|
||||||
|
'checked_at': date.now()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
return mediator.on('!projects/clear', function() {
|
||||||
|
return _this.set('list', []);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -106,7 +151,8 @@
|
|||||||
'data': {
|
'data': {
|
||||||
'provider': "local",
|
'provider': "local",
|
||||||
'id': "0",
|
'id': "0",
|
||||||
'uid': "local:0"
|
'uid': "local:0",
|
||||||
|
'token': null
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -162,30 +208,6 @@
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// github.coffee
|
|
||||||
root.require.register('burnchart/src/modules/github.js', function(exports, require, module) {
|
|
||||||
|
|
||||||
var auth, github, setToken, user;
|
|
||||||
|
|
||||||
user = require('../models/user');
|
|
||||||
|
|
||||||
auth = 'oauth';
|
|
||||||
|
|
||||||
github = null;
|
|
||||||
|
|
||||||
(setToken = function(token) {
|
|
||||||
return github = new Github({
|
|
||||||
token: token,
|
|
||||||
auth: auth
|
|
||||||
});
|
|
||||||
})(null);
|
|
||||||
|
|
||||||
user.observe('accessToken', setToken);
|
|
||||||
|
|
||||||
module.exports = github;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// mediator.coffee
|
// mediator.coffee
|
||||||
root.require.register('burnchart/src/modules/mediator.js', function(exports, require, module) {
|
root.require.register('burnchart/src/modules/mediator.js', function(exports, require, module) {
|
||||||
|
|
||||||
@ -193,16 +215,176 @@
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// request.coffee
|
||||||
|
root.require.register('burnchart/src/modules/request.js', function(exports, require, module) {
|
||||||
|
|
||||||
|
var defaults, error, headers, request, response, user;
|
||||||
|
|
||||||
|
user = require('../models/user');
|
||||||
|
|
||||||
|
superagent.parse = {
|
||||||
|
'application/json': function(res) {
|
||||||
|
var e;
|
||||||
|
try {
|
||||||
|
return JSON.parse(res);
|
||||||
|
} catch (_error) {
|
||||||
|
e = _error;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
'github': {
|
||||||
|
'host': 'api.github.com',
|
||||||
|
'protocol': 'https'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'repo': function(repo, cb) {
|
||||||
|
var data;
|
||||||
|
data = _.defaults({
|
||||||
|
'protocol': repo.protocol,
|
||||||
|
'host': repo.host,
|
||||||
|
'path': "/repos/" + repo.owner + "/" + repo.name,
|
||||||
|
'headers': headers(user.get('token'))
|
||||||
|
}, defaults.github);
|
||||||
|
return request(data, cb);
|
||||||
|
},
|
||||||
|
'allMilestones': function(repo, cb) {
|
||||||
|
var data;
|
||||||
|
data = _.defaults({
|
||||||
|
'protocol': repo.protocol,
|
||||||
|
'host': repo.host,
|
||||||
|
'path': "/repos/" + repo.owner + "/" + repo.name + "/milestones",
|
||||||
|
'query': {
|
||||||
|
'state': 'open',
|
||||||
|
'sort': 'due_date',
|
||||||
|
'direction': 'asc'
|
||||||
|
},
|
||||||
|
'headers': headers(user.get('token'))
|
||||||
|
}, defaults.github);
|
||||||
|
return request(data, cb);
|
||||||
|
},
|
||||||
|
'oneMilestone': function(repo, number, cb) {
|
||||||
|
return request({
|
||||||
|
'protocol': repo.protocol,
|
||||||
|
'host': repo.host,
|
||||||
|
'path': "/repos/" + repo.owner + "/" + repo.name + "/milestones/" + number,
|
||||||
|
'query': {
|
||||||
|
'state': 'open',
|
||||||
|
'sort': 'due_date',
|
||||||
|
'direction': 'asc'
|
||||||
|
},
|
||||||
|
'headers': headers(user.get('token'))
|
||||||
|
}, cb);
|
||||||
|
},
|
||||||
|
'allIssues': function(repo, query, cb) {
|
||||||
|
return request({
|
||||||
|
'protocol': repo.protocol,
|
||||||
|
'host': repo.host,
|
||||||
|
'path': "/repos/" + repo.owner + "/" + repo.name + "/issues",
|
||||||
|
'query': _.extend(query, {
|
||||||
|
'per_page': '100'
|
||||||
|
}),
|
||||||
|
'headers': headers(user.get('token'))
|
||||||
|
}, cb);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request = function(_arg, cb) {
|
||||||
|
var exited, headers, host, k, path, protocol, q, query, req, timeout, v;
|
||||||
|
protocol = _arg.protocol, host = _arg.host, path = _arg.path, query = _arg.query, headers = _arg.headers;
|
||||||
|
exited = false;
|
||||||
|
q = query ? '?' + ((function() {
|
||||||
|
var _results;
|
||||||
|
_results = [];
|
||||||
|
for (k in query) {
|
||||||
|
v = query[k];
|
||||||
|
_results.push("" + k + "=" + v);
|
||||||
|
}
|
||||||
|
return _results;
|
||||||
|
})()).join('&') : '';
|
||||||
|
req = superagent.get("" + protocol + "://" + host + path + q);
|
||||||
|
for (k in headers) {
|
||||||
|
v = headers[k];
|
||||||
|
req.set(k, v);
|
||||||
|
}
|
||||||
|
timeout = setTimeout(function() {
|
||||||
|
exited = true;
|
||||||
|
return cb('Request has timed out');
|
||||||
|
}, 1e4);
|
||||||
|
return req.end(function(err, data) {
|
||||||
|
if (exited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exited = true;
|
||||||
|
clearTimeout(timeout);
|
||||||
|
return response(err, data, cb);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
response = function(err, data, cb) {
|
||||||
|
var _ref;
|
||||||
|
if (err) {
|
||||||
|
return cb(error(err));
|
||||||
|
}
|
||||||
|
if (data.statusType !== 2) {
|
||||||
|
if ((data != null ? (_ref = data.body) != null ? _ref.message : void 0 : void 0) != null) {
|
||||||
|
return cb(data.body.message);
|
||||||
|
}
|
||||||
|
return cb(data.error.message);
|
||||||
|
}
|
||||||
|
return cb(null, data.body);
|
||||||
|
};
|
||||||
|
|
||||||
|
headers = function(token) {
|
||||||
|
var h;
|
||||||
|
h = _.extend({}, {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/vnd.github.v3'
|
||||||
|
});
|
||||||
|
if (token != null) {
|
||||||
|
h.Authorization = "token " + token;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
};
|
||||||
|
|
||||||
|
error = function(err) {
|
||||||
|
var message;
|
||||||
|
switch (false) {
|
||||||
|
case !_.isString(err):
|
||||||
|
message = err;
|
||||||
|
break;
|
||||||
|
case !_.isArray(err):
|
||||||
|
message = err[1];
|
||||||
|
break;
|
||||||
|
case !(_.isObject(err) && _.isString(err.message)):
|
||||||
|
message = err.message;
|
||||||
|
}
|
||||||
|
if (!message) {
|
||||||
|
try {
|
||||||
|
message = JSON.stringify(err);
|
||||||
|
} catch (_error) {
|
||||||
|
message = err.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
// header.mustache
|
// header.mustache
|
||||||
root.require.register('burnchart/src/templates/header.js', function(exports, require, module) {
|
root.require.register('burnchart/src/templates/header.js', function(exports, require, module) {
|
||||||
|
|
||||||
module.exports = ["<div id=\"head\">"," <div class=\"right\">"," {{#user.displayName}}"," {{user.displayName}} logged in"," {{else}}"," <a class=\"github\" on-click=\"!login\"><span class=\"icon github\"></span> Sign In</a>"," {{/user.displayName}}"," </div>",""," <h1><span class=\"icon fire-station\"></span></h1>",""," <div class=\"q\">"," <span class=\"icon search\"></span>"," <span class=\"icon down-open\"></span>"," <input type=\"text\" placeholder=\"Jump to...\">"," </div>",""," <ul>"," <li><a href=\"#project/add\" class=\"add\"><span class=\"icon plus-circled\"></span> Add a Project</a></li>"," <li><a href=\"#\" class=\"faq\">FAQ</a></li>"," </ul>","</div>"].join("\n");
|
module.exports = ["<div id=\"head\">"," <div class=\"right\">"," {{#user.displayName}}"," {{user.displayName}} logged in"," {{else}}"," <a class=\"github\" on-click=\"!login\"><span class=\"icon github\"></span> Sign In</a>"," {{/user.displayName}}"," </div>",""," <h1><span class=\"icon fire-station\"></span></h1>",""," <div class=\"q\">"," <span class=\"icon search\"></span>"," <span class=\"icon down-open\"></span>"," <input type=\"text\" placeholder=\"Jump to...\">"," </div>",""," <ul>"," <li><a href=\"#project/add\" class=\"add\"><span class=\"icon plus-circled\"></span> Add a Project</a></li>"," <li><a href=\"#\" class=\"faq\">FAQ</a></li>"," <li><a href=\"#reset\">DB Reset</a></li>"," </ul>","</div>"].join("\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// hero.mustache
|
// hero.mustache
|
||||||
root.require.register('burnchart/src/templates/hero.js', function(exports, require, module) {
|
root.require.register('burnchart/src/templates/hero.js', function(exports, require, module) {
|
||||||
|
|
||||||
module.exports = ["{{^projects.items}}"," <div id=\"hero\">"," <div class=\"content\">"," <span class=\"icon address\"></span>"," <h2>See your project progress</h2>"," <p>Not sure where to start? Just add a demo repo to see a chart. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.</p>"," <div class=\"cta\">"," <a href=\"#project/add\" class=\"primary\"><span class=\"icon plus-circled\"></span> Add your project</a>"," <a href=\"#\" class=\"secondary\">Read the Guide</a>"," </div>"," </div>"," </div>","{{/projects.items}}"].join("\n");
|
module.exports = ["{{^projects.list}}"," <div id=\"hero\">"," <div class=\"content\">"," <span class=\"icon address\"></span>"," <h2>See your project progress</h2>"," <p>Not sure where to start? Just add a demo repo to see a chart. There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable.</p>"," <div class=\"cta\">"," <a href=\"#project/add\" class=\"primary\"><span class=\"icon plus-circled\"></span> Add your project</a>"," <a href=\"#\" class=\"secondary\">Read the Guide</a>"," </div>"," </div>"," </div>","{{/projects.list}}"].join("\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
// layout.mustache
|
// layout.mustache
|
||||||
@ -226,7 +408,62 @@
|
|||||||
// projects.mustache
|
// projects.mustache
|
||||||
root.require.register('burnchart/src/templates/projects.js', function(exports, require, module) {
|
root.require.register('burnchart/src/templates/projects.js', function(exports, require, module) {
|
||||||
|
|
||||||
module.exports = ["{{#projects.items}}"," <div id=\"projects\">"," <div class=\"header\">"," <a href=\"#\" class=\"sort\"><span class=\"icon sort-alphabet\"></span> Sorted by priority</a>"," <h2>Projects</h2>"," </div>",""," <table>"," {{#projects.items}}"," <tr>"," <td><a class=\"repo\" href=\"#\">{{owner}}/{{name}}</a></td>"," <td><span class=\"milestone\">??? <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">10%</span>"," <span class=\"due\">???</span>"," <div class=\"outer bar\">"," <div class=\"inner bar green\" style=\"width:10%\"></div>"," </div>"," </div>"," </td>"," </tr>"," {{/projects.items}}",""," <tr>"," <td><a class=\"repo\" href=\"#\">radekstepan/disposable</a></td>"," <td><span class=\"milestone\">Milestone 1.0 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">40%</span>"," <span class=\"due\">due on Friday</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:40%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr class=\"done\">"," <td><a class=\"repo\" href=\"#\">radekstepan/burnchart</a></td>"," <td><span class=\"milestone\">Beta Milestone <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">100%</span>"," <span class=\"due\">due tomorrow</span>"," <div class=\"outer bar\">"," <div class=\"inner bar green\" style=\"width:100%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">intermine/intermine</a></td>"," <td><span class=\"milestone\">Emma Release 96 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">27%</span>"," <span class=\"due\">due in 2 weeks</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:27%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">microsoft/windows</a></td>"," <td><span class=\"milestone\">RC 9 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">90%</span>"," <span class=\"due red\">overdue by a month</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:90%\"></div>"," </div>"," </div>"," </td>"," </tr>"," </table>",""," <div class=\"footer\">"," <a href=\"#\"><span class=\"icon cog\"></span> Edit</a>"," </div>"," </div>","{{/projects.items}}"].join("\n");
|
module.exports = ["{{#projects.list}}"," <div id=\"projects\">"," <div class=\"header\">"," <a href=\"#\" class=\"sort\"><span class=\"icon sort-alphabet\"></span> Sorted by priority</a>"," <h2>Projects</h2>"," </div>",""," <table>"," {{#projects.list}}"," <tr>"," <td><a class=\"repo\" href=\"#\">{{owner}}/{{name}}</a></td>"," {{# { milestone: getMilestone(milestones.list) } }}"," {{#milestone}}"," <td>"," <span class=\"milestone\">"," {{ milestone.title }}"," <span class=\"icon down-open\">"," </span>"," </td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>"," <span class=\"due\">due {{format.fromNow(due_on)}}</span>"," <div class=\"outer bar\">"," <div class=\"inner bar {{format.onTime(milestone)}}\" style=\"width:{{format.progress(closed_issues, open_issues)}}%\"></div>"," </div>"," </div>"," </td>"," {{/milestone}}"," {{^milestone}}"," <td colspan=\"2\"><span class=\"milestone\"><em>No milestones yet</em></td>"," {{/milestone}}"," {{/}}"," </tr>"," {{/projects.list}}",""," <tr>"," <td><a class=\"repo\" href=\"#\">radekstepan/disposable</a></td>"," <td><span class=\"milestone\">Milestone 1.0 <span class=\"icon down-open\"></span></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">40%</span>"," <span class=\"due\">due on Friday</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:40%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr class=\"done\">"," <td><a class=\"repo\" href=\"#\">radekstepan/burnchart</a></td>"," <td><span class=\"milestone\">Beta Milestone <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">100%</span>"," <span class=\"due\">due tomorrow</span>"," <div class=\"outer bar\">"," <div class=\"inner bar green\" style=\"width:100%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">intermine/intermine</a></td>"," <td><span class=\"milestone\">Emma Release 96 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">27%</span>"," <span class=\"due\">due in 2 weeks</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:27%\"></div>"," </div>"," </div>"," </td>"," </tr>"," <tr>"," <td><a class=\"repo\" href=\"#\">microsoft/windows</a></td>"," <td><span class=\"milestone\">RC 9 <span class=\"icon down-open\"></span></a></td>"," <td>"," <div class=\"progress\">"," <span class=\"percent\">90%</span>"," <span class=\"due red\">overdue by a month</span>"," <div class=\"outer bar\">"," <div class=\"inner bar red\" style=\"width:90%\"></div>"," </div>"," </div>"," </td>"," </tr>"," </table>",""," <div class=\"footer\">"," <a href=\"#\"><span class=\"icon cog\"></span> Edit</a>"," </div>"," </div>","{{/projects.list}}"].join("\n");
|
||||||
|
});
|
||||||
|
|
||||||
|
// date.coffee
|
||||||
|
root.require.register('burnchart/src/utils/date.js', function(exports, require, module) {
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
now: function() {
|
||||||
|
return new Date().toJSON();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// format.coffee
|
||||||
|
root.require.register('burnchart/src/utils/format.js', function(exports, require, module) {
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
'progress': _.memoize(function(a, b) {
|
||||||
|
return 100 * (a / (b + a));
|
||||||
|
}),
|
||||||
|
'onTime': _.memoize(function(milestone) {
|
||||||
|
var a, b, c, points, time;
|
||||||
|
points = this.progress(milestone.closed_issues, milestone.open_issues);
|
||||||
|
a = +new Date(milestone.created_at);
|
||||||
|
b = +(new Date);
|
||||||
|
c = +new Date(milestone.due_on);
|
||||||
|
time = this.progress(b - a, c - b);
|
||||||
|
return ['red', 'green'][+(points > time)];
|
||||||
|
}),
|
||||||
|
'fromNow': _.memoize(function(jsonDate) {
|
||||||
|
return moment(new Date(jsonDate)).fromNow();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// mixins.coffee
|
||||||
|
root.require.register('burnchart/src/utils/mixins.js', function(exports, require, module) {
|
||||||
|
|
||||||
|
_.mixin({
|
||||||
|
'pluckMany': function(source, keys) {
|
||||||
|
if (!_.isArray(keys)) {
|
||||||
|
throw '`keys` needs to be an Array';
|
||||||
|
}
|
||||||
|
return _.map(source, function(item) {
|
||||||
|
var obj;
|
||||||
|
obj = {};
|
||||||
|
_.each(keys, function(key) {
|
||||||
|
return obj[key] = item[key];
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// model.coffee
|
// model.coffee
|
||||||
@ -294,18 +531,16 @@
|
|||||||
// addProject.coffee
|
// addProject.coffee
|
||||||
root.require.register('burnchart/src/views/pages/addProject.js', function(exports, require, module) {
|
root.require.register('burnchart/src/views/pages/addProject.js', function(exports, require, module) {
|
||||||
|
|
||||||
var github, mediator, user;
|
var mediator, user;
|
||||||
|
|
||||||
mediator = require('../../modules/mediator');
|
mediator = require('../../modules/mediator');
|
||||||
|
|
||||||
github = require('../../modules/github');
|
|
||||||
|
|
||||||
user = require('../../models/user');
|
user = require('../../models/user');
|
||||||
|
|
||||||
module.exports = Ractive.extend({
|
module.exports = Ractive.extend({
|
||||||
'template': require('../../templates/pages/addProject'),
|
'template': require('../../templates/pages/addProject'),
|
||||||
'data': {
|
'data': {
|
||||||
'value': null,
|
'value': 'radekstepan/disposable',
|
||||||
user: user
|
user: user
|
||||||
},
|
},
|
||||||
'adapt': [Ractive.adaptors.Ractive],
|
'adapt': [Ractive.adaptors.Ractive],
|
||||||
@ -316,14 +551,12 @@
|
|||||||
'init': false
|
'init': false
|
||||||
});
|
});
|
||||||
return this.on('submit', function() {
|
return this.on('submit', function() {
|
||||||
var name, owner, repo, _ref;
|
var name, owner, _ref;
|
||||||
_ref = this.get('value').split('/'), owner = _ref[0], name = _ref[1];
|
_ref = this.get('value').split('/'), owner = _ref[0], name = _ref[1];
|
||||||
repo = github.getRepo(owner, name);
|
return mediator.fire('!projects/add', {
|
||||||
return repo.show(function(err, repo, xhr) {
|
owner: owner,
|
||||||
if (err) {
|
name: name
|
||||||
throw err;
|
}, function() {
|
||||||
}
|
|
||||||
mediator.fire('!projects/add', repo);
|
|
||||||
return window.location.hash = '#';
|
return window.location.hash = '#';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -335,17 +568,25 @@
|
|||||||
// index.coffee
|
// index.coffee
|
||||||
root.require.register('burnchart/src/views/pages/index.js', function(exports, require, module) {
|
root.require.register('burnchart/src/views/pages/index.js', function(exports, require, module) {
|
||||||
|
|
||||||
var Hero, Projects;
|
var Hero, Projects, format;
|
||||||
|
|
||||||
Hero = require('../hero');
|
Hero = require('../hero');
|
||||||
|
|
||||||
Projects = require('../projects');
|
Projects = require('../projects');
|
||||||
|
|
||||||
|
format = require('../../utils/format');
|
||||||
|
|
||||||
module.exports = Ractive.extend({
|
module.exports = Ractive.extend({
|
||||||
'template': require('../../templates/pages/index'),
|
'template': require('../../templates/pages/index'),
|
||||||
'components': {
|
'components': {
|
||||||
Hero: Hero,
|
Hero: Hero,
|
||||||
Projects: Projects
|
Projects: Projects
|
||||||
|
},
|
||||||
|
'data': {
|
||||||
|
'format': format,
|
||||||
|
getMilestone: function(list) {
|
||||||
|
return _.findWhere(list, 'active');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
( require "./models/#{key}" for key in [
|
( require "./#{key}" for key in [
|
||||||
'projects'
|
'utils/mixins'
|
||||||
|
'models/projects'
|
||||||
] )
|
] )
|
||||||
|
|
||||||
Header = require './views/header'
|
Header = require './views/header'
|
||||||
|
|
||||||
|
mediator = require './modules/mediator'
|
||||||
|
|
||||||
el = '#page'
|
el = '#page'
|
||||||
|
|
||||||
@ -15,6 +17,10 @@ route = (page, req, evt) ->
|
|||||||
router =
|
router =
|
||||||
'': _.partial route, 'index'
|
'': _.partial route, 'index'
|
||||||
'project/add': _.partial route, 'addProject'
|
'project/add': _.partial route, 'addProject'
|
||||||
|
# TODO: remove in production.
|
||||||
|
'reset': ->
|
||||||
|
mediator.fire '!projects/clear'
|
||||||
|
window.location.hash = '#'
|
||||||
|
|
||||||
App = Ractive.extend
|
App = Ractive.extend
|
||||||
|
|
||||||
|
@ -1,4 +1,16 @@
|
|||||||
{
|
{
|
||||||
"firebase": "burnchart",
|
"firebase": "burnchart",
|
||||||
"provider": "github"
|
"provider": "github",
|
||||||
|
"fields": {
|
||||||
|
"milestone": [
|
||||||
|
"closed_issues",
|
||||||
|
"created_at",
|
||||||
|
"description",
|
||||||
|
"due_on",
|
||||||
|
"number",
|
||||||
|
"open_issues",
|
||||||
|
"title",
|
||||||
|
"updated_at"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,21 +1,52 @@
|
|||||||
mediator = require '../modules/mediator'
|
mediator = require '../modules/mediator'
|
||||||
|
request = require '../modules/request'
|
||||||
Model = require '../utils/model'
|
Model = require '../utils/model'
|
||||||
|
date = require '../utils/date'
|
||||||
|
config = require './config'
|
||||||
user = require './user'
|
user = require './user'
|
||||||
|
|
||||||
module.exports = new Model
|
module.exports = new Model
|
||||||
|
|
||||||
'data':
|
'data':
|
||||||
'items': []
|
'list': []
|
||||||
|
|
||||||
init: ->
|
init: ->
|
||||||
|
# Fetches a list of milestones for a repo.
|
||||||
|
getMilestones = ->
|
||||||
|
|
||||||
# Initialize with items stored locally.
|
# Initialize with items stored locally.
|
||||||
localforage.getItem 'projects', (items=[]) =>
|
localforage.getItem 'projects', (projects=[]) =>
|
||||||
@set 'items', items
|
@set 'list', projects
|
||||||
|
|
||||||
# Persist in local storage.
|
# Persist in local storage.
|
||||||
@observe 'items', ->
|
@observe 'list', (projects) ->
|
||||||
localforage.setItem 'projects', @get('items')
|
localforage.setItem 'projects', projects
|
||||||
|
|
||||||
mediator.on '!projects/add', (repo) =>
|
mediator.on '!projects/add', (repo, done) =>
|
||||||
# TODO: deal with repo.hasIssues and warn if there are none.
|
# TODO: warn when we are adding an existing repo (or
|
||||||
@push 'items', { 'owner': repo.owner.login, 'name': repo.name }
|
# silently go to index again).
|
||||||
|
|
||||||
|
# Fetch milestones (which validates repo too).
|
||||||
|
request.allMilestones repo, (err, res) =>
|
||||||
|
throw err if err
|
||||||
|
|
||||||
|
# Pluck these fields for milestones.
|
||||||
|
milestones = _.pluckMany res, config.fields.milestone
|
||||||
|
|
||||||
|
# Set the default milestone as the soonest one with issues.
|
||||||
|
active = _.find milestones, (m) ->
|
||||||
|
0 < m.open_issues + m.closed_issues
|
||||||
|
|
||||||
|
active?.active = true
|
||||||
|
|
||||||
|
# Push to the stack
|
||||||
|
@push 'list', _.merge repo,
|
||||||
|
'milestones':
|
||||||
|
'list': milestones
|
||||||
|
'checked_at': do date.now # checked now
|
||||||
|
|
||||||
|
# Call back so we can redirect.
|
||||||
|
do done
|
||||||
|
|
||||||
|
mediator.on '!projects/clear', =>
|
||||||
|
@set 'list', []
|
@ -9,3 +9,4 @@ module.exports = new Model
|
|||||||
'provider': "local"
|
'provider': "local"
|
||||||
'id': "0"
|
'id': "0"
|
||||||
'uid': "local:0"
|
'uid': "local:0"
|
||||||
|
'token': null
|
@ -1,13 +0,0 @@
|
|||||||
user = require '../models/user'
|
|
||||||
|
|
||||||
auth = 'oauth'
|
|
||||||
|
|
||||||
github = null
|
|
||||||
|
|
||||||
do setToken = (token=null) ->
|
|
||||||
github = new Github { token, auth }
|
|
||||||
|
|
||||||
# Set token when we have one (otherwise init to null).
|
|
||||||
user.observe 'accessToken', setToken
|
|
||||||
|
|
||||||
module.exports = github
|
|
129
src/modules/request.coffee
Normal file
129
src/modules/request.coffee
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
user = require '../models/user'
|
||||||
|
|
||||||
|
# Custom JSON parser.
|
||||||
|
superagent.parse =
|
||||||
|
'application/json': (res) ->
|
||||||
|
try
|
||||||
|
JSON.parse res
|
||||||
|
catch e
|
||||||
|
{} # it was not to be...
|
||||||
|
|
||||||
|
# Default args.
|
||||||
|
defaults =
|
||||||
|
'github':
|
||||||
|
'host': 'api.github.com'
|
||||||
|
'protocol': 'https'
|
||||||
|
|
||||||
|
# Public api.
|
||||||
|
module.exports =
|
||||||
|
|
||||||
|
# Get a repo.
|
||||||
|
'repo': (repo, cb) ->
|
||||||
|
data = _.defaults
|
||||||
|
'protocol': repo.protocol
|
||||||
|
'host': repo.host
|
||||||
|
'path': "/repos/#{repo.owner}/#{repo.name}"
|
||||||
|
'headers': headers user.get('token')
|
||||||
|
, defaults.github
|
||||||
|
|
||||||
|
request data, cb
|
||||||
|
|
||||||
|
# Get all open milestones.
|
||||||
|
'allMilestones': (repo, cb) ->
|
||||||
|
data = _.defaults
|
||||||
|
'protocol': repo.protocol
|
||||||
|
'host': repo.host
|
||||||
|
'path': "/repos/#{repo.owner}/#{repo.name}/milestones"
|
||||||
|
'query': { 'state': 'open', 'sort': 'due_date', 'direction': 'asc' }
|
||||||
|
'headers': headers user.get('token')
|
||||||
|
, defaults.github
|
||||||
|
|
||||||
|
request data, cb
|
||||||
|
|
||||||
|
# Get one open milestone.
|
||||||
|
'oneMilestone': (repo, number, cb) ->
|
||||||
|
request
|
||||||
|
'protocol': repo.protocol
|
||||||
|
'host': repo.host
|
||||||
|
'path': "/repos/#{repo.owner}/#{repo.name}/milestones/#{number}"
|
||||||
|
'query': { 'state': 'open', 'sort': 'due_date', 'direction': 'asc' }
|
||||||
|
'headers': headers user.get('token')
|
||||||
|
, cb
|
||||||
|
|
||||||
|
# Get all issues for a state.
|
||||||
|
'allIssues': (repo, query, cb) ->
|
||||||
|
request
|
||||||
|
'protocol': repo.protocol
|
||||||
|
'host': repo.host
|
||||||
|
'path': "/repos/#{repo.owner}/#{repo.name}/issues"
|
||||||
|
'query': _.extend query, { 'per_page': '100' }
|
||||||
|
'headers': headers user.get('token')
|
||||||
|
, cb
|
||||||
|
|
||||||
|
# Make a request using SuperAgent.
|
||||||
|
request = ({ protocol, host, path, query, headers }, cb) ->
|
||||||
|
exited = no
|
||||||
|
|
||||||
|
# Make the query params.
|
||||||
|
q = if query then '?' + ( "#{k}=#{v}" for k, v of query ).join('&') else ''
|
||||||
|
|
||||||
|
# The URI.
|
||||||
|
req = superagent.get("#{protocol}://#{host}#{path}#{q}")
|
||||||
|
# Add headers.
|
||||||
|
( req.set(k, v) for k, v of headers )
|
||||||
|
|
||||||
|
# Timeout for requests that do not finish... see #32.
|
||||||
|
timeout = setTimeout ->
|
||||||
|
exited = yes
|
||||||
|
cb 'Request has timed out'
|
||||||
|
, 1e4 # give us 10s
|
||||||
|
|
||||||
|
# Send.
|
||||||
|
req.end (err, data) ->
|
||||||
|
# Arrived too late.
|
||||||
|
return if exited
|
||||||
|
# All fine.
|
||||||
|
exited = yes
|
||||||
|
clearTimeout timeout
|
||||||
|
# Actually process the response.
|
||||||
|
response err, data, cb
|
||||||
|
|
||||||
|
# How do we respond to a response?
|
||||||
|
response = (err, data, cb) ->
|
||||||
|
return cb error err if err
|
||||||
|
# 2xx?
|
||||||
|
if data.statusType isnt 2
|
||||||
|
# Do we have a message from GitHub?
|
||||||
|
return cb data.body.message if data?.body?.message?
|
||||||
|
# Use SA one.
|
||||||
|
return cb data.error.message
|
||||||
|
# All good.
|
||||||
|
cb null, data.body
|
||||||
|
|
||||||
|
# Give us headers.
|
||||||
|
headers = (token) ->
|
||||||
|
# The defaults.
|
||||||
|
h = _.extend {},
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
'Accept': 'application/vnd.github.v3'
|
||||||
|
# Add token?
|
||||||
|
h.Authorization = "token #{token}" if token?
|
||||||
|
h
|
||||||
|
|
||||||
|
# Parse an error.
|
||||||
|
error = (err) ->
|
||||||
|
switch
|
||||||
|
when _.isString err
|
||||||
|
message = err
|
||||||
|
when _.isArray err
|
||||||
|
message = err[1]
|
||||||
|
when _.isObject(err) and _.isString(err.message)
|
||||||
|
message = err.message
|
||||||
|
|
||||||
|
unless message
|
||||||
|
try
|
||||||
|
message = JSON.stringify err
|
||||||
|
catch
|
||||||
|
message = do err.toString
|
||||||
|
|
||||||
|
message
|
@ -18,5 +18,6 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="#project/add" class="add"><span class="icon plus-circled"></span> Add a Project</a></li>
|
<li><a href="#project/add" class="add"><span class="icon plus-circled"></span> Add a Project</a></li>
|
||||||
<li><a href="#" class="faq">FAQ</a></li>
|
<li><a href="#" class="faq">FAQ</a></li>
|
||||||
|
<li><a href="#reset">DB Reset</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
@ -1,4 +1,4 @@
|
|||||||
{{^projects.items}}
|
{{^projects.list}}
|
||||||
<div id="hero">
|
<div id="hero">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<span class="icon address"></span>
|
<span class="icon address"></span>
|
||||||
@ -10,4 +10,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/projects.items}}
|
{{/projects.list}}
|
@ -1,4 +1,4 @@
|
|||||||
{{#projects.items}}
|
{{#projects.list}}
|
||||||
<div id="projects">
|
<div id="projects">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<a href="#" class="sort"><span class="icon sort-alphabet"></span> Sorted by priority</a>
|
<a href="#" class="sort"><span class="icon sort-alphabet"></span> Sorted by priority</a>
|
||||||
@ -6,25 +6,37 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
{{#projects.items}}
|
{{#projects.list}}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a class="repo" href="#">{{owner}}/{{name}}</a></td>
|
<td><a class="repo" href="#">{{owner}}/{{name}}</a></td>
|
||||||
<td><span class="milestone">??? <span class="icon down-open"></span></a></td>
|
{{# { milestone: getMilestone(milestones.list) } }}
|
||||||
<td>
|
{{#milestone}}
|
||||||
<div class="progress">
|
<td>
|
||||||
<span class="percent">10%</span>
|
<span class="milestone">
|
||||||
<span class="due">???</span>
|
{{ milestone.title }}
|
||||||
<div class="outer bar">
|
<span class="icon down-open">
|
||||||
<div class="inner bar green" style="width:10%"></div>
|
</span>
|
||||||
</div>
|
</td>
|
||||||
</div>
|
<td>
|
||||||
</td>
|
<div class="progress">
|
||||||
|
<span class="percent">{{Math.floor(format.progress(closed_issues, open_issues))}}%</span>
|
||||||
|
<span class="due">due {{format.fromNow(due_on)}}</span>
|
||||||
|
<div class="outer bar">
|
||||||
|
<div class="inner bar {{format.onTime(milestone)}}" style="width:{{format.progress(closed_issues, open_issues)}}%"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
{{/milestone}}
|
||||||
|
{{^milestone}}
|
||||||
|
<td colspan="2"><span class="milestone"><em>No milestones yet</em></td>
|
||||||
|
{{/milestone}}
|
||||||
|
{{/}}
|
||||||
</tr>
|
</tr>
|
||||||
{{/projects.items}}
|
{{/projects.list}}
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><a class="repo" href="#">radekstepan/disposable</a></td>
|
<td><a class="repo" href="#">radekstepan/disposable</a></td>
|
||||||
<td><span class="milestone">Milestone 1.0 <span class="icon down-open"></span></a></td>
|
<td><span class="milestone">Milestone 1.0 <span class="icon down-open"></span></td>
|
||||||
<td>
|
<td>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<span class="percent">40%</span>
|
<span class="percent">40%</span>
|
||||||
@ -80,4 +92,4 @@
|
|||||||
<a href="#"><span class="icon cog"></span> Edit</a>
|
<a href="#"><span class="icon cog"></span> Edit</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/projects.items}}
|
{{/projects.list}}
|
2
src/utils/date.coffee
Normal file
2
src/utils/date.coffee
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
module.exports =
|
||||||
|
now: -> new Date().toJSON()
|
24
src/utils/format.coffee
Normal file
24
src/utils/format.coffee
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
module.exports =
|
||||||
|
|
||||||
|
# Progress in percentages.
|
||||||
|
'progress': _.memoize (a, b) ->
|
||||||
|
100 * (a / (b + a))
|
||||||
|
|
||||||
|
# Is a milestone on time?
|
||||||
|
'onTime': _.memoize (milestone) ->
|
||||||
|
# Progress in points.
|
||||||
|
points = @progress milestone.closed_issues, milestone.open_issues
|
||||||
|
|
||||||
|
# Calculate the progress in days.
|
||||||
|
a = +new Date milestone.created_at
|
||||||
|
b = +new Date
|
||||||
|
c = +new Date milestone.due_on
|
||||||
|
|
||||||
|
# Progress in time.
|
||||||
|
time = @progress b - a, c - b
|
||||||
|
|
||||||
|
[ 'red', 'green' ][ +(points > time) ]
|
||||||
|
|
||||||
|
# When is this milestone due?
|
||||||
|
'fromNow': _.memoize (jsonDate) ->
|
||||||
|
moment(new Date(jsonDate)).fromNow()
|
8
src/utils/mixins.coffee
Normal file
8
src/utils/mixins.coffee
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
_.mixin
|
||||||
|
'pluckMany': (source, keys) ->
|
||||||
|
throw '`keys` needs to be an Array' unless _.isArray keys
|
||||||
|
_.map source, (item) ->
|
||||||
|
obj = {}
|
||||||
|
_.each keys, (key) ->
|
||||||
|
obj[key] = item[key]
|
||||||
|
obj
|
@ -1,12 +1,11 @@
|
|||||||
mediator = require '../../modules/mediator'
|
mediator = require '../../modules/mediator'
|
||||||
github = require '../../modules/github'
|
|
||||||
user = require '../../models/user'
|
user = require '../../models/user'
|
||||||
|
|
||||||
module.exports = Ractive.extend
|
module.exports = Ractive.extend
|
||||||
|
|
||||||
'template': require '../../templates/pages/addProject'
|
'template': require '../../templates/pages/addProject'
|
||||||
|
|
||||||
'data': { 'value': null, user }
|
'data': { 'value': 'radekstepan/disposable', user }
|
||||||
|
|
||||||
'adapt': [ Ractive.adaptors.Ractive ]
|
'adapt': [ Ractive.adaptors.Ractive ]
|
||||||
|
|
||||||
@ -22,11 +21,9 @@ module.exports = Ractive.extend
|
|||||||
# TODO: listen to Enter keypress.
|
# TODO: listen to Enter keypress.
|
||||||
@on 'submit', ->
|
@on 'submit', ->
|
||||||
[ owner, name ] = @get('value').split('/')
|
[ owner, name ] = @get('value').split('/')
|
||||||
repo = github.getRepo owner, name
|
|
||||||
repo.show (err, repo, xhr) ->
|
# TODO: save repo & persist.
|
||||||
throw err if err
|
mediator.fire '!projects/add', { owner, name }, ->
|
||||||
# TODO: save repo to us & Firebase.
|
|
||||||
mediator.fire '!projects/add', repo
|
|
||||||
# Redirect to the dashboard.
|
# Redirect to the dashboard.
|
||||||
# TODO: trigger a named route
|
# TODO: trigger a named route
|
||||||
window.location.hash = '#'
|
window.location.hash = '#'
|
@ -1,8 +1,15 @@
|
|||||||
Hero = require '../hero'
|
Hero = require '../hero'
|
||||||
Projects = require '../projects'
|
Projects = require '../projects'
|
||||||
|
format = require '../../utils/format'
|
||||||
|
|
||||||
module.exports = Ractive.extend
|
module.exports = Ractive.extend
|
||||||
|
|
||||||
'template': require '../../templates/pages/index'
|
'template': require '../../templates/pages/index'
|
||||||
|
|
||||||
'components': { Hero, Projects }
|
'components': { Hero, Projects }
|
||||||
|
|
||||||
|
'data':
|
||||||
|
'format': format
|
||||||
|
# Find the milestone that is active.
|
||||||
|
getMilestone: (list) ->
|
||||||
|
_.findWhere list, 'active'
|
Loading…
x
Reference in New Issue
Block a user