salesforce data migration services

Building a COVID-19 Live Dashboard on Salesforce Platform

In this blog, I am going to demonstrate – ‘ Building a COVID-19 Live Dashboard on Salesforce Platform using Lightning Components and JSON Parser ‘.

If you are a developer at your organization, you can use this as a template and understand the approach on how you can use JSON, parse it and use it in the lightning component to display the COVID-19 live data. If you are still learning how to build a lightning component or want to learn how you can parse the JSON, this could be a good use case to build too!

Click here to see the final look of the dashboard (which of course could be better designed ).

You would need to take below steps to do that :

  1. Understand the JSON Data Source
  2. Build  JSON Parsing Apex Classes
  3. Build Lightning Components
  4. Configure Lightning Component on the Community Page

1. Analyze COVID-19 Live Dashboard JSON Data Source :

We are fetching JSON Data from the below URL and parsing these two JSONs.If you are interested in understanding this JSON you can look at by hitting below links :

  1. https://corona.lmao.ninja/countries
  2. https://corona.lmao.ninja/all

2.Build JSON Parser Apex Classes

Now go ahead and Build an Apex Class that can be  used to parse the JSON data  from the URL – https://corona.lmao.ninja/countries

Name – CovidCountries


public class Covid19CountriesClass {
@AuraEnabled
public String country {get;set;}
@AuraEnabled
public Integer cases {get;set;}
@AuraEnabled
public Integer todayCases {get;set;}
@AuraEnabled
public Integer deaths {get;set;}
@AuraEnabled
public Integer todayDeaths {get;set;}
@AuraEnabled
public Integer recovered {get;set;}
@AuraEnabled
public Integer active {get;set;}
@AuraEnabled
public Integer critical {get;set;}
@AuraEnabled
public Integer casesPerOneMillion {get;set;}
@AuraEnabled
public Integer deathsPerOneMillion {get;set;}

public Covid19CountriesClass (JSONParser parser) {
while (parser.nextToken() != System.JSONToken.END_OBJECT) {
if (parser.getCurrentToken() == System.JSONToken.FIELD_NAME) {
String text = parser.getText();
if (parser.nextToken() != System.JSONToken.VALUE_NULL) {
if (text == 'country') {
country = parser.getText();
} else if (text == 'cases') {
cases = parser.getIntegerValue();
} else if (text == 'todayCases') {
todayCases = parser.getIntegerValue();
} else if (text == 'deaths') {
deaths = parser.getIntegerValue();
} else if (text == 'todayDeaths') {
todayDeaths = parser.getIntegerValue();
} else if (text == 'recovered') {
recovered = parser.getIntegerValue();
} else if (text == 'active') {
active = parser.getIntegerValue();
} else if (text == 'critical') {
critical = parser.getIntegerValue();
} else if (text == 'casesPerOneMillion') {
casesPerOneMillion = parser.getIntegerValue();
} else if (text == 'deathsPerOneMillion') {
deathsPerOneMillion = parser.getIntegerValue();
} else {
//System.debug(LoggingLevel.WARN, 'Covid19CountriesClass consuming unrecognized property: '+text);
consumeObject(parser);
}
}
}
}
}

public static List parse(String json) {
System.JSONParser parser = System.JSON.createParser(json);
return arrayOfCovid19CountriesClass(parser);
}

public static void consumeObject(System.JSONParser parser) {
Integer depth = 0;
do {
System.JSONToken curr = parser.getCurrentToken();
if (curr == System.JSONToken.START_OBJECT ||
curr == System.JSONToken.START_ARRAY) {
depth++;
} else if (curr == System.JSONToken.END_OBJECT ||
curr == System.JSONToken.END_ARRAY) {
depth--;
}
} while (depth > 0 && parser.nextToken() != null);
}

private static List arrayOfCovid19CountriesClass(System.JSONParser p) {
List res = new List();
if (p.getCurrentToken() == null) p.nextToken();
while (p.nextToken() != System.JSONToken.END_ARRAY) {
res.add(new Covid19CountriesClass(p));
}
return res;
}
}

Next, you need to Build an Apex Class which can be  used to parse JSON data from the URL –  https://corona.lmao.ninja/all

Name – CovidAll


