Expense Analysis Report

This sample shows how to create a simple expense analysis report using FlexGrid and FlexPie. The FlexGridPdfConverter.draw method is used to export the FlexGrid, and the PdfPageArea's drawImage method is used to export the FlexPie which had previously been converted to an image.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as wijmo from '@grapecity/wijmo'; import * as grid from '@grapecity/wijmo.grid'; import * as chart from '@grapecity/wijmo.chart'; import '@grapecity/wijmo.chart.render'; import * as pdf from '@grapecity/wijmo.pdf'; import * as gridPdf from '@grapecity/wijmo.grid.pdf'; // import { getEmployee } from './data'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { let employee = getEmployee(); // let flexGrid = new grid.FlexGrid('#flexGrid', { autoGenerateColumns: false, headersVisibility: grid.HeadersVisibility.Column, allowMerging: grid.AllowMerging.All, itemsSource: employee.expenses.items, columns: [ { header: 'Date', binding: 'date', format: 'd', minWidth: 80 }, { header: 'Hotel', binding: 'hotel', format: 'c' }, { header: 'Transport', binding: 'transport', format: 'c', minWidth: 80 }, { header: 'Meal', binding: 'meal', format: 'c' }, { header: 'Fuel', binding: 'fuel', format: 'c' }, { header: 'Misc', binding: 'misc', format: 'c' } ] }); // let flexPie = new chart.FlexPie('#flexPie', { itemsSource: ((totals) => [ { name: 'Hotel', value: totals.hotel }, { name: 'Transport', value: totals.transport }, { name: 'Meal', value: totals.meal }, { name: 'Fuel', value: totals.fuel }, { name: 'Misc', value: totals.misc } ])(employee.expenses.totals), binding: 'value', bindingName: 'name', innerRadius: 0.75, dataLabel: { content: '{value:c1}', position: chart.PieLabelPosition.Inside } }); // document.querySelector('#btnExport').addEventListener('click', () => { let doc = new pdf.PdfDocument({ header: { declarative: { text: 'Expense Analysis Report', font: new pdf.PdfFont('times', 12), brush: '#bfc1c2' } }, lineGap: 2, pageSettings: { margins: { left: 36, right: 36, top: 36, bottom: 36 } }, ended: (sender, args) => pdf.saveBlob(args.blob, 'FlexGrid.pdf') }); // drawEmployee(doc, flexGrid, flexPie, employee, () => doc.end()); }); } // function drawEmployee(doc, flexGrid, flexPie, employee, done) { let expenses = employee.expenses.items.sort((a, b) => a.date.getTime() - b.date.getTime()), minDate = expenses[0].date, maxDate = expenses[expenses.length - 1].date, bold = new pdf.PdfFont('times', 10, 'normal', 'bold'); // doc.moveDown(2); // doc.drawText('Name: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(employee.name); // doc.drawText('From: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(wijmo.changeType(minDate, wijmo.DataType.String, 'd')); // doc.drawText('To: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(wijmo.changeType(maxDate, wijmo.DataType.String, 'd')); // doc.moveDown(2); let y = doc.y; // doc.drawText('Expense details:', 0, y); doc.drawText('Total expenses by category:', doc.width * 0.5 + 20, y); y = doc.y; // gridPdf.FlexGridPdfConverter.draw(flexGrid, doc, doc.width * 0.5, null, { styles: { cellStyle: { backgroundColor: '#ffffff', borderColor: '#c6c6c6' }, altCellStyle: { backgroundColor: '#f9f9f9' }, groupCellStyle: { font: { weight: 'bold' }, backgroundColor: '#dddddd' }, headerCellStyle: { backgroundColor: '#eaeaea' } } }); // flexPie.saveImageToDataUrl(chart.ImageFormat.Png, (url) => { doc.drawImage(url, doc.width * 0.5 + 20, y, { width: doc.width * 0.5 - 20 }); done(); }); } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Expense analysis report</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"> <!-- Export button --> <button class="btn btn-default" id="btnExport">Export</button> <div class="row"> <div class="col-xs-6"> <div id="flexGrid" class="grid"></div> </div> <div class="col-xs-6"> <div id="flexPie"></div> </div> </div> </div> </body> </html> // export function getEmployee() { return { id: 'E892659', name: 'Robert King', department: 'Sales', position: 'Sales Representative', ssn: 'A37830', manager: 'Andrew Fuller', purpose: 'On business', attachment: true, advance: 1000, expenses: getExpenses() }; } // function getExpenses() { // [5; 10] let count = 5 + Math.round(Math.random() * 5), ret = { items: [], totals: { hotel: 0, transport: 0, fuel: 0, meal: 0, misc: 0, total: 0 } }, msPerDay = 1000 * 24 * 60 * 60, curDate = Date.now() - 60 * msPerDay; // for (let i = 0; i < count; i++) { let item = { date: new Date(curDate), description: 'Customer visit', hotel: 30 + Math.random() * 200, transport: 10 + Math.random() * 150, fuel: Math.random() * 50, meal: 30 + Math.random() * 170, misc: Math.random() * 220, total: 0 }; // item.total = item.hotel + item.transport + item.fuel + item.meal + item.misc; // ret.totals.fuel += item.fuel; ret.totals.hotel += item.hotel; ret.totals.meal += item.meal; ret.totals.misc += item.misc; ret.totals.total += item.total; ret.totals.transport += item.transport; // ret.items.push(item); // curDate += msPerDay * Math.round(Math.random() * 4); } // return ret; } body { margin-bottom: 24px; } .grid { height: 300px; } .col-xs-6 { margin: 6px 0 0 0; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as wijmo from '@grapecity/wijmo'; import * as chart from '@grapecity/wijmo.chart'; import '@grapecity/wijmo.chart.render'; import * as grid from '@grapecity/wijmo.grid'; import * as pdf from '@grapecity/wijmo.pdf'; import * as gridPdf from '@grapecity/wijmo.grid.pdf'; // import { Component, Inject, enableProdMode, NgModule, ViewChild } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { DataService, IEmployee, IExpense } from './app.data'; import { WjGridModule } from '@grapecity/wijmo.angular2.grid'; import { WjChartModule } from '@grapecity/wijmo.angular2.chart'; // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { employee: IEmployee; totals: any[]; @ViewChild('flexGrid') flexGrid: grid.FlexGrid; @ViewChild('flexPie') flexPie: chart.FlexPie; // constructor(@Inject(DataService) private dataService: DataService) { this.employee = dataService.getEmployee(); // this.totals = ((totals: IExpense) => [ { name: 'Hotel', value: totals.hotel }, { name: 'Transport', value: totals.transport }, { name: 'Meal', value: totals.meal }, { name: 'Fuel', value: totals.fuel }, { name: 'Misc', value: totals.misc } ])(this.employee.expenses.totals); } // export() { let doc = new pdf.PdfDocument({ header: { declarative: { text: 'Expense Analysis Report', font: new pdf.PdfFont('times', 12), brush: '#bfc1c2' } }, lineGap: 2, pageSettings: { margins: { left: 36, right: 36, top: 36, bottom: 36 } }, ended: (sender: pdf.PdfDocument, args: pdf.PdfDocumentEndedEventArgs) => pdf.saveBlob(args.blob, 'FlexGrid.pdf') }); // this.drawEmployee(doc, this.flexGrid, this.flexPie, this.employee, () => doc.end()); } // drawEmployee(doc: pdf.PdfDocument, flexGrid: grid.FlexGrid, flexPie: chart.FlexPie, employee: IEmployee, done: Function) { let expenses = employee.expenses.items.sort((a, b) => a.date.getTime() - b.date.getTime()), minDate = expenses[0].date, maxDate = expenses[expenses.length - 1].date, bold = new pdf.PdfFont('times', 10, 'normal', 'bold'); // doc.moveDown(2); // doc.drawText('Name: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(employee.name); // doc.drawText('From: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(wijmo.changeType(minDate, wijmo.DataType.String, 'd')); // doc.drawText('To: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(wijmo.changeType(maxDate, wijmo.DataType.String, 'd')); // doc.moveDown(2); let y = doc.y; // doc.drawText('Expense details:', 0, y) doc.drawText('Total expenses by category:', doc.width * 0.5 + 20, y); y = doc.y; // gridPdf.FlexGridPdfConverter.draw(flexGrid, doc, doc.width * 0.5, null, { styles: { cellStyle: { backgroundColor: '#ffffff', borderColor: '#c6c6c6' }, altCellStyle: { backgroundColor: '#f9f9f9' }, groupCellStyle: { font: { weight: 'bold' }, backgroundColor: '#dddddd' }, headerCellStyle: { backgroundColor: '#eaeaea' } } }); // flexPie.saveImageToDataUrl(chart.ImageFormat.Png, (url: string) => { doc.drawImage(url, doc.width * 0.5 + 20, y, { width: doc.width * 0.5 - 20 }); done(); }); } } // @NgModule({ imports: [WjGridModule, WjChartModule, 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>Expense analysis report</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"> <!-- Export button --> <button class="btn btn-default" (click)="export()">Export</button> <div class="row"> <div class="col-xs-6"> <wj-flex-grid class="grid" #flexGrid [autoGenerateColumns]="false" headersVisibility="Column" allowMerging="All" [itemsSource]="employee.expenses.items"> <wj-flex-grid-column header="Date" binding="date" format="d" [minWidth]="80"></wj-flex-grid-column> <wj-flex-grid-column header="Hotel" binding="hotel" format="c"></wj-flex-grid-column> <wj-flex-grid-column header="Transport" binding="transport" format="c" [minWidth]="80"></wj-flex-grid-column> <wj-flex-grid-column header="Meal" binding="meal" format="c"></wj-flex-grid-column> <wj-flex-grid-column header="Fuel" binding="fuel" format="c"></wj-flex-grid-column> <wj-flex-grid-column header="Misc" binding="misc" format="c"></wj-flex-grid-column> </wj-flex-grid> </div> <div class="col-xs-6"> <wj-flex-pie #flexPie [itemsSource]="totals" binding="value" bindingName="name" [innerRadius]="0.75"> <wj-flex-pie-data-label content="{value:c1}" position="Inside"></wj-flex-pie-data-label> </wj-flex-pie> </div> </div> </div> import { Injectable } from '@angular/core'; // export interface IExpense { hotel: number; transport: number; fuel: number; meal: number; misc: number; total: number; } // export interface IExpenseDetails extends IExpense { date: Date; description: string; } // export interface IExpenses { items: IExpenseDetails[]; totals: IExpense; } // export interface IEmployee { id: string; name: string; department: string; position: string; ssn: string; manager: string; purpose: string; attachment: boolean; advance: number; expenses: IExpenses; } // @Injectable() export class DataService { getEmployee(): IEmployee { return { id: 'E892659', name: 'Robert King', department: 'Sales', position: 'Sales Representative', ssn: 'A37830', manager: 'Andrew Fuller', purpose: 'On business', attachment: true, advance: 1000, expenses: this.getExpenses() }; } // private getExpenses(): IExpenses { // [5; 10] let count = 5 + Math.round(Math.random() * 5), ret: IExpenses = { items: [], totals: { hotel: 0, transport: 0, fuel: 0, meal: 0, misc: 0, total: 0 } }, msPerDay = 1000 * 24 * 60 * 60, curDate = Date.now() - 60 * msPerDay; // for (let i = 0; i < count; i++) { let item: IExpenseDetails = { date: new Date(curDate), description: 'Customer visit', hotel: 30 + Math.random() * 200, transport: 10 + Math.random() * 150, fuel: Math.random() * 50, meal: 30 + Math.random() * 170, misc: Math.random() * 220, total: 0 }; // item.total = item.hotel + item.transport + item.fuel + item.meal + item.misc; // ret.totals.fuel += item.fuel; ret.totals.hotel += item.hotel; ret.totals.meal += item.meal; ret.totals.misc += item.misc; ret.totals.total += item.total; ret.totals.transport += item.transport; // ret.items.push(item); // curDate += msPerDay * Math.round(Math.random() * 4); } // return ret; } } body { margin-bottom: 24px; } .grid { height: 300px; } .col-xs-6 { margin: 6px 0 0 0; } <template> <div class="container-fluid"> <!-- Export button --> <button class="btn btn-default" @click="exportPDF">Export</button> <div class="row"> <div class="col-xs-6"> <wj-flex-grid class="grid" :autoGenerateColumns=false headersVisibility="Column" allowMerging="All" :itemsSource="employee.expenses.items" :initialized="initializeGrid"> <wj-flex-grid-column header="Date" binding="date" format="d" :minWidth=80></wj-flex-grid-column> <wj-flex-grid-column header="Hotel" binding="hotel" format="c"></wj-flex-grid-column> <wj-flex-grid-column header="Transport" binding="transport" format="c" :minWidth=80></wj-flex-grid-column> <wj-flex-grid-column header="Meal" binding="meal" format="c"></wj-flex-grid-column> <wj-flex-grid-column header="Fuel" binding="fuel" format="c"></wj-flex-grid-column> <wj-flex-grid-column header="Misc" binding="misc" format="c"></wj-flex-grid-column> </wj-flex-grid> </div> <div class="col-xs-6"> <wj-flex-pie :itemsSource="totals" binding="value" bindingName="name" :innerRadius=0.75 :initialized="initializePie"> <wj-flex-pie-data-label content="{value:c1}" position="Inside"></wj-flex-pie-data-label> </wj-flex-pie> </div> </div> </div> </template> <script> import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import Vue from 'vue'; import { getEmployee } from './data'; import { WjGridModule } from '@grapecity/wijmo.vue2.grid'; import { WjChartModule } from '@grapecity/wijmo.vue2.chart'; import * as wijmo from '@grapecity/wijmo'; import * as chart from '@grapecity/wijmo.chart'; import '@grapecity/wijmo.chart.render'; import * as pdf from '@grapecity/wijmo.pdf'; import * as gridPdf from '@grapecity/wijmo.grid.pdf'; var employee = getEmployee(); new Vue({ el: '#app', data: { employee: employee, totals: ((totals) => [ { name: 'Hotel', value: totals.hotel }, { name: 'Transport', value: totals.transport }, { name: 'Meal', value: totals.meal }, { name: 'Fuel', value: totals.fuel }, { name: 'Misc', value: totals.misc } ])(employee.expenses.totals) }, methods: { initializeGrid(ctl) { this.flexGrid = ctl; }, initializePie(ctl) { this.flexPie = ctl; }, exportPDF() { let doc = new pdf.PdfDocument({ header: { declarative: { text: 'Expense Analysis Report', font: new pdf.PdfFont('times', 12), brush: '#bfc1c2' } }, lineGap: 2, pageSettings: { margins: { left: 36, right: 36, top: 36, bottom: 36 } }, ended: (sender, args) => pdf.saveBlob(args.blob, 'FlexGrid.pdf') }); // this.drawEmployee(doc, this.flexGrid, this.flexPie, this.employee, () => doc.end()); }, drawEmployee(doc, flexGrid, flexPie, employee, done) { let expenses = employee.expenses.items.sort((a, b) => a.date.getTime() - b.date.getTime()), minDate = expenses[0].date, maxDate = expenses[expenses.length - 1].date, bold = new pdf.PdfFont('times', 10, 'normal', 'bold'); // doc.moveDown(2); // doc.drawText('Name: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(employee.name); // doc.drawText('From: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(wijmo.changeType(minDate, wijmo.DataType.String, 'd')); // doc.drawText('To: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(wijmo.changeType(maxDate, wijmo.DataType.String, 'd')); // doc.moveDown(2); let y = doc.y; // doc.drawText('Expense details:', 0, y) doc.drawText('Total expenses by category:', doc.width * 0.5 + 20, y); y = doc.y; // gridPdf.FlexGridPdfConverter.draw(flexGrid, doc, doc.width * 0.5, null, { styles: { cellStyle: { backgroundColor: '#ffffff', borderColor: '#c6c6c6' }, altCellStyle: { backgroundColor: '#f9f9f9' }, groupCellStyle: { font: { weight: 'bold' }, backgroundColor: '#dddddd' }, headerCellStyle: { backgroundColor: '#eaeaea' } } }); // flexPie.saveImageToDataUrl(chart.ImageFormat.Png, (url) => { doc.drawImage(url, doc.width * 0.5 + 20, y, { width: doc.width * 0.5 - 20 }); done(); }); } } }); </script> <style> body { margin-bottom: 24px; } .grid { height: 200px; } .col-xs-6 { margin: 6px 0 0 0; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Expense analysis report</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> export function getEmployee() { return { id: 'E892659', name: 'Robert King', department: 'Sales', position: 'Sales Representative', ssn: 'A37830', manager: 'Andrew Fuller', purpose: 'On business', attachment: true, advance: 1000, expenses: getExpenses() }; } function getExpenses(){ // [5; 10] let count = 5 + Math.round(Math.random() * 5), ret = { items: [], totals: { hotel: 0, transport: 0, fuel: 0, meal: 0, misc: 0, total: 0 } }, msPerDay = 1000 * 24 * 60 * 60, curDate = Date.now() - 60 * msPerDay; // for (let i = 0; i < count; i++) { let item = { date: new Date(curDate), description: 'Customer visit', hotel: 30 + Math.random() * 200, transport: 10 + Math.random() * 150, fuel: Math.random() * 50, meal: 30 + Math.random() * 170, misc: Math.random() * 220, total: 0 }; // item.total = item.hotel + item.transport + item.fuel + item.meal + item.misc; // ret.totals.fuel += item.fuel; ret.totals.hotel += item.hotel; ret.totals.meal += item.meal; ret.totals.misc += item.misc; ret.totals.total += item.total; ret.totals.transport += item.transport; // ret.items.push(item); // curDate += msPerDay * Math.round(Math.random() * 4); } // return ret; } import "@grapecity/wijmo.styles/wijmo.css"; import "bootstrap.css"; import "./app.css"; // import * as React from 'react'; import * as ReactDOM from 'react-dom'; // import * as wjcCore from "@grapecity/wijmo"; import * as wjcGrid from "@grapecity/wijmo.react.grid"; import * as gridPdf from "@grapecity/wijmo.grid.pdf"; import * as wjChart from '@grapecity/wijmo.chart'; import * as wjChartModule from '@grapecity/wijmo.react.chart'; import * as pdf from '@grapecity/wijmo.pdf'; import { getEmployee } from "./data"; class App extends React.Component { constructor(props) { super(props); this.employee = getEmployee(); this.state = { employee: this.employee, totals: ((totals) => [ { name: 'Hotel', value: totals.hotel }, { name: 'Transport', value: totals.transport }, { name: 'Meal', value: totals.meal }, { name: 'Fuel', value: totals.fuel }, { name: 'Misc', value: totals.misc } ])(this.employee.expenses.totals) }; } render() { return <div className="container-fluid"> <button className="btn btn-default" onClick={this.exportPDF.bind(this)}>Export</button> <wjcGrid.FlexGrid className="grid" autoGenerateColumns={false} headersVisibility="Column" allowMerging="All" itemsSource={this.state.employee.expenses.items} initialized={this.initializeGrid.bind(this)}> <wjcGrid.FlexGridColumn header="Date" binding="date" format="d" minWidth={80}></wjcGrid.FlexGridColumn> <wjcGrid.FlexGridColumn header="Hotel" binding="hotel" format="c"></wjcGrid.FlexGridColumn> <wjcGrid.FlexGridColumn header="Transport" binding="transport" format="c" minWidth={80}></wjcGrid.FlexGridColumn> <wjcGrid.FlexGridColumn header="Meal" binding="meal" format="c"></wjcGrid.FlexGridColumn> <wjcGrid.FlexGridColumn header="Fuel" binding="fuel" format="c"></wjcGrid.FlexGridColumn> <wjcGrid.FlexGridColumn header="Misc" binding="misc" format="c"></wjcGrid.FlexGridColumn> </wjcGrid.FlexGrid> <div className="col-xs-6"> <wjChartModule.FlexPie binding="value" bindingName="name" innerRadius={0.75} initialized={this.initializePie.bind(this)} itemsSource={this.state.totals}> </wjChartModule.FlexPie> </div> </div>; } initializeGrid(ctl) { this.flexGrid = ctl; } initializePie(ctl) { this.flexPie = ctl; } exportPDF() { let doc = new pdf.PdfDocument({ header: { declarative: { text: 'Expense Analysis Report', font: new pdf.PdfFont('times', 12), brush: '#bfc1c2' } }, lineGap: 2, pageSettings: { margins: { left: 36, right: 36, top: 36, bottom: 36 } }, ended: (sender, args) => pdf.saveBlob(args.blob, 'FlexGrid.pdf') }); // this.drawEmployee(doc, this.flexGrid, this.flexPie, this.employee, () => doc.end()); } drawEmployee(doc, flexGrid, flexPie, employee, done) { let expenses = employee.expenses.items.sort((a, b) => a.date.getTime() - b.date.getTime()), minDate = expenses[0].date, maxDate = expenses[expenses.length - 1].date, bold = new pdf.PdfFont('times', 10, 'normal', 'bold'); // doc.moveDown(2); // doc.drawText('Name: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(employee.name); // doc.drawText('From: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(wjcCore.changeType(minDate, wjcCore.DataType.String, 'd')); // doc.drawText('To: ', undefined, undefined, { font: bold, continued: true }); doc.drawText(wjcCore.changeType(maxDate, wjcCore.DataType.String, 'd')); // doc.moveDown(2); let y = doc.y; // doc.drawText('Expense details:', 0, y); doc.drawText('Total expenses by category:', doc.width * 0.5 + 20, y); y = doc.y; // gridPdf.FlexGridPdfConverter.draw(flexGrid, doc, doc.width * 0.5, null, { styles: { cellStyle: { backgroundColor: '#ffffff', borderColor: '#c6c6c6' }, altCellStyle: { backgroundColor: '#f9f9f9' }, groupCellStyle: { font: { weight: 'bold' }, backgroundColor: '#dddddd' }, headerCellStyle: { backgroundColor: '#eaeaea' } } }); // flexPie.saveImageToDataUrl(wjChart.ImageFormat.Png, (url) => { doc.drawImage(url, doc.width * 0.5 + 20, y, { width: doc.width * 0.5 - 20 }); done(); }); } } 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>Grapecity Wijmo OLAP Pivot Chart Overview</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; } .grid { height: 200px; } .col-xs-6 { margin: 6px 0 0 0; } export function getEmployee() { return { id: 'E892659', name: 'Robert King', department: 'Sales', position: 'Sales Representative', ssn: 'A37830', manager: 'Andrew Fuller', purpose: 'On business', attachment: true, advance: 1000, expenses: getExpenses() }; } function getExpenses() { // [5; 10] let count = 5 + Math.round(Math.random() * 5), ret = { items: [], totals: { hotel: 0, transport: 0, fuel: 0, meal: 0, misc: 0, total: 0 } }, msPerDay = 1000 * 24 * 60 * 60, curDate = Date.now() - 60 * msPerDay; // for (let i = 0; i < count; i++) { let item = { date: new Date(curDate), description: 'Customer visit', hotel: 30 + Math.random() * 200, transport: 10 + Math.random() * 150, fuel: Math.random() * 50, meal: 30 + Math.random() * 170, misc: Math.random() * 220, total: 0 }; // item.total = item.hotel + item.transport + item.fuel + item.meal + item.misc; // ret.totals.fuel += item.fuel; ret.totals.hotel += item.hotel; ret.totals.meal += item.meal; ret.totals.misc += item.misc; ret.totals.total += item.total; ret.totals.transport += item.transport; // ret.items.push(item); // curDate += msPerDay * Math.round(Math.random() * 4); } // return ret; }