state.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. import axios from 'axios';
  2. import {format, parse} from 'date-fns';
  3. import React, {useEffect, useRef, useState} from 'react';
  4. import {Link, useParams} from 'react-router-dom';
  5. import * as Icon from 'react-feather';
  6. import {
  7. formatDateAbsolute,
  8. formatNumber,
  9. parseStateTimeseries,
  10. } from '../utils/common-functions';
  11. import {MAP_META, STATE_CODES} from '../constants';
  12. import Clusters from './clusters';
  13. import DeltaBarGraph from './deltabargraph';
  14. import Level from './level';
  15. import MapExplorer from './mapexplorer';
  16. import Minigraph from './minigraph';
  17. import TimeSeries from './timeseries';
  18. import Footer from './footer';
  19. function State(props) {
  20. const mapRef = useRef();
  21. const tsRef = useRef();
  22. const {stateCode} = useParams();
  23. const [fetched, setFetched] = useState(false);
  24. const [timeseries, setTimeseries] = useState({});
  25. const [graphOption, setGraphOption] = useState(1);
  26. const [timeseriesMode, setTimeseriesMode] = useState(true);
  27. const [timeseriesLogMode, setTimeseriesLogMode] = useState(false);
  28. const [stateData, setStateData] = useState({});
  29. const [testData, setTestData] = useState({});
  30. const [sources, setSources] = useState({});
  31. const [districtData, setDistrictData] = useState({});
  32. const [stateName] = useState(STATE_CODES[stateCode]);
  33. useEffect(() => {
  34. if (fetched === false) {
  35. getState(stateCode);
  36. }
  37. }, [fetched, stateCode]);
  38. const getState = async (code) => {
  39. try {
  40. const [
  41. {data: dataResponse},
  42. {data: stateDistrictWiseResponse},
  43. {data: statesDailyResponse},
  44. {data: stateTestResponse},
  45. {data: sourcesResponse},
  46. ] = await Promise.all([
  47. axios.get('https://api.covid19india.org/data.json'),
  48. axios.get('https://api.covid19india.org/state_district_wise.json'),
  49. axios.get('https://api.covid19india.org/states_daily.json'),
  50. axios.get('https://api.covid19india.org/state_test_data.json'),
  51. axios.get('https://api.covid19india.org/sources_list.json'),
  52. ]);
  53. const states = dataResponse.statewise;
  54. setStateData(states.find((s) => s.statecode === code));
  55. const ts = parseStateTimeseries(statesDailyResponse)[code];
  56. setTimeseries(ts);
  57. const statesTests = stateTestResponse.states_tested_data;
  58. const name = STATE_CODES[code];
  59. setTestData(
  60. statesTests.filter(
  61. (obj) => obj.state === name && obj.totaltested !== ''
  62. )
  63. );
  64. setDistrictData({
  65. [name]: stateDistrictWiseResponse[name],
  66. });
  67. const sourceList = sourcesResponse.sources_list;
  68. setSources(sourceList.filter((state) => state.statecode === code));
  69. setFetched(true);
  70. } catch (err) {
  71. console.log(err);
  72. }
  73. };
  74. const testObjLast = testData[testData.length - 1];
  75. return (
  76. <React.Fragment>
  77. <div className="State">
  78. <div className="state-left">
  79. <div className="breadcrumb fadeInUp">
  80. <Link to="/">Home</Link>/
  81. <Link to={`${stateCode}`}>{stateName}</Link>
  82. </div>
  83. <div className="header">
  84. <div
  85. className="header-left fadeInUp"
  86. style={{animationDelay: '0.3s'}}
  87. >
  88. <h1>{stateName}</h1>
  89. <h5>
  90. Last Updated on{' '}
  91. {Object.keys(stateData).length
  92. ? formatDateAbsolute(stateData.lastupdatedtime)
  93. : ''}
  94. </h5>
  95. </div>
  96. <div
  97. className="header-right fadeInUp"
  98. style={{animationDelay: '0.5s'}}
  99. >
  100. <h5>Tested</h5>
  101. <h2>{formatNumber(testObjLast?.totaltested)}</h2>
  102. <h5 className="timestamp">
  103. {!isNaN(parse(testObjLast?.updatedon, 'dd/MM/yyyy', new Date()))
  104. ? `As of ${format(
  105. parse(testObjLast?.updatedon, 'dd/MM/yyyy', new Date()),
  106. 'dd MMM'
  107. )}`
  108. : ''}
  109. </h5>
  110. <h5>
  111. {'per '}
  112. {testObjLast?.totaltested && (
  113. <a href={testObjLast.source} target="_noblank">
  114. source
  115. </a>
  116. )}
  117. </h5>
  118. </div>
  119. </div>
  120. {fetched && <Level data={stateData} />}
  121. {fetched && <Minigraph timeseries={timeseries} />}
  122. {fetched && (
  123. <React.Fragment>
  124. {
  125. <MapExplorer
  126. forwardRef={mapRef}
  127. mapMeta={MAP_META[stateName]}
  128. states={[stateData]}
  129. stateDistrictWiseData={districtData}
  130. stateTestData={testData}
  131. isCountryLoaded={false}
  132. />
  133. }
  134. </React.Fragment>
  135. )}
  136. {fetched && (
  137. <div className="meta-secondary">
  138. <div className="unknown">
  139. <Icon.AlertCircle />
  140. <div className="unknown-right">
  141. Awaiting district details for{' '}
  142. {districtData[stateName]?.districtData['Unknown']
  143. ?.confirmed || '0'}{' '}
  144. cases
  145. </div>
  146. </div>
  147. <div className="sources">
  148. <Icon.Compass />
  149. <div className="sources-right">
  150. Data collected from sources{' '}
  151. {Object.keys(sources[0]).map((key) => {
  152. if (key.match('source') && sources[0][key] !== '') {
  153. const num = key.match(/\d+/);
  154. return (
  155. <React.Fragment>
  156. {num > 1 ? ',' : ''}
  157. <a href={sources[0][key]}>{num}</a>
  158. </React.Fragment>
  159. );
  160. }
  161. return null;
  162. })}
  163. </div>
  164. </div>
  165. </div>
  166. )}
  167. </div>
  168. <div className="state-right">
  169. {fetched && (
  170. <React.Fragment>
  171. <div className="district-bar">
  172. <div
  173. className="district-bar-left fadeInUp"
  174. style={{animationDelay: '0.6s'}}
  175. >
  176. <h2>Top districts</h2>
  177. <div className="districts">
  178. {Object.keys(districtData[stateName].districtData)
  179. .slice(0, 6)
  180. .sort(
  181. (a, b) =>
  182. districtData[stateName].districtData[b].confirmed -
  183. districtData[stateName].districtData[a].confirmed
  184. )
  185. .map((district, index) => {
  186. return (
  187. <div key={index} className="district">
  188. <h2>
  189. {
  190. districtData[stateName].districtData[district]
  191. .confirmed
  192. }
  193. </h2>
  194. <h5>{district}</h5>
  195. <div className="delta">
  196. <Icon.ArrowUp />
  197. <h6>
  198. {
  199. districtData[stateName].districtData[district]
  200. .delta.confirmed
  201. }
  202. </h6>
  203. </div>
  204. </div>
  205. );
  206. })}
  207. </div>
  208. </div>
  209. <div className="district-bar-right">
  210. {
  211. <DeltaBarGraph
  212. timeseries={timeseries.slice(-5)}
  213. arrayKey={'dailyconfirmed'}
  214. />
  215. }
  216. </div>
  217. </div>
  218. <Link to="/essentials">
  219. <div
  220. className="to-essentials fadeInUp"
  221. style={{animationDelay: '0.9s'}}
  222. >
  223. <h2>Go to essentials</h2>
  224. <Icon.ArrowRightCircle />
  225. </div>
  226. </Link>
  227. <div
  228. className="timeseries-header fadeInUp"
  229. style={{animationDelay: '2.5s'}}
  230. ref={tsRef}
  231. >
  232. <div className="tabs">
  233. <div
  234. className={`tab ${graphOption === 1 ? 'focused' : ''}`}
  235. onClick={() => {
  236. setGraphOption(1);
  237. }}
  238. >
  239. <h4>Cumulative</h4>
  240. </div>
  241. <div
  242. className={`tab ${graphOption === 2 ? 'focused' : ''}`}
  243. onClick={() => {
  244. setGraphOption(2);
  245. }}
  246. >
  247. <h4>Daily</h4>
  248. </div>
  249. </div>
  250. <div className="scale-modes">
  251. <label className="main">Scale Modes</label>
  252. <div className="timeseries-mode">
  253. <label htmlFor="timeseries-mode">Uniform</label>
  254. <input
  255. type="checkbox"
  256. checked={timeseriesMode}
  257. className="switch"
  258. aria-label="Checked by default to scale uniformly."
  259. onChange={(event) => {
  260. setTimeseriesMode(!timeseriesMode);
  261. }}
  262. />
  263. </div>
  264. <div
  265. className={`timeseries-logmode ${
  266. graphOption !== 1 ? 'disabled' : ''
  267. }`}
  268. >
  269. <label htmlFor="timeseries-logmode">Logarithmic</label>
  270. <input
  271. type="checkbox"
  272. checked={graphOption === 1 && timeseriesLogMode}
  273. className="switch"
  274. disabled={graphOption !== 1}
  275. onChange={(event) => {
  276. setTimeseriesLogMode(!timeseriesLogMode);
  277. }}
  278. />
  279. </div>
  280. </div>
  281. </div>
  282. <TimeSeries
  283. timeseries={timeseries}
  284. type={graphOption}
  285. mode={timeseriesMode}
  286. logMode={timeseriesLogMode}
  287. />
  288. </React.Fragment>
  289. )}
  290. </div>
  291. <div className="state-left">
  292. <div className="Clusters fadeInUp" style={{animationDelay: '0.8s'}}>
  293. <h1>Clusters</h1>
  294. <Clusters stateCode={stateCode} />
  295. </div>
  296. </div>
  297. <div className="state-right"></div>
  298. </div>
  299. <Footer />
  300. </React.Fragment>
  301. );
  302. }
  303. export default State;