public class CovidAll {
@AuraEnabled
public Integer cases {get;set;}
@AuraEnabled
public Integer deaths {get;set;}
@AuraEnabled
public Integer recovered {get;set;}
@AuraEnabled
public Long updated {get;set;}

public CovidAll(JSONParser parser) {
while (parser.nextToken() != System.JSONToken.END_OBJECT) {
if (parser.getCurrentToken() == System.JSONToken.FIELD_NAME) {
String text = parser.getText();
if (parser.nextToken() != System.JSONToken.VALUE_NULL) {
if (text == 'cases') {
cases = parser.getIntegerValue();
} else if (text == 'deaths') {
deaths = parser.getIntegerValue();
} else if (text == 'recovered') {
recovered = parser.getIntegerValue();
} else if (text == 'updated') {
updated = parser.getLongValue();
} else {
System.debug(LoggingLevel.WARN, 'CovidAll consuming unrecognized property: '+text);
consumeObject(parser);
}
}
}
}
}

public static CovidAll parse(String json) {
System.JSONParser parser = System.JSON.createParser(json);
return new CovidAll(parser);
}

public static void consumeObject(System.JSONParser parser) {
Integer depth = 0;
do {
System.JSONToken curr = parser.getCurrentToken();
if (curr == System.JSONToken.START_OBJECT ||
curr == System.JSONToken.START_ARRAY) {
depth++;
} else if (curr == System.JSONToken.END_OBJECT ||
curr == System.JSONToken.END_ARRAY) {
depth--;
}
} while (depth > 0 && parser.nextToken() != null);
}

}

Next, We create the class which will initiate the JSON parser classes we created to receive data from API  –

Name – CreateDataFromApi


public class CreateDataFromApi {

public static String method ='GET';
@AuraEnabled
public static List<Covid19CountriesClass> createData(){
List<Covid19CountriesClass> apiWrapper = new List<Covid19CountriesClass>();
system.debug('apiWrapper--' );
apiWrapper = Covid19CountriesClass.parse(HttpCallout.makeCallout('https://corona.lmao.ninja/countries',method));
system.debug('apiWrapper' + apiWrapper);
return apiWrapper;
}
@AuraEnabled
public static CovidAll createAllData(){
CovidAll WrapperReturn = CovidAll.parse(HttpCallout.makeCallout('https://corona.lmao.ninja/all',method));
system.debug('WrapperReturn' + WrapperReturn);
return WrapperReturn;
}
}public class CreateDataFromApi {

public static String method ='GET';
@AuraEnabled
public static List<Covid19CountriesClass> createData(){
List<Covid19CountriesClass> apiWrapper = new List<Covid19CountriesClass>();
system.debug('apiWrapper--' );
apiWrapper = Covid19CountriesClass.parse(HttpCallout.makeCallout('https://corona.lmao.ninja/countries',method));
system.debug('apiWrapper' + apiWrapper);
return apiWrapper;
}
@AuraEnabled
public static CovidAll createAllData(){
CovidAll WrapperReturn = CovidAll.parse(HttpCallout.makeCallout('https://corona.lmao.ninja/all',method));
system.debug('WrapperReturn' + WrapperReturn);
return WrapperReturn;
}
}

3.Build Lightning Components for COVID-19 Live Dashboard

Create two Lightning Component  :

  1. CovidTotalComponent
  2. CovidLightningComponent

Let’s start by Creating the first Lightning component

Name: COVIDLightningComponent.cmp

 


<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction"
controller = "CreateDataFromApi"
access="global" >
<aura:attribute name='totalCases' type='Integer' />
<aura:attribute name='totalDeath' type='Integer' />
<aura:attribute name='totalRecovered' type='Integer' />
<aura:handler name='init' value='{!this}' action='{!c.changeEntity}'
description = 'Trigger defined action on initialization of component'/>


<div class="slds-grid slds-gutters slds-p-around_x-small">
<div class="slds-col" >

</div>
<div class="slds-col slds-box redboxClass">
<div class = "slds-align_absolute-center fontCountLabelSizeCss">
<p>Total Cases</p>
</div>
<div class = "slds-align_absolute-center fontCountSizeCss">
<p> {!v.totalCases}</p>
</div>
</div>
<div class="slds-col" />
<div class="slds-col slds-box greyboxClass" >
<div class = "slds-align_absolute-center fontCountLabelSizeCss">
<p>Total Deaths </p>
</div>
<div class = "slds-align_absolute-center fontCountSizeCss">
<p> {!v.totalDeath}</p>
</div>
</div>
<div class="slds-col" />
<div class="slds-col slds-box greenboxClass">
<div class = "slds-align_absolute-center fontCountLabelSizeCss ">
<p>Total Recovered</p>
</div>
<div class = "slds-align_absolute-center fontCountSizeCss">
<p> {!v.totalRecovered}</p>
</div>
</div>
<div class="slds-col" />
</div>
</aura:component>

