├── .vscode └── settings.json ├── charts.js ├── homework.md ├── index.html ├── map-style.js ├── mobile.css ├── new-style.css ├── plan.md ├── readme.md ├── script.js └── style.css /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5504 3 | } -------------------------------------------------------------------------------- /charts.js: -------------------------------------------------------------------------------- 1 | let chart; 2 | 3 | const buildChartData = (data, casesType) => { 4 | let chartData = []; 5 | let lastDataPoint; 6 | for(let date in data.cases){ 7 | if(lastDataPoint){ 8 | let newDataPoint = { 9 | x: date, 10 | y: data[casesType][date] - lastDataPoint 11 | } 12 | chartData.push(newDataPoint); 13 | } 14 | lastDataPoint = data[casesType][date]; 15 | } 16 | return chartData; 17 | } 18 | 19 | const updateData = (data, borderColor, backgroundColor) => { 20 | chart.data.datasets[0].data = data; 21 | chart.data.datasets[0].borderColor = borderColor; 22 | chart.data.datasets[0].backgroundColor = backgroundColor; 23 | chart.update({ 24 | duration: 800, 25 | easing: 'easeInOutCubic' 26 | }); 27 | } 28 | 29 | const buildChart = (chartData) => { 30 | var timeFormat = 'MM/DD/YY'; 31 | var ctx = document.getElementById('myChart').getContext('2d'); 32 | chart = new Chart(ctx, { 33 | // The type of chart we want to create 34 | type: 'line', 35 | 36 | // The data for our dataset 37 | data: { 38 | datasets: [{ 39 | backgroundColor: 'rgba(204, 16, 52, 0.5)', 40 | borderColor: '#CC1034', 41 | data: chartData 42 | }] 43 | }, 44 | 45 | // Configuration options go here 46 | options: { 47 | legend: { 48 | display: false 49 | }, 50 | elements: { 51 | point:{ 52 | radius: 0 53 | } 54 | }, 55 | maintainAspectRatio: false, 56 | tooltips: { 57 | mode: 'index', 58 | intersect: false, 59 | callbacks: { 60 | label: function(tooltipItem, data) { 61 | return numeral(tooltipItem.value).format('+0,0'); 62 | } 63 | } 64 | }, 65 | scales: { 66 | xAxes: [{ 67 | type: "time", 68 | time: { 69 | format: timeFormat, 70 | tooltipFormat: 'll' 71 | } 72 | }], 73 | yAxes: [{ 74 | gridLines: { 75 | display:false 76 | }, 77 | ticks: { 78 | // Include a dollar sign in the ticks 79 | callback: function(value, index, values) { 80 | return numeral(value).format('0a'); 81 | } 82 | } 83 | }] 84 | } 85 | } 86 | }); 87 | } -------------------------------------------------------------------------------- /homework.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # June 20 Homework 4 | 5 | - Add a pie chart to the Covid Tracker at the bottom of the site 6 | - Make the card clickable so the map reflects the data selected 7 | 8 | # June 27 Homework 9 | 10 | - Make sure you are updated the functinoality of the app 11 | - Add a hover state to the Tabs 12 | - Add an active state to the Tabs 13 | - Change the line graph that will show call cases, recovered, and deaths 14 | - Add a table on the side panel show the numbers of the countries 15 | 16 | # July 8 Homework 17 | 18 | - Add the Active State for the Tabs based on the Mockup 19 | - Modify the Map to be in a Container as in the Mockup 20 | - Sort the countries Data 21 | - Make sure you are updated with the app 22 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | COVID Tracker 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 |

COVID-19 Tracker

31 |
32 |
33 | 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
Coronavirus Cases
48 |

1,436,886

49 |
1.69M Total
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
Recovered
58 |

302,964

59 |
1.69M Total
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
Deaths
68 |

82,191

69 |
1.69M Total
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |

Coronavirus Cases

79 | Last Updated April 09, 2020 80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |

Live Cases by Country

90 |
91 | 92 | 93 | 94 | 95 |
96 |
97 |
98 |
99 |

Worldwide New Cases

100 |
101 | 102 |
103 |
104 |
105 |
106 |
107 |
108 | 111 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /map-style.js: -------------------------------------------------------------------------------- 1 | var mapStyle = [ 2 | { 3 | "elementType": "geometry", 4 | "stylers": [ 5 | { 6 | "color": "#B2A4A4" 7 | } 8 | ] 9 | }, 10 | { 11 | "elementType": "labels.icon", 12 | "stylers": [ 13 | { 14 | "visibility": "off" 15 | } 16 | ] 17 | }, 18 | { 19 | "elementType": "labels.text.fill", 20 | "stylers": [ 21 | { 22 | "color": "#616161" 23 | } 24 | ] 25 | }, 26 | { 27 | "elementType": "labels.text.stroke", 28 | "stylers": [ 29 | { 30 | "color": "#f5f5f5" 31 | } 32 | ] 33 | }, 34 | { 35 | "featureType": "administrative.land_parcel", 36 | "elementType": "labels.text.fill", 37 | "stylers": [ 38 | { 39 | "color": "#bdbdbd" 40 | } 41 | ] 42 | }, 43 | { 44 | "featureType": "poi", 45 | "elementType": "geometry", 46 | "stylers": [ 47 | { 48 | "color": "#eeeeee" 49 | } 50 | ] 51 | }, 52 | { 53 | "featureType": "poi", 54 | "elementType": "labels.text.fill", 55 | "stylers": [ 56 | { 57 | "color": "#757575" 58 | } 59 | ] 60 | }, 61 | { 62 | "featureType": "poi.park", 63 | "elementType": "geometry", 64 | "stylers": [ 65 | { 66 | "color": "#e5e5e5" 67 | } 68 | ] 69 | }, 70 | { 71 | "featureType": "poi.park", 72 | "elementType": "labels.text.fill", 73 | "stylers": [ 74 | { 75 | "color": "#9e9e9e" 76 | } 77 | ] 78 | }, 79 | { 80 | "featureType": "road", 81 | "stylers": [ 82 | { 83 | "visibility": "off" 84 | } 85 | ] 86 | }, 87 | { 88 | "featureType": "road", 89 | "elementType": "geometry", 90 | "stylers": [ 91 | { 92 | "color": "#ffffff" 93 | } 94 | ] 95 | }, 96 | { 97 | "featureType": "road.arterial", 98 | "elementType": "labels.text.fill", 99 | "stylers": [ 100 | { 101 | "color": "#757575" 102 | } 103 | ] 104 | }, 105 | { 106 | "featureType": "road.highway", 107 | "elementType": "geometry", 108 | "stylers": [ 109 | { 110 | "color": "#dadada" 111 | } 112 | ] 113 | }, 114 | { 115 | "featureType": "road.highway", 116 | "elementType": "labels.text.fill", 117 | "stylers": [ 118 | { 119 | "color": "#616161" 120 | } 121 | ] 122 | }, 123 | { 124 | "featureType": "road.local", 125 | "elementType": "labels.text.fill", 126 | "stylers": [ 127 | { 128 | "color": "#9e9e9e" 129 | } 130 | ] 131 | }, 132 | { 133 | "featureType": "transit.line", 134 | "elementType": "geometry", 135 | "stylers": [ 136 | { 137 | "color": "#e5e5e5" 138 | } 139 | ] 140 | }, 141 | { 142 | "featureType": "transit.station", 143 | "elementType": "geometry", 144 | "stylers": [ 145 | { 146 | "color": "#eeeeee" 147 | } 148 | ] 149 | }, 150 | { 151 | "featureType": "water", 152 | "elementType": "geometry", 153 | "stylers": [ 154 | { 155 | "color": "#F3F2F8" 156 | } 157 | ] 158 | }, 159 | { 160 | "featureType": "water", 161 | "elementType": "labels.text.fill", 162 | "stylers": [ 163 | { 164 | "color": "#9e9e9e" 165 | } 166 | ] 167 | } 168 | ] -------------------------------------------------------------------------------- /mobile.css: -------------------------------------------------------------------------------- 1 | @media (max-width: 992px) { 2 | .side-panel-container { 3 | border-radius: 30px; 4 | } 5 | } 6 | 7 | @media (min-width: 1370px) { 8 | .side-panel-container { 9 | border-top-right-radius: 12px; 10 | border-bottom-right-radius: 12px; 11 | } 12 | } -------------------------------------------------------------------------------- /new-style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #F5F6FA; 3 | } 4 | 5 | .main { 6 | min-height: 900px; 7 | max-width: 1370px; 8 | } 9 | 10 | .search-container { 11 | display: flex; 12 | align-items: center; 13 | } 14 | 15 | .map-container { 16 | background-color: white; 17 | border-radius: 16px; 18 | padding: 1rem; 19 | border: 1px solid rgba(0,0,0,.05); 20 | box-shadow: 0 0 8px -5px rgba(0,0,0, 0.5); 21 | } 22 | 23 | .map-header { 24 | display: flex; 25 | justify-content: space-between; 26 | align-items: center; 27 | margin-bottom: 1rem; 28 | color: #6A5D5D; 29 | } 30 | 31 | .map-header h4 { 32 | font-weight: 400; 33 | margin-bottom: 0; 34 | } 35 | 36 | #map { 37 | border-radius: 12px; 38 | overflow: hidden; 39 | } 40 | 41 | .chart-container { 42 | margin-left: 0; 43 | margin-right: 0; 44 | } 45 | 46 | .chart-container .col { 47 | background-color: #EFF2F6; 48 | border-radius: 12px; 49 | } 50 | 51 | .linear-chart { 52 | height: 100%; 53 | } 54 | 55 | .title-container h2 { 56 | color: #fc3c3c; 57 | margin-bottom: 0; 58 | } 59 | 60 | .title-container h4 { 61 | text-transform: uppercase; 62 | color: #CACBCF; 63 | } 64 | 65 | .stats-container .card { 66 | overflow: hidden; 67 | cursor: pointer; 68 | background-color: white; 69 | border-radius: 12px; 70 | box-shadow: 0 0 8px -4px rgba(0,0,0, 0.5); 71 | } 72 | 73 | .stats-container .card-body { 74 | padding: 1rem; 75 | } 76 | 77 | .stats-container .card-title { 78 | color: #695d5c; 79 | font-weight: 400; 80 | } 81 | 82 | .stats-container .card-subtitle { 83 | font-weight: 600; 84 | } 85 | 86 | .stats-container .total { 87 | font-size: 0.8rem; 88 | } 89 | 90 | .side-panel-container { 91 | border-bottom-left-radius: 30px; 92 | border-top-left-radius: 30px; 93 | background-color: white; 94 | box-shadow: 0 0 8px -4px rgba(0,0,0, 0.5); 95 | padding-top: 15px; 96 | } 97 | 98 | .table-cases-number { 99 | text-align: right; 100 | font-weight: 500; 101 | } 102 | 103 | .total-number { 104 | color: #cc1034; 105 | } 106 | 107 | .recovered-number { 108 | color: #7fd922; 109 | } 110 | 111 | .deaths-number { 112 | color: #FA5575; 113 | } 114 | 115 | .cases-table-container h4, .chart-container h4 { 116 | color: #6A5D5D; 117 | font-weight: 400; 118 | } 119 | 120 | .country-data .table { 121 | color: #6A5D5D; 122 | } 123 | 124 | .country-data .table td { 125 | padding: 0.5rem; 126 | border: none; 127 | } 128 | 129 | .country-data .table-striped tbody tr:nth-of-type(odd) { 130 | background-color: #F3F2F8; 131 | } 132 | 133 | .tab-selection { 134 | position: absolute; 135 | top: 0; 136 | left: 0; 137 | right: 0; 138 | height: 8px; 139 | display: none; 140 | } 141 | 142 | .tab-selection.selected { 143 | display: block !important; 144 | } 145 | 146 | .cases .tab-selection { 147 | background-color: #CC1034; 148 | } 149 | 150 | .recovered .tab-selection { 151 | background-color: #7fd922; 152 | } 153 | 154 | .deaths .tab-selection { 155 | background-color: #FA5575; 156 | } 157 | 158 | -------------------------------------------------------------------------------- /plan.md: -------------------------------------------------------------------------------- 1 | # July 8 Plan Of Action 2 | 3 | - Modify COVID Title DONE 4 | - Modify the colors of the application TO DO 5 | - Modify Side Panel DONE 6 | - Add Side Panel to Load Data by Country DONE 7 | - Add the Active State on Click TO DO 8 | - Move the Graph into the Sidebar DONE 9 | - Load real numbers from CORONA API DONE 10 | 11 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # COVID Tracker 2 | 3 | - Modify wireframe DONE 4 | - Get Country data 5 | - Show country data on the map 6 | - Show country data in a table -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | const startUIDropdown = (data) => { 2 | $('.ui.dropdown').dropdown({ 3 | values: data, 4 | onChange: function(value, text){ 5 | changeCountrySelection(value); 6 | } 7 | }); 8 | } 9 | 10 | window.onload = () => { 11 | initMap(); 12 | getCountriesData(); 13 | getHistoricalData(); 14 | getWorldCoronaData(); 15 | setHoverState(); 16 | } 17 | 18 | 19 | var map; 20 | var infoWindow; 21 | let coronaGlobalData; 22 | let coronaHystoricalData; 23 | let mapCircles = []; 24 | let countrySelection = 'worldwide'; 25 | let mapCenter = {lat: 34.80746, lng: -40.4796} 26 | let worldwideSelect = { 27 | name: "Worldwide", 28 | value: "worldwide", 29 | selected: true 30 | } 31 | var casesTypeColors = { 32 | cases: { 33 | hex: '#CC1034', 34 | rgb: 'rgb(204, 16, 52)', 35 | half_op: 'rgba(204, 16, 52, 0.5)', 36 | multiplier: 800 37 | }, 38 | recovered: { 39 | hex: '#7dd71d', 40 | rgb: 'rgb(125, 215, 29)', 41 | half_op: 'rgba(125, 215, 29, 0.5)', 42 | multiplier: 1200 43 | }, 44 | deaths: { 45 | hex: '#fb4443', 46 | rgb: 'rgb(251, 68, 67)', 47 | half_op: 'rgba(251, 68, 67, 0.5)', 48 | multiplier: 2000 49 | } 50 | } 51 | 52 | function initMap() { 53 | map = L.map('map', { 54 | center: [mapCenter.lat, mapCenter.lng], 55 | zoom: 3 56 | }); 57 | 58 | L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { 59 | attribution: '© OpenStreetMap contributors' 60 | }).addTo(map); 61 | } 62 | 63 | const changeCountrySelection = (countryCode) => { 64 | if(countryCode !== countrySelection){ 65 | if(countryCode == worldwideSelect.value){ 66 | getWorldCoronaData(countryCode); 67 | } else { 68 | getCountryCoronaData(countryCode); 69 | } 70 | countrySelection = countryCode; 71 | } 72 | } 73 | 74 | const changeDataSelection = (casesType) => { 75 | setSelectedTab(casesType); 76 | changeMapTitle(casesType); 77 | clearTheMap(); 78 | showDataOnMap(coronaGlobalData, casesType); 79 | let chartData = buildChartData(coronaHystoricalData, casesType); 80 | updateData(chartData, casesTypeColors[casesType].rgb, casesTypeColors[casesType].half_op); 81 | } 82 | 83 | const changeMapTitle = (casesType) => { 84 | let casesText = casesType.charAt(0).toUpperCase() + casesType.slice(1) 85 | document.querySelector('.map-header h4').textContent = `Coronavirus ${casesText}`; 86 | document.querySelector('.chart-container .cases-type').textContent = casesText; 87 | } 88 | 89 | const setHoverState = () => { 90 | $('.card').hover(function() { 91 | $(this).find('.tab-selection').not('.selected').fadeIn(200); 92 | }, function(){ 93 | $(this).find('.tab-selection').not('.selected').fadeOut(200); 94 | }) 95 | } 96 | 97 | const updateDate = (dateTimestamp) => { 98 | let date = moment(dateTimestamp).format("[Last Updated] MMMM DD, YYYY"); 99 | document.querySelector('.map-header .date').textContent = date; 100 | } 101 | 102 | const clearTheMap = () => { 103 | for(let circle of mapCircles){ 104 | map.removeLayer(circle); 105 | } 106 | } 107 | 108 | const setSelectedTab = (casesType) => { 109 | const tabs = document.querySelectorAll('.tab-selection'); 110 | for(let tab of tabs){ 111 | tab.classList.remove('selected'); 112 | tab.style.display = 'none'; 113 | } 114 | const activeTab = document.querySelector(`.${casesType} .tab-selection`); 115 | activeTab.classList.add('selected'); 116 | } 117 | 118 | const setMapCountryCenter = (lat, long, zoom) => { 119 | console.log("setView"); 120 | map.setView([lat, long], zoom); 121 | } 122 | 123 | const getCountriesData = () => { 124 | fetch("https://disease.sh/v3/covid-19/countries") 125 | .then((response)=>{ 126 | return response.json() 127 | }).then((data)=>{ 128 | coronaGlobalData = data; 129 | setCountrySelection(data); 130 | showDataOnMap(data); 131 | let sortedData = sortData(data); 132 | showDataInTable(sortedData); 133 | }) 134 | } 135 | 136 | const sortData = (data) => { 137 | let sortedData = [...data] 138 | sortedData.sort((a, b)=>{ 139 | if(a.cases > b.cases){ 140 | return -1; 141 | } else { 142 | return 1 143 | } 144 | }) 145 | return sortedData 146 | } 147 | 148 | const setCountrySelection = (data) => { 149 | let countries = []; 150 | countries.push(worldwideSelect) 151 | data.forEach((country)=>{ 152 | countries.push({ 153 | name: country.country, 154 | value: country.countryInfo.iso2 155 | }) 156 | }) 157 | startUIDropdown(countries); 158 | } 159 | 160 | const getCountryCoronaData = (countryCode) => { 161 | const url = `https://disease.sh/v3/covid-19/countries/${countryCode}`; 162 | fetch(url) 163 | .then((response)=>{ 164 | return response.json(); 165 | }).then((data)=>{ 166 | updateDate(data.updated); 167 | setStatsData(data); 168 | setMapCountryCenter(data.countryInfo.lat, data.countryInfo.long, 4); 169 | }) 170 | } 171 | 172 | const getWorldCoronaData = () => { 173 | fetch("https://disease.sh/v3/covid-19/all") 174 | .then((response)=>{ 175 | return response.json() 176 | }).then((data)=>{ 177 | updateDate(data.updated); 178 | setStatsData(data); 179 | setMapCountryCenter(mapCenter.lat, mapCenter.lng, 3); 180 | }) 181 | } 182 | 183 | const setStatsData = (data) => { 184 | let addedCases = numeral(data.todayCases).format('+0,0'); 185 | let addedRecovered = numeral(data.todayRecovered).format('+0,0'); 186 | let addedDeaths = numeral(data.todayDeaths).format('+0,0'); 187 | let totalCases = numeral(data.cases).format('0.0a'); 188 | let totalRecovered = numeral(data.recovered).format('0.0a'); 189 | let totalDeaths = numeral(data.deaths).format('0.0a'); 190 | document.querySelector('.total-number').innerHTML = addedCases; 191 | document.querySelector('.recovered-number').innerHTML = addedRecovered; 192 | document.querySelector('.deaths-number').innerHTML = addedDeaths; 193 | document.querySelector('.cases-total').innerHTML = `${totalCases} Total`; 194 | document.querySelector('.recovered-total').innerHTML = `${totalRecovered} Total`; 195 | document.querySelector('.deaths-total').innerHTML = `${totalDeaths} Total`; 196 | } 197 | 198 | const getHistoricalData = () => { 199 | fetch("https://disease.sh/v3/covid-19/historical/all?lastdays=120") 200 | .then((response)=>{ 201 | return response.json() 202 | }).then((data)=>{ 203 | coronaHystoricalData = data; 204 | let chartData = buildChartData(data, 'cases'); 205 | buildChart(chartData); 206 | }) 207 | } 208 | 209 | const openInfoWindow = () => { 210 | infoWindow.open(map); 211 | } 212 | 213 | const showDataOnMap = (data, casesType="cases") => { 214 | 215 | data.map((country)=>{ 216 | let countryCenter = { 217 | lat: country.countryInfo.lat, 218 | lng: country.countryInfo.long 219 | } 220 | 221 | var circle = L.circle([countryCenter.lat, countryCenter.lng], { 222 | color: casesTypeColors[casesType].hex, 223 | fillColor: casesTypeColors[casesType].hex, 224 | fillOpacity: 0.4, 225 | radius: Math.sqrt(country[casesType])*casesTypeColors[casesType].multiplier 226 | }).addTo(map); 227 | 228 | var html = ` 229 |
230 |
231 |
232 |
233 | ${country.country} 234 |
235 |
236 | Cases: ${numeral(country.cases).format('0,0')} 237 |
238 |
239 | Recovered: ${numeral(country.recovered).format('0,0')} 240 |
241 |
242 | Deaths: ${numeral(country.deaths).format('0,0')} 243 |
244 |
245 | ` 246 | 247 | circle.bindPopup(html); 248 | mapCircles.push(circle); 249 | }) 250 | 251 | } 252 | 253 | const showDataInTable = (data) => { 254 | var html = ''; 255 | data.forEach((country)=>{ 256 | html += ` 257 | 258 | ${country.country} 259 | ${numeral(country.cases).format('0,0')} 260 | 261 | ` 262 | }) 263 | document.getElementById('table-data').innerHTML = html; 264 | } 265 | 266 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | #nav { 6 | height: 50px; 7 | } 8 | 9 | #map { 10 | width: 100%; 11 | height: 500px; 12 | } 13 | 14 | .chart-data { 15 | width: 30%; 16 | margin-left: 30px; 17 | margin-top: 20px; 18 | } 19 | 20 | .map-info { 21 | background-color: bisque; 22 | height: 400px; 23 | } 24 | 25 | .data-info { 26 | height: 700px; 27 | display: flex; 28 | justify-content: center; 29 | } 30 | 31 | .country-data { 32 | margin-top: 20px; 33 | overflow: scroll; 34 | height: 400px; 35 | } 36 | 37 | .info-flag img { 38 | width: 100px; 39 | border-radius: 5px; 40 | } 41 | 42 | .info-name { 43 | font-size: 20px; 44 | font-weight: bold; 45 | color: #555; 46 | } 47 | 48 | .info-container { 49 | width: 150px; 50 | } 51 | 52 | .info-flag { 53 | height: 80px; 54 | width: 100%; 55 | background-size: cover; 56 | border-radius: 8px; 57 | } 58 | 59 | .info-confirmed, .info-recovered, .info-deaths { 60 | font-size: 16px; 61 | margin-top: 5px; 62 | } 63 | 64 | --------------------------------------------------------------------------------