From 790156f28d7aa41528dc13371d0ecb5f148e21b9 Mon Sep 17 00:00:00 2001 From: Gregory Eremin Date: Thu, 12 Mar 2015 03:18:14 +0700 Subject: [PATCH] Same layout for each dashboard + barchart animations --- app/scripts/src/app.jsx | 123 +++++++++---------------- app/scripts/src/bar_chart.jsx | 101 +++++++++++++++++--- app/scripts/src/stacked_area_chart.jsx | 46 ++++++--- db/stat.go | 4 +- 4 files changed, 161 insertions(+), 113 deletions(-) diff --git a/app/scripts/src/app.jsx b/app/scripts/src/app.jsx index db5748e..6dd5495 100644 --- a/app/scripts/src/app.jsx +++ b/app/scripts/src/app.jsx @@ -115,88 +115,49 @@ var Org = React.createClass({ } }); -var OrgStats = React.createClass({ +var Dashboard = React.createClass({ mixins: [Router.State], render: function(){ - var org = Storage.get('org', this.getParams().org); + var p = this.getParams(), + infoImage, infoTitle, infoText, + bcApi, bcItems, + sacApi, sacItems; + + if (p.team) { + infoTitle = p.team; + bcApi = '/api/stat/teams/top'; + bcItems = ['repo', 'user'], + sacApi = '/api/stat/teams/activity'; + sacItems = ['user', 'repo']; + } else if (p.user) { + infoTitle = p.user; + bcApi = '/api/stat/users/top'; + bcItems = ['repo'], + sacApi = '/api/stat/users/activity'; + sacItems = ['repo']; + } else if (p.repo) { + infoTitle = p.repo; + bcApi = '/api/stat/repos/top'; + bcItems = ['user', 'team'], + sacApi = '/api/stat/repos/activity'; + sacItems = ['user', 'team']; + } else { + var info = Storage.get('org', p.org); + infoImage = info.avatar_url; + infoTitle = info.login; + infoText = info.descr; + bcApi = '/api/stat/orgs/top'; + bcItems = ['repo', 'team', 'user'], + sacApi = '/api/stat/orgs/activity'; + sacItems = ['team', 'user', 'repo']; + } + return (
- - - -
- ); - } -}); - -var TeamStats = React.createClass({ - mixins: [Router.State], - - render: function(){ - return ( -
- - - -
- ); - } -}); - -var UserStats = React.createClass({ - mixins: [Router.State], - render: function(){ - return ( -
- - - -
- ); - } -}); - -var RepoStats = React.createClass({ - mixins: [Router.State], - render: function(){ - return ( -
- - - + + +
); } @@ -236,10 +197,10 @@ var routes = [ - - - - + + + + ]; diff --git a/app/scripts/src/bar_chart.jsx b/app/scripts/src/bar_chart.jsx index 4e64ebc..d0a0192 100644 --- a/app/scripts/src/bar_chart.jsx +++ b/app/scripts/src/bar_chart.jsx @@ -7,13 +7,16 @@ var BarChart = React.createClass({ getInitialState: function() { return { + currentApi: null, + currentParams: null, item: this.props.items[0], sort: 'commits', rawData: [], points: [], + oldPoints: [], min: 0, max: 1, - canvasWidth: 500 + canvasWidth: 500, }; }, @@ -24,11 +27,17 @@ var BarChart = React.createClass({ }, componentDidMount: function() { - this.fetchData(); this.calculateViewBoxWidth(); window.addEventListener('resize', this.calculateViewBoxWidth); }, + componentWillReceiveProps: function(newProps) { + this.setState({ + 'item': newProps.items[0], + 'sort': 'commits' + }, this.fetchData); + }, + handleFilter: function(thing, i) { if (thing === 'item' && this.props.items[i] !== this.state.item) { this.setState({ @@ -48,14 +57,30 @@ var BarChart = React.createClass({ }, fetchData: function() { - $.get(this.props.api, this.apiParams(), function(res){ - this.setState({ - rawData: res - }, this.sort); + if (!this.apiParams().item) { + return; + } + if (this.state.currentApi === this.props.api && + this.state.currentParams === JSON.stringify(this.apiParams())) { + return; + } + + console.log('-----> fetching', this.state.currentApi, this.props.api); + this.setState({ + currentApi: this.props.api, + currentParams: JSON.stringify(this.apiParams()) + }, function() { + $.get(this.props.api, this.apiParams(), function(res){ + this.setState({ + rawData: res, + oldPoints: this.state.points + }, this.sort); + }.bind(this)); }.bind(this)); }, sort: function() { + console.log('-----> sorting'); var sortFun = function(a, b) { return Math.abs(b[this.state.sort]) - Math.abs(a[this.state.sort]); }.bind(this); @@ -132,9 +157,15 @@ var BarChart = React.createClass({ y = this.y(i); return ( - ); } @@ -143,6 +174,36 @@ var BarChart = React.createClass({ var Bar = React.createClass({ mixins: [Router.Navigation], + getInitialState: function() { + return {lastx: 0, lastw: 0}; + }, + + componentWillReceiveProps: function(newProps) { + console.log("New bar props!", newProps.item, newProps.x, newProps.width); + this.setState({ + lastx: this.props.x, + lastw: this.props.width + }, this.animate); + }, + + animate: function() { + var bar = this.refs.bar.getDOMNode(), + anim = anim = document.createElementNS(SVGNS, 'animate'); + + if (bar.childNodes.length > 0) { + bar.removeChild(bar.childNodes[0]); + } + + anim.setAttributeNS(null, 'attributeType', 'XML'); + anim.setAttributeNS(null, 'attributeName', 'width'); + anim.setAttributeNS(null, 'from', this.state.lastw); + anim.setAttributeNS(null, 'to', this.props.width); + anim.setAttributeNS(null, 'dur', '300ms'); + anim.setAttributeNS(null, 'repeatCount', '1'); + bar.appendChild(anim); + anim.beginElement(); + }, + render: function() { var val = this.props.value, item = this.props.item, @@ -183,13 +244,23 @@ var Bar = React.createClass({ return ( - - + + {label} ); diff --git a/app/scripts/src/stacked_area_chart.jsx b/app/scripts/src/stacked_area_chart.jsx index a7b7619..8fc5ec5 100644 --- a/app/scripts/src/stacked_area_chart.jsx +++ b/app/scripts/src/stacked_area_chart.jsx @@ -15,6 +15,13 @@ var StackedAreaChart = React.createClass({ }; }, + componentWillReceiveProps: function(newProps) { + this.setState({ + 'item': newProps.items[0], + 'sort': 'commits' + }, this.fetchData); + }, + calculateViewBoxWidth: function() { this.setState({ canvasWidth: this.refs.svg.getDOMNode().offsetWidth @@ -77,20 +84,20 @@ var StackedAreaChart = React.createClass({ if (res[el.week] === undefined) { res[el.week] = {}; } - res[el.week][el.item] = el.commits; + if (top.indexOf(el.item) > -1) { + res[el.week][el.item] = el.commits; + } return res; }, {}); + var max = _.chain(weeks).keys().sort().reverse().take(15).map(function(week) { + return _.sum(_.values(weeks[week])); + }) + .max() + .value(); - var max = _.chain(this.state.rawData) - .reduce(function(res, el) { - if (res[el.week] === undefined) { - res[el.week] = 0; - } - res[el.week] += el.commits; - return res; - }, {}) - .max() - .value(); + // var max = _.max(_.map(weeks, function(items, week) { + // return _.sum(_.values(items)); + // })); this.setState({ top: top, @@ -102,11 +109,12 @@ var StackedAreaChart = React.createClass({ buildPathD: function(points) { var maxWidth = this.state.canvasWidth, maxHeight = this.height, + maxValue = this.state.max, len = points.length; var d = _.map(points, function(point, i) { - return 'L'+ Math.floor(i/len*maxWidth) +','+ (maxHeight - point); + return 'L'+ Math.floor(i/len*maxWidth) +','+ Math.floor(maxHeight - point); }); - d.unshift('M0,'+maxHeight); + d.unshift('M0,'+ maxHeight); d.push('L'+ maxWidth +','+ maxHeight +'Z'); // for (var i = 0; i < missing; i++) { @@ -133,8 +141,10 @@ var StackedAreaChart = React.createClass({ }); var sum = 0; + // console.log('----------'); var points = _.map(values, function(val) { sum += Math.floor(val/max*maxHeight); + // console.log(val, max, maxHeight, sum); return sum; }); @@ -182,7 +192,10 @@ var StackedAreaChart = React.createClass({
    {_.pairs(colors).map(function(pair){ return ( -
  • {pair[0]}
  • +
  • +
    + {pair[0]} +
  • ); })}
@@ -194,7 +207,10 @@ var StackedAreaChart = React.createClass({ var StackedArea = React.createClass({ render: function() { return ( - + ); } }); diff --git a/db/stat.go b/db/stat.go index eb23f37..c10f964 100644 --- a/db/stat.go +++ b/db/stat.go @@ -148,7 +148,7 @@ order by commits desc` const repoActivityQuery = ` select - c.week as weel, + c.week as week, %s as item, sum(c.commits) as commits, sum(c.additions) - sum(c.deletions) as delta @@ -211,6 +211,6 @@ func StatRepoTop(p map[string]interface{}) (res []StatItem) { func StatRepoActivity(p map[string]interface{}) (res []StatPoint) { defer measure("StatRepoActivity", time.Now()) - mustSelectN(&res, fmt.Sprintf(repoTopQuery, p["item"]), p) + mustSelectN(&res, fmt.Sprintf(repoActivityQuery, p["item"]), p) return }