code everywhere

Building a basic Metrics dashboard using CSS / JavaScript

posted on April 10, 2017, 12:00 am in

Using some basic JavaScript and CSS, we can create a small Metrics front end to display data from various API sources.

Based on a 4x4 Grid and designed to look good on 1080p TV's.

Demo Screenshot

Code

CSS Styles

* { margin: 0; padding: 0; }
img { border: 0; }
body { font-family: 'Roboto Condensed', sans-serif; overflow: hidden; }

.
dash-wrapper {
    
width: 1920px;
    
height: 1080px;
    
position: relative;
    
background: #191919;
}    

.
dash-widget {
    
position: absolute;
    
border-radius: 4px;
    
overflow: hidden;                
    
box-sizing: border-box;
    
border: 10px solid #191919;
    
padding: 15px;
    
text-align: center;
    
background: #31343B;
    
color: #fff;
}
.
dash-widget .label {  color: #a6a6a6; }

.dash-announcement {
    
position: absolute;
    
box-sizing: border-box;
    
text-align: center;
    
color: #fff;
    
background: #000;
    
left: 0;
    
width: 100%;
    
height: 10%;
    
top: 90%;
    
padding: 10px;
    
font-size: 57px;
}

.
x-1 { left: 0; }
.
x-2 { left: 25%; }
.
x-3 { left: 50%; }
.
x-4 { left: 75%;}

.
y-1 { top: 0; }
.
y-2 { top: 22.5%; }
.
y-3 { top: 45%; }
.
y-4 { top: 67.5%;}

.
width-1 { width: 25%; }
.
width-2 { width: 50%; }
.
width-3 { width: 75%; }
.
width-4 { width: 100%; }

.
height-1 { height: 22.5%; }
.
height-2 { height: 45%; }
.
height-3 { height: 67.5%; }
.
height-4 { height: 90%; }

.
title, .value { margin-bottom: 12px; text-transform: uppercase; }
.
title { font-size: 30px; font-weight: 400; }
.
value { font-size: 80px; font-weight: 700;}
.
label { font-size: 30px; text-transform: uppercase; font-weight: 400;}

.
red { background: #d02127; }
.green { background: #81B72B; }
.orange { background: #F0A542; }
.red .label, .green .label, .orange .label { color: #fff !important; }

.list { margin: 0 20px 0 20px; }
.list .
left, .list .right { float: left; width: 50%; font-size: 36px; }
.list .
left { text-align: left;  }
.list .
right { text-align: right; }
.
clear { clear: both; }

JavaScript Code

/*
* APIDash.js (Apr 10 2017)
* Copyright 2017, http://codeeverywhere.ca
* Licensed under the MIT license.
*/

(function(document, window) {
    
let counter = 0;
    var 
APIDash = function(args) {
        const 
animationInterval = 1000 * 10
        
const APICallInterval = 1000 * 30
        
const uuid = () => 'uid-' + counter++

        
this.init = function(className) {
            
document.querySelector(className).innerHTML = `
                <div class="dash-wrapper">
                    <div class="dash-widgets"></div>
                    <div class="dash-announcement">... text ...</div>
                </div>
            
`
        }

        
this.message = function(str){
            
document.querySelector('.dash-announcement').innerHTML = str
        
}
    
        
this.textWidget = function(title, className, callAPI) {

            const 
id = uuid()
            const 
animate = (className) => { document.querySelector(`#${id} .data`).className = `data ${className}` }

            
let format = [{label:"no data", value: "--"}]
            
let index = 0

            document
.querySelector('.dash-widgets').innerHTML += `
                <div class="dash-widget 
${className} animated fadeInUp" id="${id}">
                    <div class="title">
${title}</div>
                    <div class="data">
                        <div class="value">--</div>
                        <div class="label">--</div>
                    </div>
                </div>
            
`

            const 
slideOut = () => {
                
animate('animated slideOutLeft')
                
index = (index + 1) % format.length
                setTimeout
(slideIn, 500)
            }

            const 
slideIn = () => {
                
animate('animated slideInRight')
                
document.querySelector(`#${id} .value`).innerHTML = format[index].value
                document
.querySelector(`#${id} .label`).innerHTML = format[index].label
            
}

            const 
runCallAPI = () => { 
                
callAPI().then( (resp) => { format = resp })
            }
            
runCallAPI()
            
setInterval(runCallAPI, APICallInterval)

            
// add some random time so the animations aren't in sync
            
setInterval( slideOut, animationInterval + Math.floor(Math.random()*10000) )
        }

        
this.listWidget = (title, className, callAPI) => {
        
            const 
id = uuid()
            const 
animate = (className) => { document.querySelector(`#${id} .list`).className = `list ${className}` }
        
            
let format = [{label:"--", value: "--"}]
        
            
document.querySelector('.dash-widgets').innerHTML += `
                <div class="dash-widget 
${className} animated fadeInUp" id="${id}">
                    <div class="title">
${title}</div>
                    <div class="list">--</div>
                </div>
            
`
                        
            const 
slideOut = () => {
                
animate('animated slideOutLeft')
                
setTimeout(slideIn, 500)
            }
        
            const 
slideIn = () => {
                
animate('animated slideInRight')
                
document.querySelector(`#${id} .list`).innerHTML = format.reduce( ( acc, item ) => acc + `
                    <div class="left">
${item.label}</div>
                    <div class="right">
${item.value}</div>
                    <div class="clear"></div>
                
`, '')
            }
        
            const 
runCallAPI = () => { callAPI().then( (resp) => { format = resp }) }
            
runCallAPI()
            
setInterval(runCallAPI, APICallInterval)
        
            
setInterval( slideOut, animationInterval + Math.floor(Math.random()*10000) )
        }

        return 
this;
    }
    
window.APIDash = APIDash;
})(
document, window);

HTML Template

<!DOCTYPE html>
<
html>
    <
head>
        <
title>API Dashboard</title>
        <
link href='https://fonts.googleapis.com/css?family=Roboto+Condensed:300,400,700' rel='stylesheet' type='text/css'>
        <
link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.min.css">
        <
link rel="stylesheet" href="APIDash.css">
        <
script type="text/javascript" src="APIDash.js"></script>
    </head>
    <body>
        <div class="dash-app"></div>
        
        <script type="text/javascript">
            
            // Initialize APIDash ..
            APIDash().init('.dash-app')

            APIDash().message('hello!')

            // Get weather data, get every 3 hrs
            let weather = "-- &#8451;"
            const getWeather = () => {
                const url = 'https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22montreal%2C%20qc%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys'

                fetch(url).then((resp) => resp.json()).then(
                    data => {
                        const celsius = Math.floor((parseInt(data.query.results.channel.item.condition.temp) - 32) * .5556);
                        weather = celsius + "&#8451;"
                    },
                    fail => { }
                )
            }
            getWeather()
            setInterval(getWeather, 1000 * 60 * 60 * 3)



            // Sample Widget 1
            APIDash().textWidget('time/weather ( widget 1 )', 'width-1 height-1 x-3 y-1', () => {
                return Promise.resolve([
                    {
                        value: weather,
                        label: "weather"
                    },
                    {
                        value: new Date().toString().substr(16, 5),
                        label: new Date().toString().substr(0, 10)
                    }
                ])
            })



            // Sample Widget 2
            let outsideVar = 0
            APIDash().textWidget('active users ( widget 2 )', 'width-1 height-1 x-1 y-1 green', () => {
                return Promise.resolve([{
                    value: (outsideVar++).toLocaleString(),
                    label: "today"
                },
                {
                    value: (outsideVar * 10).toLocaleString(),
                    label: "this week"
                }])
            })



            // Sample Widget 3
            APIDash().textWidget('error rate ( widget 3 )', 'width-1 height-1 x-4 y-1 red', () => {
                return Promise.resolve([{
                    value: (999).toLocaleString(),
                    label: "past 24H"
                }])
            })



            // Sample Widget 4
            APIDash().textWidget('error rate ( widget 4 )', 'width-1 height-1 x-3 y-2', () => {
                return Promise.resolve([{
                    value: (999).toLocaleString(),
                    label: "past 24H"
                }])
            })



            // Sample Widget 5
            APIDash().listWidget('list ( widget 5 )', 'width-1 height-3 x-2 y-2', () => {
                return Promise.resolve([
                    {
                        label: "item1",
                        value: 1234
                    },
                    {
                        label: "item2",
                        value: 1234
                    },
                    {
                        label: "item3",
                        value: 1234
                    }
                ])
            })



            // Sample Widget 6
            APIDash().textWidget('long text ( widget 6 )', 'width-2 height-1 x-3 y-3', () => {
                return Promise.resolve([
                    {
                        value: (1234).toLocaleString(),
                        label: "complete"
                    },
                    {
                        value: (5678).toLocaleString(),
                        label: "failed"
                    }
                ])
            })
        </script>
    </body>
</html>

recent posts

< back
find something helpful? send us some coin BTC 19u6CWTxH4cH9V2yTfAemA7gmmh7rANgiv

(ad) Need Hosting? Try Linode, VPS Starting from $5

privacy policy