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

7 comments

  1. 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}

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s