Now add the Controller.JS file related to the lightning component as below :

Name: CovidLightningController.JS 

 


({
changeEntity : function(component, event, helper) {
var action = component.get('c.createAllData');

action.setCallback(this, function(a){
var state = a.getState(); // get the response state
if(state === 'SUCCESS') {
var resp = a.getReturnValue();
component.set('v.totalCases', resp.cases);
component.set('v.totalDeath', resp.deaths);
component.set('v.totalRecovered', resp.recovered);
console.log(resp.cases);
console.log(resp.deaths);
console.log(resp.recovered);
}
});
$A.enqueueAction(action);
}
})

Now add the.CSS file related to the lightning component as below :

Name: CovidLightningComponent.CSS

 


.THIS .redboxClass{
display: inline-block;
vertical-align: middle;
-webkit-transform: perspective(1px) translateZ(0);
transform: perspective(1px) translateZ(0);
box-shadow: 0 0 1px rgba(0, 0, 0, 0);
-webkit-transition-duration: 0.3s;
transition-duration: 0.3s;
-webkit-transition-property: transform;
transition-property: transform;

background: #fed7d7;
color: #e53e3e;

}
.THIS .redboxClass:hover, .THIS.hvr-grow:focus, .THIS.hvr-grow:active {
-webkit-transform: scale(1.1);
transform: scale(1.1);

background: #e53e3e;
color: #fed7d7;

}
.THIS .greenboxClass{
display: inline-block;
vertical-align: middle;
-webkit-transform: perspective(1px) translateZ(0);
transform: perspective(1px) translateZ(0);
box-shadow: 0 0 1px rgba(0, 0, 0, 0);
-webkit-transition-duration: 0.3s;
transition-duration: 0.3s;
-webkit-transition-property: transform;
transition-property: transform;

background: #99cc99;
color: #198c19;

}
.THIS .greenboxClass:hover, .THIS.hvr-grow:focus, .THIS.hvr-grow:active {
-webkit-transform: scale(1.1);
transform: scale(1.1);
background: #198c19;
color: #99cc99;
}
.THIS .greyboxClass{
display: inline-block;
vertical-align: middle;
-webkit-transform: perspective(1px) translateZ(0);
transform: perspective(1px) translateZ(0);
box-shadow: 0 0 1px rgba(0, 0, 0, 0);
-webkit-transition-duration: 0.3s;
transition-duration: 0.3s;
-webkit-transition-property: transform;
transition-property: transform;

background: #bfbfbf;
color: #718096;

}
.THIS .greyboxClass:hover, .THIS.hvr-grow:focus, .THIS.hvr-grow:active {
-webkit-transform: scale(1.1);
transform: scale(1.1);
background: #718096;
color: #bfbfbf;
}
.THIS .fontCountSizeCss {
font-size: 1.87rem;
font-weight: 700;
}
.THIS .fontCountLabelSizeCss {
font-size: 1rem;
font-weight: 600;
}
.THIS .fontLabelSizeCss {
font-size: 3rem;
font-weight: 700;
}.THIS .redboxClass{

display: inline-block;

vertical-align: middle;

-webkit-transform: perspective(1px) translateZ(0);

transform: perspective(1px) translateZ(0);

box-shadow: 0 0 1px rgba(0, 0, 0, 0);

-webkit-transition-duration: 0.3s;

transition-duration: 0.3s;

-webkit-transition-property: transform;

transition-property: transform;

&nbsp;

background: #fed7d7;

color: #e53e3e;

&nbsp;

}

.THIS .redboxClass:hover, .THIS.hvr-grow:focus, .THIS.hvr-grow:active {

-webkit-transform: scale(1.1);

transform: scale(1.1);

&nbsp;

background: #e53e3e;

color: #fed7d7;

&nbsp;

}

