COVID-19 Tracker App

Hello,

I hope everyone is safe and sound. As we all know the COVID-19 pandemic is causing lots of deaths around the world. It is a very difficult time for all of us. Stay at home and Be Safe.

I have developed a COVID-19 tracking app for India on the Salesforce platform and just want to share this with you.

Check out this DEMO link: https://covid19-ind-tracker-developer-edition.ap17.force.com/

In this app, I have created some lightning components, Apex classes, and a VF page. Some third-party APIs & libraries are used in this app.

  • Covid19india.org open-source API was used to get the data.
  • NewsAPI is used to show the latest news/articles about COVID-19.
  • ChartJS library is used to draw the chart.

To host the page I have used the force.com site in the Salesforce org. Follow this link to Setting Up Salesforce Sites.

The force.com site can only host the Visulaforce page so a VF page is created. This VF page contains the lightning components.

Find the project source code below:

Covid19_IND_TrackerCmp:

<aura:component controller="Covid19_IND_TrackerController" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<aura:attribute name="total" type="String"/>
<aura:attribute name="confirmed" type="String"/>
<aura:attribute name="active" type="String"/>
<aura:attribute name="recovered" type="String"/>
<aura:attribute name="deaths" type="String"/>
<!-- attributes -->
<aura:attribute name="sortDirection" type="String" default="asc" />
<aura:attribute name="defaultSortDirection" type="String" default="asc" />
<aura:attribute name="sortedBy" type="String" />
<aura:attribute name="data" type="Object"/>
<aura:attribute name="columns" type="List"/>
<aura:handler event="aura:waiting" action="{!c.showSpinner}"/>
<aura:handler event="aura:doneWaiting" action="{!c.hideSpinner}"/>
<aura:attribute name="Spinner" type="boolean" default="false"/>
<aura:attribute name="lastUpdated" type="String"/>
<div class="slds-page-header">
<div class="slds-grid">
<div class="slds-col slds-size--7-of-12">
<span style="font-size:22px;">COVID 19 (State-Wise) Tracker</span>
</div>
<div class="slds-col slds-size--4-of-12">
<span style="color:green; font-size:14px; font-style:bold; position:relative;top:10px;"><!--Last Update :--> <b>{!v.lastUpdated}</b></span>
</div>
<div class="slds-col slds-size--1-of-12">
<lightning:buttonIcon iconName="utility:refresh" size="large" variant="bare" onclick="{! c.doInit }" alternativeText="Refresh" title="Refresh" />
</div>
</div>
</div>
<!--loading spinner start... style=Brand Medium (blue dots)-->
<aura:if isTrue="{!v.Spinner}">
<div aura:id="spinnerId" class="slds-spinner_container">
<div class="slds-spinner--brand slds-spinner slds-spinner--large slds-is-relative" role="alert">
<span class="slds-assistive-text">Loading</span>
<div class="slds-spinner__dot-a"></div>
<div class="slds-spinner__dot-b"></div>
</div>
</div>
</aura:if>
<lightning:tabset selectedTabId="one">
<lightning:tab label="LIVE DATA" id="one">
<div class="slds-grid">
<div class="slds-col slds-size--3-of-12" style="background-color:red; padding:10px;">
<span style="color:white; font-size:14px;">CONFIRMED : <b style="font-size:18px;">{!v.confirmed}</b></span>
</div>
<div class="slds-col slds-size--3-of-12" style="background-color:#009DDC; padding:10px;">
<span style="color:white;font-size:14px;"> ACTIVE : <b style="font-size:18px;">{!v.active}</b></span>
</div>
<div class="slds-col slds-size--3-of-12" style="background-color:green; padding:10px;">
<span style="color:white;font-size:14px;">RECOVERED : <b style="font-size:18px;">{!v.recovered}</b></span>
</div>
<div class="slds-col slds-size--3-of-12" style="background-color:gray; padding:10px;">
<span style="color:white;font-size:14px;">DECEASED: <b style="font-size:18px;">{!v.deaths} </b></span>
</div>
</div>
<!-- the container element determine the height of the datatable -->
<div style="height: 100%; font-size: 18px; padding: 1%;">
<lightning:datatable
keyField="id"
data="{! v.data }"
columns="{! v.columns }"
hideCheckboxColumn="true"
defaultSortDirection="{!v.defaultSortDirection}"
sortedDirection="{!v.sortDirection}"
sortedBy="{!v.sortedBy}"
onsort="{!c.handleSort}">
</lightning:datatable>
</div>
</lightning:tab>
<lightning:tab label="CHART" id="three">
<div id="confirmedCase">
<c:Covid19_Tracking_Chart/>
</div>
</lightning:tab>
<lightning:tab label="NEWS" id="two">
<c:Covid19_News/>
</lightning:tab>
<lightning:tab label="INFORMATION AND ADVISORY" id="four">
<div style="padding:2%; ">
<b style="font-size:25px; color:blue;">INFORMATION AND ADVISORY:By GOI</b> <br/><br/>
<ul style="font-size:16px;">
<li>
<a href="https://www.mohfw.gov.in/pdf/Poster1GHFanDHGA.pdf&quot; target="_blank">
What is Novel Coronavirus?
</a>
</li><br/>
</ul>
</div>
</lightning:tab>
</lightning:tabset>
<div class="slds-page-header">
<div class="slds-grid">
<div class="slds-col slds-size--3-of-12">
Developed By: <b> <a href="https://sfdclesson.com/&quot; target="_blank">SFDC LESSONS</a></b>
</div>
<div class="slds-col slds-size--5-of-12">
COVID-19 stats and patient tracing in India (Unofficial).
</div>
<div class="slds-col slds-size--4-of-12" style="font-size:10px">
Powered By: <a href="https://api.covid19india.org/">covid19india.org</a&gt; &amp; <a href="https://newsapi.org/">NewsAPI</a&gt;
</div>
</div>
</div>
</aura:component>

