<!DOCTYPE html>
<html class="zc-html">
<head>
<meta charset="utf-8">
<title>ZingSoft Demo</title>
<script nonce="undefined" src="https://cdn.zingchart.com/zingchart.min.js"></script>
<script nonce="undefined" src="https://cdn.zinggrid.com/zinggrid.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css">
<style>
#expenses {
min-height: 330px;
}
#treemap {
min-height: 445px;
}
#estimations,
#operational {
min-height: 200px;
}
.zc-body {
background: #e6e6e6;
}
.zc-body zing-grid[loading] {
min-height: 450px;
}
.zc-body zing-grid {
/* ZG-GRID
----------------------------------------------------- */
--zing-grid-border-bottom: 0;
--zing-grid-border-left: 0;
--zing-grid-border-right: 0;
--zing-grid-border-top: 0;
--zing-grid-box-shadow: 0 15px 35px rgba(50, 50, 93, .1), 0 5px 15px rgba(0, 0, 0, .07);
--zing-grid-font-family: 'Helvetica';
--zing-grid-font-weight: 500;
--zing-grid-color: #78909c;
/* ZG-ICON */
--zg-icon-color: #ACACAC;
/* ZG-BUTTON
----------------------------------------------------- */
--zg-button-opacity_disabled: .5;
/* ZG-CELL
----------------------------------------------------- */
--zg-cell-background_sorted: rgba(139, 177, 202, .3);
/* ZG-HEAD
----------------------------------------------------- */
--zg-head-background: #fff;
/* ZG-HEAD-CELL
----------------------------------------------------- */
--zg-head-cell-background_sorted: rgba(139, 177, 202, .6);
--zg-head-cell-font-weight_sorted: 700;
--zg-head-cell-icon-color_sorted: rgba(65, 117, 171, 1.0);
/* ZG-PAGER
----------------------------------------------------- */
/* --zg-pager-icon-color: red; */
/* ZG-ROW
----------------------------------------------------- */
--zg-row-body-background_even: #fff;
--zg-row-body-background_odd: rgba(229, 236, 243, .7);
--zg-row-body-background_hover: rgba(229, 236, 243, 1.0);
}
zg-head-cell[sorted] {
font-weight: 700;
}
[data-importance] {
height: 10px;
width: 10px;
border-radius: 15px;
padding: 5px 8px;
background-color: #bdbdbd;
color: #fff;
}
[data-importance~="Sales"] {
background-color: #03a9f4;
}
[data-importance~="Internal"] {
background-color: #F0B827;
}
[data-importance~="Marketing"] {
background-color: #D95234;
}
[data-importance~="Networking"] {
background-color: #65BAA6;
}
.accented--text {
font-weight: 700;
}
.cell--positive {
color: #00c853;
}
.cell--negative {
color: #d50000;
}
body {
background: #E5E5E5;
height: 100%;
}
#dashboard {
display: flex;
flex-direction: column;
}
#dashboard>header {
padding-left: 3rem;
border-bottom: 1px solid #BBB;
}
#dashboard header {
padding-left: 1rem;
font-family: 'Helvetica', Arial;
}
#dashboard h1 {
margin-left: 1rem;
}
.db__content {
display: flex;
min-height: 800px;
flex-wrap: wrap;
padding: 1rem;
flex: 1;
flex-direction: column;
}
.db-col {
display: flex;
flex-direction: column;
flex: 1;
/* margin: 1rem; */
}
.block {
display: flex;
flex-direction: column;
background: white;
border-radius: 5px;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
padding: 1rem;
margin: 1rem;
}
.block header h2 {
font-size: 1.2rem;
}
@media only screen and (min-width: 875px) {
.db__content {
flex-direction: row;
}
}
zing-grid[loading] {
height: 1111px;
}
</style>
</head>
<body class="zc-body">
<section id="dashboard">
<header>
<h1>Employee Expenses</h1>
</header>
<section class="db__content">
<section class="db-col db-col-1">
<section class="block">
<header>
<h2>Expenses Over Time</h2>
</header>
<div id="expenses"></div>
</section>
<section class="block">
<header>
<h2>Expenses By Category</h2>
</header>
<div id="treemap"></div>
</section>
</section>
<section class="db-col db-col-2">
<section class="block">
<header>
<h2>Expense Estimations</h2>
</header>
<div id="estimations"></div>
</section>
<section class="block">
<header>
<h2>Expenses vs Operational Costs</h2>
</header>
<div id="operational"></div>
</section>
</section>
</section>
</section>
<script>
ZC.LICENSE = ["569d52cefae586f634c54f86dc99e6a9", "b55b025e438fa8a98e32482b5f768ff5"];
ZingGrid.setLicense(['26ccbfec16b8be9ee98c7d57bee6e498']);
const STEP = 60 * 60 * 24;
const START_TIME = (1577836800 + STEP) * 1000;
function generateSeries(count, min, max) {
const arr = new Array(count).fill().map(() => {
return parseFloat(randomDollarAmount(min, max).toFixed(2));
});
return arr;
}
function randomDollarAmount(min, max) {
return parseFloat((Math.random() * (max - min) + min).toFixed(2));
}
const AccommodationsValues = generateSeries(91, 1000, 1500);
const foodValues = generateSeries(91, 1000, 1500);
const transportationValues = generateSeries(91, 1000, 3000);
const suppliesValues = generateSeries(91, 700, 1300);
let barConfig = {
type: 'bar',
legend: {
align: 'center',
border: '0px',
layout: 'float',
marker: {
type: 'circle',
},
toggleAction: 'remove',
verticalAlign: 'bottom',
},
plot: {
lineWidth: '3px',
// line node styling
marker: {
borderWidth: '0px',
size: '6px',
},
stacked: true,
// hoverstate
tooltip: {
visible: false,
},
},
plotarea: {
margin: '20px dynamic dynamic dynamic',
},
scaleX: {
guide: {
lineWidth: '0px',
},
label: {
text: '',
},
minValue: START_TIME,
step: 'day',
tick: {
lineWidth: '0px',
},
transform: {
type: 'date',
all: '%M %d',
},
},
scaleY: {
// scale label with unicode character
guide: {
lineStyle: 'dashed',
},
label: {
text: 'Total Expense Cost<br>in dollars',
paddingRight: '30px',
},
lineColor: 'none',
short: true,
shortUnit: 'K',
tick: {
visible: false,
},
},
crosshairX: {
alpha: 0.28,
lineWidth: '100%',
plotLabel: {
borderRadius: '3px',
padding: '10px 15px',
sortByValue: 'asc',
},
},
series: [{
text: 'Food and Drinks',
// plot values
values: foodValues,
backgroundColor: '#65BAA6',
},
{
text: 'Transportation',
// plot values
values: transportationValues,
backgroundColor: '#03a9f4',
},
{
text: 'Accommodations',
// plot values
values: AccommodationsValues,
backgroundColor: '#F0B827',
},
{
text: 'Supplies',
// plot values
values: suppliesValues,
backgroundColor: '#D95234',
},
],
};
let treemapConfig = {
type: 'treemap',
options: {
splitType: 'squarify',
palette: ['#03a9f4', '#F0B827', '#D95234', '#65BAA6'],
box: {
borderColor: '#FFF',
borderRadius: '5px',
borderWidth: '3px',
margin: '5px',
padding: '10px',
},
},
tooltip: {},
series: [{
text: 'Transportation',
children: [{
text: 'Car Rental',
value: 2000,
},
{
text: 'Airplane',
value: 3000,
},
{
text: 'Ride Share',
value: 800,
},
{
text: 'Taxi',
value: 200,
},
],
},
{
text: 'Food',
children: [{
text: 'Catering',
value: 400,
},
{
text: 'Fast Food',
value: 300,
},
{
text: 'Dining',
value: 1500,
},
{
text: 'Snacks',
value: 800,
},
{
text: 'Coffee',
value: 700,
},
{
text: 'Alcohol',
value: 1000,
},
],
},
{
text: 'Accommodations',
children: [{
text: 'Hotel',
value: 4000,
},
{
text: 'Airbnb',
value: 1500,
},
{
text: 'Camping',
value: 300,
},
],
},
{
text: 'Supplies',
children: [{
text: 'Copy',
value: 300,
},
{
text: 'Electronics',
value: 3000,
},
{
text: 'Misc',
value: 500,
},
],
},
],
};
// Expense Estimation
const totalValues = Array(91)
.fill()
.map((value, index) => {
return parseFloat(
(
transportationValues[index] +
AccommodationsValues[index] +
suppliesValues[index] +
foodValues[index]
).toFixed(2)
);
});
const estimationsConfig = {
type: 'line',
legend: {
align: 'center',
border: '0px',
layout: '1x2',
marker: {
type: 'circle',
},
verticalAlign: 'bottom',
},
plot: {
lineWidth: '3px',
marker: {
visible: false,
},
},
plotarea: {
margin: '20px dynamic dynamic dynamic',
},
scaleX: {
guide: {
lineWidth: '0px',
},
label: {
text: '',
},
minValue: START_TIME,
step: 'day',
tick: {
lineWidth: '0px',
},
transform: {
type: 'date',
all: '%M %d',
},
},
scaleY: {
values: '3000:7000:1000',
guide: {
lineStyle: 'dashed',
},
label: {
text: 'Total Expense Cost<br>in dollars',
paddingRight: '30px',
},
lineColor: 'none',
short: true,
shortUnit: 'K',
tick: {
visible: false,
},
},
series: [{
text: 'Actual',
values: totalValues,
lineColor: '#03a9f4',
},
{
text: 'Projected',
values: Array(91)
.fill()
.map(() => {
return randomDollarAmount(5000, 5300);
}),
lineColor: '#65BAA6',
},
],
};
const operationalConfig = {
type: 'bar',
legend: {
align: 'center',
border: '0px',
layout: '1x2',
marker: {
type: 'circle',
},
verticalAlign: 'bottom',
},
plot: {
lineWidth: '3px',
marker: {
visible: false,
},
stacked: true,
stackType: '100%',
},
plotarea: {
margin: '20px dynamic dynamic dynamic',
},
scaleX: {
guide: {
lineWidth: '0px',
},
label: {
text: '',
},
minValue: START_TIME,
step: 'day',
tick: {
lineWidth: '0px',
},
transform: {
type: 'date',
all: '%M %d',
},
},
scaleY: {
values: '0:100:10',
// scale label with unicode character
label: {
text: 'Percent of costs',
paddingRight: '30px',
},
lineColor: 'none',
tick: {
visible: false,
},
guide: {
lineStyle: 'dashed',
},
format: '%v%',
},
series: [{
text: 'Operational',
values: totalValues,
backgroundColor: '#A7E2C0',
},
{
text: 'Projected',
values: Array(91)
.fill()
.map(() => {
return randomDollarAmount(5000, 5300);
}),
backgroundColor: '#65BAA6',
},
],
};
// ZingGrid
function posNegClass(difference, cellDOMRef, cellRef) {
if (difference > 0) return 'cell--positive';
else if (difference < 0) return 'cell--negative';
// return nothing if zero difference
return;
}
// window:load event for Javascript to run after HTML
// because this Javascript is injected into the document head
window.addEventListener('load', () => {
// Javascript code to execute after DOM content
zingchart.render({
id: 'expenses',
data: barConfig,
width: '100%',
height: '330px',
});
zingchart.render({
id: 'treemap',
data: treemapConfig,
width: '100%',
height: '445px',
});
zingchart.render({
id: 'estimations',
data: estimationsConfig,
width: '100%',
height: '200px',
});
zingchart.render({
id: 'operational',
data: operationalConfig,
width: '100%',
height: '200px',
});
});
</script>
</body>
</html>
const STEP = 60 * 60 * 24;
const START_TIME = (1577836800 + STEP) * 1000;
function generateSeries(count, min, max) {
const arr = new Array(count).fill().map(() => {
return parseFloat(randomDollarAmount(min, max).toFixed(2));
});
return arr;
}
function randomDollarAmount(min, max) {
return parseFloat((Math.random() * (max - min) + min).toFixed(2));
}
const AccommodationsValues = generateSeries(91, 1000, 1500);
const foodValues = generateSeries(91, 1000, 1500);
const transportationValues = generateSeries(91, 1000, 3000);
const suppliesValues = generateSeries(91, 700, 1300);
let barConfig = {
type: 'bar',
legend: {
align: 'center',
border: '0px',
layout: 'float',
marker: {
type: 'circle',
},
toggleAction: 'remove',
verticalAlign: 'bottom',
},
plot: {
lineWidth: '3px',
// line node styling
marker: {
borderWidth: '0px',
size: '6px',
},
stacked: true,
// hoverstate
tooltip: {
visible: false,
},
},
plotarea: {
margin: '20px dynamic dynamic dynamic',
},
scaleX: {
guide: {
lineWidth: '0px',
},
label: {
text: '',
},
minValue: START_TIME,
step: 'day',
tick: {
lineWidth: '0px',
},
transform: {
type: 'date',
all: '%M %d',
},
},
scaleY: {
// scale label with unicode character
guide: {
lineStyle: 'dashed',
},
label: {
text: 'Total Expense Cost<br>in dollars',
paddingRight: '30px',
},
lineColor: 'none',
short: true,
shortUnit: 'K',
tick: {
visible: false,
},
},
crosshairX: {
alpha: 0.28,
lineWidth: '100%',
plotLabel: {
borderRadius: '3px',
padding: '10px 15px',
sortByValue: 'asc',
},
},
series: [
{
text: 'Food and Drinks',
// plot values
values: foodValues,
backgroundColor: '#65BAA6',
},
{
text: 'Transportation',
// plot values
values: transportationValues,
backgroundColor: '#03a9f4',
},
{
text: 'Accommodations',
// plot values
values: AccommodationsValues,
backgroundColor: '#F0B827',
},
{
text: 'Supplies',
// plot values
values: suppliesValues,
backgroundColor: '#D95234',
},
],
};
let treemapConfig = {
type: 'treemap',
options: {
splitType: 'squarify',
palette: ['#03a9f4', '#F0B827', '#D95234', '#65BAA6'],
box: {
borderColor: '#FFF',
borderRadius: '5px',
borderWidth: '3px',
margin: '5px',
padding: '10px',
},
},
tooltip: {},
series: [
{
text: 'Transportation',
children: [
{
text: 'Car Rental',
value: 2000,
},
{
text: 'Airplane',
value: 3000,
},
{
text: 'Ride Share',
value: 800,
},
{
text: 'Taxi',
value: 200,
},
],
},
{
text: 'Food',
children: [
{
text: 'Catering',
value: 400,
},
{
text: 'Fast Food',
value: 300,
},
{
text: 'Dining',
value: 1500,
},
{
text: 'Snacks',
value: 800,
},
{
text: 'Coffee',
value: 700,
},
{
text: 'Alcohol',
value: 1000,
},
],
},
{
text: 'Accommodations',
children: [
{
text: 'Hotel',
value: 4000,
},
{
text: 'Airbnb',
value: 1500,
},
{
text: 'Camping',
value: 300,
},
],
},
{
text: 'Supplies',
children: [
{
text: 'Copy',
value: 300,
},
{
text: 'Electronics',
value: 3000,
},
{
text: 'Misc',
value: 500,
},
],
},
],
};
// Expense Estimation
const totalValues = Array(91)
.fill()
.map((value, index) => {
return parseFloat(
(
transportationValues[index] +
AccommodationsValues[index] +
suppliesValues[index] +
foodValues[index]
).toFixed(2)
);
});
const estimationsConfig = {
type: 'line',
legend: {
align: 'center',
border: '0px',
layout: '1x2',
marker: {
type: 'circle',
},
verticalAlign: 'bottom',
},
plot: {
lineWidth: '3px',
marker: {
visible: false,
},
},
plotarea: {
margin: '20px dynamic dynamic dynamic',
},
scaleX: {
guide: {
lineWidth: '0px',
},
label: {
text: '',
},
minValue: START_TIME,
step: 'day',
tick: {
lineWidth: '0px',
},
transform: {
type: 'date',
all: '%M %d',
},
},
scaleY: {
values: '3000:7000:1000',
guide: {
lineStyle: 'dashed',
},
label: {
text: 'Total Expense Cost<br>in dollars',
paddingRight: '30px',
},
lineColor: 'none',
short: true,
shortUnit: 'K',
tick: {
visible: false,
},
},
series: [
{
text: 'Actual',
values: totalValues,
lineColor: '#03a9f4',
},
{
text: 'Projected',
values: Array(91)
.fill()
.map(() => {
return randomDollarAmount(5000, 5300);
}),
lineColor: '#65BAA6',
},
],
};
const operationalConfig = {
type: 'bar',
legend: {
align: 'center',
border: '0px',
layout: '1x2',
marker: {
type: 'circle',
},
verticalAlign: 'bottom',
},
plot: {
lineWidth: '3px',
marker: {
visible: false,
},
stacked: true,
stackType: '100%',
},
plotarea: {
margin: '20px dynamic dynamic dynamic',
},
scaleX: {
guide: {
lineWidth: '0px',
},
label: {
text: '',
},
minValue: START_TIME,
step: 'day',
tick: {
lineWidth: '0px',
},
transform: {
type: 'date',
all: '%M %d',
},
},
scaleY: {
values: '0:100:10',
// scale label with unicode character
label: {
text: 'Percent of costs',
paddingRight: '30px',
},
lineColor: 'none',
tick: {
visible: false,
},
guide: {
lineStyle: 'dashed',
},
format: '%v%',
},
series: [
{
text: 'Operational',
values: totalValues,
backgroundColor: '#A7E2C0',
},
{
text: 'Projected',
values: Array(91)
.fill()
.map(() => {
return randomDollarAmount(5000, 5300);
}),
backgroundColor: '#65BAA6',
},
],
};
// ZingGrid
function posNegClass(difference, cellDOMRef, cellRef) {
if (difference > 0) return 'cell--positive';
else if (difference < 0) return 'cell--negative';
// return nothing if zero difference
return;
}
// window:load event for Javascript to run after HTML
// because this Javascript is injected into the document head
window.addEventListener('load', () => {
// Javascript code to execute after DOM content
zingchart.render({
id: 'expenses',
data: barConfig,
width: '100%',
height: '330px',
});
zingchart.render({
id: 'treemap',
data: treemapConfig,
width: '100%',
height: '445px',
});
zingchart.render({
id: 'estimations',
data: estimationsConfig,
width: '100%',
height: '200px',
});
zingchart.render({
id: 'operational',
data: operationalConfig,
width: '100%',
height: '200px',
});
});