diff --git a/package-lock.json b/package-lock.json
index c8489b4..6a3edaf 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5130,9 +5130,9 @@
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"eventemitter3": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
- "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg=="
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
+ "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ=="
},
"events": {
"version": "3.1.0",
@@ -6297,9 +6297,9 @@
"integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q="
},
"http-proxy": {
- "version": "1.18.0",
- "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz",
- "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==",
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
"requires": {
"eventemitter3": "^4.0.0",
"follow-redirects": "^1.0.0",
@@ -10990,6 +10990,18 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "react-redux": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz",
+ "integrity": "sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==",
+ "requires": {
+ "@babel/runtime": "^7.5.5",
+ "hoist-non-react-statics": "^3.3.0",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.7.2",
+ "react-is": "^16.9.0"
+ }
+ },
"react-router": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz",
@@ -11198,6 +11210,15 @@
"strip-indent": "^3.0.0"
}
},
+ "redux": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
+ "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "symbol-observable": "^1.2.0"
+ }
+ },
"regenerate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
@@ -11418,6 +11439,11 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
+ "reselect": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz",
+ "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA=="
+ },
"resolve": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz",
@@ -12659,6 +12685,11 @@
"util.promisify": "~1.0.0"
}
},
+ "symbol-observable": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ=="
+ },
"symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
diff --git a/package.json b/package.json
index 28d6450..ac57993 100644
--- a/package.json
+++ b/package.json
@@ -9,8 +9,11 @@
"novelcovid": "^1.2.7",
"react": "^16.13.1",
"react-dom": "^16.13.1",
+ "react-redux": "^7.2.0",
"react-router-dom": "^5.2.0",
- "react-scripts": "3.4.1"
+ "react-scripts": "3.4.1",
+ "redux": "^4.0.5",
+ "reselect": "^4.0.0"
},
"scripts": {
"start": "react-scripts start",
diff --git a/src/API/API.js b/src/API/API.js
index 2681fa3..9111625 100644
--- a/src/API/API.js
+++ b/src/API/API.js
@@ -1,95 +1,202 @@
-import { NovelCovid } from 'novelcovid';
+import { NovelCovid } from "novelcovid";
const track = new NovelCovid();
-const WORLD_POPULATION = 7_784_000_000; //Approximately the world population now. Should be constant for the sake of this progam.
+const WORLD_POPULATION = 7_784_000_000; //Approximately the world population now. Should be constant for the sake of this program.
export async function getCountryData(country) {
-
try {
-
const response = await track.countries(country); //unfortunately, has to fetch all countries even if one is needed to find the ranking
- console.log(response)
- if (response.message === "Country not found or doesn't have any cases") {
+ if (
+ response.message === "Country not found or doesn't have any cases"
+ ) {
throw response.message;
}
- let res = { //TEMP
+ let res = {
+ //TEMP
cases: response.cases,
recovered: response.recovered,
deaths: response.deaths,
- casepermil: response.casesPerOneMillion,
- testpermil: response.testsPerOneMillion,
+ casesPerOneMillion: response.casesPerOneMillion,
+ testsPerOneMillion: response.testsPerOneMillion,
tests: response.tests,
todayCases: response.todayCases,
- todayDeaths: response.todayDeaths
+ todayDeaths: response.todayDeaths,
};
- let restructuredResponse = { //TEMP, until rankings are added and the fetch request rewritten.
+ let restructuredResponse = {
+ //TEMP, until rankings are added and the fetch request rewritten.
covidinfo: {
- cases: { title: "Total Cases", number: res.cases, ranking: null, daily: (res.todayCases > 0 && "+") + res.todayCases },
- recovered: { title: "Recovered", number: res.recovered, ranking: null, daily: null },
- deaths: { title: "Died", number: res.deaths, ranking: null, daily: (res.todayDeaths > 0 && "+") + res.todayDeaths },
- casepermil: { title: "Cases per million", number: res.casepermil, ranking: null, daily: null},
- recoverate: {title: "Recovery rate", number: res.cases > 0 ? (100 * res.recovered / res.cases).toFixed(2) + '%' : null},
- mortality: { title: "Mortality", number: res.cases > 0 ? (100 * res.deaths / res.cases).toFixed(2) + '%' : "N/A", ranking: null, daily: null },
- tests: { title: "Tests", number: res.tests, ranking: null, daily: null },
- testpermil: {title: "Tests per million", number: res.testpermil, ranking: null, daily: null},
- testrate: { title: "Positive Tests", number: res.cases > 0 ? (100 * res.cases / res.tests).toFixed(2) + '%' : "N/A", ranking: null, daily: null }
+ cases: {
+ title: "Total Cases",
+ number: res.cases,
+ ranking: null,
+ daily: (res.todayCases > 0 && "+") + res.todayCases,
+ },
+ recovered: {
+ title: "Recovered",
+ number: res.recovered,
+ ranking: null,
+ daily: null,
+ },
+ deaths: {
+ title: "Died",
+ number: res.deaths,
+ ranking: null,
+ daily: (res.todayDeaths > 0 && "+") + res.todayDeaths,
+ },
+ casesPerOneMillion: {
+ title: "Cases per million",
+ number: res.casesPerOneMillion,
+ ranking: null,
+ daily: null,
+ },
+ recoverate: {
+ title: "Recovery rate",
+ number:
+ res.cases > 0
+ ? ((100 * res.recovered) / res.cases).toFixed(2) +
+ "%"
+ : null,
+ },
+ mortality: {
+ title: "Mortality",
+ number:
+ res.cases > 0
+ ? ((100 * res.deaths) / res.cases).toFixed(2) + "%"
+ : "N/A",
+ ranking: null,
+ daily: null,
+ },
+ tests: {
+ title: "Tests",
+ number: res.tests,
+ ranking: null,
+ daily: null,
+ },
+ testsPerOneMillion: {
+ title: "Tests per million",
+ number: res.testsPerOneMillion,
+ ranking: null,
+ daily: null,
+ },
+ testrate: {
+ title: "Positive Tests",
+ number:
+ res.cases > 0
+ ? ((100 * res.cases) / res.tests).toFixed(2) + "%"
+ : "N/A",
+ ranking: null,
+ daily: null,
+ },
},
country: response.country,
- countryIcon: response.countryInfo.flag
+ countryIcon: response.countryInfo.flag,
};
return restructuredResponse;
-
} catch (reason) {
-
console.log(`The reason is: ${reason}`);
return null;
}
-
-
-};
+}
export async function getEachCountryData(country) {
-
try {
-
const response = await track.countries();
- // console.log(`Sample response ${response}`);
- console.log(response);
let result = [];
for (let entry of response) {
if (entry.country.toLowerCase().startsWith(country.toLowerCase())) {
result.push({
covidinfo: {
- cases: { title: "Total Cases", number: entry.cases, ranking: null, daily: null },
- recovered: { title: "Recovered", number: entry.recovered, ranking: null, daily: null },
- deaths: { title: "Died", number: entry.deaths, ranking: null, daily: null },
- tests: { title: "Tests", number: entry.tests, ranking: null, daily: null }
+ cases: {
+ title: "Total Cases",
+ number: entry.cases,
+ ranking: null,
+ daily: null,
+ },
+ recovered: {
+ title: "Recovered",
+ number: entry.recovered,
+ ranking: null,
+ daily: null,
+ },
+ deaths: {
+ title: "Died",
+ number: entry.deaths,
+ ranking: null,
+ daily: null,
+ },
+ casesPerOneMillion: {
+ title: "Cases per million",
+ number: entry.casesPerOneMillion,
+ ranking: null,
+ daily: null,
+ },
+ recoverate: {
+ title: "Recovery rate",
+ number:
+ entry.cases > 0
+ ? (
+ (100 * entry.recovered) /
+ entry.cases
+ ).toFixed(2) + "%"
+ : null,
+ },
+ mortality: {
+ title: "Mortality",
+ number:
+ entry.cases > 0
+ ? (
+ (100 * entry.deaths) /
+ entry.cases
+ ).toFixed(2) + "%"
+ : "N/A",
+ ranking: null,
+ daily: null,
+ },
+ tests: {
+ title: "Tests",
+ number: entry.tests,
+ ranking: null,
+ daily: null,
+ },
+ testsPerOneMillion: {
+ title: "Tests per million",
+ number: entry.testsPerOneMillion,
+ ranking: null,
+ daily: null,
+ },
+ testrate: {
+ title: "Positive Tests",
+ number:
+ entry.cases > 0
+ ? (
+ (100 * entry.cases) /
+ entry.tests
+ ).toFixed(2) + "%"
+ : "N/A",
+ ranking: null,
+ daily: null,
+ },
},
country: entry.country,
- countryIcon: entry.countryInfo.flag
+ iso2: entry.countryInfo.iso2 || "",
+ iso3: entry.countryInfo.iso3 || "",
+ countryIcon: entry.countryInfo.flag,
});
}
}
- console.log(result)
if (result.length === 0) {
throw new Error("No match");
}
return result;
-
} catch (reason) {
-
console.log(`The reason is: ${reason}`);
return null;
}
-};
-
+}
export async function getGlobalData() {
-
try {
-
const response = await track.countries();
- console.log(response);
let res = response.reduce((x, y) => ({
cases: x.cases + y.cases,
recovered: x.recovered + y.recovered,
@@ -98,29 +205,90 @@ export async function getGlobalData() {
testsPerOneMillion: x.testsPerOneMillion + y.testsPerOneMillion,
tests: x.tests + y.tests,
todayCases: x.todayCases + y.todayCases,
- todayDeaths: x.todayDeaths + y.todayDeaths
- }))
- console.log(res);
+ todayDeaths: x.todayDeaths + y.todayDeaths,
+ }));
let restructuredResponse = {
covidinfo: {
- cases: { title: "Total Cases", number: res.cases, ranking: null, daily: (res.todayCases > 0 && "+") + res.todayCases },
- recovered: { title: "Recovered", number: res.recovered, ranking: null, daily: null },
- deaths: { title: "Died", number: res.deaths, ranking: null, daily: (res.todayDeaths > 0 && "+") + res.todayDeaths },
- casepermil: { title: "Cases per million", number: (100_000_0 * res.cases / WORLD_POPULATION).toFixed(0), ranking: null, daily: null},
- recoverate: { title: "Recovery rate", number: res.cases > 0 ? (100 * res.recovered / res.cases).toFixed(2) + '%' : null},
- mortality: { title: "Mortality", number: res.cases > 0 ? (100 * res.deaths / res.cases).toFixed(2) + '%' : "N/A", ranking: null, daily: null },
- tests: { title: "Tests", number: res.tests, ranking: null, daily: null },
- testpermil: { title: "Tests per million", number: (100_000_0 * res.tests / WORLD_POPULATION).toFixed(0), ranking: null, daily: null},
- testrate: { title: "Positive Tests", number: res.cases > 0 ? (100 * res.cases / res.tests).toFixed(2) + '%' : "N/A", ranking: null, daily: null }
- }
+ cases: {
+ title: "Total Cases",
+ number: res.cases,
+ ranking: null,
+ daily: (res.todayCases > 0 && "+") + res.todayCases,
+ },
+ recovered: {
+ title: "Recovered",
+ number: res.recovered,
+ ranking: null,
+ daily: null,
+ },
+ deaths: {
+ title: "Died",
+ number: res.deaths,
+ ranking: null,
+ daily: (res.todayDeaths > 0 && "+") + res.todayDeaths,
+ },
+ casesPerOneMillion: {
+ title: "Cases per million",
+ number: (
+ (100_000_0 * res.cases) /
+ WORLD_POPULATION
+ ).toFixed(0),
+ ranking: null,
+ daily: null,
+ },
+ recoverate: {
+ title: "Recovery rate",
+ number:
+ res.cases > 0
+ ? ((100 * res.recovered) / res.cases).toFixed(2) +
+ "%"
+ : null,
+ },
+ mortality: {
+ title: "Mortality",
+ number:
+ res.cases > 0
+ ? ((100 * res.deaths) / res.cases).toFixed(2) + "%"
+ : "N/A",
+ ranking: null,
+ daily: null,
+ },
+ tests: {
+ title: "Tests",
+ number: res.tests,
+ ranking: null,
+ daily: null,
+ },
+ testsPerOneMillion: {
+ title: "Tests per million",
+ number: (
+ (100_000_0 * res.tests) /
+ WORLD_POPULATION
+ ).toFixed(0),
+ ranking: null,
+ daily: null,
+ },
+ testrate: {
+ title: "Positive Tests",
+ number:
+ res.cases > 0
+ ? ((100 * res.cases) / res.tests).toFixed(2) + "%"
+ : "N/A",
+ ranking: null,
+ daily: null,
+ },
+ },
};
return restructuredResponse;
-
} catch (reason) {
-
console.log(`The reason is: ${reason}`);
return null;
}
+}
-
-};
\ No newline at end of file
+export async function getEachCountryAndGlobal(country) {
+ return {
+ countries: await getEachCountryData(country),
+ global: await getGlobalData(),
+ };
+}
diff --git a/src/App.js b/src/App.js
index f91655a..ae7d3f9 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,91 +1,61 @@
-import React from 'react';
-import { Body, Search, Navbar, Ranking } from './components';
-import { getGlobalData, getEachCountryData } from './API/API.js';
-import { Switch, Route } from 'react-router-dom';
-import './App.css';
-
+import React from "react";
+import { Body, Search, Navbar, Ranking } from "./components";
+import { getEachCountryAndGlobal } from "./API/API.js";
+import { Switch, Route, BrowserRouter } from "react-router-dom";
+import { Provider } from "react-redux";
+import "./App.css";
+import { createStore, combineReducers } from "redux";
+import { countryReducer, inputReducer, navigationReducer } from "./reducers";
+import { updateCountryData } from "./actions";
+
+const store = createStore(
+ combineReducers({
+ data: countryReducer,
+ input: inputReducer,
+ nav: navigationReducer,
+ })
+);
+//TODO handle history through react-redux-router, async data directly through redux.
class App extends React.Component {
- constructor() {
- super();
- this.state = {
- data: null,
- rating_data: null,
- path_changed: false
- }
- }
-
- async componentDidMount() {
-
- console.log("Awaited");
- this.setState({
- rating_data: await getEachCountryData(""), //displays all countries by default
- data: await getGlobalData() //displays global data by default
- });
- }
-
- handleChangeCountry = async (info) => {
-
- if (window.location.pathname === "/") {
- this.setState({
- data: await info
- });
- } else {
- this.setState({
- rating_data: await info
- })
+ async componentDidMount() {
+ let response = await getEachCountryAndGlobal(""); //fetches all countries by default
+ store.dispatch(updateCountryData(response));
}
- console.log("The state has been reset");
- }
- handleStateFlush = async (addr) => {
-
- if (addr === "/") {
- this.setState({
- data: await getGlobalData()
- });
- } else {
- this.setState({
- rating_data: await getEachCountryData("")
- });
+ render() {
+ //Notice the components wrapped in routes. This is one way to pass a common history to each component
+ return (
+ Covid-19 Tracker
+