├── _config.yml ├── .firebaserc ├── public ├── favicon.ico └── index.html ├── src ├── images │ └── image.png ├── components │ ├── CountryPicker │ │ ├── CountryPicker.module.css │ │ └── CountryPicker.jsx │ ├── Cards │ │ ├── Card │ │ │ ├── Card.module.css │ │ │ └── Card.jsx │ │ ├── Cards.module.css │ │ └── Cards.jsx │ ├── index.js │ └── Chart │ │ ├── Chart.module.css │ │ └── Chart.jsx ├── index.js ├── App.module.css ├── App.js └── api │ └── index.js ├── firebase.json ├── .gitignore ├── README.md ├── package.json ├── .firebase └── hosting.YnVpbGQ.cache └── .eslintrc.js /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "alanbinu-covid19-tracker" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanBinu007/React-Covid19-Tracker/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/images/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlanBinu007/React-Covid19-Tracker/HEAD/src/images/image.png -------------------------------------------------------------------------------- /src/components/CountryPicker/CountryPicker.module.css: -------------------------------------------------------------------------------- 1 | .formControl { 2 | width: 30%; 3 | margin-bottom: 30px !important; 4 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /src/components/Cards/Card/Card.module.css: -------------------------------------------------------------------------------- 1 | .card { 2 | margin: 0 2% !important; 3 | } 4 | @media only screen and (max-width: 770px) { 5 | .card { 6 | margin: 2% 0 !important; 7 | } 8 | } -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default as Chart } from './Chart/Chart'; 2 | export { default as CountryPicker } from './CountryPicker/CountryPicker'; 3 | export { default as Cards } from './Cards/Cards'; 4 | -------------------------------------------------------------------------------- /src/components/Chart/Chart.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | justify-content: center; 4 | width: 65%; 5 | } 6 | 7 | @media only screen and (max-width: 770px) { 8 | .container { 9 | width: 100%; 10 | } 11 | } -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Cards/Cards.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | margin: 50px 0; 6 | } 7 | 8 | .infected { 9 | border-bottom: 10px solid rgba(0, 0, 255, 0.5); 10 | } 11 | 12 | .recovered{ 13 | border-bottom: 10px solid rgba(0, 255, 0, 0.5); 14 | } 15 | 16 | .deaths{ 17 | border-bottom: 10px solid rgba(255, 0, 0, 0.5); 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/App.module.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: rgb(250, 250,250); 3 | } 4 | 5 | .container { 6 | display: flex; 7 | align-items: center; 8 | flex-direction: column; 9 | } 10 | 11 | .image { 12 | width: 370px; 13 | margin-top: 50px; 14 | } 15 | 16 | @media only screen and (max-width: 770px) { 17 | .container { 18 | margin: 0 10%; 19 | } 20 | 21 | .image { 22 | width: 100%; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Covid-19 Traker 2 | 3 | **Project Link** - ***https://alanbinu-covid19-tracker.web.app/*** 4 | 5 | ## Tech We Used 6 | 7 | - ReactJs 8 | - React Routing 9 | - Firebase Hosting 10 | - Firebase Storage 11 | - Basic HTML5 and CSS 12 | 13 | ## Features 14 | 15 | - Search by country 16 | - Sort options 17 | - Easy visualization 18 | - Neat and clean UI 19 | 20 | ## Steps to run in your machine 21 | 22 | #### Run the following commands 23 | ``` 24 | npm i 25 | npm run start 26 | ``` 27 | 28 | 29 | 30 | 31 | #### Hope you liked this project, dont forget to ⭐ the repo. 32 | -------------------------------------------------------------------------------- /src/components/CountryPicker/CountryPicker.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { NativeSelect, FormControl } from '@material-ui/core'; 3 | 4 | import { fetchCountries } from '../../api'; 5 | 6 | import styles from './CountryPicker.module.css'; 7 | 8 | const Countries = ({ handleCountryChange }) => { 9 | const [countries, setCountries] = useState([]); 10 | 11 | useEffect(() => { 12 | const fetchAPI = async () => { 13 | setCountries(await fetchCountries()); 14 | }; 15 | 16 | fetchAPI(); 17 | }, []); 18 | 19 | return ( 20 | 21 | handleCountryChange(e.target.value)}> 22 | 23 | {countries.map((country, i) => )} 24 | 25 | 26 | ); 27 | }; 28 | 29 | export default Countries; 30 | -------------------------------------------------------------------------------- /src/components/Cards/Card/Card.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Card, CardContent, Typography, Grid } from '@material-ui/core'; 3 | import CountUp from 'react-countup'; 4 | import cx from 'classnames'; 5 | 6 | import styles from './Card.module.css'; 7 | 8 | const CardComponent = ({ className, cardTitle, value, lastUpdate, cardSubtitle }) => ( 9 | 10 | 11 | 12 | {cardTitle} 13 | 14 | 15 | 16 | 17 | 18 | {new Date(lastUpdate).toDateString()} 19 | 20 | 21 | {cardSubtitle} 22 | 23 | 24 | 25 | ); 26 | 27 | export default CardComponent; 28 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { Cards, CountryPicker, Chart } from './components'; 4 | import { fetchData } from './api/'; 5 | import styles from './App.module.css'; 6 | 7 | import image from './images/image.png'; 8 | 9 | class App extends React.Component { 10 | state = { 11 | data: {}, 12 | country: '', 13 | } 14 | 15 | async componentDidMount() { 16 | const data = await fetchData(); 17 | 18 | this.setState({ data }); 19 | } 20 | 21 | handleCountryChange = async (country) => { 22 | const data = await fetchData(country); 23 | 24 | this.setState({ data, country: country }); 25 | } 26 | 27 | render() { 28 | const { data, country } = this.state; 29 | 30 | return ( 31 |
32 | COVID-19 33 | 34 | 35 | 36 |
37 | ); 38 | } 39 | } 40 | 41 | export default App; -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | 4 | // new api url 5 | 6 | const url = 'https://covid19.mathdro.id/api'; 7 | 8 | export const fetchData = async (country) => { 9 | let changeableUrl = url; 10 | 11 | if (country) { 12 | changeableUrl = `${url}/countries/${country}`; 13 | } 14 | 15 | try { 16 | const { data: { confirmed, recovered, deaths, lastUpdate } } = await axios.get(changeableUrl); 17 | 18 | return { confirmed, recovered, deaths, lastUpdate }; 19 | } catch (error) { 20 | return error; 21 | } 22 | }; 23 | 24 | export const fetchDailyData = async () => { 25 | try { 26 | const { data } = await axios.get('https://api.covidtracking.com/v1/us/daily.json'); 27 | 28 | return data.map(({ positive, recovered, death, dateChecked: date }) => ({ confirmed: positive, recovered, deaths: death, date })); 29 | } catch (error) { 30 | return error; 31 | } 32 | }; 33 | 34 | export const fetchCountries = async () => { 35 | try { 36 | const { data: { countries } } = await axios.get(`${url}/countries`); 37 | 38 | return countries.map((country) => country.name); 39 | } catch (error) { 40 | return error; 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "corona_app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@material-ui/core": "^4.9.7", 7 | "@testing-library/jest-dom": "^4.2.4", 8 | "@testing-library/react": "^9.5.0", 9 | "@testing-library/user-event": "^7.2.1", 10 | "axios": "^0.19.2", 11 | "chart.js": "^2.9.3", 12 | "classnames": "^2.2.6", 13 | "react": "^16.13.1", 14 | "react-chartjs-2": "^2.9.0", 15 | "react-countup": "^4.3.3", 16 | "react-dom": "^16.13.1", 17 | "react-scripts": "3.4.1" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": "react-app" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.2%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | }, 40 | "devDependencies": { 41 | "eslint": "^6.8.0", 42 | "eslint-config-airbnb": "^18.1.0", 43 | "eslint-plugin-import": "^2.20.2", 44 | "eslint-plugin-jsx-a11y": "^6.2.3", 45 | "eslint-plugin-react": "^7.19.0", 46 | "eslint-plugin-react-hooks": "^2.5.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/components/Cards/Cards.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Typography, Grid } from '@material-ui/core'; 3 | import CardComponent from './Card/Card'; 4 | import styles from './Cards.module.css'; 5 | 6 | const Info = ({ data: { confirmed, recovered, deaths, lastUpdate } }) => { 7 | if (!confirmed) { 8 | return 'Loading...'; 9 | } 10 | 11 | return ( 12 |
13 | Global 14 | 15 | 22 | 29 | 36 | 37 |
38 | ); 39 | }; 40 | 41 | export default Info; 42 | -------------------------------------------------------------------------------- /.firebase/hosting.YnVpbGQ.cache: -------------------------------------------------------------------------------- 1 | asset-manifest.json,1631344553719,1e5bc2a6e3b8e498ebb170d513ad42b248ad0518a6b067c94ec2313bac6e1b2f 2 | favicon.ico,1631343641691,65e87e033f11e13e46eed5b6fce9c2cb507aa6d6481c82a2168f50c5c654b2f8 3 | precache-manifest.7e7879c4dbf90b24d7ceaaaa24cad34e.js,1631344553719,7efd7139939e9b90ca766dd56a299a346f395fec11346d4f4d57c67fae9c60c3 4 | index.html,1631344553719,01209522b20398b0c29dc9f40d0fc0810f86dd10f9a27ee0a7902221695659a6 5 | service-worker.js,1631344553719,7b5c5205eea5976b0c83f76382cc4d9c8d6fc0f01e0936c9c377bb2d6360fba5 6 | static/js/2.ea59d149.chunk.js.LICENSE.txt,1631344553728,c859aeea5c8f5e9ac1f434469ea188ddb57eec02441a3d618d373bf705dcf549 7 | static/css/main.26cbd97d.chunk.css.map,1631344553728,0eedbef9f9bf31f18187f62e82d6e252ee023a3fd23a87c9c7feb8f823ae5d22 8 | static/css/main.26cbd97d.chunk.css,1631344553720,b6df2b3ca8d919ecf416847237f676171315e0b35f4a015fdf2dc1c84d4c94e5 9 | static/js/main.69f1b8a0.chunk.js,1631344553721,302e7d358b9cc1fabad89d25b494617f3a689bb60f8b17b0eb62efd6f6f801cf 10 | static/js/runtime-main.20a4ba51.js,1631344553728,11f11f2c7503af11635eaa157a24f60a4b4e2880d9d0d4f8a3270e54bf7ee157 11 | static/js/runtime-main.20a4ba51.js.map,1631344553728,fc879e4a0264dc83be83988d85df6ac59d07b55ef9de3e4de2e0623499d5f740 12 | static/js/main.69f1b8a0.chunk.js.map,1631344553728,4df4b1fb34afd0502b6922cfee453123052fefbef4d78045c20a1cc869cc9a90 13 | static/media/image.d7265326.png,1631344553720,9088a185740b8a9ea070bbb1d5a5572aa3a53c0a8fff7676afd57b297187df4b 14 | static/js/2.ea59d149.chunk.js,1631344553728,7e9d721115c6fd04dba79745498eada375b38ac239452ee906be03d6833a5686 15 | static/js/2.ea59d149.chunk.js.map,1631344553729,874ac5eaacaf6ca79b4c92952b1fd37560122cb046b9d48a05bab0cb6eea301e 16 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | }, 6 | extends: [ 7 | 'plugin:react/recommended', 8 | 'airbnb', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaFeatures: { 16 | jsx: true, 17 | }, 18 | ecmaVersion: 2018, 19 | sourceType: 'module', 20 | }, 21 | plugins: [ 22 | 'react', 23 | ], 24 | rules: { 25 | "import/prefer-default-export": 0, 26 | "max-len": [ 27 | 2, 28 | 250 29 | ], 30 | "no-multiple-empty-lines": [ 31 | "error", 32 | { 33 | "max": 1, 34 | "maxEOF": 1 35 | } 36 | ], 37 | "no-underscore-dangle": [ 38 | "error", 39 | { 40 | "allow": [ 41 | "_d", 42 | "_dh", 43 | "_h", 44 | "_id", 45 | "_m", 46 | "_n", 47 | "_t", 48 | "_text" 49 | ] 50 | } 51 | ], 52 | "object-curly-newline": 0, 53 | "react/prop-types": 0, 54 | "react/jsx-filename-extension": 0, 55 | "react/jsx-one-expression-per-line": 0, 56 | "jsx-a11y/click-events-have-key-events": 0, 57 | "jsx-a11y/alt-text": 0, 58 | "jsx-a11y/no-autofocus": 0, 59 | "jsx-a11y/no-static-element-interactions": 0, 60 | "react/no-array-index-key": 0, 61 | "jsx-a11y/anchor-is-valid": [ 62 | "error", 63 | { 64 | "components": [ 65 | "Link" 66 | ], 67 | "specialLink": [ 68 | "to", 69 | "hrefLeft", 70 | "hrefRight" 71 | ], 72 | "aspects": [ 73 | "noHref", 74 | "invalidHref", 75 | "preferButton" 76 | ] 77 | } 78 | ] 79 | } 80 | }; 81 | -------------------------------------------------------------------------------- /src/components/Chart/Chart.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { Line, Bar } from 'react-chartjs-2'; 3 | 4 | import { fetchDailyData } from '../../api'; 5 | 6 | import styles from './Chart.module.css'; 7 | 8 | const Chart = ({ data: { confirmed, recovered, deaths }, country }) => { 9 | const [dailyData, setDailyData] = useState({}); 10 | 11 | useEffect(() => { 12 | const fetchMyAPI = async () => { 13 | const initialDailyData = await fetchDailyData(); 14 | 15 | setDailyData(initialDailyData); 16 | }; 17 | 18 | fetchMyAPI(); 19 | }, []); 20 | 21 | const barChart = ( 22 | confirmed ? ( 23 | 39 | ) : null 40 | ); 41 | 42 | const lineChart = ( 43 | dailyData[0] ? ( 44 | new Date(date).toLocaleDateString()), 47 | datasets: [{ 48 | data: dailyData.map((data) => data.confirmed), 49 | label: 'Infected', 50 | borderColor: '#3333ff', 51 | fill: true, 52 | }, { 53 | data: dailyData.map((data) => data.deaths), 54 | label: 'Deaths', 55 | borderColor: 'red', 56 | backgroundColor: 'rgba(255, 0, 0, 0.5)', 57 | fill: true, 58 | }, { 59 | data: dailyData.map((data) => data.recovered), 60 | label: 'Recovered', 61 | borderColor: 'green', 62 | backgroundColor: 'rgba(0, 255, 0, 0.5)', 63 | fill: true, 64 | }, 65 | ], 66 | }} 67 | /> 68 | ) : null 69 | ); 70 | 71 | return ( 72 |
73 | {country ? barChart : lineChart} 74 |
75 | ); 76 | }; 77 | 78 | export default Chart; 79 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Corona Virus Tracker 10 | 11 | 12 | 13 |
14 | 72 | 73 | 74 | --------------------------------------------------------------------------------