From 85e947fb87b01afc7fa674de936e34e427bebfba Mon Sep 17 00:00:00 2001 From: Radek Stepan Date: Fri, 19 Sep 2014 20:33:25 -0700 Subject: [PATCH] titles on chart pages --- README.md | 3 + public/css/app.bundle.css | 6 +- public/css/app.css | 6 +- public/js/app.bundle.js | 131 ++++++++++++++----------- public/js/app.js | 131 ++++++++++++++----------- src/modules/milestone.coffee | 35 +++++++ src/modules/milestones.coffee | 43 -------- src/modules/project.coffee | 15 +-- src/styles/app.styl | 5 +- src/templates/pages/index.mustache | 8 -- src/templates/pages/showChart.mustache | 8 ++ src/templates/projects.mustache | 2 +- src/utils/format.coffee | 12 ++- src/views/pages/showChart.coffee | 18 +++- 14 files changed, 227 insertions(+), 196 deletions(-) create mode 100644 src/modules/milestone.coffee delete mode 100644 src/modules/milestones.coffee diff --git a/README.md b/README.md index aa1a776..3aee57f 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ GitHub Burndown Chart as a service. Public repos are free, for private access au ### The 20% +- [ ] deal with no due date milestones - always on track +- [ ] be able to go back to homepage - [ ] calculate left margin based on the total number of points text width - [ ] Do not show login/logged-in state when we are still fetching that information from Firebase - [ ] Handle 404 on routes; from catch all check if '/' or go 404 controller @@ -23,6 +25,7 @@ GitHub Burndown Chart as a service. Public repos are free, for private access au - [ ] Check that we have not run out of requests to make - [ ] Show loading sign on top of [browser window](https://github.com/buunguyen/topbar) which is unobtrusive enough we can show it immediately. - [ ] show a countdown clock towards the end of the milestone or show overdue +- [x] show title on the chart page - [x] work for `mbostock/d3` - [x] allow people to go straight to a URL that fetches the repo, if public, for them; to demo our app without adding a repo (add it behind the scenes); *req* cache repos - [x] closed issues can be moved to a newly created milestone, this messes up the chart since we assume milestone is created first! diff --git a/public/css/app.bundle.css b/public/css/app.bundle.css index e4e64d9..775172a 100644 --- a/public/css/app.bundle.css +++ b/public/css/app.bundle.css @@ -474,9 +474,9 @@ ul li{display:inline-block} #head .right{float:right;margin-right:20px;line-height:64px;color:#e0808d;} #head .right a{-webkit-border-radius:2px;border-radius:2px;background:#ffbb2a;color:#c1041c;padding:11px 20px} #title{border-bottom:3px solid #f3f4f8;} -#title h2{border-bottom:3px solid #aaafbf;margin:30px 0 -3px 0;display:inline-block;padding-bottom:20px} -#title .milestone{font-size:16px;font-weight:bold;margin:0 20px} -#title .description{display:inline-block;font-family:'MuseoSlab500Regular',serif;color:#b1b6c4} +#title .title{border-bottom:3px solid #aaafbf;margin:30px 0 -3px 0;display:inline-block;padding-bottom:20px} +#title .sub{font-size:16px;font-weight:bold;margin:0 20px} +#title .description{display:inline-block;font-family:'MuseoSlab500Regular',serif;white-space:nowrap;color:#b1b6c4} #content{padding:20px;margin-top:20px;} #content #hero{background:url("../img/hires/2.jpg") center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;-webkit-border-radius:2px;border-radius:2px;margin-bottom:30px;} #content #hero .content{-webkit-border-radius:2px;border-radius:2px;color:#fff;padding:30px;background:rgba(0,0,0,0.3);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.2);box-shadow:inset 0 1px 2px rgba(0,0,0,0.2);} diff --git a/public/css/app.css b/public/css/app.css index 52e373e..75e184c 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -67,9 +67,9 @@ ul li{display:inline-block} #head .right{float:right;margin-right:20px;line-height:64px;color:#e0808d;} #head .right a{-webkit-border-radius:2px;border-radius:2px;background:#ffbb2a;color:#c1041c;padding:11px 20px} #title{border-bottom:3px solid #f3f4f8;} -#title h2{border-bottom:3px solid #aaafbf;margin:30px 0 -3px 0;display:inline-block;padding-bottom:20px} -#title .milestone{font-size:16px;font-weight:bold;margin:0 20px} -#title .description{display:inline-block;font-family:'MuseoSlab500Regular',serif;color:#b1b6c4} +#title .title{border-bottom:3px solid #aaafbf;margin:30px 0 -3px 0;display:inline-block;padding-bottom:20px} +#title .sub{font-size:16px;font-weight:bold;margin:0 20px} +#title .description{display:inline-block;font-family:'MuseoSlab500Regular',serif;white-space:nowrap;color:#b1b6c4} #content{padding:20px;margin-top:20px;} #content #hero{background:url("../img/hires/2.jpg") center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;-webkit-border-radius:2px;border-radius:2px;margin-bottom:30px;} #content #hero .content{-webkit-border-radius:2px;border-radius:2px;color:#fff;padding:30px;background:rgba(0,0,0,0.3);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.2);box-shadow:inset 0 1px 2px rgba(0,0,0,0.2);} diff --git a/public/js/app.bundle.js b/public/js/app.bundle.js index 20b3afa..5d635de 100644 --- a/public/js/app.bundle.js +++ b/public/js/app.bundle.js @@ -40452,52 +40452,45 @@ if (typeof exports === 'object') { }); - // milestones.coffee - root.require.register('burnchart/src/modules/milestones.js', function(exports, require, module) { + // milestone.coffee + root.require.register('burnchart/src/modules/milestone.js', function(exports, require, module) { var request; request = require('./request'); - module.exports = function(repo, cb) { - var parse; - parse = function(data) { - if (data.description) { - data.description = marked(data.description).slice(3, -5); - } - return data; - }; - if (repo.milestone) { - return request.oneMilestone(repo, repo.milestone, function(err, m) { - if (err) { - return cb(err); - } - if (m.open_issues + m.closed_issues === 0) { - return cb(null, "No issues for milestone `" + m.title + "`"); - } - m = parse(m); - 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 + module.exports = { + get: function(repo, cb) { + if (repo.milestone) { + return request.oneMilestone(repo, repo.milestone, function(err, m) { + if (err) { + return cb(err); + } + if (m.open_issues + m.closed_issues === 0) { + return cb(null, "No issues for milestone `" + m.title + "`"); + } + return cb(null, null, m); }); - m = m[0] ? m[0] : data[0]; - if (m.open_issues + m.closed_issues === 0) { - return cb(null, "No issues for milestone `" + m.title + "`"); - } - m = parse(m); - 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); + }); + } } }; @@ -40506,9 +40499,7 @@ if (typeof exports === 'object') { // project.coffee root.require.register('burnchart/src/modules/project.js', function(exports, require, module) { - var chart, issues, milestones; - - milestones = require('./milestones'); + var chart, issues; issues = require('./issues'); @@ -40517,17 +40508,6 @@ if (typeof exports === 'object') { module.exports = function(opts, cb) { return async.waterfall([ function(cb) { - return milestones(opts, function(err, warn, milestone) { - if (err) { - return cb(err); - } - if (warn) { - return cb(warn); - } - opts.milestone = milestone; - return cb(null); - }); - }, function(cb) { return issues.get_all(opts, cb); }, function(all, cb) { return async.map(all, function(array, cb) { @@ -40756,19 +40736,19 @@ if (typeof exports === 'object') { // index.mustache root.require.register('burnchart/src/templates/pages/index.js', function(exports, require, module) { - module.exports = ["
","
","