.THIS .greenboxClass{

display: inline-block;

vertical-align: middle;

-webkit-transform: perspective(1px) translateZ(0);

transform: perspective(1px) translateZ(0);

box-shadow: 0 0 1px rgba(0, 0, 0, 0);

-webkit-transition-duration: 0.3s;

transition-duration: 0.3s;

-webkit-transition-property: transform;

transition-property: transform;

&nbsp;

background: #99cc99;

color: #198c19;

&nbsp;

}

.THIS .greenboxClass:hover, .THIS.hvr-grow:focus, .THIS.hvr-grow:active {

-webkit-transform: scale(1.1);

transform: scale(1.1);

background: #198c19;

color: #99cc99;

}

.THIS .greyboxClass{

display: inline-block;

vertical-align: middle;

-webkit-transform: perspective(1px) translateZ(0);

transform: perspective(1px) translateZ(0);

box-shadow: 0 0 1px rgba(0, 0, 0, 0);

-webkit-transition-duration: 0.3s;

transition-duration: 0.3s;

-webkit-transition-property: transform;

transition-property: transform;

&nbsp;

background: #bfbfbf;

color: #718096;

&nbsp;

}

.THIS .greyboxClass:hover, .THIS.hvr-grow:focus, .THIS.hvr-grow:active {

-webkit-transform: scale(1.1);

transform: scale(1.1);

background: #718096;

color: #bfbfbf;

}

.THIS .fontCountSizeCss {

font-size: 1.87rem;

font-weight: 700;

}

.THIS .fontCountLabelSizeCss {

font-size: 1rem;

font-weight: 600;

}

.THIS .fontLabelSizeCss {

font-size: 3rem;

font-weight: 700;

}

.THIS .redboxClass{
display: inline-block;
vertical-align: middle;
-webkit-transform: perspective(1px) translateZ(0);
transform: perspective(1px) translateZ(0);
box-shadow: 0 0 1px rgba(0, 0, 0, 0);
-webkit-transition-duration: 0.3s;
transition-duration: 0.3s;
-webkit-transition-property: transform;
transition-property: transform;

background: #fed7d7;
color: #e53e3e;

}
.THIS .redboxClass:hover, .THIS.hvr-grow:focus, .THIS.hvr-grow:active {
-webkit-transform: scale(1.1);
transform: scale(1.1);

background: #e53e3e;
color: #fed7d7;

}
.THIS .greenboxClass{
display: inline-block;
vertical-align: middle;
-webkit-transform: perspective(1px) translateZ(0);
transform: perspective(1px) translateZ(0);
box-shadow: 0 0 1px rgba(0, 0, 0, 0);
-webkit-transition-duration: 0.3s;
transition-duration: 0.3s;
-webkit-transition-property: transform;
transition-property: transform;

background: #99cc99;
color: #198c19;

}
.THIS .greenboxClass:hover, .THIS.hvr-grow:focus, .THIS.hvr-grow:active {
-webkit-transform: scale(1.1);
transform: scale(1.1);
background: #198c19;
color: #99cc99;
}
.THIS .greyboxClass{
display: inline-block;
vertical-align: middle;
-webkit-transform: perspective(1px) translateZ(0);
transform: perspective(1px) translateZ(0);
box-shadow: 0 0 1px rgba(0, 0, 0, 0);
-webkit-transition-duration: 0.3s;
transition-duration: 0.3s;
-webkit-transition-property: transform;
transition-property: transform;

background: #bfbfbf;
color: #718096;

}
.THIS .greyboxClass:hover, .THIS.hvr-grow:focus, .THIS.hvr-grow:active {
-webkit-transform: scale(1.1);
transform: scale(1.1);
background: #718096;
color: #bfbfbf;
}
.THIS .fontCountSizeCss {
font-size: 1.87rem;
font-weight: 700;
}
.THIS .fontCountLabelSizeCss {
font-size: 1rem;
font-weight: 600;
}
.THIS .fontLabelSizeCss {
font-size: 3rem;
font-weight: 700;
}

Now add the master lightning component which includes the summary :

Name – COVIDLightningComponent.cmp

 


<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction"
access="global"
controller = "CreateDataFromApi">

<aura:attribute name="listOfAllRecords" type="Object"/>
<aura:attribute name="PaginationList" type="list"/>
<aura:attribute name="selectedCount" type="integer" default="0" description="selected Records Count"/>

<aura:attribute name="totalRecordsCount" type="Integer"/>