Covid19_IND_TrackerController:

({
doInit : function(component, event, helper) {
helper.fetchData(component,event,helper);
helper.setColumns(component);
helper.setData(component);
},
handleSort: function(component, event, helper) {
helper.handleSort(component, event);
},
// this function automatic call by aura:waiting event
showSpinner: function(component, event, helper) {
// make Spinner attribute true for display loading spinner
component.set("v.Spinner", true);
},
// this function automatic call by aura:doneWaiting event
hideSpinner : function(component,event,helper){
// make Spinner attribute to false for hide loading spinner
component.set("v.Spinner", false);
}
})

Covid19_IND_TrackerHelper:

({
COLUMNS:[
{label: 'STATE/UT', fieldName: 'STATEUT', type: 'text'},
{label: 'CNFMD', fieldName: 'CNFMD_CASE',
type: 'number',
sortable: true,
cellAttributes: { alignment: 'left' }},
{label: 'ACTIVE', fieldName: 'ACTIVE_CASE', type: 'text'},
{label: 'RCVRD', fieldName: 'RCVRD_CASE', type: 'text'},
{label: 'DECEASED', fieldName: 'DEATH', type: 'text'}
],
DATA: [],
fetchData : function(component,event,helper) {
var action = component.get("c.fecthCovid19Data");
action.setCallback(this,function(response){
var state1 = response.getState();
if(state1 =='SUCCESS'){
var result = JSON.parse(JSON.stringify(response.getReturnValue()));
component.set('v.columns', [
{label: 'STATE/UT', fieldName: 'STATEUT', type: 'text',sortable: true},
{label: 'CNFMD', fieldName: 'CNFMD_CASE',
type: 'number',cellAttributes: { alignment: 'left' }},
{label: 'ACTIVE', fieldName: 'ACTIVE_CASE', type: 'text'},
{label: 'RCVRD', fieldName: 'RCVRD_CASE', type: 'text'},
{label: 'DECEASED', fieldName: 'DEATH', type: 'text'}
]);
component.set('v.confirmed',result.statewise[0].confirmed);
component.set('v.active',result.statewise[0].active);
component.set('v.recovered',result.statewise[0].recovered);
component.set('v.deaths',result.statewise[0].deaths);
var dataArry = new Array();
for(var i=0; i< result.statewise.length; i++){
//alert(result.statewise[i].state);
console.log('count >> '+i +' '+JSON.stringify(result.statewise[i].state));
var fetchData = {
id : i,
STATEUT: result.statewise[i].state,
CNFMD_CASE : result.statewise[i].confirmed,
ACTIVE_CASE : result.statewise[i].active,
RCVRD_CASE : result.statewise[i].recovered,
DEATH : result.statewise[i].deaths
};
dataArry.push(fetchData);
}
dataArry.shift();
console.log('data Array = '+dataArry);
component.set('v.data',dataArry);
this.DATA = dataArry;
//component.set('v.lastUpdated',result.key_values[0].lastupdatedtime);
}
});
$A.enqueueAction(action);
},
setColumns: function(component) {
component.set('v.columns', this.COLUMNS);
},
setData: function(component) {
component.set('v.data', this.DATA);
},
// Used to sort the 'Age' column
sortBy: function(field, reverse, primer) {
var key = primer
? function(x) {
return primer(x[field]);
}
: function(x) {
return x[field];
};
return function(a, b) {
a = key(a);
b = key(b);
return reverse * ((a > b) - (b > a));
};
},
handleSort: function(component, event) {
var sortedBy = event.getParam('fieldName');
var sortDirection = event.getParam('sortDirection');
//alert(component.get('v.data'));
//var DATA = component.get('v.data');
var cloneData = this.DATA.slice(0);
cloneData.sort((this.sortBy(sortedBy, sortDirection === 'asc' ? 1 : -1)));
component.set('v.data', cloneData);
component.set('v.sortDirection', sortDirection);
component.set('v.sortedBy', sortedBy);
}
})

