Merge pull request #10 from Pear-Trading/TBSliver/Widget-Graph-Improvements
Refactored large graph
This commit is contained in:
commit
5279dfae10
8 changed files with 190 additions and 226 deletions
4
src/app/_interfaces/chart-data.ts
Normal file
4
src/app/_interfaces/chart-data.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export interface ChartData {
|
||||||
|
data: Array<number>;
|
||||||
|
label: string;
|
||||||
|
}
|
|
@ -9,73 +9,5 @@
|
||||||
[dataType]="widget.dataType"></widget-graph>
|
[dataType]="widget.dataType"></widget-graph>
|
||||||
</div><!--/.col-->
|
</div><!--/.col-->
|
||||||
</div><!--/.row-->
|
</div><!--/.row-->
|
||||||
<div class="card">
|
<panel-graph></panel-graph>
|
||||||
<div class="card-block">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-5">
|
|
||||||
<h4 class="card-title mb-0">Customers</h4>
|
|
||||||
<div class="small text-muted">November 2015</div>
|
|
||||||
</div><!--/.col-->
|
|
||||||
<div class="col-sm-7 hidden-sm-down">
|
|
||||||
<button type="button" class="btn btn-primary float-right"><i class="icon-cloud-download"></i></button>
|
|
||||||
<div class="btn-toolbar float-right" role="toolbar" aria-label="Toolbar with button groups">
|
|
||||||
<div class="btn-group mr-3" data-toggle="buttons" aria-label="First group">
|
|
||||||
<label class="btn btn-outline-secondary">
|
|
||||||
<input type="radio" name="options" id="option1"> Day
|
|
||||||
</label>
|
|
||||||
<label class="btn btn-outline-secondary active">
|
|
||||||
<input type="radio" name="options" id="option2" checked> Month
|
|
||||||
</label>
|
|
||||||
<label class="btn btn-outline-secondary">
|
|
||||||
<input type="radio" name="options" id="option3"> Year
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div><!--/.col-->
|
|
||||||
</div><!--/.row-->
|
|
||||||
<div class="chart-wrapper" style="height:300px;margin-top:40px;">
|
|
||||||
<canvas baseChart class="chart"
|
|
||||||
[datasets]="mainChartData"
|
|
||||||
[labels]="mainChartLabels"
|
|
||||||
[options]="mainChartOptions"
|
|
||||||
[colors]="mainChartColours"
|
|
||||||
[legend]="mainChartLegend"
|
|
||||||
[chartType]="mainChartType"
|
|
||||||
(chartHover)="chartHovered($event)"
|
|
||||||
(chartClick)="chartClicked($event)"></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer">
|
|
||||||
<ul>
|
|
||||||
<li class="hidden-sm-down">
|
|
||||||
<div class="text-muted">Customers</div>
|
|
||||||
<strong>24.093 Users (20%)</strong>
|
|
||||||
<div class="progress progress-xs mt-2">
|
|
||||||
<div class="progress-bar bg-info" role="progressbar" style="width: 20%" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<div class="text-muted">Pageviews</div>
|
|
||||||
<strong>78.706 Views (60%)</strong>
|
|
||||||
<div class="progress progress-xs mt-2">
|
|
||||||
<div class="progress-bar bg-warning" role="progressbar" style="width: 60%" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"></div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="hidden-sm-down">
|
|
||||||
<div class="text-muted">New Users</div>
|
|
||||||
<strong>22.123 Users (80%)</strong>
|
|
||||||
<div class="progress progress-xs mt-2">
|
|
||||||
<div class="progress-bar bg-danger" role="progressbar" style="width: 80%" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="hidden-sm-down">
|
|
||||||
<div class="text-muted">Bounce Rate</div>
|
|
||||||
<strong>40.15%</strong>
|
|
||||||
<div class="progress progress-xs mt-2">
|
|
||||||
<div class="progress-bar" role="progressbar" style="width: 40%" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import { Directive, Component, OnInit } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { GraphWidget } from '../widgets/graph-widget.component';
|
import { GraphWidget } from '../widgets/graph-widget.component';
|
||||||
import { OrgBarSnippetComponent } from '../snippets/org-snippet-bar.component';
|
import { OrgBarSnippetComponent } from '../snippets/org-snippet-bar.component';
|
||||||
|
import { GraphPanel } from '../panels/graph-panel.component';
|
||||||
import { DataType } from '../shared/data-types.enum';
|
import { DataType } from '../shared/data-types.enum';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: 'dashboard.component.html'
|
templateUrl: 'dashboard.component.html'
|
||||||
})
|
})
|
||||||
export class DashboardComponent implements OnInit {
|
export class DashboardComponent {
|
||||||
|
|
||||||
shuffledArray: any;
|
|
||||||
|
|
||||||
public widgetList = [
|
public widgetList = [
|
||||||
{
|
{
|
||||||
|
@ -52,153 +51,5 @@ export class DashboardComponent implements OnInit {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
constructor(
|
constructor() { }
|
||||||
) {
|
|
||||||
this.shuffle = this.shuffledArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fisher-Yates shuffle function
|
|
||||||
public shuffle(array) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
let counter = array.length;
|
|
||||||
|
|
||||||
// While there are elements in the array
|
|
||||||
while (counter > 0) {
|
|
||||||
// Pick a random index
|
|
||||||
const index = Math.floor(Math.random() * counter);
|
|
||||||
|
|
||||||
// Decrease counter by 1
|
|
||||||
counter--;
|
|
||||||
|
|
||||||
// And swap the last element with it
|
|
||||||
const temp = array[counter];
|
|
||||||
array[counter] = array[index];
|
|
||||||
array[index] = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.shuffledArray = array;
|
|
||||||
resolve(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public brandSuccess = '#4dbd74';
|
|
||||||
public brandInfo = '#63c2de';
|
|
||||||
public brandDanger = '#f86c6b';
|
|
||||||
|
|
||||||
// convert Hex to RGBA
|
|
||||||
public convertHex(hex: string, opacity: number) {
|
|
||||||
hex = hex.replace('#', '');
|
|
||||||
const r = parseInt(hex.substring(0, 2), 16);
|
|
||||||
const g = parseInt(hex.substring(2, 4), 16);
|
|
||||||
const b = parseInt(hex.substring(4, 6), 16);
|
|
||||||
|
|
||||||
const rgba = 'rgba(' + r + ', ' + g + ', ' + b + ', ' + opacity / 100 + ')';
|
|
||||||
return rgba;
|
|
||||||
}
|
|
||||||
|
|
||||||
// events
|
|
||||||
public chartClicked(e: any): void {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public chartHovered(e: any): void {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// mainChart
|
|
||||||
|
|
||||||
public random(min: number, max: number) {
|
|
||||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
||||||
}
|
|
||||||
|
|
||||||
public mainChartElements = 27;
|
|
||||||
public mainChartData1: Array<number> = [];
|
|
||||||
public mainChartData2: Array<number> = [];
|
|
||||||
public mainChartData3: Array<number> = [];
|
|
||||||
|
|
||||||
public mainChartData: Array<any> = [
|
|
||||||
{
|
|
||||||
data: this.mainChartData1,
|
|
||||||
label: 'Current'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: this.mainChartData2,
|
|
||||||
label: 'Previous'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
data: this.mainChartData3,
|
|
||||||
label: 'BEP'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
/* tslint:disable:max-line-length */
|
|
||||||
public mainChartLabels: Array<any> = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday', 'Monday', 'Thursday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
|
||||||
/* tslint:enable:max-line-length */
|
|
||||||
public mainChartOptions: any = {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
scales: {
|
|
||||||
xAxes: [{
|
|
||||||
gridLines: {
|
|
||||||
drawOnChartArea: false,
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
callback: function(value: any) {
|
|
||||||
return value.charAt(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
yAxes: [{
|
|
||||||
ticks: {
|
|
||||||
beginAtZero: true,
|
|
||||||
maxTicksLimit: 5,
|
|
||||||
stepSize: Math.ceil(250 / 5),
|
|
||||||
max: 250
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
elements: {
|
|
||||||
line: {
|
|
||||||
borderWidth: 2
|
|
||||||
},
|
|
||||||
point: {
|
|
||||||
radius: 0,
|
|
||||||
hitRadius: 10,
|
|
||||||
hoverRadius: 4,
|
|
||||||
hoverBorderWidth: 3,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public mainChartColours: Array<any> = [
|
|
||||||
{ // brandInfo
|
|
||||||
backgroundColor: this.convertHex(this.brandInfo, 10),
|
|
||||||
borderColor: this.brandInfo,
|
|
||||||
pointHoverBackgroundColor: '#fff'
|
|
||||||
},
|
|
||||||
{ // brandSuccess
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
borderColor: this.brandSuccess,
|
|
||||||
pointHoverBackgroundColor: '#fff'
|
|
||||||
},
|
|
||||||
{ // brandDanger
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
borderColor: this.brandDanger,
|
|
||||||
pointHoverBackgroundColor: '#fff',
|
|
||||||
borderWidth: 1,
|
|
||||||
borderDash: [8, 5]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
public mainChartLegend = false;
|
|
||||||
public mainChartType = 'line';
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
// generate random values for mainChart
|
|
||||||
for (let i = 0; i <= this.mainChartElements; i++) {
|
|
||||||
this.mainChartData1.push(this.random(50, 200));
|
|
||||||
this.mainChartData2.push(this.random(80, 100));
|
|
||||||
this.mainChartData3.push(this.random(50, 200));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { TransactionLogComponent } from './transaction-log.component';
|
||||||
|
|
||||||
import { GraphWidget } from '../widgets/graph-widget.component';
|
import { GraphWidget } from '../widgets/graph-widget.component';
|
||||||
import { OrgBarSnippetComponent } from '../snippets/org-snippet-bar.component';
|
import { OrgBarSnippetComponent } from '../snippets/org-snippet-bar.component';
|
||||||
|
import { GraphPanel } from '../panels/graph-panel.component';
|
||||||
|
|
||||||
import { DashboardRoutingModule } from './dashboard.routing';
|
import { DashboardRoutingModule } from './dashboard.routing';
|
||||||
import { OrgResultComponent } from '../shared/org-result.component';
|
import { OrgResultComponent } from '../shared/org-result.component';
|
||||||
|
@ -45,6 +46,7 @@ import { TransactionResultComponent } from '../shared/transaction-result.compone
|
||||||
FeedbackComponent,
|
FeedbackComponent,
|
||||||
GraphWidget,
|
GraphWidget,
|
||||||
OrgBarSnippetComponent,
|
OrgBarSnippetComponent,
|
||||||
|
GraphPanel,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
CurrencyPipe
|
CurrencyPipe
|
||||||
|
|
29
src/app/panels/graph-panel.component.html
Normal file
29
src/app/panels/graph-panel.component.html
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-block">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-5">
|
||||||
|
<h4 class="card-title mb-0">Customers</h4>
|
||||||
|
</div><!--/.col-->
|
||||||
|
<div class="col-sm-7 hidden-sm-down">
|
||||||
|
<div class="btn-toolbar float-right" role="toolbar" aria-label="Toolbar with button groups">
|
||||||
|
<div class="btn-group mr-3" data-toggle="buttons" aria-label="First group">
|
||||||
|
<label class="btn btn-outline-secondary active">
|
||||||
|
<input type="radio" name="options" id="option2" checked> Week
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!--/.col-->
|
||||||
|
</div><!--/.row-->
|
||||||
|
<div class="chart-wrapper" style="height:300px;margin-top:40px;">
|
||||||
|
<canvas baseChart class="chart"
|
||||||
|
[datasets]="chartData"
|
||||||
|
[labels]="chartLabels"
|
||||||
|
[options]="mainChartOptions"
|
||||||
|
[colors]="mainChartColours"
|
||||||
|
[legend]="chartLegend"
|
||||||
|
[chartType]="chartType"
|
||||||
|
(chartHover)="chartHovered($event)"
|
||||||
|
(chartClick)="chartClicked($event)"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
145
src/app/panels/graph-panel.component.ts
Normal file
145
src/app/panels/graph-panel.component.ts
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
import { OrgGraphsService } from '../providers/org-graphs.service';
|
||||||
|
import { DataType } from '../shared/data-types.enum';
|
||||||
|
import { ChartData } from '../_interfaces/chart-data';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'panel-graph',
|
||||||
|
templateUrl: 'graph-panel.component.html',
|
||||||
|
})
|
||||||
|
export class GraphPanel implements OnInit {
|
||||||
|
|
||||||
|
public chartType = 'line';
|
||||||
|
public chartLegend = true;
|
||||||
|
|
||||||
|
public rawChartData: Array<number> = [];
|
||||||
|
|
||||||
|
public chartData: Array<ChartData> = [
|
||||||
|
{
|
||||||
|
data: [],
|
||||||
|
label: 'This Week'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: [],
|
||||||
|
label: 'Last Week'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data: [],
|
||||||
|
label: 'Week Before Last'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
public rawChartLabels: Array<string> = [];
|
||||||
|
public chartLabels: Array<string> = [];
|
||||||
|
|
||||||
|
public brandSuccess = '#4dbd74';
|
||||||
|
public brandInfo = '#63c2de';
|
||||||
|
public brandDanger = '#f86c6b';
|
||||||
|
|
||||||
|
public mainChartElements = 7;
|
||||||
|
|
||||||
|
public mainChartOptions: any = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
unit: 'day',
|
||||||
|
displayFormats: {
|
||||||
|
day: 'dddd',
|
||||||
|
},
|
||||||
|
tooltipFormat: 'dddd',
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
drawOnChartArea: false,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
stepSize: 1,
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
elements: {
|
||||||
|
line: {
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
point: {
|
||||||
|
radius: 0,
|
||||||
|
hitRadius: 10,
|
||||||
|
hoverRadius: 4,
|
||||||
|
hoverBorderWidth: 3,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
public mainChartColours: Array<any> = [
|
||||||
|
{ // brandInfo
|
||||||
|
backgroundColor: this.convertHex(this.brandInfo, 10),
|
||||||
|
borderColor: this.brandInfo,
|
||||||
|
pointHoverBackgroundColor: '#fff'
|
||||||
|
},
|
||||||
|
{ // brandSuccess
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderColor: this.brandSuccess,
|
||||||
|
pointHoverBackgroundColor: '#fff'
|
||||||
|
},
|
||||||
|
{ // brandDanger
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderColor: this.brandDanger,
|
||||||
|
pointHoverBackgroundColor: '#fff',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderDash: [8, 5]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private graphService: OrgGraphsService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
const end = moment().startOf('day');
|
||||||
|
const start = end.clone().subtract(this.mainChartElements * 3, 'days');
|
||||||
|
this.graphService.getGraph('customers_range', {
|
||||||
|
start: start.format('YYYY-MM-DD'),
|
||||||
|
end: end.format('YYYY-MM-DD'),
|
||||||
|
}).subscribe( result => this.setData(result.graph) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private setData(data: any) {
|
||||||
|
this.chartLabels = data.labels.slice(this.mainChartElements * 2, this.mainChartElements * 3);
|
||||||
|
this.chartData[2].data = data.data.slice(0, this.mainChartElements);
|
||||||
|
this.chartData[1].data = data.data.slice(this.mainChartElements, this.mainChartElements * 2);
|
||||||
|
this.chartData[0].data = data.data.slice(this.mainChartElements * 2, this.mainChartElements * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert Hex to RGBA
|
||||||
|
public convertHex(hex: string, opacity: number) {
|
||||||
|
hex = hex.replace('#', '');
|
||||||
|
const r = parseInt(hex.substring(0, 2), 16);
|
||||||
|
const g = parseInt(hex.substring(2, 4), 16);
|
||||||
|
const b = parseInt(hex.substring(4, 6), 16);
|
||||||
|
|
||||||
|
const rgba = 'rgba(' + r + ', ' + g + ', ' + b + ', ' + opacity / 100 + ')';
|
||||||
|
return rgba;
|
||||||
|
}
|
||||||
|
|
||||||
|
// events
|
||||||
|
public chartClicked(e: any): void {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public chartHovered(e: any): void {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mainChart
|
||||||
|
|
||||||
|
public random(min: number, max: number) {
|
||||||
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -7,7 +7,8 @@ export class OrgGraphsService {
|
||||||
|
|
||||||
constructor(private api: ApiService) { }
|
constructor(private api: ApiService) { }
|
||||||
|
|
||||||
public getGraph(name: string) {
|
public getGraph(name: string, data: any = {}) {
|
||||||
return this.api.post(this.orgGraphUrl, { graph: name });
|
data.graph = name;
|
||||||
|
return this.api.post(this.orgGraphUrl, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
"directive-selector": [true, "attribute", "app", "camelCase"],
|
"directive-selector": [true, "attribute", "app", "camelCase"],
|
||||||
"component-selector": [true, "element", ["app", "widget", "snippet"], "kebab-case"],
|
"component-selector": [true, "element", ["app", "widget", "snippet", "panel"], "kebab-case"],
|
||||||
"use-input-property-decorator": true,
|
"use-input-property-decorator": true,
|
||||||
"use-output-property-decorator": true,
|
"use-output-property-decorator": true,
|
||||||
"use-host-property-decorator": true,
|
"use-host-property-decorator": true,
|
||||||
|
@ -107,7 +107,7 @@
|
||||||
"no-output-rename": true,
|
"no-output-rename": true,
|
||||||
"use-life-cycle-interface": true,
|
"use-life-cycle-interface": true,
|
||||||
"use-pipe-transform-interface": true,
|
"use-pipe-transform-interface": true,
|
||||||
"component-class-suffix": [true, "Component", "Widget"],
|
"component-class-suffix": [true, "Component", "Widget", "Panel"],
|
||||||
"directive-class-suffix": true,
|
"directive-class-suffix": true,
|
||||||
"no-access-missing-member": true,
|
"no-access-missing-member": true,
|
||||||
"templates-use-public": true,
|
"templates-use-public": true,
|
||||||
|
|
Reference in a new issue