Sales Dashboard

This sample shows how to create a yearly sales dashboard using FlexChart, FlexPie and bullet charts.

Click the column bar to show the detailed sales data of selected month.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import * as core from '@grapecity/wijmo'; import * as chart from '@grapecity/wijmo.chart'; import * as gauge from '@grapecity/wijmo.gauge'; import { getSales } from './data'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { let data = getSales(); let bullets = []; var barChart = new chart.FlexChart('#chart', { header: '2018 Annual Sales', legend: { position: chart.Position.None }, bindingX: 'month', series: [{ binding: 'actual', name: 'Sales' }], selectionMode: chart.SelectionMode.Point, itemsSource: data.month, selectionChanged: function (sender, args) { var selIndex = sender._selectionIndex; // if (selIndex == null || selIndex == -1) { pieChart.itemsSource = data.category; pieChart.header = '2018 Product Sales'; setBullets(bullets, data.category); } else { let d = data.month[selIndex]; pieChart.itemsSource = d.items; pieChart.header = d.month + ' Product Sales'; setBullets(bullets, d.items); } } }); // var pieChart = new chart.FlexPie('#pie', { header: '2018 Product Sales', legend: { position: chart.Position.Bottom }, bindingName: 'category', binding: 'actual', itemsSource: data.category, dataLabel: { content: function (ht) { let sum = ht.chart.itemsSource.map(c => c.actual).reduce((sum, cur) => sum + cur); return `${core.Globalize.format(ht.value / sum, 'p0')}`; } } }); createBullets(bullets, data.category); } // function createBullets(bullets, categories) { let bulletsContainer = document.querySelector('.bullets'); categories.forEach(category => { let li = document.createElement('li'); bulletsContainer.appendChild(li); let bulletLable = document.createElement('label'); bulletLable.innerHTML = category.category; li.appendChild(bulletLable); let bulletEle = document.createElement('div'); li.appendChild(bulletEle); let bullet = new gauge.BulletGraph(bulletEle, { showText: gauge.ShowText.Value }); bullets.push(bullet); }); setBullets(bullets, categories); } function setBullets(bullets, categories) { categories.forEach((cat, i) => { let bullet = bullets[i]; bullet.target = cat.target; bullet.max = cat.max; bullet.good = cat.good; bullet.bad = cat.bad; bullet.value = cat.actual; }); } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Sales Dashboard</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div class="container-fluid"> <div class="row"> <div id="chart"></div> </div> <div class="row"> <div class="col"> <div id="pie"></div> </div> <div class="col"> <ul class="bullets"></ul> </div> </div> </div> </body> </html> //generate random sales data let year = '2018', months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], categories = ['Music', 'Video', 'Books', 'Electronics', 'Computers']; // export function getSales() { let data = getData(), cats = []; data.forEach(d => { let items = d.items; items.forEach((item, idx) => { if (idx >= cats.length) { cats.push({ category: item.category, max: 0, target: 0, bad: 0, good: 0, actual: 0 }); } let cat = cats[idx]; cat.max += item.max; cat.target += item.target; cat.bad += item.bad; cat.good += item.good; cat.actual += item.actual; }); }); return { month: data, category: cats }; } function getData() { let data = []; // months.forEach(month => { let val = { month: month + ' ' + year, max: 50000, actual: 0, target: 25000, bad: 15000, good: 40000, items: [] }; // categories.forEach(category => { let v = { category: category, max: 10000, target: 5000, bad: 3000, good: 8000, actual: 0 }; let actual = Math.random(); actual = Math.round(actual * 10000); v.actual = actual; val.actual += actual; val.items.push(v); }); data.push(val); }); return data; } body { margin-bottom: 24px; } .wj-flexchart { border: none; } .col { float:left; width: 50%; } li { list-style-type: none; } .wj-gauge { margin: 17px 1em; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import { Component, Inject, enableProdMode, NgModule } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { WjChartModule } from '@grapecity/wijmo.angular2.chart'; import { WjGaugeModule } from '@grapecity/wijmo.angular2.gauge'; import { DataService } from './app.data'; import * as wjCore from '@grapecity/wijmo'; import * as wjChart from '@grapecity/wijmo.chart'; // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { data: any; chartData: any[]; pieData: any[]; bulletsData: any[]; pieHeader: string; // constructor(@Inject(DataService) private dataService: DataService) { this.data = dataService.getSales(); this.chartData = this.data.month; this.pieData = this.data.category; this.bulletsData = this.data.category; this.pieHeader = "2018 Product Sales"; } // pieLabel(ht: any) { let sum = ht.chart.itemsSource.map( (c:any) => c.actual).reduce((sum:number, cur:number) => sum + cur); return wjCore.Globalize.format(ht.value / sum, 'p0'); } // chartSelectionChanged(chart: wjChart.FlexChart) { var selIndex = chart._selectionIndex; let data = this.data; // if(selIndex == null || selIndex == -1) { this.pieData = data.category; this.pieHeader = '2018 Product Sales'; this.bulletsData = data.category; } else { let d = data.month[selIndex]; this.pieData = d.items; this.pieHeader = d.month + ' Product Sales'; this.bulletsData = d.items; } } } // @NgModule({ imports: [WjChartModule, WjGaugeModule, BrowserModule], declarations: [AppComponent], providers: [DataService], bootstrap: [AppComponent] }) export class AppModule { } // enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Sales Dashboard</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.js"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html> <div class="container-fluid"> <div class="form-group"> <div class="row"> <wj-flex-chart #theChart header="2018 Annual Sales" bindingX="month" selectionMode="Point" [itemsSource]="chartData" (selectionChanged)="chartSelectionChanged(theChart)"> <wj-flex-chart-legend position="None"></wj-flex-chart-legend> <wj-flex-chart-series binding="actual" name="Sales"></wj-flex-chart-series> </wj-flex-chart> </div> <div class="row"> <div class="col"> <wj-flex-pie #thePie [header]="pieHeader" bindingName="category" binding="actual" [itemsSource]="pieData" [labelContent]="pieLabel"> <wj-flex-chart-legend position="None"></wj-flex-chart-legend> </wj-flex-pie> </div> <div class="col"> <ul class="bullets"> <li *ngFor="let item of bulletsData"> <label>{{item.category}}</label> <wj-bullet-graph showText="Value" [target]="item.target" [max]="item.max" [good]="item.good" [bad]="item.bad" [value]="item.actual"> </wj-bullet-graph> </li> </ul> </div> </div> </div> </div> import { Injectable } from '@angular/core'; // @Injectable() export class DataService { getData() { let year = '2018', months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], categories = ['Music', 'Video', 'Books', 'Electronics', 'Computers']; let data: any[] = []; // months.forEach(month => { let val = { month: month + ' ' + year, max: 50000, actual: 0, target: 25000, bad: 15000, good: 40000, items: <any>[] }; // categories.forEach(category => { let v = { category: category, max: 10000, target: 5000, bad: 3000, good: 8000, actual: 0 }; let actual = Math.random(); actual = Math.round(actual * 10000); v.actual = actual; val.actual += actual; val.items.push(v); }); data.push(val); }); return data; } // getSales() { let data = this.getData(), cats: any[] = []; // data.forEach(d => { let items = d.items; items.forEach((item: any, idx: number) => { if(idx >= cats.length) { cats.push({ category: item.category, max: 0, target: 0, bad: 0, good: 0, actual: 0 }); } let cat = cats[idx]; cat.max += item.max; cat.target += item.target; cat.bad += item.bad; cat.good += item.good; cat.actual += item.actual; }); }); return { month: data, category: cats } } } body { margin-bottom: 24px; } .wj-flexchart { border: none; } .col { float:left; width: 50%; } li { list-style-type: none; } .wj-gauge { margin: 17px 1em; } <template> <div class="container-fluid"> <div class="form-group"> <div class="row"> <wj-flex-chart header="2018 Annual Sales" bindingX="month" selectionMode="Point" :initialized="initializeChart" :itemsSource="chartData" :selectionChanged="chartSelectionChanged"> <wj-flex-chart-legend position="None"></wj-flex-chart-legend> <wj-flex-chart-series binding="actual" name="Sales"></wj-flex-chart-series> </wj-flex-chart> </div> <div class="row"> <div class="col"> <wj-flex-pie :header="pieHeader" bindingName="category" binding="actual" :itemsSource="pieData" :labelContent="pieLabel" :initialized="initializePie"> <wj-flex-chart-legend position="None"></wj-flex-chart-legend> </wj-flex-pie> </div> <div class="col"> <ul class="bullets"> <li v-for="item in bulletsData"> <label>{{item.category}}</label> <wj-bullet-graph showText="Value" :target="item.target" :max="item.max" :good="item.good" :bad="item.bad" :value="item.actual"> </wj-bullet-graph> </li> </ul> </div> </div> </div> </div> </template> <script> /* data: any; chartData: any[]; pieData: any[]; bulletsData: any[]; pieHeader: string; */ import "@grapecity/wijmo.styles/wijmo.css"; import 'bootstrap.css'; import Vue from 'vue'; import '@grapecity/wijmo.vue2.core'; import '@grapecity/wijmo.vue2.chart'; import '@grapecity/wijmo.vue2.gauge'; import { getSales } from './data'; import * as wjCore from '@grapecity/wijmo'; import * as wjChart from '@grapecity/wijmo.chart'; let App = Vue.extend({ name: 'app', data: function () { return { data: getSales(), chartData: null, pieData: null, bulletsData: null, pieHeader: '2018 Product Sales' } }, methods: { initializeChart(flex) { this.theChart = flex; this.chartData = this.data.month; this.pieData = this.data.category; this.bulletsData = this.data.category; }, initializePie(flex) { this.thePie = flex; }, pieLabel(ht) { return `${wjCore.Globalize.format(ht.value / (ht.chart._sum || ht.chart._sums[0]), 'p2')}`; }, chartSelectionChanged() { var selIndex = this.theChart._selectionIndex; let data = this.data; // if(selIndex == null || selIndex == -1) { this.pieData = data.category; this.pieHeader = '2018 Product Sales'; this.bulletsData = data.category; } else { let d = data.month[selIndex]; this.pieData = d.items; this.pieHeader = d.month + ' Product Sales'; this.bulletsData = d.items; } } } }) new Vue({ render: h => h(App) }).$mount('#app'); </script> <style> body { margin-bottom: 24px; } .wj-flexchart { border: none; } .col { float:left; width: 50%; } li { list-style-type: none; } .wj-gauge { margin: 17px 1em; } .wj-gauge svg { overflow: visible !important; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexChart Sales Dashboard</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app.vue'); </script> </head> <body> <div id="app"></div> </body> </html> function getData() { let year = '2018', months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], categories = ['Music', 'Video', 'Books', 'Electronics', 'Computers']; let data = []; // months.forEach(month => { let val = { month: month + ' ' + year, max: 50000, actual: 0, target: 25000, bad: 15000, good: 40000, items: [] }; // categories.forEach(category => { let v = { category: category, max: 10000, target: 5000, bad: 3000, good: 8000, actual: 0 }; let actual = Math.random(); actual = Math.round(actual * 10000); v.actual = actual; val.actual += actual; val.items.push(v); }); data.push(val); }); return data; } // export function getSales() { let data = getData(), cats = []; // data.forEach(d => { let items = d.items; items.forEach((item, idx) => { if(idx >= cats.length) { cats.push({ category: item.category, max: 0, target: 0, bad: 0, good: 0, actual: 0 }); } let cat = cats[idx]; cat.max += item.max; cat.target += item.target; cat.bad += item.bad; cat.good += item.good; cat.actual += item.actual; }); }); return { month: data, category: cats } } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './app.css'; // import * as React from 'react'; import * as ReactDOM from 'react-dom'; // import * as wjCore from '@grapecity/wijmo'; import * as wjChart from '@grapecity/wijmo.react.chart'; import * as wjGauge from '@grapecity/wijmo.react.gauge'; import { getSales } from './data'; // class App extends React.Component { constructor(props) { super(props); this.initializeChart = (flex) => { this.theChart = flex; }; this.initializePie = (flex) => { flex.dataLabel.content = function (ht) { let sum = ht.chart.itemsSource.map((c) => c.actual).reduce((sum, cur) => sum + cur); return `${wjCore.Globalize.format(ht.value / sum, 'p0')}`; }; this.thePie = flex; }; this.chartSelectionChanged = () => { var selIndex = this.theChart._selectionIndex; let data = this.state.data; // if (selIndex == null || selIndex == -1) { this.setState({ pieData: data.category, pieHeader: '2018 Product Sales', bulletsData: data.category }); } else { let d = data.month[selIndex]; this.setState({ pieData: d.items, pieHeader: d.month + ' Product Sales', bulletsData: d.items }); } }; let data = getSales(); this.state = { data: data, chartData: data.month, pieData: data.category, bulletsData: data.category, pieHeader: '2018 Product Sales' }; } render() { return <div className="container-fluid"> <div className="form-group"> <div className="row"> <wjChart.FlexChart header="2018 Annual Sales" bindingX="month" selectionMode="Point" initialized={this.initializeChart} itemsSource={this.state.chartData} selectionChanged={this.chartSelectionChanged}> <wjChart.FlexChartLegend position="None"></wjChart.FlexChartLegend> <wjChart.FlexChartSeries binding="actual" name="Sales"></wjChart.FlexChartSeries> </wjChart.FlexChart> </div> <div className="row"> <div className="col"> <wjChart.FlexPie header={this.state.pieHeader} bindingName="category" binding="actual" itemsSource={this.state.pieData} initialized={this.initializePie}> <wjChart.FlexChartLegend position="Bottom"></wjChart.FlexChartLegend> </wjChart.FlexPie> </div> <div className="col"> <ul className="bullets"> {this.state.bulletsData.map((item) => { return <li key={item}> <label>{item.category}</label> <wjGauge.BulletGraph showText="Value" target={item.target} max={item.max} good={item.good} bad={item.bad} value={item.actual}> </wjGauge.BulletGraph> </li>; })} </ul> </div> </div> </div> </div>; } } ReactDOM.render(<App />, document.getElementById('app')); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>AutoComplete</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html> body { margin-bottom: 24px; } .wj-flexchart { border: none; } .col { float:left; width: 50%; } li { list-style-type: none; } .wj-gauge { margin: 17px 1em; } export function getData() { let year = '2018', months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], categories = ['Music', 'Video', 'Books', 'Electronics', 'Computers']; let data = []; // months.forEach(month => { let val = { month: month + ' ' + year, max: 50000, actual: 0, target: 25000, bad: 15000, good: 40000, items: [] }; categories.forEach(category => { let v = { category: category, max: 10000, target: 5000, bad: 3000, good: 8000, actual: 0 }; let actual = Math.random(); actual = Math.round(actual * 10000); v.actual = actual; val.actual += actual; val.items.push(v); }); data.push(val); }); return data; } // export function getSales() { let data = getData(), cats = []; // data.forEach((d) => { let items = d.items; items.forEach((item, idx) => { if (idx >= cats.length) { cats.push({ category: item.category, max: 0, target: 0, bad: 0, good: 0, actual: 0 }); } let cat = cats[idx]; cat.max += item.max; cat.target += item.target; cat.bad += item.bad; cat.good += item.good; cat.actual += item.actual; }); }); return { month: data, category: cats }; }