Disposable Project

"," Milestone 1.0","

The one where we deliver all that we promised.

","
","
","","
"," "," ","
"].join("\n"); + module.exports = ["
"," "," ","
"].join("\n"); }); // showChart.mustache root.require.register('burnchart/src/templates/pages/showChart.js', function(exports, require, module) { - module.exports = ["
","
","
","
","
"].join("\n"); + module.exports = ["
","
","

{{ milestone.title }}

"," {{ format.due(milestone.due_on) }}","

{{{ format.markdown(milestone.description) }}}

","
","
","","
","
","
","
","
"].join("\n"); }); // projects.mustache root.require.register('burnchart/src/templates/projects.js', function(exports, require, module) { - module.exports = ["{{#projects.list.length}}","
","
"," Sorted by priority","

Projects

","
",""," "," {{#projects.list}}"," {{#milestones}}"," "," "," "," "," "," {{/milestones}}"," {{/projects.list}}",""," ","
{{owner}}/{{name}}"," {{ title }}"," ","
"," {{Math.floor(format.progress(closed_issues, open_issues))}}%"," due {{format.fromNow(due_on)}}","
","
","
","
","
","","
"," Edit","
","
","{{/projects.list}}"].join("\n"); + module.exports = ["{{#projects.list.length}}","
","
"," Sorted by priority","

Projects

","
",""," "," {{#projects.list}}"," {{#milestones}}"," "," "," "," "," "," {{/milestones}}"," {{/projects.list}}",""," ","
{{owner}}/{{name}}"," {{ title }}"," ","
"," {{Math.floor(format.progress(closed_issues, open_issues))}}%"," {{format.due(due_on)}}","
","
","
","
","
","","
"," Edit","
","
","{{/projects.list}}"].join("\n"); }); // date.coffee @@ -40800,7 +40780,13 @@ if (typeof exports === 'object') { }), 'fromNow': _.memoize(function(jsonDate) { return moment(new Date(jsonDate)).fromNow(); - }) + }), + 'due': function(jsonDate) { + return ['due', this.fromNow(jsonDate)].join(' '); + }, + 'markdown': function(markup) { + return marked(markup); + } }; }); @@ -40952,15 +40938,40 @@ if (typeof exports === 'object') { // showChart.coffee root.require.register('burnchart/src/views/pages/showChart.js', function(exports, require, module) { - var project; + var format, milestone, project; + + milestone = require('../../modules/milestone'); project = require('../../modules/project'); + format = require('../../utils/format'); + module.exports = Ractive.extend({ 'template': require('../../templates/pages/showChart'), 'adapt': [Ractive.adaptors.Ractive], + 'data': { + format: format + }, init: function() { - return project(this.get('route')); + var route, + _this = this; + route = this.get('route'); + return milestone.get(route, function(err, warn, obj) { + if (err) { + throw err; + } + if (warn) { + throw warn; + } + _this.set('milestone', obj); + route.milestone = obj; + return project(route, function(err) { + if (err) { + throw err; + } + return console.log('Done'); + }); + }); } }); diff --git a/public/js/app.js b/public/js/app.js index 8f7869e..e928665 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -486,52 +486,45 @@ }); - // milestones.coffee - root.require.register('burnchart/src/modules/milestones.js', function(exports, require, module) { + // milestone.coffee + root.require.register('burnchart/src/modules/milestone.js', function(exports, require, module) { var request; request = require('./request'); - module.exports = function(repo, cb) { - var parse; - parse = function(data) { - if (data.description) { - data.description = marked(data.description).slice(3, -5); - } - return data; - }; - if (repo.milestone) { - return request.oneMilestone(repo, repo.milestone, function(err, m) { - if (err) { - return cb(err); - } - if (m.open_issues + m.closed_issues === 0) { - return cb(null, "No issues for milestone `" + m.title + "`"); - } - m = parse(m); - 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 + module.exports = { + get: function(repo, cb) { + if (repo.milestone) { + return request.oneMilestone(repo, repo.milestone, function(err, m) { + if (err) { + return cb(err); + } + if (m.open_issues + m.closed_issues === 0) { + return cb(null, "No issues for milestone `" + m.title + "`"); + } + return cb(null, null, m); }); - m = m[0] ? m[0] : data[0]; - if (m.open_issues + m.closed_issues === 0) { - return cb(null, "No issues for milestone `" + m.title + "`"); - } - m = parse(m); - 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); + }); + } } }; @@ -540,9 +533,7 @@ // project.coffee root.require.register('burnchart/src/modules/project.js', function(exports, require, module) { - var chart, issues, milestones; - - milestones = require('./milestones'); + var chart, issues; issues = require('./issues'); @@ -551,17 +542,6 @@ module.exports = function(opts, cb) { return async.waterfall([ function(cb) { - return milestones(opts, function(err, warn, milestone) { - if (err) { - return cb(err); - } - if (warn) { - return cb(warn); - } - opts.milestone = milestone; - return cb(null); - }); - }, function(cb) { return issues.get_all(opts, cb); }, function(all, cb) { return async.map(all, function(array, cb) { @@ -790,19 +770,19 @@ // index.mustache root.require.register('burnchart/src/templates/pages/index.js', function(exports, require, module) { - module.exports = ["
","
","

Disposable Project

"," Milestone 1.0","

The one where we deliver all that we promised.

","
","
","","
"," "," ","
"].join("\n"); + module.exports = ["
"," "," ","
"].join("\n"); }); // showChart.mustache root.require.register('burnchart/src/templates/pages/showChart.js', function(exports, require, module) { - module.exports = ["
","
","
","
","
"].join("\n"); + module.exports = ["
","
","

{{ milestone.title }}

"," {{ format.due(milestone.due_on) }}","

{{{ format.markdown(milestone.description) }}}

","
","
","","
","
","
","
","
"].join("\n"); }); // projects.mustache root.require.register('burnchart/src/templates/projects.js', function(exports, require, module) { - module.exports = ["{{#projects.list.length}}","
","
"," Sorted by priority","

Projects

","
",""," "," {{#projects.list}}"," {{#milestones}}"," "," "," "," "," "," {{/milestones}}"," {{/projects.list}}",""," ","
{{owner}}/{{name}}"," {{ title }}"," ","
"," {{Math.floor(format.progress(closed_issues, open_issues))}}%"," due {{format.fromNow(due_on)}}","
","
","
","
","
","","
"," Edit","
","
","{{/projects.list}}"].join("\n"); + module.exports = ["{{#projects.list.length}}","
","
"," Sorted by priority","

Projects

","
",""," "," {{#projects.list}}"," {{#milestones}}"," "," "," "," "," "," {{/milestones}}"," {{/projects.list}}",""," ","
{{owner}}/{{name}}"," {{ title }}"," ","
"," {{Math.floor(format.progress(closed_issues, open_issues))}}%"," {{format.due(due_on)}}","
","
","
","
","
","","
"," Edit","
","
","{{/projects.list}}"].join("\n"); }); // date.coffee @@ -834,7 +814,13 @@ }), 'fromNow': _.memoize(function(jsonDate) { return moment(new Date(jsonDate)).fromNow(); - }) + }), + 'due': function(jsonDate) { + return ['due', this.fromNow(jsonDate)].join(' '); + }, + 'markdown': function(markup) { + return marked(markup); + } }; }); @@ -986,15 +972,40 @@ // showChart.coffee root.require.register('burnchart/src/views/pages/showChart.js', function(exports, require, module) { - var project; + var format, milestone, project; + + milestone = require('../../modules/milestone'); project = require('../../modules/project'); + format = require('../../utils/format'); + module.exports = Ractive.extend({ 'template': require('../../templates/pages/showChart'), 'adapt': [Ractive.adaptors.Ractive], + 'data': { + format: format + }, init: function() { - return project(this.get('route')); + var route, + _this = this; + route = this.get('route'); + return milestone.get(route, function(err, warn, obj) { + if (err) { + throw err; + } + if (warn) { + throw warn; + } + _this.set('milestone', obj); + route.milestone = obj; + return project(route, function(err) { + if (err) { + throw err; + } + return console.log('Done'); + }); + }); } }); diff --git a/src/modules/milestone.coffee b/src/modules/milestone.coffee new file mode 100644 index 0000000..8d6c1ce --- /dev/null +++ b/src/modules/milestone.coffee @@ -0,0 +1,35 @@ +#!/usr/bin/env coffee +request = require './request' + +module.exports = + # Get current/specified milestone for a repo. + get: (repo, cb) -> + # Get a specific milestone. + if repo.milestone + request.oneMilestone repo, repo.milestone, (err, m) -> + # Errors? + return cb err if err + # Empty milestone? + if m.open_issues + m.closed_issues is 0 + return cb null, "No issues for milestone `#{m.title}`" + + 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 \ No newline at end of file diff --git a/src/modules/milestones.coffee b/src/modules/milestones.coffee deleted file mode 100644 index b9084db..0000000 --- a/src/modules/milestones.coffee +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env coffee -request = require './request' - -# Get current/specified milestone for a repo. -module.exports = (repo, cb) -> - # Has description? Parse GFM. - parse = (data) -> - data.description = marked(data.description)[3...-5] if data.description - data - - # Get a specific milestone. - if repo.milestone - request.oneMilestone repo, repo.milestone, (err, m) -> - # Errors? - return cb err if err - # Empty milestone? - if m.open_issues + m.closed_issues is 0 - return cb null, "No issues for milestone `#{m.title}`" - # Parse GFM. - m = parse m - - 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}`" - # Parse GFM. - m = parse m - - cb null, null, m \ No newline at end of file diff --git a/src/modules/project.coffee b/src/modules/project.coffee index 2efe0fa..79a656d 100644 --- a/src/modules/project.coffee +++ b/src/modules/project.coffee @@ -1,21 +1,12 @@ #!/usr/bin/env coffee -milestones = require './milestones' -issues = require './issues' -chart = require './chart' +issues = require './issues' +chart = require './chart' # Setup a project and render it. module.exports = (opts, cb) -> - # Resolve the milestone. - async.waterfall [ (cb) -> - milestones opts, (err, warn, milestone) -> - return cb err if err - return cb warn if warn - opts.milestone = milestone - cb null - # Get all issues. - (cb) -> + async.waterfall [ (cb) -> issues.get_all opts, cb # Filter them to labeled ones. diff --git a/src/styles/app.styl b/src/styles/app.styl index dda80bd..89e54ae 100644 --- a/src/styles/app.styl +++ b/src/styles/app.styl @@ -105,13 +105,13 @@ ul #title border-bottom: 3px solid #F3F4F8 - h2 + .title border-bottom: 3px solid #AAAFBF margin: 30px 0 -3px 0 display: inline-block padding-bottom: 20px - .milestone + .sub font-size: 16px font-weight: bold margin: 0 20px @@ -119,6 +119,7 @@ ul .description display: inline-block font-family: $serif_font + white-space: nowrap color: #B1B6C4 #content diff --git a/src/templates/pages/index.mustache b/src/templates/pages/index.mustache index 85ac838..dc3f35f 100644 --- a/src/templates/pages/index.mustache +++ b/src/templates/pages/index.mustache @@ -1,11 +1,3 @@ -
-
-

Disposable Project

- Milestone 1.0 -

The one where we deliver all that we promised.

-
-
-
diff --git a/src/templates/pages/showChart.mustache b/src/templates/pages/showChart.mustache index 827f64a..05a3d56 100644 --- a/src/templates/pages/showChart.mustache +++ b/src/templates/pages/showChart.mustache @@ -1,3 +1,11 @@ +
+
+

{{ milestone.title }}

+ {{ format.due(milestone.due_on) }} +

{{{ format.markdown(milestone.description) }}}

+
+
+
diff --git a/src/templates/projects.mustache b/src/templates/projects.mustache index b4719fb..2153b04 100644 --- a/src/templates/projects.mustache +++ b/src/templates/projects.mustache @@ -16,7 +16,7 @@
{{Math.floor(format.progress(closed_issues, open_issues))}}% - due {{format.fromNow(due_on)}} + {{format.due(due_on)}}
diff --git a/src/utils/format.coffee b/src/utils/format.coffee index 79b597d..ca19f57 100644 --- a/src/utils/format.coffee +++ b/src/utils/format.coffee @@ -19,6 +19,14 @@ module.exports = [ 'red', 'green' ][ +(points > time) ] - # When is this milestone due? + # Time from now. 'fromNow': _.memoize (jsonDate) -> - moment(new Date(jsonDate)).fromNow() \ No newline at end of file + moment(new Date(jsonDate)).fromNow() + + # When is a milestone due? + 'due': (jsonDate) -> + [ 'due', @fromNow jsonDate ].join(' ') + + # Markdown formatting. + 'markdown': (markup) -> + marked markup \ No newline at end of file diff --git a/src/views/pages/showChart.coffee b/src/views/pages/showChart.coffee index 62f9f53..a602b0a 100644 --- a/src/views/pages/showChart.coffee +++ b/src/views/pages/showChart.coffee @@ -1,4 +1,6 @@ -project = require '../../modules/project' +milestone = require '../../modules/milestone' +project = require '../../modules/project' +format = require '../../utils/format' module.exports = Ractive.extend @@ -6,5 +8,17 @@ module.exports = Ractive.extend 'adapt': [ Ractive.adaptors.Ractive ] + 'data': { format } + init: -> - project @get 'route' \ No newline at end of file + route = @get 'route' + milestone.get route, (err, warn, obj) => + throw err if err + throw warn if warn + # Save the milestone on the route. + @set 'milestone', obj + route.milestone = obj + + project route, (err) -> + throw err if err + console.log 'Done' \ No newline at end of file