Covid19_IND_TrackerCSS:

.THIS #one__item{
font-size:20px;
}
.THIS #two__item{
font-size:20px;
}
.THIS #three__item{
font-size:20px;
}
.THIS #four__item{
font-size:20px;
}

APEX CLASSES:

Covid19_IND_TrackerController:

public class Covid19_IND_TrackerController {
@AuraEnabled
public static covid19DataParser fecthCovid19Data(){
string baseURL='https://api.covid19india.org/data.json&#39;;
HttpRequest reqest = new HttpRequest();
reqest.setEndpoint(baseURL);
reqest.setMethod('GET');
reqest.setHeader('Accept','application/json');
Http h = new Http();
HTTPResponse response = h.send(reqest);
system.debug('Response : ' +response.getBody());
covid19DataParser prsr = covid19DataParser.parse(response.getBody());
system.debug('Object = ' +prsr);
return prsr;
}
@AuraEnabled
public static JsonNewWrapper fetchNews(){
string apiKey=''; // put your API Key (NewsAPI.org)
Integer day = system.today().day()-2;
String formDate = string.valueOf(system.today().year()+'-0'+system.today().month()+'-0'+day);
//string baseURL='http://newsapi.org/v2/everything?q=covid19&sources=Google News (India),BBC News,Financial Times,The Wall Street Journal,Reddit,Time,The Economist,National Geographic,Google News,Fortune,Business Insider UK,Cnn,The Times Of India,Techcrunch,The New York Times,The Washington Post,Cnbc,The Hindu,The Verge&language=en&from=2020-03-04&sortBy=publishedAt&apiKey='+apiKey;
string baseURL='http://newsapi.org/v2/everything?q=covid19&domains=ndtv.com,timesofindia.indiatimes.com,thehindu.com,indiatoday.in,republicworld.com,news.google.com,zeenews.india.com&language=en&from='+formDate+'&sortBy=publishedAt&apiKey='+apiKey://+system.today().format()+
HttpRequest reqest = new HttpRequest();
reqest.setEndpoint(baseURL);
reqest.setMethod('GET');
reqest.setHeader('Accept','application/json');
Http h = new Http();
HTTPResponse response = h.send(reqest);
system.debug('status: '+response.getstatusCode());
system.debug('body: '+response.getBody());
JsonNewWrapper jnw = JsonNewWrapper.parse(response.getBody());
return jnw;
}
}

Note: You need to signup to get the NewsAPI Key.

covid19DataParser:

public class covid19DataParser {
@AuraEnabled
public List<Cases_time_series> cases_time_series;
@AuraEnabled
public List<Key_values> key_values;
@AuraEnabled
public List<Statewise> statewise;
@AuraEnabled
public List<Tested> tested;
public class Key_values {
@AuraEnabled
public String confirmeddelta;
@AuraEnabled
public String counterforautotimeupdate;
@AuraEnabled
public String deceaseddelta;
@AuraEnabled
public String lastupdatedtime;
@AuraEnabled
public String recovereddelta;
@AuraEnabled
public String statesdelta;
}
public class Delta {
@AuraEnabled
public Integer active;
@AuraEnabled
public Integer confirmed;
@AuraEnabled
public Integer deaths;
@AuraEnabled
public Integer recovered;
}
public class Statewise {
@AuraEnabled
public String active;
@AuraEnabled
public String confirmed;
@AuraEnabled
public String deaths;
@AuraEnabled
public Delta delta;
@AuraEnabled
public String deltaconfirmed;
@AuraEnabled
public String deltadeaths;
@AuraEnabled
public String deltarecovered;
@AuraEnabled
public String lastupdatedtime;
@AuraEnabled
public String recovered;
@AuraEnabled
public String state;
@AuraEnabled
public String statecode;
}
public class Cases_time_series {
@AuraEnabled
public String dailyconfirmed;
@AuraEnabled
public String dailydeceased;
@AuraEnabled
public String dailyrecovered;
@AuraEnabled
public String date1;
@AuraEnabled
public String totalconfirmed;
@AuraEnabled
public String totaldeceased;
@AuraEnabled
public String totalrecovered;
}
public class Tested {
@AuraEnabled
public String source;
@AuraEnabled
public String testsconductedbyprivatelabs;
@AuraEnabled
public String totalindividualstested;
@AuraEnabled
public String totalpositivecases;
@AuraEnabled
public String totalsamplestested;
@AuraEnabled
public String updatetimestamp;
}
public static covid19DataParser parse(String json) {
return (covid19DataParser) System.JSON.deserialize(json, covid19DataParser.class);
}
}

JsonNewsWrapper:

public class JsonNewWrapper{
@AuraEnabled
public String status;
@AuraEnabled
public Integer totalResults;
@AuraEnabled
public List<Articles> articles;
public class Articles {
@AuraEnabled
public Source source;
@AuraEnabled
public String author;
@AuraEnabled
public String title;
@AuraEnabled
public String description;
@AuraEnabled
public String url;
@AuraEnabled
public String urlToImage;
@AuraEnabled
public String publishedAt;
@AuraEnabled
public String content;
}
public class Source {
@AuraEnabled
public String id;
@AuraEnabled
public String name;
}
public static JsonNewWrapper parse(String json) {
return (JsonNewWrapper) System.JSON.deserialize(json, JsonNewWrapper.class);
}
}
view raw JsonNewsWrapper hosted with ❤ by GitHub

Covid19_News lightning component was created to show the news/articles about coronavirus from Indian news sources.

Covid19_News:

<aura:component controller="Covid19_IND_TrackerController" access="global">
<aura:attribute name="newsData" type="Object"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<div class="slds-grid">
<div class="slds-col slds-size--12-of-12">
<div aura:id="new_content" style="padding:20px;">
<aura:iteration items="{!v.newsData}" var="news" >
<div class="contain" >
<a href="{!news.url}" id="link" target="_blank">
<span id="title" style="font-size:25px;"><b>{!news.title}</b></span>
</a><br/>
<span style="color:blue; font-size:14px;">Source : {!news.source.name}, Author : {!news.author}<br/></span>
<span id="desc" style="font-size:20px;">{!news.description}</span><br/>
<a href="{!news.url}" id="link" target="_blank">
<img src="{!news.urlToImage}" id="image" style="width:1000px; height:600px;"/>
</a> <br/><br/>
</div>
<br/>
</aura:iteration>
</div>
</div>
</div>
</aura:component>
view raw Covid19_News hosted with ❤ by GitHub

Covid19_NewsController:

({
doInit : function(component, event, helper) {
helper.fetchNewsData(component, event, helper);
}
})

Covid19_NewsHelper:

({
fetchNewsData : function(component, event, helper) {
var action=component.get("c.fetchNews");
action.setCallback(this, function(response) {
var state = response.getState();
if (state === "SUCCESS") {
var result = JSON.parse(JSON.stringify(response.getReturnValue()));
component.set('v.newsData',result.articles);
console.log('Articles = '+JSON.stringify(result.articles));
//console.log(' Result : ' +JSON.stringify(result.articles[0].title));
}
});
$A.enqueueAction(action);
}
})

Covid19_Tracking_ChartCmp lightning component was created to show the charts based on the data provided by API.

Covid19_Tracking_ChartCmp:

