<!DOCTYPE html>
<html class="zc-html">
<head>
<meta charset="utf-8">
<title>ZingSoft Demo</title>
<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;
min-width: 800px;
flex-wrap: wrap;
padding: 1rem;
flex: 1;
}
.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;
min-width: 500px;
}
.block header h2 {
font-size: 1.2rem;
}
zing-grid[loading] {
height: 386px;
}
</style>
</head>
<body class="zc-body">
<script src="https://cdn.zingchart.com/zingchart.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css">
<script src="https://cdn.zinggrid.com/zinggrid.min.js" defer></script>
<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 class="block">
<header>
<h2>Transaction Overview</h2>
</header>
<zing-grid pager page-size="5" page-size-options="5,10,50" sort viewport-stop zebra>
<zg-colgroup>
<zg-column index="item" cell-class="accented--text"></zg-column>
<zg-column index="cost" type="currency"></zg-column>
<zg-column index="budget" type="currency"></zg-column>
<zg-column index="difference" type="currency" cell-class="posNegClass"></zg-column>
<zg-column index="category" sort="disabled">
<span data-importance="[[index.category]]">[[index.category]]</span>
</zg-column>
<zg-column index="dateApproved" type="date"></zg-column>
</zg-colgroup>
</zing-grid>
</section>
</section>
</section>
</section>
<script>
ZC.LICENSE = ["569d52cefae586f634c54f86dc99e6a9", "b55b025e438fa8a98e32482b5f768ff5"];
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: {
toggleAction: 'remove',
verticalAlign: 'bottom',
align: 'center',
layout: "2x4",
border: '0px',
marker: {
type: 'circle'
},
},
plotarea: {
margin: '20px dynamic dynamic dynamic'
},
plot: {
stacked: true,
// hoverstate
tooltip: {
visible: false
},
lineWidth: '3px',
// line node styling
marker: {
borderWidth: '0px',
size: '6px'
}
},
scaleX: {
label: {
text: ''
},
tick: {
lineWidth: 0,
},
guide: {
lineWidth: 0
},
step: 'day',
minValue: START_TIME,
transform: {
type: 'date',
all: '%M %d'
}
},
scaleY: {
// scale label with unicode character
label: {
text: 'Total Expense Cost<br>in dollars',
paddingRight: 30
},
lineColor: 'none',
tick: {
visible: false,
},
guide: {
lineStyle: 'dashed',
},
short: true,
shortUnit: 'K',
},
crosshairX: {
plotLabel: {
padding: '10px 15px',
borderRadius: '3px',
sortByValue: 'asc',
},
lineWidth: '100%',
alpha: .28,
},
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',
},
]
};
var treemapConfig = {
"graphset": [{
"type": "treemap",
"tooltip": {
},
"options": {
splitType: 'squarify',
palette: [
'#03a9f4',
'#F0B827',
'#D95234',
'#65BAA6',
],
box: {
borderWidth: '3px',
borderColor: '#FFF',
padding: '10px',
borderRadius: 5,
margin: 5
}
},
"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 estimations = {
type: 'line',
plot: {
lineWidth: 3,
marker: {
visible: false,
}
},
legend: {
verticalAlign: 'bottom',
align: 'center',
layout: "1x2",
border: '0px',
marker: {
type: 'circle'
},
},
scaleX: {
label: {
text: ''
},
tick: {
lineWidth: 0,
},
guide: {
lineWidth: 0
},
step: 'day',
minValue: START_TIME,
transform: {
type: 'date',
all: '%M %d'
}
},
series: [{
values: totalValues,
lineColor: '#03a9f4',
text: 'Actual'
},
{
values: Array(91).fill().map(() => {
return randomDollarAmount(5000, 5300)
}),
lineColor: '#65BAA6',
text: 'Projected'
}
],
plotarea: {
margin: '20px dynamic dynamic dynamic'
},
scaleY: {
values: '3000:7000:1000',
label: {
text: 'Total Expense Cost<br>in dollars',
paddingRight: 30
},
lineColor: 'none',
tick: {
visible: false,
},
guide: {
lineStyle: 'dashed',
},
short: true,
shortUnit: 'K',
}
};
const operational = {
type: 'bar',
plot: {
stacked: true,
stackType: '100%',
lineWidth: 3,
marker: {
visible: false,
}
},
legend: {
verticalAlign: 'bottom',
align: 'center',
layout: "1x2",
border: '0px',
marker: {
type: 'circle'
},
},
scaleX: {
label: {
text: ''
},
tick: {
lineWidth: 0,
},
guide: {
lineWidth: 0
},
step: 'day',
minValue: START_TIME,
transform: {
type: 'date',
all: '%M %d'
}
},
series: [{
values: totalValues,
backgroundColor: '#A7E2C0',
text: 'Operational'
},
{
values: Array(91).fill().map(() => {
return randomDollarAmount(5000, 5300)
}),
backgroundColor: '#65BAA6',
text: 'Projected'
}
],
plotarea: {
margin: '20px dynamic dynamic dynamic'
},
scaleY: {
values: '0:100:10',
// scale label with unicode character
label: {
text: 'Percent of costs',
paddingRight: 30
},
lineColor: 'none',
tick: {
visible: false,
},
guide: {
lineStyle: 'dashed',
},
format: '%v%'
}
};
// 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
// get reference to zinggrid DOM element
const zgRef = document.querySelector('zing-grid');
const data = [{
item: 'Flight to New York',
cost: 400,
budget: 1800,
category: 'Sales',
dateApproved: '2/10/20'
},
{
item: 'Lunch Meeting',
cost: 260,
budget: 100,
category: 'Sales',
dateApproved: '2/12/20'
},
{
item: 'Software License',
cost: 1999,
budget: 1500,
category: 'Internal',
dateApproved: '2/16/20'
},
{
item: 'AP3 Conference',
cost: 10623,
budget: 9000,
category: 'Marketing',
dateApproved: '1/12/20'
},
{
item: 'SO3 Conference',
cost: 8054,
budget: 10000,
category: 'Networking',
dateApproved: '1/04/20'
},
{
item: 'Paid Vue Sponsorship',
cost: 500,
budget: 1000,
category: 'Marketing',
dateApproved: '1/01/20'
},
{
item: 'Paid Article Sponsorship',
cost: 1000,
budget: 1000,
category: 'Marketing',
dateApproved: '1/01/20'
},
{
item: 'Team Lunch Meeting',
cost: 85,
budget: 100,
category: 'Sales',
dateApproved: '1/31/20'
},
{
item: 'Team Lunch Meeting',
cost: 125,
budget: 100,
category: 'Sales',
dateApproved: '1/24/20'
},
{
item: 'Browserstack',
cost: 199,
budget: 199,
category: 'Internal',
dateApproved: '1/01/20'
},
{
item: 'Browserstack',
cost: 199,
budget: 199,
category: 'Internal',
dateApproved: '2/01/20'
},
]
// once you get data from endpoint
// massage data to calculate difference
data.forEach(index => {
index.difference = index.cost - index.budget;
});
zgRef.setData(data);
zingchart.render({
id: 'expenses',
data: barConfig,
width: '100%',
height: 330,
});
zingchart.render({
id: 'treemap',
data: treemapConfig,
width: '100%',
height: 445,
})
zingchart.render({
id: 'estimations',
data: estimations,
width: '100%',
height: 200,
});
zingchart.render({
id: 'operational',
data: operational,
width: '100%',
height: 200,
});
});
</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: {
toggleAction: 'remove',
verticalAlign: 'bottom',
align: 'center',
layout: "2x4",
border: '0px',
marker: {
type: 'circle'
},
},
plotarea: {
margin: '20px dynamic dynamic dynamic'
},
plot: {
stacked: true,
// hoverstate
tooltip: {
visible: false
},
lineWidth: '3px',
// line node styling
marker: {
borderWidth: '0px',
size: '6px'
}
},
scaleX: {
label: {
text: ''
},
tick: {
lineWidth: 0,
},
guide: {
lineWidth: 0
},
step: 'day',
minValue: START_TIME,
transform: {
type: 'date',
all: '%M %d'
}
},
scaleY: {
// scale label with unicode character
label: {
text: 'Total Expense Cost<br>in dollars',
paddingRight: 30
},
lineColor: 'none',
tick: {
visible: false,
},
guide: {
lineStyle: 'dashed',
},
short: true,
shortUnit: 'K',
},
crosshairX: {
plotLabel: {
padding: '10px 15px',
borderRadius: '3px',
sortByValue: 'asc',
},
lineWidth: '100%',
alpha: .28,
},
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',
},
]
};
var treemapConfig = {
"graphset": [{
"type": "treemap",
"tooltip": {
},
"options": {
splitType: 'squarify',
palette: [
'#03a9f4',
'#F0B827',
'#D95234',
'#65BAA6',
],
box: {
borderWidth: '3px',
borderColor: '#FFF',
padding: '10px',
borderRadius: 5,
margin: 5
}
},
"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 estimations = {
type: 'line',
plot: {
lineWidth: 3,
marker: {
visible: false,
}
},
legend: {
verticalAlign: 'bottom',
align: 'center',
layout: "1x2",
border: '0px',
marker: {
type: 'circle'
},
},
scaleX: {
label: {
text: ''
},
tick: {
lineWidth: 0,
},
guide: {
lineWidth: 0
},
step: 'day',
minValue: START_TIME,
transform: {
type: 'date',
all: '%M %d'
}
},
series: [{
values: totalValues,
lineColor: '#03a9f4',
text: 'Actual'
},
{
values: Array(91).fill().map(() => {
return randomDollarAmount(5000, 5300)
}),
lineColor: '#65BAA6',
text: 'Projected'
}
],
plotarea: {
margin: '20px dynamic dynamic dynamic'
},
scaleY: {
values: '3000:7000:1000',
label: {
text: 'Total Expense Cost<br>in dollars',
paddingRight: 30
},
lineColor: 'none',
tick: {
visible: false,
},
guide: {
lineStyle: 'dashed',
},
short: true,
shortUnit: 'K',
}
};
const operational = {
type: 'bar',
plot: {
stacked: true,
stackType: '100%',
lineWidth: 3,
marker: {
visible: false,
}
},
legend: {
verticalAlign: 'bottom',
align: 'center',
layout: "1x2",
border: '0px',
marker: {
type: 'circle'
},
},
scaleX: {
label: {
text: ''
},
tick: {
lineWidth: 0,
},
guide: {
lineWidth: 0
},
step: 'day',
minValue: START_TIME,
transform: {
type: 'date',
all: '%M %d'
}
},
series: [{
values: totalValues,
backgroundColor: '#A7E2C0',
text: 'Operational'
},
{
values: Array(91).fill().map(() => {
return randomDollarAmount(5000, 5300)
}),
backgroundColor: '#65BAA6',
text: 'Projected'
}
],
plotarea: {
margin: '20px dynamic dynamic dynamic'
},
scaleY: {
values: '0:100:10',
// scale label with unicode character
label: {
text: 'Percent of costs',
paddingRight: 30
},
lineColor: 'none',
tick: {
visible: false,
},
guide: {
lineStyle: 'dashed',
},
format: '%v%'
}
};
// 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
// get reference to zinggrid DOM element
const zgRef = document.querySelector('zing-grid');
const data = [{
item: 'Flight to New York',
cost: 400,
budget: 1800,
category: 'Sales',
dateApproved: '2/10/20'
},
{
item: 'Lunch Meeting',
cost: 260,
budget: 100,
category: 'Sales',
dateApproved: '2/12/20'
},
{
item: 'Software License',
cost: 1999,
budget: 1500,
category: 'Internal',
dateApproved: '2/16/20'
},
{
item: 'AP3 Conference',
cost: 10623,
budget: 9000,
category: 'Marketing',
dateApproved: '1/12/20'
},
{
item: 'SO3 Conference',
cost: 8054,
budget: 10000,
category: 'Networking',
dateApproved: '1/04/20'
},
{
item: 'Paid Vue Sponsorship',
cost: 500,
budget: 1000,
category: 'Marketing',
dateApproved: '1/01/20'
},
{
item: 'Paid Article Sponsorship',
cost: 1000,
budget: 1000,
category: 'Marketing',
dateApproved: '1/01/20'
},
{
item: 'Team Lunch Meeting',
cost: 85,
budget: 100,
category: 'Sales',
dateApproved: '1/31/20'
},
{
item: 'Team Lunch Meeting',
cost: 125,
budget: 100,
category: 'Sales',
dateApproved: '1/24/20'
},
{
item: 'Browserstack',
cost: 199,
budget: 199,
category: 'Internal',
dateApproved: '1/01/20'
},
{
item: 'Browserstack',
cost: 199,
budget: 199,
category: 'Internal',
dateApproved: '2/01/20'
},
]
// once you get data from endpoint
// massage data to calculate difference
data.forEach(index => {
index.difference = index.cost - index.budget;
});
zgRef.setData(data);
zingchart.render({
id: 'expenses',
data: barConfig,
width: '100%',
height: 330,
});
zingchart.render({
id: 'treemap',
data: treemapConfig,
width: '100%',
height: 445,
})
zingchart.render({
id: 'estimations',
data: estimations,
width: '100%',
height: 200,
});
zingchart.render({
id: 'operational',
data: operational,
width: '100%',
height: 200,
});
});