mirror of
				https://github.com/aclindsa/moneygo.git
				synced 2025-11-04 02:23:26 -05:00 
			
		
		
		
	WIP: Stacked bar chart
This commit is contained in:
		@@ -1,24 +1,6 @@
 | 
				
			|||||||
var React = require('react');
 | 
					var React = require('react');
 | 
				
			||||||
var ReactDOM = require('react-dom');
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
var ReactBootstrap = require('react-bootstrap');
 | 
					var StackedBarChart = require('../components/StackedBarChart');
 | 
				
			||||||
var Grid = ReactBootstrap.Grid;
 | 
					 | 
				
			||||||
var Row = ReactBootstrap.Row;
 | 
					 | 
				
			||||||
var Col = ReactBootstrap.Col;
 | 
					 | 
				
			||||||
var Form = ReactBootstrap.Form;
 | 
					 | 
				
			||||||
var FormGroup = ReactBootstrap.FormGroup;
 | 
					 | 
				
			||||||
var FormControl = ReactBootstrap.FormControl;
 | 
					 | 
				
			||||||
var ControlLabel = ReactBootstrap.ControlLabel;
 | 
					 | 
				
			||||||
var Button = ReactBootstrap.Button;
 | 
					 | 
				
			||||||
var ButtonGroup = ReactBootstrap.ButtonGroup;
 | 
					 | 
				
			||||||
var ButtonToolbar = ReactBootstrap.ButtonToolbar;
 | 
					 | 
				
			||||||
var Glyphicon = ReactBootstrap.Glyphicon;
 | 
					 | 
				
			||||||
var ListGroup = ReactBootstrap.ListGroup;
 | 
					 | 
				
			||||||
var ListGroupItem = ReactBootstrap.ListGroupItem;
 | 
					 | 
				
			||||||
var Modal = ReactBootstrap.Modal;
 | 
					 | 
				
			||||||
