import React, { useState, useEffect } from 'react'; import { useSelector } from 'react-redux'; import { useTranslation } from 'react-i18next'; import { BigNumber } from 'ethers'; import { Bar } from 'react-chartjs-2'; import { Button, ButtonGroup, makeStyles, createStyles, useTheme } from '@material-ui/core'; import { MeterReadsClient, ReadDTO } from '@energyweb/origin-energy-api-client'; import { moment, formatDate } from '../../../utils/time'; import { reverse } from '../../../utils/helper'; import { EnergyFormatter } from '../../../utils/EnergyFormatter'; import { useOriginConfiguration } from '../../../utils/configuration'; import { fromGeneralSelectors } from '../../../features'; enum TimeframeOptions { DAY = 'day', WEEK = 'week', MONTH = 'month', YEAR = 'year' } const DEFAULT_TIMEFRAME = TimeframeOptions.MONTH; interface IProps { meterId: string; } export const EnergySmartMeterChart = ({ meterId }: IProps) => { const originConfiguration = useOriginConfiguration(); const environment = useSelector(fromGeneralSelectors.getEnvironment); const marketUtcOffset = Number(environment?.MARKET_UTC_OFFSET) || 0; const backendClient = useSelector(fromGeneralSelectors.getBackendClient); const meterReadsClient: MeterReadsClient = backendClient?.meterReadsClient; const originBgColor = originConfiguration?.styleConfig?.MAIN_BACKGROUND_COLOR; const useStyles = makeStyles(() => createStyles({ selected: { backgroundColor: originBgColor } }) ); const classes = useStyles(useTheme()); const { t } = useTranslation(); const [selectedTimeFrame, setSelectedTimeFrame] = useState({ timeframe: DEFAULT_TIMEFRAME, startDate: moment().startOf('month').toISOString(), endDate: moment().endOf('month').toISOString() }); const [readings, setReadings] = useState([]); useEffect(() => { (async () => { const { startDate, endDate } = selectedTimeFrame; const { data: defaultReadings } = await meterReadsClient.getReads( meterId, startDate, endDate ); setReadings(defaultReadings); })(); }, [meterId]); const graphOptions = { maintainAspectRatio: false, scales: { yAxes: [ { ticks: { beginAtZero: true } } ] }, tooltips: { callbacks: { label: (tooltipItem, data) => { const tooltipValue = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; return parseInt(tooltipValue, 10).toLocaleString(); } } } }; const formatTimeframe = (timeframe: TimeframeOptions) => { if (timeframe === TimeframeOptions.DAY) { return 'day'; } else if (timeframe === TimeframeOptions.WEEK) { return 'week'; } else if (timeframe === TimeframeOptions.MONTH) { return 'month'; } else { return 'year'; } }; const endDateInTimezone = moment(selectedTimeFrame?.endDate).utcOffset(marketUtcOffset).clone(); function changeSelectedTimeFrame(increment = true) { const { timeframe } = selectedTimeFrame; const measurementUnit = formatTimeframe(timeframe); const currentDate = endDateInTimezone; const newEndDate = increment ? currentDate.add(1, measurementUnit) : currentDate.subtract(1, measurementUnit); setSelectedTimeFrame({ timeframe, startDate: newEndDate.startOf(measurementUnit).toISOString(), endDate: newEndDate.endOf(measurementUnit).toISOString() }); } function getFormattedReadings() { const formatted = []; let measurementUnit: string; let amount: number; let keyFormat: string; let chartEndDate; switch (selectedTimeFrame.timeframe) { case TimeframeOptions.DAY: measurementUnit = 'hour'; amount = 24; keyFormat = 'HH'; chartEndDate = endDateInTimezone.endOf('day'); break; case TimeframeOptions.WEEK: measurementUnit = 'day'; amount = 7; keyFormat = 'ddd D MMM'; chartEndDate = endDateInTimezone.endOf('week'); break; case TimeframeOptions.MONTH: measurementUnit = 'day'; amount = endDateInTimezone.daysInMonth(); keyFormat = 'D MMM'; chartEndDate = endDateInTimezone.endOf('month'); break; case TimeframeOptions.YEAR: measurementUnit = 'month'; amount = 12; keyFormat = 'MMM'; chartEndDate = endDateInTimezone.endOf('year'); } let currentIndex = 0; while (currentIndex < amount) { const currentDate = chartEndDate .clone() .subtract(currentIndex, measurementUnit) .utcOffset(marketUtcOffset); let totalEnergy = BigNumber.from(0); for (const reading of readings) { const readingDate = moment .unix(Number(reading.timestamp)) .utcOffset(marketUtcOffset); if (readingDate.isSame(currentDate, TimeframeOptions[measurementUnit])) { totalEnergy = totalEnergy.add(reading.value); } } formatted.push({ label: measurementUnit !== 'hour' ? currentDate.format(keyFormat) : `${currentDate.format(keyFormat)}:00`, color: originConfiguration?.styleConfig?.PRIMARY_COLOR, value: EnergyFormatter.getValueInDisplayUnit(totalEnergy) }); currentIndex += 1; } return reverse(formatted); } function getCurrentRangeInfo(): string { const { timeframe } = selectedTimeFrame; if (timeframe === TimeframeOptions.WEEK) { return `${formatDate(endDateInTimezone.startOf('week'))} - ${formatDate( endDateInTimezone.endOf('week') )}`; } else if (timeframe === TimeframeOptions.MONTH) { return endDateInTimezone.format('MMM YYYY'); } else if (timeframe === TimeframeOptions.YEAR) { return endDateInTimezone.format('YYYY'); } return formatDate(endDateInTimezone); } const currentRangeInfo = getCurrentRangeInfo(); const formattedReadings = getFormattedReadings(); const data = { labels: formattedReadings.map((entry) => entry.label), datasets: [ { label: t('meterReads.properties.energy', { unit: EnergyFormatter.displayUnit }), backgroundColor: formattedReadings.map((entry) => entry.color), data: formattedReadings.map((entry) => entry.value) } ] }; const timeFrameButtons = Object.keys(TimeframeOptions).map((timeframe, index) => { const onClick = () => { const measurementUnit = formatTimeframe(TimeframeOptions[timeframe]); setSelectedTimeFrame({ timeframe: TimeframeOptions[timeframe], startDate: moment().startOf(measurementUnit).toISOString(), endDate: moment().endOf(measurementUnit).toISOString() }); }; const isCurrentlySelected = selectedTimeFrame.timeframe === TimeframeOptions[timeframe]; return ( ); }); (graphOptions as any).title = { display: true, text: currentRangeInfo }; return (
changeSelectedTimeFrame(false)} > {'<'}
{timeFrameButtons}
changeSelectedTimeFrame(true)} > {'>'}
); };