import {STATE_ROW_STATISTICS, DISTRICT_ROW_STATISTICS} from '../constants'; import { formatDate, formatNumber, capitalize, abbreviate, } from '../utils/common-functions'; import classnames from 'classnames'; import {formatDistance} from 'date-fns'; import equal from 'fast-deep-equal'; import React, {useState, useCallback, useMemo} from 'react'; import * as Icon from 'react-feather'; import {useTranslation} from 'react-i18next'; import {useHistory} from 'react-router-dom'; import ReactTooltip from 'react-tooltip'; import {createBreakpoint, useLocalStorage, useEffectOnce} from 'react-use'; const useBreakpoint = createBreakpoint({XL: 1280, L: 768, S: 350}); function StateCell({state, statistic}) { const ArrowUp = useMemo(() => <Icon.ArrowUp />, []); return ( <td> <span className={classnames('delta', `is-${statistic}`)}> {state[`delta${statistic}`] > 0 && ArrowUp} {state[`delta${statistic}`] > 0 && state[`delta${statistic}`]} </span> <span className="total"> {state[statistic] === 0 ? '-' : formatNumber(state[statistic])} </span> </td> ); } function DistrictHeaderCell({handleSort, statistic, sortData}) { const breakpoint = useBreakpoint(); const {t} = useTranslation(); return ( <td onClick={() => handleSort(statistic)}> <div className="heading-content"> <abbr className={classnames({[`is-${statistic}`]: breakpoint === 'S'})} title={statistic} > {breakpoint === 'L' ? statistic.slice(0) : breakpoint === 'S' ? capitalize( abbreviate(statistic === 'deaths' ? 'deceased' : statistic) ) : t(capitalize(statistic === 'deaths' ? 'deceased' : statistic))} </abbr> <div style={{ display: sortData.sortColumn === statistic ? 'initial' : 'none', }} > {sortData.isAscending ? ( <div className="arrow-up" /> ) : ( <div className="arrow-down" /> )} </div> </div> </td> ); } function PureDistrictCell({district, statistic}) { return ( <td> <span className={classnames('delta', `is-${statistic}`)}> {district.delta[statistic] > 0 && <Icon.ArrowUp />} {district.delta[statistic] > 0 && district.delta[statistic]} </span> <span className="total">{formatNumber(district[statistic])}</span> </td> ); } const DistrictCell = React.memo(PureDistrictCell); const isDistrictRowEqual = (prevProps, currProps) => { if (equal(prevProps.regionHighlighted?.district, prevProps.district)) { return false; } if (equal(currProps.regionHighlighted?.district, currProps.district)) { return false; } return true; }; function PureDistrictRow({ regionHighlighted, district, state, zone, onHighlightDistrict, sortedDistricts, districts, }) { const {t} = useTranslation(); return ( <tr key={district.district} className={classnames('district', { 'is-highlighted': regionHighlighted?.district === district, })} onMouseEnter={() => onHighlightDistrict(district, state)} > <td className={classnames(`is-${zone?.zone}`)}> <div className="title-chevron"> <span className="title-icon"> {t(district)} <span data-for="district" data-tip={[[sortedDistricts[district].notes]]} data-event="touchstart mouseover" data-event-off="mouseleave" onClick={(e) => e.stopPropagation()} > {sortedDistricts[district].notes && <Icon.Info />} </span> </span> </div> </td> {DISTRICT_ROW_STATISTICS.map((statistic) => ( <DistrictCell key={statistic} district={districts[district]} statistic={statistic} /> ))} </tr> ); } const DistrictRow = React.memo(PureDistrictRow, isDistrictRowEqual); const isEqual = (prevProps, currProps) => { if (!equal(prevProps.state.state, currProps.state.state)) { return false; } if ( !equal( prevProps.regionHighlighted?.state, currProps.regionHighlighted?.state ) ) { return false; } if ( !equal( prevProps.regionHighlighted?.district, currProps.regionHighlighted?.district ) ) { return false; } return true; }; function Row({ index, state, districts, zones, regionHighlighted, onHighlightState, onHighlightDistrict, }) { const [sortedDistricts, setSortedDistricts] = useState(districts); const [showDistricts, setShowDistricts] = useState(false); const [sortData, setSortData] = useLocalStorage('districtSortData', { sortColumn: 'confirmed', isAscending: false, }); const history = useHistory(); const {t} = useTranslation(); const Chevron = useMemo( () => ( <span className={classnames( 'dropdown', {rotateRightDown: showDistricts}, {rotateDownRight: !showDistricts} )} > <Icon.ChevronDown /> </span> ), [showDistricts] ); const _onHighlightState = useCallback( (state) => { if (!equal(state.state, regionHighlighted?.state)) { onHighlightState(state); } }, [onHighlightState, regionHighlighted] ); const doSort = useCallback( (sortData) => { const sorted = {}; Object.keys(sortedDistricts) .sort((district1, district2) => { if (sortData.sortColumn !== 'district') { return sortData.isAscending ? parseInt(sortedDistricts[district1][sortData.sortColumn]) - parseInt(sortedDistricts[district2][sortData.sortColumn]) : parseInt(sortedDistricts[district2][sortData.sortColumn]) - parseInt(sortedDistricts[district1][sortData.sortColumn]); } else { return sortData.isAscending ? district1.localeCompare(district2) : district2.localeCompare(district1); } }) .forEach((key) => { sorted[key] = sortedDistricts[key]; }); setSortedDistricts(sorted); }, [sortedDistricts] ); const handleSort = useCallback( (statistic) => { const newSortData = { isAscending: !sortData.isAscending, sortColumn: statistic, }; doSort(newSortData); setSortData(Object.assign({}, sortData, newSortData)); }, [doSort, setSortData, sortData] ); useEffectOnce(() => { if (state.statecode !== 'TT') doSort(sortData); }); return ( <React.Fragment> <tr className={classnames( 'state', {'is-total': state.statecode === 'TT'}, {'is-highlighted': regionHighlighted?.state === state.state}, {'is-odd': index % 2 === 0} )} onMouseEnter={() => _onHighlightState(state)} onClick={ state.statecode !== 'TT' ? () => { setShowDistricts(!showDistricts); } : null } > <td> <div className="title-chevron"> {state.statecode !== 'TT' && Chevron} <span className="title-icon"> {t(state.state)} <span data-tip={[t(`${state.statenotes}`)]} data-event="touchstart mouseover" data-event-off="mouseleave" onClick={(e) => e.stopPropagation()} > {state.statenotes && <Icon.Info />} </span> </span> </div> </td> {STATE_ROW_STATISTICS.map((statistic, index) => ( <StateCell key={index} state={state} statistic={statistic} /> ))} </tr> {showDistricts && ( <React.Fragment> <tr className="is-spacer"> <td colSpan={5}> <p /> </td> </tr> <tr className={'state-last-update'}> <td colSpan={3} style={{paddingBottom: 0}}> <p className="spacer"></p> <p> {isNaN(Date.parse(formatDate(state.lastupdatedtime))) ? '' : `${t('Last updated')} ${formatDistance( new Date(formatDate(state.lastupdatedtime)), new Date() )} ${t('ago')}`} </p> {sortedDistricts?.Unknown && ( <div className="disclaimer"> <Icon.AlertCircle /> {t('District-wise numbers are under reconciliation')} </div> )} </td> <td align="center" className="state-page-link" colSpan={2} onClick={() => { history.push(`state/${state.statecode}`); }} >{`View ${t(state.state)}'s Page`}</td> </tr> <tr className={classnames('district-heading')}> <td onClick={() => handleSort('district')}> <div className="heading-content"> <abbr title="District">{t('District')}</abbr> <div style={{ display: sortData.sortColumn === 'district' ? 'initial' : 'none', }} > {sortData.isAscending ? ( <div className="arrow-up" /> ) : ( <div className="arrow-down" /> )} </div> </div> </td> {DISTRICT_ROW_STATISTICS.map((statistic, index) => ( <DistrictHeaderCell key={index} handleSort={handleSort} statistic={statistic} sortData={sortData} /> ))} </tr> </React.Fragment> )} {showDistricts && Object.keys(sortedDistricts).map((district, index) => ( <DistrictRow key={district} state={state} district={district} districts={districts} zone={zones[district]} sortedDistricts={sortedDistricts} regionHighlighted={regionHighlighted} onHighlightDistrict={onHighlightDistrict} /> ))} {showDistricts && ( <tr className="is-spacer"> <td colSpan={5}> <p /> <ReactTooltip id="district" place="right" type="dark" effect="solid" multiline={true} scrollHide={true} globalEventOff="click" /> </td> </tr> )} </React.Fragment> ); } export default React.memo(Row, isEqual);