├── .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 |
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 |
85 |
86 |
87 |
88 |
89 |
Live Cases by Country
90 |
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 |
--------------------------------------------------------------------------------