diff --git a/src/components/CommonKit/Spinner/StyledSpinner.tsx b/src/components/CommonKit/Spinner/StyledSpinner.tsx index 3afde4af9c76131e6a4b7aa27f1bdaab61270ac0..390ecdcb17d097f51573064091aafa56052ef4a0 100644 --- a/src/components/CommonKit/Spinner/StyledSpinner.tsx +++ b/src/components/CommonKit/Spinner/StyledSpinner.tsx @@ -31,13 +31,15 @@ const SpinnerGas = withStyles({ interface StyledSpinnerProps extends CircularProgressProps { fluidTypes?: FluidType[] + isHome?: boolean } const StyledSpinner: React.ComponentType<StyledSpinnerProps> = ({ fluidTypes, + isHome, ...props }: StyledSpinnerProps) => { - if (!fluidTypes) { + if (!fluidTypes || isHome) { return <SpinnerBase {...props} /> } else if (fluidTypes.length === 1) { switch (fluidTypes[0]) { diff --git a/src/components/ContainerComponents/ViewContainer/HomeViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/HomeViewContainer.tsx index b17278c282a74a8a5893064dcca49042eb6791df..75a302a56101fabf2d192d86a87a5286a723d1e6 100644 --- a/src/components/ContainerComponents/ViewContainer/HomeViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/HomeViewContainer.tsx @@ -68,14 +68,14 @@ const HomeViewContainer: React.FC = () => { /> </Header> <Content height={headerHeight}> - {(isChartLoading || isIndicatorsLoading || !chartIsLoaded) && ( + {(isChartLoading || isIndicatorsLoading) && ( <div className="content-view-loading"> <StyledSpinner size="5em" /> </div> )} <div className={`${ - isChartLoading || isIndicatorsLoading || !chartIsLoaded + isChartLoading || isIndicatorsLoading ? 'chart-indicator-none' : 'chart-indicator-block' }`} diff --git a/src/components/ContainerComponents/ViewContainer/SingleFluidViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/SingleFluidViewContainer.tsx index 9394f64f5f368eb045273a8ba4d913ff97c63d99..19ac1cd296ebe92d7011d111e40580d598d90c6f 100644 --- a/src/components/ContainerComponents/ViewContainer/SingleFluidViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/SingleFluidViewContainer.tsx @@ -73,14 +73,14 @@ const SingleFluidViewContainer: React.FC<SingleFluidViewContainerProps> = ({ /> </Header> <Content height={headerHeight}> - {(isChartLoading || isIndicatorsLoading || !chartIsLoaded) && ( + {(isChartLoading || isIndicatorsLoading) && ( <div className="content-view-loading"> <StyledSpinner size="5em" fluidTypes={fluidTypes} /> </div> )} <div className={`${ - isChartLoading || isIndicatorsLoading || !chartIsLoaded + isChartLoading || isIndicatorsLoading ? 'chart-indicator-none' : 'chart-indicator-block' }`} diff --git a/src/components/ContentComponents/Charts/BarChart.tsx b/src/components/ContentComponents/Charts/BarChart.tsx index 8fa45f2fb68be6e2cfbf6776b9ca827e1c20cfe3..1c7a7eb7583e2038a54b80071a4ac4e2f6058d1a 100644 --- a/src/components/ContentComponents/Charts/BarChart.tsx +++ b/src/components/ContentComponents/Charts/BarChart.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useContext, useEffect, useState } from 'react' import { scaleBand, ScaleBand, scaleLinear, ScaleLinear } from 'd3-scale' import { IDataload, @@ -12,6 +12,7 @@ import Hash from 'components/ContentComponents/Charts/Hash' import AxisBottom from 'components/ContentComponents/Charts/AxisBottom' import AxisRight from 'components/ContentComponents/Charts/AxisRight' import { DateTime } from 'luxon' +import { AppContext } from 'components/Contexts/AppContextProvider' export interface BarChartProps { chartData: IChartData @@ -32,6 +33,7 @@ export interface BarChartProps { marginTop?: number marginBottom?: number isSwitching: boolean + isHome: boolean } interface DefaultProps { @@ -62,7 +64,10 @@ const BarChart: React.FC<BarChartProps> = (props: BarChartProps) => { marginTop, marginBottom, isSwitching, + isHome, } = props as PropsWithDefaults + const { setChartIsLoaded, maxLoads } = useContext(AppContext) + const getContentWidth = () => { return width - marginLeft - marginRight } @@ -72,14 +77,23 @@ const BarChart: React.FC<BarChartProps> = (props: BarChartProps) => { } const getMaxLoad = () => { - let max = chartData.actualData - ? Math.max(...chartData.actualData.map(d => d.value)) - : 0 - const maxCompare = chartData.comparisonData - ? Math.max(...chartData.comparisonData.map(d => d.value)) - : 0 - max = max <= 0 ? 15 : max - return showCompare ? Math.max(max, maxCompare) : max + if (timeStep === TimeStep.DAY && !showCompare) { + const actualMonth = selectedDate.startOf('week').month + const actualYear = selectedDate.startOf('week').year + const key = `${actualMonth}/${actualYear}-${isHome}-${fluidTypes + .sort() + .join('-')}` + return maxLoads[key] > 0 ? maxLoads[key] : 15 + } else { + let max = chartData.actualData + ? Math.max(...chartData.actualData.map(d => d.value)) + : 0 + const maxCompare = chartData.comparisonData + ? Math.max(...chartData.comparisonData.map(d => d.value)) + : 0 + max = max <= 0 ? 15 : max + return showCompare ? Math.max(max, maxCompare) : max + } } const xScale: ScaleBand<string> = scaleBand() @@ -102,6 +116,16 @@ const BarChart: React.FC<BarChartProps> = (props: BarChartProps) => { const yScale: ScaleLinear<number, number> = scaleLinear() .domain([0, getMaxLoad()]) .range([getContentHeight(), 0]) + const size = Object.keys(maxLoads).length + useEffect(() => { + setChartIsLoaded(true) + if (size > 0) { + setChartIsLoaded(true) + } + }, [size]) + useEffect(() => { + setChartIsLoaded(true) + }, []) return ( <svg width={width} height={height}> diff --git a/src/components/ContentComponents/FluidChart/FluidChartSlide.tsx b/src/components/ContentComponents/FluidChart/FluidChartSlide.tsx index fa2726ba53ba9db67234584cac10717248f06dee..a42e77ee207b8990a7462047e032ff44b792a0a7 100644 --- a/src/components/ContentComponents/FluidChart/FluidChartSlide.tsx +++ b/src/components/ContentComponents/FluidChart/FluidChartSlide.tsx @@ -13,6 +13,7 @@ import { FluidType } from 'enum/fluid.enum' import BarChart from 'components/ContentComponents/Charts/BarChart' import { AppContext } from 'components/Contexts/AppContextProvider' +import StyledSpinner from 'components/CommonKit/Spinner/StyledSpinner' interface FluidChartSlideProps { index: number @@ -51,8 +52,14 @@ const FluidChartSlide: React.FC<FluidChartSlideProps> = ({ const [chartData, setChartData] = useState<IChartData>(new ChartData([])) const [isLoaded, setIsLoaded] = useState<boolean>(false) - const { setChartIsLoaded } = useContext(AppContext) + const { setChartIsLoaded, maxLoads, chartIsLoaded } = useContext(AppContext) + let isHome: boolean + if (!window.location.hash.split('/')[2]) { + isHome = true + } else { + isHome = false + } useEffect(() => { let subscribed = true async function loadData() { @@ -60,12 +67,6 @@ const FluidChartSlide: React.FC<FluidChartSlideProps> = ({ defineTimePeriod(referenceDate, timeStep, index), defineTimePeriod(referenceDate, timeStep, index + 1), ]) - let isHome - if (!window.location.hash.split('/')[2]) { - isHome = true - } else { - isHome = false - } const graphData = await consumptionDataManager.getGraphData( timePeriod, @@ -78,7 +79,6 @@ const FluidChartSlide: React.FC<FluidChartSlideProps> = ({ if (subscribed && graphData && graphData.actualData.length > 0) { setChartData(graphData) setIsLoaded(true) - setChartIsLoaded(true) } } setIsLoaded(false) @@ -89,24 +89,88 @@ const FluidChartSlide: React.FC<FluidChartSlideProps> = ({ } }, [timeStep, fluidTypes]) + useEffect(() => { + let subscribed = true + + const actualMonth = selectedDate.startOf('week').month + const actualYear = selectedDate.startOf('week').year + + const maxTimePeriod = { + startDate: + selectedDate.startOf('month').startOf('week').month !== + selectedDate.startOf('month').endOf('week').month + ? selectedDate + .startOf('week') + .startOf('month') + .plus({ days: +7 }) + .startOf('week') + : selectedDate.startOf('week').startOf('month'), + endDate: + selectedDate.startOf('week').month !== selectedDate.endOf('week').month + ? selectedDate.endOf('week') + : selectedDate.endOf('month').endOf('week'), + } + + const compareMaxTimePeriod = maxTimePeriod + + const key = `${actualMonth}/${actualYear}-${isHome}-${fluidTypes + .sort() + .join('-')}` + async function loadData() { + setChartIsLoaded(false) + const graphMaxLoad = await consumptionDataManager.getMaxLoad( + maxTimePeriod, + timeStep, + fluidTypes, + compareMaxTimePeriod, + isHome + ) + maxLoads[key] = graphMaxLoad + + key in maxLoads ? (maxLoads[key] = graphMaxLoad) : null + + if (subscribed && graphMaxLoad) { + setIsLoaded(true) + } + } + if (key in maxLoads === false && timeStep === TimeStep.DAY) { + loadData() + } + return () => { + subscribed = false + } + }, [selectedDate, fluidTypes]) + return ( - <div className="fs-slide"> - {!isLoaded ? null : ( - <BarChart - chartData={chartData} - fluidTypes={fluidTypes} - timeStep={timeStep} - multiFluid={multiFluid} - selectedDate={selectedDate} - showCompare={showCompare} - handleClickData={handleClickData} - height={height} - width={width} - challengePeriod={challengePeriod} - isSwitching={isSwitching} - /> - )} - </div> + <> + <div className="fs-slide"> + {(!chartIsLoaded || !isLoaded) && ( + <div className="chart-loading"> + <StyledSpinner size="5em" fluidTypes={fluidTypes} /> + </div> + )} + <div + className={`${ + !chartIsLoaded || !isLoaded ? 'chart-none' : 'chart-block' + }`} + > + <BarChart + chartData={chartData} + fluidTypes={fluidTypes} + timeStep={timeStep} + multiFluid={multiFluid} + selectedDate={selectedDate} + showCompare={showCompare} + handleClickData={handleClickData} + height={height} + width={width} + challengePeriod={challengePeriod} + isSwitching={isSwitching} + isHome={isHome} + /> + </div> + </div> + </> ) } diff --git a/src/components/Contexts/AppContextProvider.tsx b/src/components/Contexts/AppContextProvider.tsx index 1cf2982fb3bee221305107e38e6fde79cfa62e40..4286ee96bf5dc0f0592a8477335050adbdecd834 100644 --- a/src/components/Contexts/AppContextProvider.tsx +++ b/src/components/Contexts/AppContextProvider.tsx @@ -34,6 +34,8 @@ interface AppContextProps { feedbackIsOpened: boolean setFeedbackOpened: Function disableBackgroundScroll: Function + maxLoads: Record<string, any> + setMaxLoads: Function } export const AppContext = React.createContext<AppContextProps>({ @@ -62,6 +64,8 @@ export const AppContext = React.createContext<AppContextProps>({ feedbackIsOpened: false, setFeedbackOpened: () => null, disableBackgroundScroll: () => null, + maxLoads: {}, + setMaxLoads: () => null, }) interface AppContextProviderProps { @@ -76,6 +80,7 @@ const AppContextProvider: React.FC<AppContextProviderProps> = ({ const [chartIsLoaded, setChartIsLoaded] = useState<boolean>(false) const [feedbackIsOpened, setFeedbackOpened] = useState<boolean>(false) const [isIndexesLoading, setIndexesLoading] = useState<boolean>(false) + const [maxLoads, setMaxLoads] = useState<object>({}) const [isIndexesLoadingSuccess, setIndexesLoadingSuccess] = useState< boolean | null >(null) @@ -349,6 +354,8 @@ const AppContextProvider: React.FC<AppContextProviderProps> = ({ feedbackIsOpened, setFeedbackOpened, disableBackgroundScroll, + maxLoads, + setMaxLoads, }} > {children} diff --git a/src/services/consumptionDataManagerService.ts b/src/services/consumptionDataManagerService.ts index 072de5291e9b0caab88e75d41bd66d6fc61a1b37..9b8b0cc25f5932598c8cf3c822b5c0d9edbefa41 100644 --- a/src/services/consumptionDataManagerService.ts +++ b/src/services/consumptionDataManagerService.ts @@ -61,6 +61,7 @@ export default class ConsumptionDataManager implements IConsumptionDataManager { timePeriod, timeStep, fluidTypes[0], + false, compareTimePeriod ) @@ -83,6 +84,7 @@ export default class ConsumptionDataManager implements IConsumptionDataManager { timePeriod, timeStep, fluidType, + false, compareTimePeriod ) @@ -112,6 +114,39 @@ export default class ConsumptionDataManager implements IConsumptionDataManager { return mappedData } + public async getMaxLoad( + maxTimePeriod: ITimePeriod, + timeStep: TimeStep, + fluidTypes: FluidType[], + compareMaxTimePeriod?: ITimePeriod, + isHome?: boolean + ): Promise<number | null> { + let allData + if (isHome) { + allData = await this.getGraphData( + maxTimePeriod, + timeStep, + fluidTypes, + compareMaxTimePeriod, + isHome + ) + + const max = + allData && allData.actualData + ? Math.max(...allData.actualData.map(d => d.value)) + : 0 + + return max + } else { + const max = await this._queryRunner.fetchFluidMaxData( + maxTimePeriod, + timeStep, + fluidTypes[0] + ) + return max + } + } + public async getPerformanceIndicators( timePeriod: ITimePeriod, timeStep: TimeStep, @@ -191,12 +226,19 @@ export default class ConsumptionDataManager implements IConsumptionDataManager { timePeriod: ITimePeriod, timeStep: TimeStep, fluidType: FluidType, + maxLoad: boolean, compareTimePeriod?: ITimePeriod ): Promise<IChartData | null> { let actualData: IDataload[] | null = [] let comparisonData: IDataload[] | null = [] let singleFluidGraphData: IChartData | null = null - + if (maxLoad) { + actualData = await this._queryRunner.fetchFluidData( + timePeriod, + timeStep, + fluidType + ) + } if (compareTimePeriod) { const result = await Promise.all([ this._queryRunner.fetchFluidData(timePeriod, timeStep, fluidType), diff --git a/src/services/consumptionDataValidatorService.ts b/src/services/consumptionDataValidatorService.ts index 4ae36fed62149da06319f8ffd5be0d2e75a9c581..f861b87f600b47a8584968840c01030102ce66a1 100644 --- a/src/services/consumptionDataValidatorService.ts +++ b/src/services/consumptionDataValidatorService.ts @@ -43,7 +43,7 @@ export default class ConsumptionDataValidator { ) if (timeStep == TimeStep.HALF_AN_HOUR && interval.length('hour') > 24) return false - if (timeStep == TimeStep.DAY && interval.length('day') > 31) return false + if (timeStep == TimeStep.DAY && interval.length('day') > 40) return false if (timeStep == TimeStep.MONTH && interval.length('month') > 12) return false if (timeStep == TimeStep.YEAR && interval.length('year') > 10) return false diff --git a/src/services/dataConsumptionContracts.ts b/src/services/dataConsumptionContracts.ts index 8255488b9da0e577716535187c72a493213c0ad8..dfadef015a5c5540cc51a26a8e3504218732c536 100644 --- a/src/services/dataConsumptionContracts.ts +++ b/src/services/dataConsumptionContracts.ts @@ -77,6 +77,14 @@ export interface IConsumptionDataManager { isHome?: boolean ): Promise<IChartData | null> + getMaxLoad( + timePeriod: ITimePeriod, + timeStep: TimeStep, + fluidTypes: FluidType[], + compareTimePeriod?: ITimePeriod, + isHome?: boolean + ): Promise<number | null> + fetchLastDateData(fluidType: FluidType[]): Promise<DateTime | null> getPerformanceIndicators( diff --git a/src/services/queryRunnerService.ts b/src/services/queryRunnerService.ts index a5c0e288929f68824fd7a3de26b09e0b49cc1182..5eaf30351d2255436a0a3873550b2128d544ad7f 100644 --- a/src/services/queryRunnerService.ts +++ b/src/services/queryRunnerService.ts @@ -72,12 +72,29 @@ export class QueryRunner { if (result && result.data) { const filteredResult = this.filterDataList(result, timePeriod) const mappedResult = this.mapDataList(filteredResult) + return mappedResult } return null } + public async fetchFluidMaxData( + maxTimePeriod: ITimePeriod, + timeStep: TimeStep, + fluidType: FluidType + ): Promise<number | null> { + const result = await this.fetchData( + this.buildMaxQuery(timeStep, maxTimePeriod, fluidType, this._max_limit) + ) + if (result && result.data) { + const filteredResult = this.filterDataList(result, maxTimePeriod) + const mappedResult = this.mapDataList(filteredResult) + return mappedResult && mappedResult[0] && mappedResult[0].value + } + return null + } + public async getLastDateData(fluidType: FluidType): Promise<DateTime | null> { const result = await this.fetchData(this.buildLastDateQuery(fluidType, 1)) @@ -167,6 +184,21 @@ export class QueryRunner { .limitBy(limit) } + private buildMaxQuery( + timeStep: TimeStep, + maxTimePeriod: ITimePeriod, + fluidType: FluidType, + limit: number + ) { + const doctype = this.getRelevantDoctype(fluidType, timeStep) + + return this._client + .find(doctype) + .where(this.getPredicate(maxTimePeriod, timeStep)) + .limitBy(limit) + .sortBy([{ load: 'desc' }]) + } + private withinDateBoundaries(dateTime: DateTime, timePeriod: ITimePeriod) { return dateTime <= timePeriod.endDate && dateTime >= timePeriod.startDate } diff --git a/src/styles/components/_fluid.scss b/src/styles/components/_fluid.scss index 270a37b2c3f0af3a11a3ec8c1b3a2cb52ef47b1a..469fdd027d28c279b9ca9174dca7fd00072e4e47 100644 --- a/src/styles/components/_fluid.scss +++ b/src/styles/components/_fluid.scss @@ -120,7 +120,6 @@ height: auto; } - //FluidSwipe .fs-root { flex: 1; @@ -128,6 +127,11 @@ @media #{$large-phone} { height: 14rem; } + .chart-loading { + width: 100%; + justify-content: center; + display: flex; + } .fs-slide { min-height: 22rem; overflow-x: hidden; @@ -137,6 +141,13 @@ @media #{$large-phone} { min-height: 14rem; } + .chart-none { + visibility: hidden; + width: 0; + } + .chart-block { + visibility: visible; + } } }