mirror of
https://github.com/status-im/burnchart.git
synced 2025-01-11 11:14:46 +00:00
prep for chart to be used
This commit is contained in:
parent
648725c3dc
commit
21a0e82a85
@ -38,6 +38,9 @@ module.exports = (grunt) ->
|
||||
'vendor/localforage/dist/localforage.js'
|
||||
'vendor/async/lib/async.js'
|
||||
'vendor/moment/moment.js'
|
||||
'vendor/d3/d3.js'
|
||||
'vendor/d3-tip/index.js'
|
||||
'vendor/marked/lib/marked.js'
|
||||
# Our app.
|
||||
'public/js/app.js'
|
||||
]
|
||||
|
15
README.md
15
README.md
@ -6,16 +6,14 @@ GitHub Burndown Chart as a service. Public repos are free, for private access au
|
||||
|
||||
## Tasks
|
||||
|
||||
### MVP
|
||||
### MVP - Community Plan
|
||||
|
||||
- [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
|
||||
- [ ] sort projects based on their closest due dates
|
||||
- [ ] show only repo name if all projects are under our name
|
||||
- [x] show a list of projects and their milestones with progress & due date
|
||||
- [ ] show burnchart for that project milestone
|
||||
- [ ] show all issues as [one size](https://github.com/radekstepan/github-burndown-chart/issues/46)
|
||||
- [x] use local storage to save information about us, but keep the API open for Firebase
|
||||
- [x] use `localStorage` to save project names
|
||||
|
||||
### The 20%
|
||||
### Extras
|
||||
|
||||
- [ ] 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
|
||||
@ -32,9 +30,6 @@ GitHub Burndown Chart as a service. Public repos are free, for private access au
|
||||
- [ ] Since persistence is async, deal with the flicker (show laoding?) when we are still getting data
|
||||
- [ ] On page load get all the latest data regardless of `time_ago`
|
||||
- [ ] Show loading sign on top of [browser window](https://github.com/buunguyen/topbar) which is unobtrusive enough we can show it immediately.
|
||||
|
||||
### Extras
|
||||
|
||||
- [ ] rotate between percentage progress and points left
|
||||
- [ ] be able to config options through UI that currently have to be hardcoded in config
|
||||
- [ ] cache repos in `localStorage` for those that do not use GitHub login
|
||||
|
@ -13,6 +13,9 @@
|
||||
"localforage": "~0.9.2",
|
||||
"superagent": "~0.19.0",
|
||||
"async": "~0.9.0",
|
||||
"moment": "~2.8.3"
|
||||
"moment": "~2.8.3",
|
||||
"d3": "~3.4.11",
|
||||
"d3-tip": "~0.6.5",
|
||||
"marked": "~0.3.2"
|
||||
}
|
||||
}
|
||||
|
10852
public/js/app.bundle.js
10852
public/js/app.bundle.js
File diff suppressed because it is too large
Load Diff
@ -26,13 +26,17 @@
|
||||
document.title = 'BurnChart: GitHub Burndown Chart as a Service';
|
||||
Page = require("./views/pages/" + page);
|
||||
return new Page({
|
||||
el: el
|
||||
el: el,
|
||||
'data': {
|
||||
'route': req.params
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
router = {
|
||||
'': _.partial(route, 'index'),
|
||||
'project/add': _.partial(route, 'addProject'),
|
||||
'chart/:owner/:name/:milestone': _.partial(route, 'showChart'),
|
||||
'reset': function() {
|
||||
mediator.fire('!projects/clear');
|
||||
return window.location.hash = '#';
|
||||
@ -96,36 +100,31 @@
|
||||
'list': []
|
||||
},
|
||||
init: function() {
|
||||
var getMilestones,
|
||||
_this = this;
|
||||
getMilestones = function() {};
|
||||
var _this = this;
|
||||
localforage.getItem('projects', function(projects) {
|
||||
if (projects == null) {
|
||||
projects = [];
|
||||
}
|
||||
return _this.set('list', projects);
|
||||
return async.each(projects, function(project, cb) {
|
||||
return mediator.fire('!projects/add', project);
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
this.observe('list', function(projects) {
|
||||
return localforage.setItem('projects', projects);
|
||||
});
|
||||
mediator.on('!projects/add', function(repo, done) {
|
||||
return request.allMilestones(repo, function(err, res) {
|
||||
var active, milestones;
|
||||
var milestones;
|
||||
if (err) {
|
||||
throw err;
|
||||
return done(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()
|
||||
}
|
||||
milestones: milestones
|
||||
}));
|
||||
return done();
|
||||
});
|
||||
@ -390,7 +389,7 @@
|
||||
// layout.mustache
|
||||
root.require.register('burnchart/src/templates/layout.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<Header/>","","<div id=\"page\">"," <!-- content loaded from a router -->","</div>","","<div id=\"footer\">"," <div class=\"wrap\">"," © 2012-2014 Radek Stepan"," </div>","</div>"].join("\n");
|
||||
module.exports = ["<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");
|
||||
});
|
||||
|
||||
// addProject.mustache
|
||||
@ -405,10 +404,16 @@
|
||||
module.exports = ["<div id=\"title\">"," <div class=\"wrap\">"," <h2>Disposable Project</h2>"," <span class=\"milestone\">Milestone 1.0</span>"," <p class=\"description\">The one where we deliver all that we promised.</p>"," </div>","</div>","","<div id=\"content\" class=\"wrap\">"," <Hero/>"," <Projects/>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// showChart.mustache
|
||||
root.require.register('burnchart/src/templates/pages/showChart.js', function(exports, require, module) {
|
||||
|
||||
module.exports = ["<div id=\"content\" class=\"wrap\">"," <div id=\"chart\">"," <div id=\"tooltip\"></div>"," <div id=\"svg\"></div>"," </div>","</div>"].join("\n");
|
||||
});
|
||||
|
||||
// projects.mustache
|
||||
root.require.register('burnchart/src/templates/projects.js', function(exports, require, module) {
|
||||
|
||||
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");
|
||||
module.exports = ["{{#projects.list.length}}"," <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}}"," {{#milestones}}"," <tr>"," <td><a class=\"repo\">{{owner}}/{{name}}</a></td>"," <td>"," <a class=\"milestone\" href=\"#chart/{{owner}}/{{name}}/{{number}}\">{{ title }}</a>"," </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(this)}}\" style=\"width:{{format.progress(closed_issues, open_issues)}}%\"></div>"," </div>"," </div>"," </td>"," </tr>"," {{/milestones}}"," {{/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
|
||||
@ -583,10 +588,20 @@
|
||||
Projects: Projects
|
||||
},
|
||||
'data': {
|
||||
'format': format,
|
||||
getMilestone: function(list) {
|
||||
return _.findWhere(list, 'active');
|
||||
}
|
||||
format: format
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// showChart.coffee
|
||||
root.require.register('burnchart/src/views/pages/showChart.js', function(exports, require, module) {
|
||||
|
||||
module.exports = Ractive.extend({
|
||||
'template': require('../../templates/pages/showChart'),
|
||||
'adapt': [Ractive.adaptors.Ractive],
|
||||
init: function() {
|
||||
return console.log(this.get('route'));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -12,11 +12,12 @@ el = '#page'
|
||||
route = (page, req, evt) ->
|
||||
document.title = 'BurnChart: GitHub Burndown Chart as a Service'
|
||||
Page = require "./views/pages/#{page}"
|
||||
new Page { el }
|
||||
new Page { el, 'data': { 'route': req.params } }
|
||||
|
||||
router =
|
||||
'': _.partial route, 'index'
|
||||
'project/add': _.partial route, 'addProject'
|
||||
'': _.partial route, 'index'
|
||||
'project/add': _.partial route, 'addProject'
|
||||
'chart/:owner/:name/:milestone': _.partial route, 'showChart'
|
||||
# TODO: remove in production.
|
||||
'reset': ->
|
||||
mediator.fire '!projects/clear'
|
||||
|
@ -11,14 +11,15 @@ module.exports = new Model
|
||||
'list': []
|
||||
|
||||
init: ->
|
||||
# Fetches a list of milestones for a repo.
|
||||
getMilestones = ->
|
||||
|
||||
# Initialize with items stored locally.
|
||||
localforage.getItem 'projects', (projects=[]) =>
|
||||
@set 'list', projects
|
||||
# Fetch milestones for each of these projects.
|
||||
async.each projects, (project, cb) ->
|
||||
mediator.fire '!projects/add', project
|
||||
, (err) ->
|
||||
throw err if err
|
||||
|
||||
# Persist in local storage.
|
||||
# Persist projects in local storage.
|
||||
@observe 'list', (projects) ->
|
||||
localforage.setItem 'projects', projects
|
||||
|
||||
@ -28,24 +29,15 @@ module.exports = new Model
|
||||
|
||||
# Fetch milestones (which validates repo too).
|
||||
request.allMilestones repo, (err, res) =>
|
||||
throw err if err
|
||||
return done 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
|
||||
# Push to the stack.
|
||||
@push 'list', _.merge repo, { milestones }
|
||||
|
||||
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.
|
||||
# Call back.
|
||||
do done
|
||||
|
||||
mediator.on '!projects/clear', =>
|
||||
|
@ -233,7 +233,7 @@ ul
|
||||
table
|
||||
width: 100%
|
||||
|
||||
tr
|
||||
tr
|
||||
td
|
||||
background: #FCFCFC
|
||||
padding: 20px 30px
|
||||
|
95
src/styles/chart.styl
Normal file
95
src/styles/chart.styl
Normal file
@ -0,0 +1,95 @@
|
||||
@import 'nib'
|
||||
|
||||
// color definitions
|
||||
$closed = #4DAF7C
|
||||
$opened = #E55F3A
|
||||
$grey = #CACACA
|
||||
$brown = #64584C
|
||||
$background1 = #D7BCAB
|
||||
$background2 = #CC9485
|
||||
|
||||
// where D3 renders to
|
||||
#chart
|
||||
height: 200px
|
||||
position: relative
|
||||
|
||||
// position will be adjusted dynamically
|
||||
#tooltip
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
|
||||
svg
|
||||
path
|
||||
&.line
|
||||
fill: none
|
||||
stroke-width: 1px
|
||||
clip-path: url(#clip)
|
||||
|
||||
// actual progress
|
||||
&.actual
|
||||
stroke: $brown
|
||||
stroke-width: 3px
|
||||
|
||||
// ideal velocity throughout the sprint
|
||||
&.ideal
|
||||
stroke: $grey
|
||||
stroke-width: 3px
|
||||
|
||||
// trend of actual issue closures
|
||||
&.trendline
|
||||
stroke: $brown
|
||||
stroke-width: 1.5px
|
||||
stroke-dasharray: 5,5
|
||||
|
||||
// right now
|
||||
line
|
||||
&.today
|
||||
stroke: $grey
|
||||
stroke-width: 1px
|
||||
shape-rendering: crispEdges
|
||||
stroke-dasharray: 5,5
|
||||
|
||||
// represents one issue closed
|
||||
circle
|
||||
fill: $brown
|
||||
// make it easier to click
|
||||
stroke: transparent
|
||||
stroke-width: 15px
|
||||
cursor: pointer
|
||||
|
||||
// axes...
|
||||
.axis
|
||||
shape-rendering: crispEdges
|
||||
|
||||
line
|
||||
stroke: rgba($grey, 0.25)
|
||||
shape-rendering: crispEdges
|
||||
|
||||
text
|
||||
font-weight: bold
|
||||
fill: $grey
|
||||
|
||||
path
|
||||
display: none
|
||||
|
||||
// tooltips
|
||||
.d3-tip
|
||||
margin-top: -10px
|
||||
font-size: 11px
|
||||
padding: 8px 10px 7px 10px
|
||||
text-align: center
|
||||
background: rgba(0,0,0,0.75)
|
||||
color: #fff
|
||||
border-radius: 3px
|
||||
|
||||
&:after
|
||||
width: 100%
|
||||
color: rgba(0,0,0,0.8)
|
||||
content: "\25BC"
|
||||
position: absolute
|
||||
|
||||
&.n:after
|
||||
margin: -3px 0 0 0
|
||||
top: 100%
|
||||
left: 0
|
@ -6,6 +6,6 @@
|
||||
|
||||
<div id="footer">
|
||||
<div class="wrap">
|
||||
© 2012-2014 Radek Stepan
|
||||
© 2012-2014 <a href="http://cloudfi.re">Cloudfire Systems</a>
|
||||
</div>
|
||||
</div>
|
6
src/templates/pages/showChart.mustache
Normal file
6
src/templates/pages/showChart.mustache
Normal file
@ -0,0 +1,6 @@
|
||||
<div id="content" class="wrap">
|
||||
<div id="chart">
|
||||
<div id="tooltip"></div>
|
||||
<div id="svg"></div>
|
||||
</div>
|
||||
</div>
|
@ -1,4 +1,4 @@
|
||||
{{#projects.list}}
|
||||
{{#projects.list.length}}
|
||||
<div id="projects">
|
||||
<div class="header">
|
||||
<a href="#" class="sort"><span class="icon sort-alphabet"></span> Sorted by priority</a>
|
||||
@ -7,33 +7,26 @@
|
||||
|
||||
<table>
|
||||
{{#projects.list}}
|
||||
<tr>
|
||||
<td><a class="repo" href="#">{{owner}}/{{name}}</a></td>
|
||||
{{# { milestone: getMilestone(milestones.list) } }}
|
||||
{{#milestone}}
|
||||
{{#milestones}}
|
||||
<tr>
|
||||
<td><a class="repo">{{owner}}/{{name}}</a></td>
|
||||
<td>
|
||||
<span class="milestone">
|
||||
{{ milestone.title }}
|
||||
<span class="icon down-open">
|
||||
</span>
|
||||
<a class="milestone" href="#chart/{{owner}}/{{name}}/{{number}}">{{ title }}</a>
|
||||
</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 class="inner bar {{format.onTime(this)}}" 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>
|
||||
{{/milestones}}
|
||||
{{/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>
|
||||
@ -86,6 +79,7 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
-->
|
||||
</table>
|
||||
|
||||
<div class="footer">
|
||||
|
@ -8,8 +8,4 @@ module.exports = Ractive.extend
|
||||
|
||||
'components': { Hero, Projects }
|
||||
|
||||
'data':
|
||||
'format': format
|
||||
# Find the milestone that is active.
|
||||
getMilestone: (list) ->
|
||||
_.findWhere list, 'active'
|
||||
'data': { format }
|
8
src/views/pages/showChart.coffee
Normal file
8
src/views/pages/showChart.coffee
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = Ractive.extend
|
||||
|
||||
'template': require '../../templates/pages/showChart'
|
||||
|
||||
'adapt': [ Ractive.adaptors.Ractive ]
|
||||
|
||||
init: ->
|
||||
console.log @get 'route'
|
Loading…
x
Reference in New Issue
Block a user