<aura:component controller="Covid19_IND_TrackerController" access="global">
<!-- load chart js library from static resource-->
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<ltng:require scripts="/resource/Chart_Js/Chart.js-2.9.3/dist/Chart.bundle.js" />
<aura:attribute name="chartObj" type="object" access="public"/>
<aura:attribute name="property1" type="string"/>
<aura:attribute name="property2" type="string"/>
<div aura:id="chartContainer" style=" ">
<canvas aura:id="myChart" id="{!v.property1}" /><!--reportChart-->
</div>
</aura:component>

Covid19_Tracking_ChartController:

({
doInit : function(component, event, helper) {
helper.CreateChart(component);
}
})

Covid19_Tracking_ChartHelper:

({
CreateChart : function(component) {
var chartType= 'bar';
var action=component.get("c.fecthCovid19Data");
action.setCallback(this,function(response){
var state=response.getState();
if(state=='SUCCESS'){
var result = JSON.parse(JSON.stringify(response.getReturnValue()));
console.log('result : ' +JSON.stringify( result));
var data = [];
var Label= [];
var data1 = [];
var Label1= [];
for(var i=0; i< result.statewise.length; i++){
var tLabel = result.statewise[i].state;
Label.push(tLabel);
var tValue = result.statewise[i].confirmed;
data.push(tValue);
var tLabel = result.statewise[i].state;
Label1.push(tLabel);
var tValue = result.statewise[i].deaths;
data1.push(tValue);
}
Label.shift();
data.shift();
console.log('Label : '+Label);
console.log('data : '+data);
//Create chart1
var el=component.find('myChart').getElement();
var ctx=el.getContext('2d');
if(window.bar!=undefined){
window.bar.destroy();
}
var background_color=new Array();
for(var i=0 ; i<100 ; i++){
var w,x,y,z;
w = parseInt(Math.random()*255);
x = parseInt(Math.random()*255);
y = parseInt(Math.random()*255);
z = Math.random();
background_color.push('rgba('+w+','+x+','+y+','+z+')');
}
window.bar = new Chart(ctx, {
type: chartType,
data: {
labels: Label,
datasets: [
{
label: "CONFIRMED CASE",
fillColor: background_color,
backgroundColor: background_color,
strokeColor: "rgba(220,220,220,1)",
data: data
}
]
},
options: {
hover: {
mode: "none"
},
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
}
});
$A.enqueueAction(action);
},
})

This lightning app contains the ‘Covid19_IND_TrackerCmp’ main component.

COVID19_IND_LtngApp

<aura:application access="GLOBAL" extends="ltng:outApp" implements="ltng:allowGuestAccess">
<aura:dependency resource="c:Covid19_IND_Tracker"/>
<c:Covid19_IND_Tracker />
</aura:application>

The visualforce contains lightning app and components.

Covid19_Tracker_App VF page:

<apex:page showHeader="false" sidebar="false">
<apex:includeLightning />
<div id="LcDisplayId"></div>
<script>
$Lightning.use("c:Covid19_IND_TrackerApp", function() {
$Lightning.createComponent("c:Covid19_IND_Tracker",
{
},
"LcDisplayId",
function(component) {
});
});
</script>
</apex:page>

References:

https://github.com/covid19india/covid19india-react

https://newsapi.org/

https://www.chartjs.org/

Thanks

Arun Kumar

Arun Kumar

Arun Kumar is a Salesforce Certified Platform Developer I with over 7+ years of experience working on the Salesforce platform. He specializes in developing custom applications, integrations, and reports to help customers streamline their business processes. Arun is passionate about helping businesses leverage the power of Salesforce to achieve their goals.

This Post Has 7 Comments

  1. ankit

    Hi, the data is not getting loaded on VF page , only table labels are coming.

      1. ankit

        Thanks for the response.

        Now I am getting Below error on VF page load:

        An internal server error has occurred
        Error ID: 34096863-58308 (-1249547377)

  2. ankit

    Do i need to create a static resource for Charts

  3. ankit

    This is the error i am getting on chart:
    This page has an error. You might just need to refresh it.
    Error in $A.getCallback() [Chart is not defined]
    Callback failed: apex://Covid19_IND_TrackerController/ACTION$fecthCovid19Data
    Failing descriptor: {markup://c:Covid19_Tracking_Chart}

      1. ankit

        Arun,

        This is not a file right,which JS i need to store in Static resoure.

        Kindly help.

Leave a Reply