var Panel = ReactBootstrap.Panel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var Combobox = require('react-widgets').Combobox;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = React.createClass({
 | 
					module.exports = React.createClass({
 | 
				
			||||||
	displayName: "ReportsTab",
 | 
						displayName: "ReportsTab",
 | 
				
			||||||
@@ -29,9 +11,13 @@ module.exports = React.createClass({
 | 
				
			|||||||
		this.props.onFetchReport("monthly_expenses");
 | 
							this.props.onFetchReport("monthly_expenses");
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	render: function() {
 | 
						render: function() {
 | 
				
			||||||
		console.log(this.props.reports);
 | 
							report = [];
 | 
				
			||||||
 | 
							if (this.props.reports['monthly_expenses'])
 | 
				
			||||||
 | 
								report = (<StackedBarChart data={this.props.reports['monthly_expenses']} />);
 | 
				
			||||||
		return (
 | 
							return (
 | 
				
			||||||
			<div>hello</div>
 | 
								<div>
 | 
				
			||||||
 | 
									{report}
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
		);
 | 
							);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										162
									
								
								js/components/StackedBarChart.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								js/components/StackedBarChart.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
				
			|||||||
 | 
					var d3 = require('d3');
 | 
				
			||||||
 | 
					var React = require('react');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var Panel = require('react-bootstrap').Panel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = React.createClass({
 | 
				
			||||||
 | 
						displayName: "StackedBarChart",
 | 
				
			||||||
 | 
						calcMinMax: function(data) {
 | 
				
			||||||
 | 
							var children = [];
 | 
				
			||||||
 | 
							for (var child in data) {
 | 
				
			||||||
 | 
								if (data.hasOwnProperty(child))
 | 
				
			||||||
 | 
									children.push(data[child]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var positiveValues = [0];
 | 
				
			||||||
 | 
							var negativeValues = [0];
 | 
				
			||||||
 | 
							if (children.length > 0 && children[0].length > 0) {
 | 
				
			||||||
 | 
								for (var j = 0; j < children[0].length; j++) {
 | 
				
			||||||
 | 
									positiveValues.push(children.reduce(function(accum, curr, i, arr) {
 | 
				
			||||||
 | 
										if (arr[i][j] > 0)
 | 
				
			||||||
 | 
											return accum + arr[i][j];
 | 
				
			||||||
 | 
										return accum;
 | 
				
			||||||
 | 
									}, 0));
 | 
				
			||||||
 | 
									negativeValues.push(children.reduce(function(accum, curr, i, arr) {
 | 
				
			||||||
 | 
										if (arr[i][j] < 0)
 | 
				
			||||||
 | 
											return accum + arr[i][j];
 | 
				
			||||||
 | 
										return accum;
 | 
				
			||||||
 | 
									}, 0));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return [Math.min.apply(Math, negativeValues), Math.max.apply(Math, positiveValues)];
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						calcAxisMarkSeparation: function(minMax, height, ticksPerHeight) {
 | 
				
			||||||
 | 
							var targetTicks = height / ticksPerHeight;
 | 
				
			||||||
 | 
							var range = minMax[1]-minMax[0];
 | 
				
			||||||
 | 
							var rangePerTick = range/targetTicks;
 | 
				
			||||||
 | 
							var roundOrder = Math.floor(Math.log(rangePerTick) / Math.LN10);
 | 
				
			||||||
 | 
							var roundTo = Math.pow(10, roundOrder);
 | 
				
			||||||
 | 
							return Math.ceil(rangePerTick/roundTo)*roundTo;
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						render: function() {
 | 
				
			||||||
 | 
							var data = this.props.data.mapReduceChildren(null,
 | 
				
			||||||
 | 
								function(accumulator, currentValue, currentIndex, array) {
 | 
				
			||||||
 | 
									return accumulator + currentValue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							height = 400;
 | 
				
			||||||
 | 
							width = 600;
 | 
				
			||||||
 | 
							legendWidth = 100;
 | 
				
			||||||
 | 
							xMargin = 70;
 | 
				
			||||||
 | 
							yMargin = 70;
 | 
				
			||||||
 | 
							height -= yMargin*2;
 | 
				
			||||||
 | 
							width -= xMargin*2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var minMax = this.calcMinMax(data);
 | 
				
			||||||
 | 
							var y = d3.scaleLinear()
 | 
				
			||||||
 | 
								.range([0, height])
 | 
				
			||||||
 | 
								.domain(minMax);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var xAxisMarksEvery = this.calcAxisMarkSeparation(minMax, height, 40);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var x = d3.scaleLinear()
 | 
				
			||||||
 | 
								.range([0, width])
 | 
				
			||||||
 | 
								.domain([0, this.props.data.Labels.length + 0.5]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var bars = [];
 | 
				
			||||||
 | 
							var labels = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var barWidth = x(0.75);
 | 
				
			||||||
 | 
							var barStart = x(0.25) + (x(1) - barWidth)/2;
 | 
				
			||||||
 | 
							var childId=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Add Y axis marks and labels, and initialize positive- and
 | 
				
			||||||
 | 
							// negativeSum arrays
 | 
				
			||||||
 | 
							var positiveSum = [];
 | 
				
			||||||
 | 
							var negativeSum = [];
 | 
				
			||||||
 | 
							for (var i=0; i < this.props.data.Labels.length; i++) {
 | 
				
			||||||
 | 
								positiveSum.push(0);
 | 
				
			||||||
 | 
								negativeSum.push(0);
 | 
				
			||||||
 | 
								var labelX = x(i) + barStart + barWidth/2;
 | 
				
			||||||
 | 
								var labelY = height + 15;
 | 
				
			||||||
 | 
								labels.push((
 | 
				
			||||||
 | 
									<text x={labelX} y={labelY} transform={"rotate(45 "+labelX+" "+labelY+")"}>{this.props.data.Labels[i]}</text>
 | 
				
			||||||
 | 
								));
 | 
				
			||||||
 | 
								labels.push((
 | 
				
			||||||
 | 
									<line className="axis-tick" x1={labelX} y1={height-3} x2={labelX} y2={height+3} />
 | 
				
			||||||
 | 
								));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Make X axis marks and labels
 | 
				
			||||||
 | 
							var makeXLabel = function(value) {
 | 
				
			||||||
 | 
								labels.push((
 | 
				
			||||||
 | 
										<line className="axis-tick" x1={-3} y1={height - y(value)} x2={3} y2={height - y(value)} />
 | 
				
			||||||
 | 
								));
 | 
				
			||||||
 | 
								labels.push((
 | 
				
			||||||
 | 
										<text is x={-10} y={height - y(value) + 6} text-anchor={"end"}>{value}</text>
 | 
				
			||||||
 | 
								));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for (var i=0; i < minMax[1]; i+= xAxisMarksEvery)
 | 
				
			||||||
 | 
								makeXLabel(i);
 | 
				
			||||||
 | 
							for (var i=0-xAxisMarksEvery; i > minMax[0]; i -= xAxisMarksEvery)
 | 
				
			||||||
 | 
								makeXLabel(i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							//TODO handle Values from current series?
 | 
				
			||||||
 | 
							var legendMap = {};
 | 
				
			||||||
 | 
							for (var child in data) {
 | 
				
			||||||
 | 
								childId++;
 | 
				
			||||||
 | 
								var rectClasses = "chart-element chart-color" + (childId % 12);
 | 
				
			||||||
 | 
								if (data.hasOwnProperty(child)) {
 | 
				
			||||||
 | 
									for (var i=0; i < data[child].length; i++) {
 | 
				
			||||||
 | 
										var value = data[child][i];
 | 
				
			||||||
 | 
										if (value == 0)
 | 
				
			||||||
 | 
											continue;
 | 
				
			||||||
 | 
										legendMap[child] = childId;
 | 
				
			||||||
 | 
										if (value > 0) {
 | 
				
			||||||
 | 
											rectHeight = y(value) - y(0);
 | 
				
			||||||
 | 
											positiveSum[i] += rectHeight;
 | 
				
			||||||
 | 
											rectY = height - y(0) - positiveSum[i];
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											rectHeight = y(0) - y(value);
 | 
				
			||||||
 | 
											rectY = height - y(0) + negativeSum[i];
 | 
				
			||||||
 | 
											negativeSum[i] += rectHeight;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										bars.push((
 | 
				
			||||||
 | 
											<rect className={rectClasses} x={x(i) + barStart} y={rectY} width={barWidth} height={rectHeight} rx={1} ry={1}/>
 | 
				
			||||||
 | 
										));
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var legend = [];
 | 
				
			||||||
 | 
							for (var series in legendMap) {
 | 
				
			||||||
 | 
								var legendClasses = "chart-color" + (legendMap[series] % 12);
 | 
				
			||||||
 | 
								var legendY = (legendMap[series] - 1)*15;
 | 
				
			||||||
 | 
								legend.push((
 | 
				
			||||||
 | 
									<rect className={legendClasses} x={0} y={legendY} width={10} height={10}/>
 | 
				
			||||||
 | 
								));
 | 
				
			||||||
 | 
								legend.push((
 | 
				
			||||||
 | 
									<text x={0 + 15} y={legendY + 10}>{series}</text>
 | 
				
			||||||
 | 
								));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return (
 | 
				
			||||||
 | 
								<Panel header={this.props.data.Title}>
 | 
				
			||||||
 | 
									<svg height={height + 2*yMargin} width={width + 2*xMargin + legendWidth}>
 | 
				
			||||||
 | 
										<g className="stacked-bar-chart" transform={"translate("+xMargin+" "+yMargin+")"}>
 | 
				
			||||||
 | 
											{bars}
 | 
				
			||||||
 | 
											<line className="axis x-axis" x1={0} y1={height} x2={width} y2={height} />
 | 
				
			||||||
 | 
											<line className="axis y-axis" x1={0} y1={0} x2={0} y2={height} />
 | 
				
			||||||
 | 
											{labels}
 | 
				
			||||||
 | 
										</g>
 | 
				
			||||||
 | 
										<g className="chart-legend" transform={"translate("+(width + 2*xMargin)+" "+yMargin+")"}>
 | 
				
			||||||
 | 
											{legend}
 | 
				
			||||||
 | 
										</g>
 | 
				
			||||||
 | 
									</svg>
 | 
				
			||||||
 | 
								</Panel>
 | 
				
			||||||
 | 
							);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										46
									
								
								js/models.js
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								js/models.js
									
									
									
									
									
								
							@@ -425,6 +425,39 @@ Series.prototype.fromJSONobj = function(json_obj) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Series.prototype.mapReduceChildren = function(mapFn, reduceFn) {
 | 
				
			||||||
 | 
						var children = {}
 | 
				
			||||||
 | 
						for (var child in this.Children) {
 | 
				
			||||||
 | 
							if (this.Children.hasOwnProperty(child))
 | 
				
			||||||
 | 
								children[child] = this.Children[child].mapReduce(mapFn, reduceFn);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return children;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Series.prototype.mapReduce = function(mapFn, reduceFn) {
 | 
				
			||||||
 | 
						var childValues = [];
 | 
				
			||||||
 | 
						if (mapFn)
 | 
				
			||||||
 | 
							childValues.push(this.Values.map(mapFn));
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							childValues.push(this.Values.slice());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (var child in this.Children) {
 | 
				
			||||||
 | 
							if (this.Children.hasOwnProperty(child))
 | 
				
			||||||
 | 
								childValues.push(this.Children[child].mapReduce(mapFn, reduceFn));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var reducedValues = [];
 | 
				
			||||||
 | 
						if (reduceFn && childValues.length > 0 && childValues[0].length > 0) {
 | 
				
			||||||
 | 
							for (var j = 0; j < childValues[0].length; j++) {
 | 
				
			||||||
 | 
								reducedValues.push(childValues.reduce(function(accum, curr, i, arr) {
 | 
				
			||||||
 | 
									return reduceFn(accum, arr[i][j]);
 | 
				
			||||||
 | 
								}, childValues[0][j]));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return reducedValues;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Report() {
 | 
					function Report() {
 | 
				
			||||||
	this.ReportId = "";
 | 
						this.ReportId = "";
 | 
				
			||||||
	this.Title = "";
 | 
						this.Title = "";
 | 
				
			||||||
@@ -475,6 +508,19 @@ Report.prototype.fromJSON = function(json_input) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Report.prototype.mapReduceChildren = function(mapFn, reduceFn) {
 | 
				
			||||||
 | 
						var series = {}
 | 
				
			||||||
 | 
						for (var child in this.Series) {
 | 
				
			||||||
 | 
							if (this.Series.hasOwnProperty(child))
 | 
				
			||||||
 | 
								series[child] = this.Series[child].mapReduce(mapFn, reduceFn);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return series;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Report.prototype.mapReduceSeries = function(mapFn, reduceFn) {
 | 
				
			||||||
 | 
						return this.mapReduceChildren(mapFn, reduceFn);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = models = {
 | 
					module.exports = models = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Classes
 | 
						// Classes
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@
 | 
				
			|||||||
    "big.js": "^3.1.3",
 | 
					    "big.js": "^3.1.3",
 | 
				
			||||||
    "browserify": "^13.1.0",
 | 
					    "browserify": "^13.1.0",
 | 
				
			||||||
    "cldr-data": "^29.0.2",
 | 
					    "cldr-data": "^29.0.2",
 | 
				
			||||||
 | 
					    "d3": "^4.5.0",
 | 
				
			||||||
    "globalize": "^1.1.1",
 | 
					    "globalize": "^1.1.1",
 | 
				
			||||||
    "keymirror": "^0.1.1",
 | 
					    "keymirror": "^0.1.1",
 | 
				
			||||||
    "react": "^15.3.2",
 | 
					    "react": "^15.3.2",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										67
									
								
								static/css/reports.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								static/css/reports.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					.chart-color1 {
 | 
				
			||||||
 | 
						fill: #a6cee3;
 | 
				
			||||||
 | 
						stroke: #86aec3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color2 {
 | 
				
			||||||
 | 
						fill: #1f78b4;
 | 
				
			||||||
 | 
						stroke: #005894;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color3 {
 | 
				
			||||||
 | 
						fill: #b2df8a;
 | 
				
			||||||
 | 
						stroke: #92bf6a;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color4 {
 | 
				
			||||||
 | 
						fill: #33a02c;
 | 
				
			||||||
 | 
						stroke: #13800c;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color5 {
 | 
				
			||||||
 | 
						fill: #fb9a99;
 | 
				
			||||||
 | 
						stroke: #db7a79;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color6 {
 | 
				
			||||||
 | 
						fill: #e31a1c;
 | 
				
			||||||
 | 
						stroke: #c30000;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color7 {
 | 
				
			||||||
 | 
						fill: #fdbf6f;
 | 
				
			||||||
 | 
						stroke: #dd9f4f;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color8 {
 | 
				
			||||||
 | 
						fill: #ff7f00;
 | 
				
			||||||
 | 
						fill: #df5f00;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color9 {
 | 
				
			||||||
 | 
						fill: #cab2d6;
 | 
				
			||||||
 | 
						stroke: #aa92b6;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color10 {
 | 
				
			||||||
 | 
						fill: #6a3d9a;
 | 
				
			||||||
 | 
						stroke: #4a1d7a;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color11 {
 | 
				
			||||||
 | 
						fill: #ffff99;
 | 
				
			||||||
 | 
						stroke: #dfdf79;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-color12 {
 | 
				
			||||||
 | 
						fill: #b15928;
 | 
				
			||||||
 | 
						stroke: #913908;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.chart-element {
 | 
				
			||||||
 | 
						stroke-width: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-element:hover {
 | 
				
			||||||
 | 
						stroke-width: 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.chart-legend rect {
 | 
				
			||||||
 | 
						stroke-width: 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.axis {
 | 
				
			||||||
 | 
						stroke: #000;
 | 
				
			||||||
 | 
						stroke-width: 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					.axis-tick {
 | 
				
			||||||
 | 
						stroke: #000;
 | 
				
			||||||
 | 
						stroke-width: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					@import url("reports.css");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
html, body {
 | 
					html, body {
 | 
				
			||||||
	height: 100%;
 | 
						height: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
 | 
					<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
 | 
				
			||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
 | 
					<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
 | 
				
			||||||
<link rel="stylesheet" href="static/react-widgets/css/react-widgets.css">
 | 
					<link rel="stylesheet" href="static/react-widgets/css/react-widgets.css">
 | 
				
			||||||
<link rel="stylesheet" href="static/stylesheet.css">
 | 
					<link rel="stylesheet" href="static/css/stylesheet.css">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
 | 
					<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user