<aura:attribute name="bNoRecordsFound" type="boolean"/>
<aura:attribute name="loaded" type="Boolean" default="false" />
<aura:handler name='init' value='{!this}' action='{!c.doInit}' />

<div class="exampleHolder">
<aura:if isTrue="{! v.loaded }">

<aura:set attribute="else">
<lightning:spinner alternativeText="Loading" />
</aura:set>
</aura:if>
</div>
<c:CovidTotalComponent />
<aura:if isTrue="{!v.bNoRecordsFound}">

<div class="slds-notify slds-notify_alert slds-theme_alert-texture slds-theme_info" role="alert">
<span class="slds-assistive-text">error</span>
<h2>No record found.</h2>
</div>
<aura:set attribute="else">

<p class="slds-m-around_small">
<span class="slds-badge slds-badge_lightest" style="display:inline-block">
Total Countries : {!v.selectedCount > 0 ? v.selectedCount + '/' : ''} {!v.totalRecordsCount}
</span>
</p>
<div class="slds-table--header-fixed_container" style="height:35rem;">
<div class="slds-scrollable_y" style="height:100%;">

<table class="slds-table slds-table_bordered slds-table_cell-buffer slds-table--header-fixed">
<thead >
<tr class="slds-text-title_caps">

<th scope="col">
<div class="slds-truncate slds-cell-fixed" title="Countries">Countries</div>
</th>
<th scope="col">
<div class="slds-truncate slds-cell-fixed" title="Cases">Cases</div>
</th>
<th scope="col">
<div class="slds-truncate slds-cell-fixed" title="Todays Cases">Today's Cases</div>
</th>
<th scope="col">
<div class="slds-truncate slds-cell-fixed" title="Deaths">Deaths</div>
</th>
<th scope="col">
<div class="slds-truncate slds-cell-fixed" title="Todays Deaths">Today's Deaths</div>
</th>
<th scope="col">
<div class="slds-truncate slds-cell-fixed" title="Recovered">Recovered</div>
</th>
<th scope="col">
<div class="slds-truncate slds-cell-fixed" title="Critical">Critical</div>
</th>

</tr>
</thead>
<tbody>

<aura:iteration items="{!v.PaginationList}" var="obj">
<tr>

<th scope="row">
<div class="slds-truncate" title="{!obj.Covid19CountriesClass.country}">
{!obj.country}
</div>
</th>
<th scope="row">
<div class="slds-truncate" title="{!obj.Covid19CountriesClass.cases}">
{!obj.cases}
</div>
</th>
<th scope="row">
<div class="slds-truncate" title="{!obj.Covid19CountriesClass.todayCases}">
+{!obj.todayCases}
</div>
</th>
<th scope="row">
<div class="slds-truncate" title="{!obj.Covid19CountriesClass.deaths}">
{!obj.deaths}
</div>
</th>
<th scope="row" style = "color:red;">
<div class="slds-truncate" title="{!obj.Covid19CountriesClass.todayDeaths}">
+{!obj.todayDeaths}
</div>
</th>
<th scope="row">
<div class="slds-truncate" title="{!obj.Covid19CountriesClass.recovered}">
{!obj.recovered}
</div>
</th>
<th scope="row">
<div class="slds-truncate" title="{!obj.Covid19CountriesClass.critical}">
{!obj.critical}
</div>
</th>
</tr>
</aura:iteration>
</tbody>
</table>

<br/>

</div>
</div>
</aura:set>
</aura:if>
</aura:component>

4.Expose Lightning Component in Community (Optional)

If you want to make this a public URL like what I did, you will have to either set up a visualforce page and add it to publish sites to set up a community and add the lightning component – COVIDLightningComponent to the community builder. You can follow this link for how you can set up a community.

Once you set up the community you can add the lightning component to the community using experience builder Check out how to configure components in experience builder. Also, you need to create a user profile – Community Guest User and provide access to the community page to the profile.

What Next?

You can play around with the code components and design the CSS, redesign the components, add country-wise filters, build more dynamic dashboards etc.f you want to refer to a different JSON that provides you the data source you can do that too!

Stay Safe!

References :

  1. JSON Data Links –
    1. https://corona.lmao.ninja/countries
    2. https://corona.lmao.ninja/all
  2. How to build a lightning Component – Trailhead
  3. Getting Started with Apex JSON- Salesforce Documentation
  4. Experience Builder Overview- Salesforce Documentation
  5. Test Rest APIs In Salesforce