From 63a0b5e46bd9ef849ca6fd49345cdc1c3ef96355 Mon Sep 17 00:00:00 2001 From: Bastien DUMONT <bdumont@grandlyon.com> Date: Fri, 16 Jun 2023 14:29:22 +0000 Subject: [PATCH] feat(analysis): add graph to max consumption component --- .../Analysis/AnalysisConsumption.spec.tsx | 86 +-- .../Analysis/AnalysisConsumption.tsx | 14 +- src/components/Analysis/AnalysisView.spec.tsx | 3 + src/components/Analysis/AnalysisView.tsx | 8 +- src/components/Analysis/ComparisonView.tsx | 30 +- .../ElecHalfHourMonthlyAnalysis.spec.tsx | 47 +- .../Analysis/ElecHalfHourMonthlyAnalysis.tsx | 24 +- .../Analysis/MaxConsumptionCard.spec.tsx | 62 +- .../Analysis/MaxConsumptionCard.tsx | 190 +++--- .../Analysis/MonthlyAnalysis.spec.tsx | 34 +- src/components/Analysis/MonthlyAnalysis.tsx | 109 ++-- src/components/Analysis/PieChart.tsx | 5 +- .../Analysis/TotalAnalysisChart.spec.tsx | 34 +- .../Analysis/TotalAnalysisChart.tsx | 27 +- .../ElecHalfHourMonthlyAnalysis.spec.tsx.snap | 610 +++++++++--------- .../MaxConsumptionCard.spec.tsx.snap | 19 +- .../MonthlyAnalysis.spec.tsx.snap | 35 +- .../TotalAnalysisChart.spec.tsx.snap | 1 - .../Analysis/maxConsumptionCard.scss | 20 +- src/components/Charts/Bar.tsx | 11 +- src/components/Charts/BarChart.tsx | 3 + .../DateNavigator/DateNavigator.tsx | 11 +- src/components/FluidChart/FluidChartSwipe.tsx | 2 +- src/components/Splash/SplashRoot.tsx | 2 + src/locales/fr.json | 2 +- src/models/analysis.model.ts | 7 +- src/services/dateChart.service.spec.ts | 16 +- src/services/dateChart.service.ts | 2 +- src/store/analysis/analysis.slice.spec.ts | 30 +- src/store/analysis/analysis.slice.ts | 11 +- src/styles/components/_barchart.scss | 7 + src/utils/date.spec.ts | 19 - src/utils/date.ts | 8 - src/utils/utils.spec.ts | 12 + src/utils/utils.ts | 1 + tests/__mocks__/store.ts | 5 +- 36 files changed, 740 insertions(+), 767 deletions(-) diff --git a/src/components/Analysis/AnalysisConsumption.spec.tsx b/src/components/Analysis/AnalysisConsumption.spec.tsx index 1595ec68f..963c6b64f 100644 --- a/src/components/Analysis/AnalysisConsumption.spec.tsx +++ b/src/components/Analysis/AnalysisConsumption.spec.tsx @@ -4,7 +4,6 @@ import Button from '@material-ui/core/Button' import AnalysisConsumption from 'components/Analysis/AnalysisConsumption' import { FluidType } from 'enum/fluid.enum' import { mount } from 'enzyme' -import { DateTime } from 'luxon' import { PerformanceIndicator } from 'models' import React from 'react' import { act } from 'react-dom/test-utils' @@ -16,6 +15,7 @@ import { mockMonthlyForecastJanuaryTestProfile1, profileTypeData, } from '../../../tests/__mocks__/profileType.mock' +import { mockAnalysisState } from '../../../tests/__mocks__/store' jest.mock('cozy-ui/transpiled/react/I18n', () => { return { @@ -55,9 +55,16 @@ jest.mock('components/Analysis/AnalysisConsumptionRow', () => () => ( <div id="analysisconsumptionrow" /> )) -const mockStore = configureStore([]) - const modifiedProfile = { ...profileData, isProfileTypeCompleted: true } +const mockStore = configureStore([]) +const store = mockStore({ + ecolyo: { + profile: modifiedProfile, + profileType: profileTypeData, + global: globalStateData, + analysis: mockAnalysisState, + }, +}) const performanceIndicator = { compareValue: 160.42797399999998, @@ -84,21 +91,11 @@ const performanceIndicators = [ describe('AnalysisConsumption component', () => { it('should be rendered correctly', async () => { - const store = mockStore({ - ecolyo: { - profile: modifiedProfile, - profileType: profileTypeData, - global: globalStateData, - }, - }) const wrapper = mount( <Provider store={store}> <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} /> </Provider> ) @@ -111,13 +108,6 @@ describe('AnalysisConsumption component', () => { }) it('should be rendered correctly with no profil set', async () => { - const store = mockStore({ - ecolyo: { - profile: profileData, - profileType: profileTypeData, - global: globalStateData, - }, - }) const mockAggregatedPerformanceIndicator: PerformanceIndicator = { compareValue: 160.42797399999998, percentageVariation: null, @@ -128,9 +118,6 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={mockAggregatedPerformanceIndicator} performanceIndicators={performanceIndicators} - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} /> </Provider> ) @@ -142,13 +129,6 @@ describe('AnalysisConsumption component', () => { }) it('should be rendered correctly with null aggregated performance indicator', async () => { - const store = mockStore({ - ecolyo: { - profile: modifiedProfile, - profileType: profileTypeData, - global: globalStateData, - }, - }) const mockAggregatedPerformanceIndicator: PerformanceIndicator = { compareValue: 160.42797399999998, percentageVariation: null, @@ -159,9 +139,6 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={mockAggregatedPerformanceIndicator} performanceIndicators={performanceIndicators} - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} /> </Provider> ) @@ -174,21 +151,11 @@ describe('AnalysisConsumption component', () => { }) it('should be rendered correctly without fluid', async () => { - const store = mockStore({ - ecolyo: { - profile: modifiedProfile, - profileType: profileTypeData, - global: globalStateData, - }, - }) const wrapper = mount( <Provider store={store}> <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} /> </Provider> ) @@ -202,17 +169,17 @@ describe('AnalysisConsumption component', () => { }) it('should be rendered correctly with all fluids connected', async () => { - const updateGlobalState = { ...globalStateData } - updateGlobalState.fluidTypes = [ - FluidType.ELECTRICITY, - FluidType.WATER, - FluidType.GAS, - ] const store = mockStore({ ecolyo: { profile: modifiedProfile, profileType: profileTypeData, - global: updateGlobalState, + global: { + fluidTypes: [FluidType.ELECTRICITY, FluidType.WATER, FluidType.GAS], + }, + analysis: { + ...mockAnalysisState, + analysisMonth: profileData.monthlyAnalysisDate, + }, }, }) mockgetMonthlyForecast.mockReturnValue( @@ -223,7 +190,6 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} - analysisDate={profileData.monthlyAnalysisDate} /> </Provider> ) @@ -240,12 +206,11 @@ describe('AnalysisConsumption component', () => { }) it('should be rendered correctly with 2 fluids connected', async () => { - const updateGlobalState = { ...globalStateData } - updateGlobalState.fluidTypes = [FluidType.ELECTRICITY, FluidType.WATER] const store = mockStore({ ecolyo: { profile: modifiedProfile, - global: updateGlobalState, + global: { fluidTypes: [FluidType.ELECTRICITY, FluidType.WATER] }, + analysis: { analysisMonth: profileData.monthlyAnalysisDate }, }, }) mockgetMonthlyForecast.mockReturnValue( @@ -256,9 +221,6 @@ describe('AnalysisConsumption component', () => { <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} - analysisDate={DateTime.fromISO('2020-11-03T00:00:00.000Z', { - zone: 'utc', - })} /> </Provider> ) @@ -275,21 +237,11 @@ describe('AnalysisConsumption component', () => { }) it('should redirect to profileType form when click on mui button', async () => { - const store = mockStore({ - ecolyo: { - profile: profileData, - profileType: profileTypeData, - global: globalStateData, - }, - }) const wrapper = mount( <Provider store={store}> <AnalysisConsumption aggregatedPerformanceIndicator={performanceIndicator} performanceIndicators={performanceIndicators} - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} /> </Provider> ) diff --git a/src/components/Analysis/AnalysisConsumption.tsx b/src/components/Analysis/AnalysisConsumption.tsx index edbe23aff..ad5efafcd 100644 --- a/src/components/Analysis/AnalysisConsumption.tsx +++ b/src/components/Analysis/AnalysisConsumption.tsx @@ -14,7 +14,6 @@ import { Client, useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' import { FluidType } from 'enum/fluid.enum' -import { DateTime } from 'luxon' import { PerformanceIndicator } from 'models' import { MonthlyForecast, ProfileType } from 'models/profileType.model' import React, { useCallback, useEffect, useState } from 'react' @@ -29,19 +28,18 @@ import './analysisConsumption.scss' interface AnalysisConsumptionProps { aggregatedPerformanceIndicator: PerformanceIndicator performanceIndicators: PerformanceIndicator[] - analysisDate: DateTime } const AnalysisConsumption = ({ aggregatedPerformanceIndicator, performanceIndicators, - analysisDate, }: AnalysisConsumptionProps) => { const { t } = useI18n() const navigate = useNavigate() const client: Client = useClient() const userPriceConsumption: number = aggregatedPerformanceIndicator.value || 0 const { + analysis: { analysisMonth }, global: { fluidTypes }, profile, } = useSelector((state: AppStore) => state.ecolyo) @@ -106,17 +104,17 @@ const AnalysisConsumption = ({ const profileTypeEntityService = new ProfileTypeEntityService(client) const profileType: ProfileType | null = await profileTypeEntityService.getProfileType( - analysisDate.minus({ month: 1 }).startOf('month') + analysisMonth.minus({ month: 1 }).startOf('month') ) if (profileType !== null) { const profileTypeService: ProfileTypeService = new ProfileTypeService( profileType, client, - analysisDate.year + analysisMonth.year ) const monthlyForecast: MonthlyForecast = await profileTypeService.getMonthlyForecast( - analysisDate.minus({ month: 1 }).startOf('month').month + analysisMonth.minus({ month: 1 }).startOf('month').month ) if (subscribed) { setForecast(monthlyForecast) @@ -135,8 +133,8 @@ const AnalysisConsumption = ({ profile.monthlyAnalysisDate.month, getTotalValueWithConnectedFluids, client, - analysisDate.month, - analysisDate, + analysisMonth.month, + analysisMonth, ]) const profileNotCompleted = ( diff --git a/src/components/Analysis/AnalysisView.spec.tsx b/src/components/Analysis/AnalysisView.spec.tsx index 87e6fb72a..26f190042 100644 --- a/src/components/Analysis/AnalysisView.spec.tsx +++ b/src/components/Analysis/AnalysisView.spec.tsx @@ -9,6 +9,7 @@ import { globalStateData } from '../../../tests/__mocks__/globalStateData.mock' import { profileData } from '../../../tests/__mocks__/profileData.mock' import { createMockEcolyoStore, + mockAnalysisState, mockInitialChartState, } from '../../../tests/__mocks__/store' @@ -57,6 +58,7 @@ describe('AnalysisView component', () => { global: globalStateData, profile: profileData, chart: mockInitialChartState, + analysis: mockAnalysisState, }) useDispatchSpy.mockReturnValue(jest.fn()) const wrapper = mount( @@ -81,6 +83,7 @@ describe('AnalysisView component', () => { haveSeenLastAnalysis: false, }, chart: mockInitialChartState, + analysis: mockAnalysisState, }) useDispatchSpy.mockReturnValue(jest.fn()) const wrapper = mount( diff --git a/src/components/Analysis/AnalysisView.tsx b/src/components/Analysis/AnalysisView.tsx index 5891d7ba3..b807f35b6 100644 --- a/src/components/Analysis/AnalysisView.tsx +++ b/src/components/Analysis/AnalysisView.tsx @@ -19,13 +19,11 @@ const AnalysisView = () => { const client = useClient() const [headerHeight, setHeaderHeight] = useState<number>(0) const { + analysis: { analysisMonth }, chart: { selectedDate }, global: { analysisNotification }, profile: { monthlyAnalysisDate, mailToken }, } = useSelector((state: AppStore) => state.ecolyo) - - const [currentAnalysisDate, setCurrentAnalysisDate] = - useState<DateTime>(monthlyAnalysisDate) const dispatch = useDispatch<Dispatch<AppActionsTypes>>() const defineHeaderHeight = useCallback((height: number) => { setHeaderHeight(height) @@ -107,14 +105,12 @@ const AnalysisView = () => { desktopTitleKey={'common.title_analysis'} > <DateNavigator - currentAnalysisDate={currentAnalysisDate} - setCurrentAnalysisDate={setCurrentAnalysisDate} + currentAnalysisDate={analysisMonth} inlineDateDisplay={true} /> </Header> <Content height={headerHeight}> <MonthlyAnalysis - analysisDate={currentAnalysisDate} saveLastScrollPosition={saveLastScrollPosition} scrollPosition={scrollPosition} /> diff --git a/src/components/Analysis/ComparisonView.tsx b/src/components/Analysis/ComparisonView.tsx index 64386b633..db8032078 100644 --- a/src/components/Analysis/ComparisonView.tsx +++ b/src/components/Analysis/ComparisonView.tsx @@ -4,7 +4,6 @@ import FluidPerformanceIndicator from 'components/PerformanceIndicator/FluidPerf import { useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { TimeStep } from 'enum/timeStep.enum' -import { DateTime } from 'luxon' import { FluidConfig, PerformanceIndicator } from 'models' import React, { Dispatch, useEffect, useMemo, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' @@ -13,17 +12,12 @@ import { AppActionsTypes, AppStore } from 'store' import { setPeriod } from 'store/analysis/analysis.slice' import './comparisonView.scss' -interface ComparisonViewProps { - analysisDate: DateTime - fluidConfig: FluidConfig[] -} - -const ComparisonView = ({ analysisDate, fluidConfig }: ComparisonViewProps) => { +const ComparisonView = ({ fluidConfig }: { fluidConfig: FluidConfig[] }) => { const { t } = useI18n() const client = useClient() const { global: { fluidTypes }, - analysis: { period }, + analysis: { period, analysisMonth }, } = useSelector((state: AppStore) => state.ecolyo) const dispatch = useDispatch<Dispatch<AppActionsTypes>>() const [monthPerformanceIndicators, setMonthPerformanceIndicators] = useState< @@ -39,22 +33,22 @@ const ComparisonView = ({ analysisDate, fluidConfig }: ComparisonViewProps) => { ) const monthPeriod = useMemo(() => { return { - startDate: analysisDate.minus({ month: 1 }).startOf('month'), - endDate: analysisDate.minus({ month: 1 }).endOf('month'), + startDate: analysisMonth.minus({ month: 1 }).startOf('month'), + endDate: analysisMonth.minus({ month: 1 }).endOf('month'), } - }, [analysisDate]) + }, [analysisMonth]) const previousMonthPeriod = useMemo(() => { return { - startDate: analysisDate.minus({ month: 2 }).startOf('month'), - endDate: analysisDate.minus({ month: 2 }).endOf('month'), + startDate: analysisMonth.minus({ month: 2 }).startOf('month'), + endDate: analysisMonth.minus({ month: 2 }).endOf('month'), } - }, [analysisDate]) + }, [analysisMonth]) const previousYearPeriod = useMemo(() => { return { - startDate: analysisDate.minus({ year: 1, month: 1 }).startOf('month'), - endDate: analysisDate.minus({ year: 1, month: 1 }).endOf('month'), + startDate: analysisMonth.minus({ year: 1, month: 1 }).startOf('month'), + endDate: analysisMonth.minus({ year: 1, month: 1 }).endOf('month'), } - }, [analysisDate]) + }, [analysisMonth]) const loaderPlaceholderHeight = fluidTypes.length * 84 + (fluidTypes.length - 1) * 10 @@ -94,7 +88,7 @@ const ComparisonView = ({ analysisDate, fluidConfig }: ComparisonViewProps) => { }, [ client, fluidTypes, - analysisDate, + analysisMonth, consumptionService, monthPeriod, previousMonthPeriod, diff --git a/src/components/Analysis/ElecHalfHourMonthlyAnalysis.spec.tsx b/src/components/Analysis/ElecHalfHourMonthlyAnalysis.spec.tsx index 2326f69a1..2cff5c734 100644 --- a/src/components/Analysis/ElecHalfHourMonthlyAnalysis.spec.tsx +++ b/src/components/Analysis/ElecHalfHourMonthlyAnalysis.spec.tsx @@ -1,14 +1,16 @@ import { IconButton } from '@material-ui/core' import { mount } from 'enzyme' import toJson from 'enzyme-to-json' -import { DateTime } from 'luxon' import { PerformanceIndicator } from 'models' import React from 'react' +import { Provider } from 'react-redux' +import configureStore from 'redux-mock-store' import { mockDataLoadEnedisAnalysis, mockEnedisMonthlyAnalysisArray, } from '../../../tests/__mocks__/enedisMonthlyAnalysisData.mock' import { allLastFluidPrices } from '../../../tests/__mocks__/fluidPrice.mock' +import { mockAnalysisState } from '../../../tests/__mocks__/store' import { waitForComponentToPaint } from '../../../tests/__mocks__/testUtils' import ElecHalfHourMonthlyAnalysis from './ElecHalfHourMonthlyAnalysis' @@ -64,6 +66,13 @@ jest.mock('services/fluidsPrices.service', () => { }) }) +const mockStore = configureStore([]) +const store = mockStore({ + ecolyo: { + analysis: mockAnalysisState, + }, +}) + describe('ElecHalfHourMonthlyAnalysis component', () => { beforeEach(() => { mockCheckDoctypeEntries.mockClear() @@ -75,12 +84,9 @@ describe('ElecHalfHourMonthlyAnalysis component', () => { mockCheckDoctypeEntries.mockResolvedValueOnce(false) mockGetPrices.mockResolvedValue(allLastFluidPrices[0]) const wrapper = mount( - <ElecHalfHourMonthlyAnalysis - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} - perfIndicator={mockPerfIndicator} - /> + <Provider store={store}> + <ElecHalfHourMonthlyAnalysis perfIndicator={mockPerfIndicator} /> + </Provider> ) await waitForComponentToPaint(wrapper) expect(toJson(wrapper)).toMatchSnapshot() @@ -97,12 +103,9 @@ describe('ElecHalfHourMonthlyAnalysis component', () => { mockGetPrices.mockResolvedValue(allLastFluidPrices[0]) const wrapper = mount( - <ElecHalfHourMonthlyAnalysis - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} - perfIndicator={mockPerfIndicator} - /> + <Provider store={store}> + <ElecHalfHourMonthlyAnalysis perfIndicator={mockPerfIndicator} /> + </Provider> ) await waitForComponentToPaint(wrapper) expect(toJson(wrapper)).toMatchSnapshot() @@ -118,12 +121,9 @@ describe('ElecHalfHourMonthlyAnalysis component', () => { ) mockGetPrices.mockResolvedValue(allLastFluidPrices[0]) const wrapper = mount( - <ElecHalfHourMonthlyAnalysis - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} - perfIndicator={mockPerfIndicator} - /> + <Provider store={store}> + <ElecHalfHourMonthlyAnalysis perfIndicator={mockPerfIndicator} /> + </Provider> ) await waitForComponentToPaint(wrapper) wrapper.find(IconButton).first().simulate('click') @@ -142,12 +142,9 @@ describe('ElecHalfHourMonthlyAnalysis component', () => { mockGetPrices.mockResolvedValue(allLastFluidPrices[0]) const wrapper = mount( - <ElecHalfHourMonthlyAnalysis - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} - perfIndicator={mockPerfIndicator} - /> + <Provider store={store}> + <ElecHalfHourMonthlyAnalysis perfIndicator={mockPerfIndicator} /> + </Provider> ) await waitForComponentToPaint(wrapper) wrapper.find('.showmodal').first().simulate('click') diff --git a/src/components/Analysis/ElecHalfHourMonthlyAnalysis.tsx b/src/components/Analysis/ElecHalfHourMonthlyAnalysis.tsx index 4232a6a71..b6fc40720 100644 --- a/src/components/Analysis/ElecHalfHourMonthlyAnalysis.tsx +++ b/src/components/Analysis/ElecHalfHourMonthlyAnalysis.tsx @@ -10,31 +10,31 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' -import { DateTime } from 'luxon' import { FluidPrice, PerformanceIndicator } from 'models' import { AggregatedEnedisMonthlyDataloads, EnedisMonthlyAnalysisData, } from 'models/enedisMonthlyAnalysis' import React, { useCallback, useEffect, useState } from 'react' +import { useSelector } from 'react-redux' import ConsumptionService from 'services/consumption.service' import EnedisMonthlyAnalysisDataService from 'services/enedisMonthlyAnalysisData.service' import FluidPricesService from 'services/fluidsPrices.service' +import { AppStore } from 'store' import { getNavPicto } from 'utils/picto' import ElecInfoModal from './ElecInfoModal' import './elecHalfHourMonthlyAnalysis.scss' -interface ElecHalfHourMonthlyAnalysisProps { - analysisDate: DateTime - perfIndicator: PerformanceIndicator -} - const ElecHalfHourMonthlyAnalysis = ({ - analysisDate, perfIndicator, -}: ElecHalfHourMonthlyAnalysisProps) => { +}: { + perfIndicator: PerformanceIndicator +}) => { const { t } = useI18n() const client = useClient() + const { analysisMonth } = useSelector( + (state: AppStore) => state.ecolyo.analysis + ) const [isWeekend, setIsWeekend] = useState<boolean>(true) const [isHalfHourActivated, setIsHalfHourActivated] = useState<boolean>(true) const [isLoading, setIsLoading] = useState<boolean>(true) @@ -88,7 +88,7 @@ const ElecHalfHourMonthlyAnalysis = ({ if (!subscribed) return if (isHalfHourLoadActivated) { const emas = new EnedisMonthlyAnalysisDataService(client) - const aggregatedDate = analysisDate.minus({ month: 1 }) + const aggregatedDate = analysisMonth.minus({ month: 1 }) const data = await emas.getEnedisMonthlyAnalysisByDate( aggregatedDate.year, aggregatedDate.month @@ -113,7 +113,7 @@ const ElecHalfHourMonthlyAnalysis = ({ return () => { subscribed = false } - }, [analysisDate, client, perfIndicator]) + }, [analysisMonth, client, perfIndicator]) useEffect(() => { let subscribed = true @@ -121,7 +121,7 @@ const ElecHalfHourMonthlyAnalysis = ({ async function getAllLastPrices() { const price: FluidPrice = await fluidsPricesService.getPrices( FluidType.ELECTRICITY, - analysisDate.minus({ month: 1 }) + analysisMonth.minus({ month: 1 }) ) if (subscribed && price) { setElecPrice(price) @@ -132,7 +132,7 @@ const ElecHalfHourMonthlyAnalysis = ({ return () => { subscribed = false } - }, [analysisDate, client]) + }, [analysisMonth, client]) return ( <div className="special-elec-container"> diff --git a/src/components/Analysis/MaxConsumptionCard.spec.tsx b/src/components/Analysis/MaxConsumptionCard.spec.tsx index 5a3ac902d..717f549a0 100644 --- a/src/components/Analysis/MaxConsumptionCard.spec.tsx +++ b/src/components/Analysis/MaxConsumptionCard.spec.tsx @@ -1,12 +1,12 @@ import { FluidType } from 'enum/fluid.enum' import { mount } from 'enzyme' -import { DateTime } from 'luxon' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import configureStore from 'redux-mock-store' +import { graphData } from '../../../tests/__mocks__/chartData.mock' import mockClient from '../../../tests/__mocks__/client' -import { globalStateData } from '../../../tests/__mocks__/globalStateData.mock' +import { mockAnalysisState } from '../../../tests/__mocks__/store' +import { waitForComponentToPaint } from '../../../tests/__mocks__/testUtils' import MaxConsumptionCard from './MaxConsumptionCard' jest.mock('cozy-ui/transpiled/react/I18n', () => { @@ -20,10 +20,12 @@ jest.mock('cozy-ui/transpiled/react/I18n', () => { }) const mockGetMaxLoad = jest.fn(() => 0) +const mockGetGraphData = jest.fn(() => graphData) jest.mock('services/consumption.service', () => { return jest.fn(() => { return { getMaxLoad: mockGetMaxLoad, + getGraphData: mockGetGraphData, } }) }) @@ -35,76 +37,72 @@ jest.mock('cozy-client', () => { } }) +jest.mock('components/Charts/BarChart', () => 'mock-BarChart') + const mockStore = configureStore([]) -const mockUseSelector = jest.spyOn(reactRedux, 'useSelector') describe('MaxConsumptionCard component', () => { - it('should be rendered correctly', () => { + it('should be rendered correctly', async () => { const store = mockStore({ ecolyo: { - global: globalStateData, + global: { + fluidTypes: [FluidType.ELECTRICITY, FluidType.GAS], + }, + analysis: mockAnalysisState, }, }) - mockUseSelector.mockReturnValue({ - fluidTypes: [FluidType.ELECTRICITY, FluidType.GAS], - }) + const wrapper = mount( <Provider store={store}> - <MaxConsumptionCard - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} - /> + <MaxConsumptionCard /> </Provider> - ).getElement() + ) + await waitForComponentToPaint(wrapper) expect(wrapper).toMatchSnapshot() + expect(wrapper.find('mock-BarChart').exists()).toBeTruthy() }) - it('should be rendered with one fluid and not display arrows', () => { + it('should be rendered with one fluid and not display arrows', async () => { const store = mockStore({ ecolyo: { - global: globalStateData, + global: { fluidTypes: [FluidType.ELECTRICITY] }, + analysis: mockAnalysisState, }, }) - mockUseSelector.mockReturnValue({ fluidTypes: [FluidType.ELECTRICITY] }) const wrapper = mount( <Provider store={store}> - <MaxConsumptionCard - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} - /> + <MaxConsumptionCard /> </Provider> ) + await waitForComponentToPaint(wrapper) expect(wrapper.find('.arrow').exists()).toBeFalsy() }) it('should be rendered with several fluids and click navigate between fluid', async () => { const store = mockStore({ ecolyo: { - global: globalStateData, + global: { fluidTypes: [FluidType.ELECTRICITY, FluidType.GAS] }, + analysis: mockAnalysisState, }, }) - mockUseSelector.mockReturnValue({ - fluidTypes: [FluidType.ELECTRICITY, FluidType.GAS], - }) const wrapper = mount( <Provider store={store}> - <MaxConsumptionCard - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} - /> + <MaxConsumptionCard /> </Provider> ) + await waitForComponentToPaint(wrapper) expect(wrapper.find('.arrow-next').exists()).toBeTruthy() // navigate next wrapper.find('.arrow-next').first().simulate('click') + await waitForComponentToPaint(wrapper) expect(wrapper.find('.fluid').text()).toBe('FLUID.GAS.LABEL') wrapper.find('.arrow-next').first().simulate('click') + await waitForComponentToPaint(wrapper) expect(wrapper.find('.fluid').text()).toBe('FLUID.ELECTRICITY.LABEL') // navigate prev wrapper.find('.arrow-prev').first().simulate('click') + await waitForComponentToPaint(wrapper) expect(wrapper.find('.fluid').text()).toBe('FLUID.GAS.LABEL') wrapper.find('.arrow-prev').first().simulate('click') + await waitForComponentToPaint(wrapper) expect(wrapper.find('.fluid').text()).toBe('FLUID.ELECTRICITY.LABEL') }) }) diff --git a/src/components/Analysis/MaxConsumptionCard.tsx b/src/components/Analysis/MaxConsumptionCard.tsx index af62ee107..421891778 100644 --- a/src/components/Analysis/MaxConsumptionCard.tsx +++ b/src/components/Analysis/MaxConsumptionCard.tsx @@ -2,72 +2,84 @@ import IconButton from '@material-ui/core/IconButton' import GraphIcon from 'assets/icons/ico/graph-icon.svg' import LeftArrowIcon from 'assets/icons/ico/left-arrow.svg' import RightArrowIcon from 'assets/icons/ico/right-arrow.svg' +import BarChart from 'components/Charts/BarChart' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' +import DataloadSection from 'components/ConsumptionVisualizer/DataloadSection' +import { useChartResize } from 'components/Hooks/useChartResize' import Loader from 'components/Loader/Loader' import { useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' +import { DataloadSectionType } from 'enum/dataload.enum' import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' -import { DateTime } from 'luxon' -import { Dataload, TimePeriod } from 'models' -import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' +import { Datachart, Dataload, TimePeriod } from 'models' +import React, { Dispatch, useEffect, useRef, useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' import ConsumptionDataManager from 'services/consumption.service' -import { AppStore } from 'store' -import { getNavPicto } from 'utils/picto' -import { formatNumberValues } from 'utils/utils' +import { AppActionsTypes, AppStore } from 'store' +import { setSelectedDate } from 'store/chart/chart.slice' import './maxConsumptionCard.scss' -const MaxConsumptionCard = ({ analysisDate }: { analysisDate: DateTime }) => { +const MaxConsumptionCard = () => { const { t } = useI18n() const client = useClient() - - const { fluidTypes } = useSelector((state: AppStore) => state.ecolyo.global) + const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const { + global: { fluidTypes }, + analysis: { analysisMonth }, + } = useSelector((state: AppStore) => state.ecolyo) const [index, setIndex] = useState<number>(0) - + const [isLoading, setIsLoading] = useState<boolean>(true) const [maxDayData, setMaxDayData] = useState<Dataload | null>(null) - const [isLoading, setIsLoading] = useState<boolean>(false) + const [chartData, setChartData] = useState<Datachart>({ + actualData: [], + comparisonData: null, + }) + const containerRef = useRef<HTMLDivElement>(null) + const { height, width } = useChartResize(containerRef, 250, 940) - const handleChangePrevFluid = useCallback(() => { - setIsLoading(true) - if (index === 0) { - setIndex(fluidTypes.length - 1) - } else { - setIndex(prev => prev - 1) - } - }, [fluidTypes, index]) + const currentFluidType = FluidType[fluidTypes[index]] as + | 'ELECTRICITY' + | 'WATER' + | 'GAZ' + const fluidColor = currentFluidType.toLowerCase() - const handleChangeNextFluid = useCallback(() => { + const handleFluidChange = (direction: number) => { setIsLoading(true) - if (index === fluidTypes.length - 1) { - setIndex(0) - } else { - setIndex(prev => prev + 1) + let newIndex = index + direction + if (newIndex >= fluidTypes.length) { + newIndex = 0 + } else if (newIndex < 0) { + newIndex = fluidTypes.length - 1 } - }, [fluidTypes, index]) + setIndex(newIndex) + } useEffect(() => { let subscribed = true async function getMaxLoadData() { setIsLoading(true) const timePeriod: TimePeriod = { - startDate: analysisDate.minus({ month: 1 }).startOf('month'), - endDate: analysisDate.minus({ month: 1 }).endOf('month'), + startDate: analysisMonth.minus({ month: 1 }).startOf('month'), + endDate: analysisMonth.minus({ month: 1 }).endOf('month'), } const consumptionService = new ConsumptionDataManager(client) - const monthMaxData = await consumptionService.getMaxLoad( + const monthlyData = await consumptionService.getGraphData( timePeriod, TimeStep.DAY, - [fluidTypes[index]], - undefined, - false, - true + [fluidTypes[index]] ) - if (monthMaxData) { - setMaxDayData(monthMaxData as Dataload) - } else { - setMaxDayData(null) + + if (monthlyData && monthlyData?.actualData.length > 0) { + setChartData(monthlyData) + const maxDay = getMaxConsumptionDay(monthlyData.actualData) + if (maxDay) { + setMaxDayData(maxDay) + dispatch(setSelectedDate(maxDay.date)) + } else { + setMaxDayData(null) + } } setIsLoading(false) } @@ -77,40 +89,59 @@ const MaxConsumptionCard = ({ analysisDate }: { analysisDate: DateTime }) => { return () => { subscribed = false } - }, [analysisDate, client, fluidTypes, index]) + }, [analysisMonth, client, fluidTypes, index, dispatch]) + + const getMaxConsumptionDay = (dataload: Dataload[]) => { + let maxIndex = -1 + let maxValue = -1 + dataload.forEach((day, index) => { + if (day.value > 0 && day.value > maxValue) { + maxValue = day.value + maxIndex = index + } + }) + if (maxIndex === -1) return null + return dataload[maxIndex] + } + + const buttonPrev = () => ( + <IconButton + aria-label={t('consumption.accessibility.button_previous_value')} + onClick={() => handleFluidChange(-1)} + className="arrow-prev" + > + <Icon icon={LeftArrowIcon} size={24} /> + </IconButton> + ) + + const buttonNext = () => ( + <IconButton + aria-label={t('consumption.accessibility.button_next_value')} + onClick={() => handleFluidChange(1)} + className="arrow-next" + > + <Icon icon={RightArrowIcon} size={24} /> + </IconButton> + ) + return ( - <div className="max-consumption-container"> + <div className="max-consumption-container" ref={containerRef}> <StyledIcon icon={GraphIcon} size={38} /> <div className="text-16-normal title">{t('analysis.max_day')}</div> <div className="fluid-navigation"> - {fluidTypes.length > 1 && ( - <IconButton - aria-label={t('consumption.accessibility.button_previous_value')} - onClick={handleChangePrevFluid} - className="arrow-prev" - > - <Icon icon={LeftArrowIcon} size={24} /> - </IconButton> - )} - <div - className={`text-20-bold fluid ${FluidType[ - fluidTypes[index] - ].toLowerCase()}`} - > - {`${t('FLUID.' + FluidType[fluidTypes[index]] + '.LABEL')}`} + {fluidTypes.length > 1 && buttonPrev()} + <div className={`text-20-bold fluid ${fluidColor}`}> + {t(`FLUID.${currentFluidType}.LABEL`)} </div> - {fluidTypes.length > 1 && ( - <IconButton - aria-label={t('consumption.accessibility.button_previous_value')} - onClick={handleChangeNextFluid} - className="arrow-next" - > - <Icon icon={RightArrowIcon} size={24} /> - </IconButton> - )} + {fluidTypes.length > 1 && buttonNext()} </div> + <div className="data-container"> - {isLoading && <Loader fluidType={fluidTypes[index]} />} + {isLoading && ( + <div className="loaderContainer"> + <Loader fluidType={fluidTypes[index]} /> + </div> + )} {!isLoading && ( <> {!maxDayData && ( @@ -121,22 +152,23 @@ const MaxConsumptionCard = ({ analysisDate }: { analysisDate: DateTime }) => { <div className="text-24-bold maxDay-date"> {maxDayData.date.setLocale('fr').toFormat('cccc dd LLLL')} </div> - <Icon - className="dataloadvisualizer-euro-fluid-icon" - icon={getNavPicto(fluidTypes[index], true, true)} - size={38} - /> - <div className="maxDay-load"> - {formatNumberValues( - maxDayData.value, - FluidType[fluidTypes[index]] - )}{' '} - {t( - `FLUID.${FluidType[fluidTypes[index]]}.${ - maxDayData.value >= 1000 ? 'MEGAUNIT' : 'UNIT' - }` - )} + <div> + <DataloadSection + dataload={maxDayData} + fluidType={fluidTypes[index]} + dataloadSectionType={DataloadSectionType.NO_COMPARE} + toggleEstimationModal={() => null} + /> </div> + <BarChart + chartData={chartData} + fluidType={fluidTypes[index]} + timeStep={TimeStep.DAY} + height={height} + width={width} + isSwitching={false} + clickable={false} + /> </> )} </> diff --git a/src/components/Analysis/MonthlyAnalysis.spec.tsx b/src/components/Analysis/MonthlyAnalysis.spec.tsx index 3e3322e34..673cc0132 100644 --- a/src/components/Analysis/MonthlyAnalysis.spec.tsx +++ b/src/components/Analysis/MonthlyAnalysis.spec.tsx @@ -1,12 +1,11 @@ import MonthlyAnalysis from 'components/Analysis/MonthlyAnalysis' import { FluidType } from 'enum/fluid.enum' import { mount } from 'enzyme' -import { DateTime } from 'luxon' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' +import { BrowserRouter } from 'react-router-dom' import configureStore from 'redux-mock-store' -import { globalStateData } from '../../../tests/__mocks__/globalStateData.mock' +import { mockAnalysisState } from '../../../tests/__mocks__/store' jest.mock('cozy-ui/transpiled/react/I18n', () => { return { @@ -19,32 +18,27 @@ jest.mock('cozy-ui/transpiled/react/I18n', () => { }) jest.mock('services/consumption.service') jest.mock('components/Analysis/ComparisonView', () => 'mock-comparison-view') +jest.mock('components/Analysis/AnalysisConsumption', () => 'mock-analysis') const mockStore = configureStore([]) -const mockUseSelector = jest.spyOn(reactRedux, 'useSelector') - describe('MonthlyAnalysis component', () => { - mockUseSelector.mockReturnValue({ - fluidTypes: [FluidType.ELECTRICITY, FluidType.GAS], - }) - - it('should be rendered correctly', () => { + it('should be rendered correctly', async () => { const store = mockStore({ ecolyo: { - global: globalStateData, + global: { fluidTypes: [FluidType.ELECTRICITY, FluidType.GAS] }, + analysis: mockAnalysisState, }, }) const wrapper = mount( - <Provider store={store}> - <MonthlyAnalysis - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} - saveLastScrollPosition={jest.fn()} - scrollPosition={0} - /> - </Provider> + <BrowserRouter> + <Provider store={store}> + <MonthlyAnalysis + saveLastScrollPosition={jest.fn()} + scrollPosition={0} + /> + </Provider> + </BrowserRouter> ).getElement() expect(wrapper).toMatchSnapshot() }) diff --git a/src/components/Analysis/MonthlyAnalysis.tsx b/src/components/Analysis/MonthlyAnalysis.tsx index ce883fbce..6c8ff0f11 100644 --- a/src/components/Analysis/MonthlyAnalysis.tsx +++ b/src/components/Analysis/MonthlyAnalysis.tsx @@ -2,7 +2,6 @@ import Loader from 'components/Loader/Loader' import { useClient } from 'cozy-client' import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' -import { DateTime } from 'luxon' import { PerformanceIndicator } from 'models' import React, { useEffect, useState } from 'react' import { useSelector } from 'react-redux' @@ -19,53 +18,52 @@ import TotalAnalysisChart from './TotalAnalysisChart' import './monthlyanalysis.scss' interface MonthlyAnalysisProps { - analysisDate: DateTime saveLastScrollPosition: () => void scrollPosition: number } const MonthlyAnalysis = ({ - analysisDate, saveLastScrollPosition, scrollPosition, }: MonthlyAnalysisProps) => { const client = useClient() - const { fluidTypes } = useSelector((state: AppStore) => state.ecolyo.global) + const { + analysis: { analysisMonth }, + global: { fluidTypes }, + } = useSelector((state: AppStore) => state.ecolyo) const [performanceIndicators, setPerformanceIndicators] = useState< PerformanceIndicator[] >([]) - const [loadAnalysis, setLoadAnalysis] = useState<boolean>(false) const [aggregatedPerformanceIndicators, setAggregatedPerformanceIndicators] = useState<PerformanceIndicator>({ value: 0, compareValue: 0, percentageVariation: 0, }) - const [isLoading, setIsLoading] = useState<boolean>(true) + const [loadAnalysis, setLoadAnalysis] = useState<boolean>(true) const configService = new ConfigService() const fluidConfig = configService.getFluidConfig() - const timeStep = TimeStep.MONTH useEffect(() => { let subscribed = true async function populateData() { - setIsLoading(true) + setLoadAnalysis(true) const consumptionService = new ConsumptionService(client) const performanceIndicatorService = new PerformanceIndicatorService() const periods = { timePeriod: { - startDate: analysisDate.minus({ month: 1 }).startOf('month'), - endDate: analysisDate.minus({ month: 1 }).endOf('month'), + startDate: analysisMonth.minus({ month: 1 }).startOf('month'), + endDate: analysisMonth.minus({ month: 1 }).endOf('month'), }, comparisonTimePeriod: { - startDate: analysisDate.minus({ month: 2 }).startOf('month'), - endDate: analysisDate.minus({ month: 2 }).endOf('month'), + startDate: analysisMonth.minus({ month: 2 }).startOf('month'), + endDate: analysisMonth.minus({ month: 2 }).endOf('month'), }, } const fetchedPerformanceIndicators = await consumptionService.getPerformanceIndicators( periods.timePeriod, - timeStep, + TimeStep.MONTH, fluidTypes, periods.comparisonTimePeriod ) @@ -82,7 +80,7 @@ const MonthlyAnalysis = ({ ) ) } - setIsLoading(false) + setLoadAnalysis(false) } } populateData() @@ -91,73 +89,60 @@ const MonthlyAnalysis = ({ saveLastScrollPosition() subscribed = false } - }, [client, timeStep, fluidTypes, analysisDate, saveLastScrollPosition]) + }, [client, fluidTypes, analysisMonth, saveLastScrollPosition]) useEffect(() => { - if (!isLoading) { + if (!loadAnalysis) { const app = document.querySelector('.app-content') window.scrollTo(0, scrollPosition) app?.scrollTo(0, scrollPosition) } - }, [isLoading, scrollPosition]) + }, [loadAnalysis, scrollPosition]) return ( <> - {isLoading && ( + {loadAnalysis && ( <div className="analysis-container-spinner" aria-busy="true"> <Loader /> </div> )} - {!isLoading && ( + {!loadAnalysis && ( <div className="analysis-root black"> {fluidTypes.length >= 1 ? ( <> <div className="analysis-content"> - <ComparisonView - analysisDate={analysisDate} - fluidConfig={fluidConfig} - /> + <ComparisonView fluidConfig={fluidConfig} /> </div> - - {loadAnalysis && ( - <> - <div className="analysis-content"> - <div className="card rich-card"> - <TotalAnalysisChart - analysisDate={analysisDate} - fluidTypes={fluidTypes} - /> - </div> - </div> - <div className="analysis-content"> - <div className="card rich-card"> - <MaxConsumptionCard analysisDate={analysisDate} /> - </div> - </div> - <div className="analysis-content"> - <div className="card rich-card"> - <AnalysisConsumption - aggregatedPerformanceIndicator={ - aggregatedPerformanceIndicators - } - performanceIndicators={performanceIndicators} - analysisDate={analysisDate} - /> - </div> + <div className="analysis-content"> + <div className="card rich-card"> + <TotalAnalysisChart fluidTypes={fluidTypes} /> + </div> + </div> + <div className="analysis-content"> + <div className="card rich-card"> + <MaxConsumptionCard /> + </div> + </div> + <div className="analysis-content"> + <div className="card rich-card"> + <AnalysisConsumption + aggregatedPerformanceIndicator={ + aggregatedPerformanceIndicators + } + performanceIndicators={performanceIndicators} + /> + </div> + </div> + {fluidTypes.includes(FluidType.ELECTRICITY) && ( + <div className="analysis-content"> + <div className="card"> + <ElecHalfHourMonthlyAnalysis + perfIndicator={ + performanceIndicators[FluidType.ELECTRICITY] + } + /> </div> - {fluidTypes.includes(FluidType.ELECTRICITY) && ( - <div className="analysis-content"> - <div className="card"> - <ElecHalfHourMonthlyAnalysis - analysisDate={analysisDate} - perfIndicator={ - performanceIndicators[FluidType.ELECTRICITY] - } - /> - </div> - </div> - )} - </> + </div> )} </> ) : ( diff --git a/src/components/Analysis/PieChart.tsx b/src/components/Analysis/PieChart.tsx index 3570646e7..5a215454d 100644 --- a/src/components/Analysis/PieChart.tsx +++ b/src/components/Analysis/PieChart.tsx @@ -4,8 +4,7 @@ import * as d3 from 'd3' import { DateTime } from 'luxon' import { DataloadValueDetail } from 'models' import React, { useCallback, useEffect, useRef, useState } from 'react' -import { convertDateToMonthString } from 'utils/date' -import { formatNumberValues } from 'utils/utils' +import { formatNumberValues, getMonthNameWithPrep } from 'utils/utils' import './totalAnalysisChart.scss' interface PieProps { @@ -102,7 +101,7 @@ const PieChart = ({ </div> <div className="text-16-normal date"> {t('analysis_pie.month') + - convertDateToMonthString(currentAnalysisDate).substring(3)} + getMonthNameWithPrep(currentAnalysisDate.minus({ month: 1 }))} </div> <div className="text-14-normal estimation-text" diff --git a/src/components/Analysis/TotalAnalysisChart.spec.tsx b/src/components/Analysis/TotalAnalysisChart.spec.tsx index 352503150..f214ab9c2 100644 --- a/src/components/Analysis/TotalAnalysisChart.spec.tsx +++ b/src/components/Analysis/TotalAnalysisChart.spec.tsx @@ -6,8 +6,9 @@ import { Datachart } from 'models' import React from 'react' import { act } from 'react-dom/test-utils' import { Provider } from 'react-redux' +import configureStore from 'redux-mock-store' import { graphMonthData } from '../../../tests/__mocks__/chartData.mock' -import { createMockEcolyoStore } from '../../../tests/__mocks__/store' +import { mockAnalysisState } from '../../../tests/__mocks__/store' import TotalAnalysisChart from './TotalAnalysisChart' jest.mock('cozy-ui/transpiled/react/I18n', () => { @@ -29,21 +30,17 @@ jest.mock('services/consumption.service', () => { }) jest.mock('components/Analysis/PieChart', () => 'mock-piechart') +const mockStore = configureStore([]) +const store = mockStore({ + ecolyo: { + analysis: mockAnalysisState, + }, +}) describe('TotalAnalysisChart component', () => { - const store = createMockEcolyoStore() - beforeEach(() => { - store.clearActions() - }) - it('should be rendered correctly', () => { const wrapper = mount( <Provider store={store}> - <TotalAnalysisChart - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} - fluidTypes={[FluidType.ELECTRICITY]} - /> + <TotalAnalysisChart fluidTypes={[FluidType.ELECTRICITY]} /> </Provider> ).getElement() expect(wrapper).toMatchSnapshot() @@ -53,9 +50,6 @@ describe('TotalAnalysisChart component', () => { const wrapper = mount( <Provider store={store}> <TotalAnalysisChart - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} fluidTypes={[FluidType.ELECTRICITY, FluidType.WATER, FluidType.GAS]} /> </Provider> @@ -84,9 +78,6 @@ describe('TotalAnalysisChart component', () => { const wrapper = mount( <Provider store={store}> <TotalAnalysisChart - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} fluidTypes={[FluidType.ELECTRICITY, FluidType.WATER]} /> </Provider> @@ -114,12 +105,7 @@ describe('TotalAnalysisChart component', () => { mockgetGraphData.mockResolvedValueOnce(emptyData) const wrapper = mount( <Provider store={store}> - <TotalAnalysisChart - analysisDate={DateTime.fromISO('2021-07-01T00:00:00.000Z', { - zone: 'utc', - })} - fluidTypes={[FluidType.ELECTRICITY]} - /> + <TotalAnalysisChart fluidTypes={[FluidType.ELECTRICITY]} /> </Provider> ) await act(async () => { diff --git a/src/components/Analysis/TotalAnalysisChart.tsx b/src/components/Analysis/TotalAnalysisChart.tsx index 62ab39c13..1166bbc8c 100644 --- a/src/components/Analysis/TotalAnalysisChart.tsx +++ b/src/components/Analysis/TotalAnalysisChart.tsx @@ -3,25 +3,21 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' -import { DateTime } from 'luxon' import { DataloadValueDetail, TimePeriod } from 'models' import React, { useEffect, useState } from 'react' +import { useSelector } from 'react-redux' import ConsumptionDataManager from 'services/consumption.service' +import { AppStore } from 'store' import { getNavPicto } from 'utils/picto' import { formatNumberValues } from 'utils/utils' import PieChart from './PieChart' import './analysisView.scss' import './totalAnalysisChart.scss' -interface TotalAnalysisChartProps { - analysisDate: DateTime - fluidTypes: FluidType[] -} - -const TotalAnalysisChart = ({ - analysisDate, - fluidTypes, -}: TotalAnalysisChartProps) => { +const TotalAnalysisChart = ({ fluidTypes }: { fluidTypes: FluidType[] }) => { + const { analysisMonth } = useSelector( + (state: AppStore) => state.ecolyo.analysis + ) const [dataLoadValueDetailArray, setDataLoadValueDetailArray] = useState< DataloadValueDetail[] | null >(null) @@ -37,8 +33,8 @@ const TotalAnalysisChart = ({ let subscribed = true async function getTotalData() { const timePeriod: TimePeriod = { - startDate: analysisDate.minus({ month: 1 }).startOf('month'), - endDate: analysisDate.minus({ month: 1 }).endOf('month'), + startDate: analysisMonth.minus({ month: 1 }).startOf('month'), + endDate: analysisMonth.minus({ month: 1 }).endOf('month'), } const consumptionService = new ConsumptionDataManager(client) const monthTotalData = await consumptionService.getGraphData( @@ -60,7 +56,8 @@ const TotalAnalysisChart = ({ return () => { subscribed = false } - }, [analysisDate, client, fluidTypes]) + }, [analysisMonth, client, fluidTypes]) + return ( <div className="totalAnalysis-container" @@ -77,9 +74,7 @@ const TotalAnalysisChart = ({ height={radius} innerRadius={innerRadius} outerRadius={outerRadius} - currentAnalysisDate={analysisDate - .minus({ month: 1 }) - .startOf('month')} + currentAnalysisDate={analysisMonth} /> )} {dataLoadValueDetailArray && fluidTypes.length > 1 && ( diff --git a/src/components/Analysis/__snapshots__/ElecHalfHourMonthlyAnalysis.spec.tsx.snap b/src/components/Analysis/__snapshots__/ElecHalfHourMonthlyAnalysis.spec.tsx.snap index 1e836d9ae..001bf7012 100644 --- a/src/components/Analysis/__snapshots__/ElecHalfHourMonthlyAnalysis.spec.tsx.snap +++ b/src/components/Analysis/__snapshots__/ElecHalfHourMonthlyAnalysis.spec.tsx.snap @@ -1,389 +1,413 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`ElecHalfHourMonthlyAnalysis component should be rendered correctly when isHalfHourActivated is false 1`] = ` -<ElecHalfHourMonthlyAnalysis - analysisDate={"2021-07-01T00:00:00.000Z"} - perfIndicator={ +<Provider + store={ Object { - "compareValue": null, - "percentageVariation": null, - "price": null, - "value": 10, + "clearActions": [Function], + "dispatch": [Function], + "getActions": [Function], + "getState": [Function], + "replaceReducer": [Function], + "subscribe": [Function], } } > - <div - className="special-elec-container" + <ElecHalfHourMonthlyAnalysis + perfIndicator={ + Object { + "compareValue": null, + "percentageVariation": null, + "price": null, + "value": 10, + } + } > - <Icon - className="elec-icon" - icon="test-file-stub" - size={42} - spin={false} + <div + className="special-elec-container" > - <Component - className="elec-icon styles__icon___23x3R" - height={42} - style={Object {}} - width={42} + <Icon + className="elec-icon" + icon="test-file-stub" + size={42} + spin={false} > - <svg + <Component className="elec-icon styles__icon___23x3R" height={42} style={Object {}} width={42} > - <use - xlinkHref="#test-file-stub" - /> - </svg> - </Component> - </Icon> - <div - className="text-18-normal title" - > - special_elec.title - </div> - <div - className="activation-text text-18-normal" - > - timestep.half_an_hour.analysis_waiting_data + <svg + className="elec-icon styles__icon___23x3R" + height={42} + style={Object {}} + width={42} + > + <use + xlinkHref="#test-file-stub" + /> + </svg> + </Component> + </Icon> + <div + className="text-18-normal title" + > + special_elec.title + </div> + <div + className="activation-text text-18-normal" + > + timestep.half_an_hour.analysis_waiting_data + </div> + <mock-elecinfomodal + handleCloseClick={[Function]} + open={false} + /> </div> - <mock-elecinfomodal - handleCloseClick={[Function]} - open={false} - /> - </div> -</ElecHalfHourMonthlyAnalysis> + </ElecHalfHourMonthlyAnalysis> +</Provider> `; exports[`ElecHalfHourMonthlyAnalysis component should be rendered correctly when isHalfHourActivated is true 1`] = ` -<ElecHalfHourMonthlyAnalysis - analysisDate={"2021-07-01T00:00:00.000Z"} - perfIndicator={ +<Provider + store={ Object { - "compareValue": null, - "percentageVariation": null, - "price": null, - "value": 10, + "clearActions": [Function], + "dispatch": [Function], + "getActions": [Function], + "getState": [Function], + "replaceReducer": [Function], + "subscribe": [Function], } } > - <div - className="special-elec-container" + <ElecHalfHourMonthlyAnalysis + perfIndicator={ + Object { + "compareValue": null, + "percentageVariation": null, + "price": null, + "value": 10, + } + } > - <Icon - className="elec-icon" - icon="test-file-stub" - size={42} - spin={false} + <div + className="special-elec-container" > - <Component - className="elec-icon styles__icon___23x3R" - height={42} - style={Object {}} - width={42} + <Icon + className="elec-icon" + icon="test-file-stub" + size={42} + spin={false} > - <svg + <Component className="elec-icon styles__icon___23x3R" height={42} style={Object {}} width={42} > - <use - xlinkHref="#test-file-stub" - /> - </svg> - </Component> - </Icon> - <div - className="text-18-normal title" - > - special_elec.title - </div> - <div - className="navigator" - > - <WithStyles(ForwardRef(IconButton)) - aria-label="consumption.accessibility.button_previous_value" - className="arrow-prev" - onClick={[Function]} + <svg + className="elec-icon styles__icon___23x3R" + height={42} + style={Object {}} + width={42} + > + <use + xlinkHref="#test-file-stub" + /> + </svg> + </Component> + </Icon> + <div + className="text-18-normal title" > - <ForwardRef(IconButton) + special_elec.title + </div> + <div + className="navigator" + > + <WithStyles(ForwardRef(IconButton)) aria-label="consumption.accessibility.button_previous_value" className="arrow-prev" - classes={ - Object { - "colorInherit": "MuiIconButton-colorInherit", - "colorPrimary": "MuiIconButton-colorPrimary", - "colorSecondary": "MuiIconButton-colorSecondary", - "disabled": "Mui-disabled", - "edgeEnd": "MuiIconButton-edgeEnd", - "edgeStart": "MuiIconButton-edgeStart", - "label": "MuiIconButton-label", - "root": "MuiIconButton-root", - "sizeSmall": "MuiIconButton-sizeSmall", - } - } onClick={[Function]} > - <WithStyles(ForwardRef(ButtonBase)) + <ForwardRef(IconButton) aria-label="consumption.accessibility.button_previous_value" - centerRipple={true} - className="MuiIconButton-root arrow-prev" - disabled={false} - focusRipple={true} + className="arrow-prev" + classes={ + Object { + "colorInherit": "MuiIconButton-colorInherit", + "colorPrimary": "MuiIconButton-colorPrimary", + "colorSecondary": "MuiIconButton-colorSecondary", + "disabled": "Mui-disabled", + "edgeEnd": "MuiIconButton-edgeEnd", + "edgeStart": "MuiIconButton-edgeStart", + "label": "MuiIconButton-label", + "root": "MuiIconButton-root", + "sizeSmall": "MuiIconButton-sizeSmall", + } + } onClick={[Function]} > - <ForwardRef(ButtonBase) + <WithStyles(ForwardRef(ButtonBase)) aria-label="consumption.accessibility.button_previous_value" centerRipple={true} className="MuiIconButton-root arrow-prev" - classes={ - Object { - "disabled": "Mui-disabled", - "focusVisible": "Mui-focusVisible", - "root": "MuiButtonBase-root", - } - } disabled={false} focusRipple={true} onClick={[Function]} > - <button + <ForwardRef(ButtonBase) aria-label="consumption.accessibility.button_previous_value" - className="MuiButtonBase-root MuiIconButton-root arrow-prev" + centerRipple={true} + className="MuiIconButton-root arrow-prev" + classes={ + Object { + "disabled": "Mui-disabled", + "focusVisible": "Mui-focusVisible", + "root": "MuiButtonBase-root", + } + } disabled={false} - onBlur={[Function]} + focusRipple={true} onClick={[Function]} - onDragLeave={[Function]} - onFocus={[Function]} - onKeyDown={[Function]} - onKeyUp={[Function]} - onMouseDown={[Function]} - onMouseLeave={[Function]} - onMouseUp={[Function]} - onTouchEnd={[Function]} - onTouchMove={[Function]} - onTouchStart={[Function]} - tabIndex={0} - type="button" > - <span - className="MuiIconButton-label" + <button + aria-label="consumption.accessibility.button_previous_value" + className="MuiButtonBase-root MuiIconButton-root arrow-prev" + disabled={false} + onBlur={[Function]} + onClick={[Function]} + onDragLeave={[Function]} + onFocus={[Function]} + onKeyDown={[Function]} + onKeyUp={[Function]} + onMouseDown={[Function]} + onMouseLeave={[Function]} + onMouseUp={[Function]} + onTouchEnd={[Function]} + onTouchMove={[Function]} + onTouchStart={[Function]} + tabIndex={0} + type="button" > - <Icon - icon="test-file-stub" - size={24} - spin={false} + <span + className="MuiIconButton-label" > - <Component - className="styles__icon___23x3R" - height={24} - style={Object {}} - width={24} + <Icon + icon="test-file-stub" + size={24} + spin={false} > - <svg + <Component className="styles__icon___23x3R" height={24} style={Object {}} width={24} > - <use - xlinkHref="#test-file-stub" - /> - </svg> - </Component> - </Icon> - </span> - <WithStyles(memo) - center={true} - > - <ForwardRef(TouchRipple) + <svg + className="styles__icon___23x3R" + height={24} + style={Object {}} + width={24} + > + <use + xlinkHref="#test-file-stub" + /> + </svg> + </Component> + </Icon> + </span> + <WithStyles(memo) center={true} - classes={ - Object { - "child": "MuiTouchRipple-child", - "childLeaving": "MuiTouchRipple-childLeaving", - "childPulsate": "MuiTouchRipple-childPulsate", - "ripple": "MuiTouchRipple-ripple", - "ripplePulsate": "MuiTouchRipple-ripplePulsate", - "rippleVisible": "MuiTouchRipple-rippleVisible", - "root": "MuiTouchRipple-root", - } - } > - <span - className="MuiTouchRipple-root" + <ForwardRef(TouchRipple) + center={true} + classes={ + Object { + "child": "MuiTouchRipple-child", + "childLeaving": "MuiTouchRipple-childLeaving", + "childPulsate": "MuiTouchRipple-childPulsate", + "ripple": "MuiTouchRipple-ripple", + "ripplePulsate": "MuiTouchRipple-ripplePulsate", + "rippleVisible": "MuiTouchRipple-rippleVisible", + "root": "MuiTouchRipple-root", + } + } > - <TransitionGroup - childFactory={[Function]} - component={null} - exit={true} - /> - </span> - </ForwardRef(TouchRipple)> - </WithStyles(memo)> - </button> - </ForwardRef(ButtonBase)> - </WithStyles(ForwardRef(ButtonBase))> - </ForwardRef(IconButton)> - </WithStyles(ForwardRef(IconButton))> - <div - className="average text-18-normal" - > - <div - className="text-1" - > - special_elec.average - </div> + <span + className="MuiTouchRipple-root" + > + <TransitionGroup + childFactory={[Function]} + component={null} + exit={true} + /> + </span> + </ForwardRef(TouchRipple)> + </WithStyles(memo)> + </button> + </ForwardRef(ButtonBase)> + </WithStyles(ForwardRef(ButtonBase))> + </ForwardRef(IconButton)> + </WithStyles(ForwardRef(IconButton))> <div - className="text-2 text-18-bold" + className="average text-18-normal" > - special_elec.weektype - - <span - className="weekend" + <div + className="text-1" > - special_elec.weekend - </span> + special_elec.average + </div> + <div + className="text-2 text-18-bold" + > + special_elec.weektype + + <span + className="weekend" + > + special_elec.weekend + </span> + </div> </div> - </div> - <WithStyles(ForwardRef(IconButton)) - aria-label="consumption.accessibility.button_previous_value" - className="arrow-next" - onClick={[Function]} - > - <ForwardRef(IconButton) + <WithStyles(ForwardRef(IconButton)) aria-label="consumption.accessibility.button_previous_value" className="arrow-next" - classes={ - Object { - "colorInherit": "MuiIconButton-colorInherit", - "colorPrimary": "MuiIconButton-colorPrimary", - "colorSecondary": "MuiIconButton-colorSecondary", - "disabled": "Mui-disabled", - "edgeEnd": "MuiIconButton-edgeEnd", - "edgeStart": "MuiIconButton-edgeStart", - "label": "MuiIconButton-label", - "root": "MuiIconButton-root", - "sizeSmall": "MuiIconButton-sizeSmall", - } - } onClick={[Function]} > - <WithStyles(ForwardRef(ButtonBase)) + <ForwardRef(IconButton) aria-label="consumption.accessibility.button_previous_value" - centerRipple={true} - className="MuiIconButton-root arrow-next" - disabled={false} - focusRipple={true} + className="arrow-next" + classes={ + Object { + "colorInherit": "MuiIconButton-colorInherit", + "colorPrimary": "MuiIconButton-colorPrimary", + "colorSecondary": "MuiIconButton-colorSecondary", + "disabled": "Mui-disabled", + "edgeEnd": "MuiIconButton-edgeEnd", + "edgeStart": "MuiIconButton-edgeStart", + "label": "MuiIconButton-label", + "root": "MuiIconButton-root", + "sizeSmall": "MuiIconButton-sizeSmall", + } + } onClick={[Function]} > - <ForwardRef(ButtonBase) + <WithStyles(ForwardRef(ButtonBase)) aria-label="consumption.accessibility.button_previous_value" centerRipple={true} className="MuiIconButton-root arrow-next" - classes={ - Object { - "disabled": "Mui-disabled", - "focusVisible": "Mui-focusVisible", - "root": "MuiButtonBase-root", - } - } disabled={false} focusRipple={true} onClick={[Function]} > - <button + <ForwardRef(ButtonBase) aria-label="consumption.accessibility.button_previous_value" - className="MuiButtonBase-root MuiIconButton-root arrow-next" + centerRipple={true} + className="MuiIconButton-root arrow-next" + classes={ + Object { + "disabled": "Mui-disabled", + "focusVisible": "Mui-focusVisible", + "root": "MuiButtonBase-root", + } + } disabled={false} - onBlur={[Function]} + focusRipple={true} onClick={[Function]} - onDragLeave={[Function]} - onFocus={[Function]} - onKeyDown={[Function]} - onKeyUp={[Function]} - onMouseDown={[Function]} - onMouseLeave={[Function]} - onMouseUp={[Function]} - onTouchEnd={[Function]} - onTouchMove={[Function]} - onTouchStart={[Function]} - tabIndex={0} - type="button" > - <span - className="MuiIconButton-label" + <button + aria-label="consumption.accessibility.button_previous_value" + className="MuiButtonBase-root MuiIconButton-root arrow-next" + disabled={false} + onBlur={[Function]} + onClick={[Function]} + onDragLeave={[Function]} + onFocus={[Function]} + onKeyDown={[Function]} + onKeyUp={[Function]} + onMouseDown={[Function]} + onMouseLeave={[Function]} + onMouseUp={[Function]} + onTouchEnd={[Function]} + onTouchMove={[Function]} + onTouchStart={[Function]} + tabIndex={0} + type="button" > - <Icon - icon="test-file-stub" - size={24} - spin={false} + <span + className="MuiIconButton-label" > - <Component - className="styles__icon___23x3R" - height={24} - style={Object {}} - width={24} + <Icon + icon="test-file-stub" + size={24} + spin={false} > - <svg + <Component className="styles__icon___23x3R" height={24} style={Object {}} width={24} > - <use - xlinkHref="#test-file-stub" - /> - </svg> - </Component> - </Icon> - </span> - <WithStyles(memo) - center={true} - > - <ForwardRef(TouchRipple) + <svg + className="styles__icon___23x3R" + height={24} + style={Object {}} + width={24} + > + <use + xlinkHref="#test-file-stub" + /> + </svg> + </Component> + </Icon> + </span> + <WithStyles(memo) center={true} - classes={ - Object { - "child": "MuiTouchRipple-child", - "childLeaving": "MuiTouchRipple-childLeaving", - "childPulsate": "MuiTouchRipple-childPulsate", - "ripple": "MuiTouchRipple-ripple", - "ripplePulsate": "MuiTouchRipple-ripplePulsate", - "rippleVisible": "MuiTouchRipple-rippleVisible", - "root": "MuiTouchRipple-root", - } - } > - <span - className="MuiTouchRipple-root" + <ForwardRef(TouchRipple) + center={true} + classes={ + Object { + "child": "MuiTouchRipple-child", + "childLeaving": "MuiTouchRipple-childLeaving", + "childPulsate": "MuiTouchRipple-childPulsate", + "ripple": "MuiTouchRipple-ripple", + "ripplePulsate": "MuiTouchRipple-ripplePulsate", + "rippleVisible": "MuiTouchRipple-rippleVisible", + "root": "MuiTouchRipple-root", + } + } > - <TransitionGroup - childFactory={[Function]} - component={null} - exit={true} - /> - </span> - </ForwardRef(TouchRipple)> - </WithStyles(memo)> - </button> - </ForwardRef(ButtonBase)> - </WithStyles(ForwardRef(ButtonBase))> - </ForwardRef(IconButton)> - </WithStyles(ForwardRef(IconButton))> + <span + className="MuiTouchRipple-root" + > + <TransitionGroup + childFactory={[Function]} + component={null} + exit={true} + /> + </span> + </ForwardRef(TouchRipple)> + </WithStyles(memo)> + </button> + </ForwardRef(ButtonBase)> + </WithStyles(ForwardRef(ButtonBase))> + </ForwardRef(IconButton)> + </WithStyles(ForwardRef(IconButton))> + </div> + <p + className="text-20-bold no_data" + > + analysis.no_data + </p> + <mock-elecinfomodal + handleCloseClick={[Function]} + open={false} + /> </div> - <p - className="text-20-bold no_data" - > - analysis.no_data - </p> - <mock-elecinfomodal - handleCloseClick={[Function]} - open={false} - /> - </div> -</ElecHalfHourMonthlyAnalysis> + </ElecHalfHourMonthlyAnalysis> +</Provider> `; diff --git a/src/components/Analysis/__snapshots__/MaxConsumptionCard.spec.tsx.snap b/src/components/Analysis/__snapshots__/MaxConsumptionCard.spec.tsx.snap index ff4e013f8..a06993e7f 100644 --- a/src/components/Analysis/__snapshots__/MaxConsumptionCard.spec.tsx.snap +++ b/src/components/Analysis/__snapshots__/MaxConsumptionCard.spec.tsx.snap @@ -1,20 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`MaxConsumptionCard component should be rendered correctly 1`] = ` -<Provider - store={ - Object { - "clearActions": [Function], - "dispatch": [Function], - "getActions": [Function], - "getState": [Function], - "replaceReducer": [Function], - "subscribe": [Function], - } - } -> - <MaxConsumptionCard - analysisDate={"2021-07-01T00:00:00.000Z"} - /> -</Provider> -`; +exports[`MaxConsumptionCard component should be rendered correctly 1`] = `ReactWrapper {}`; diff --git a/src/components/Analysis/__snapshots__/MonthlyAnalysis.spec.tsx.snap b/src/components/Analysis/__snapshots__/MonthlyAnalysis.spec.tsx.snap index df6db5b4d..58f132c9c 100644 --- a/src/components/Analysis/__snapshots__/MonthlyAnalysis.spec.tsx.snap +++ b/src/components/Analysis/__snapshots__/MonthlyAnalysis.spec.tsx.snap @@ -1,22 +1,23 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`MonthlyAnalysis component should be rendered correctly 1`] = ` -<Provider - store={ - Object { - "clearActions": [Function], - "dispatch": [Function], - "getActions": [Function], - "getState": [Function], - "replaceReducer": [Function], - "subscribe": [Function], +<BrowserRouter> + <Provider + store={ + Object { + "clearActions": [Function], + "dispatch": [Function], + "getActions": [Function], + "getState": [Function], + "replaceReducer": [Function], + "subscribe": [Function], + } } - } -> - <MonthlyAnalysis - analysisDate={"2021-07-01T00:00:00.000Z"} - saveLastScrollPosition={[MockFunction]} - scrollPosition={0} - /> -</Provider> + > + <MonthlyAnalysis + saveLastScrollPosition={[MockFunction]} + scrollPosition={0} + /> + </Provider> +</BrowserRouter> `; diff --git a/src/components/Analysis/__snapshots__/TotalAnalysisChart.spec.tsx.snap b/src/components/Analysis/__snapshots__/TotalAnalysisChart.spec.tsx.snap index f3c1e0102..7a0694330 100644 --- a/src/components/Analysis/__snapshots__/TotalAnalysisChart.spec.tsx.snap +++ b/src/components/Analysis/__snapshots__/TotalAnalysisChart.spec.tsx.snap @@ -14,7 +14,6 @@ exports[`TotalAnalysisChart component should be rendered correctly 1`] = ` } > <TotalAnalysisChart - analysisDate={"2021-07-01T00:00:00.000Z"} fluidTypes={ Array [ 0, diff --git a/src/components/Analysis/maxConsumptionCard.scss b/src/components/Analysis/maxConsumptionCard.scss index a3da1133e..66ac47a38 100644 --- a/src/components/Analysis/maxConsumptionCard.scss +++ b/src/components/Analysis/maxConsumptionCard.scss @@ -16,6 +16,9 @@ justify-content: space-between; align-items: center; width: 100%; + div { + font-weight: 900; + } } .fluid { min-width: 120px; @@ -40,10 +43,25 @@ flex-direction: column; justify-content: space-between; align-items: center; + + .loaderContainer { + height: 22rem; + display: flex; + } .maxDay-date { text-transform: capitalize; - margin: 0.8rem; + margin: 0.5rem 0; text-align: center; } + .maxDay-load span { + font-size: 2rem; + font-weight: 700; + } } } + +.dataloadvisualizer-section { + display: flex; + flex-direction: column; + align-items: center; +} diff --git a/src/components/Charts/Bar.tsx b/src/components/Charts/Bar.tsx index 9d198936f..57f86f317 100644 --- a/src/components/Charts/Bar.tsx +++ b/src/components/Charts/Bar.tsx @@ -27,6 +27,7 @@ interface BarProps { isDuel?: boolean isMultiMissingFluid?: boolean weekdays?: 'week' | 'weekend' + clickable?: boolean } const Bar = ({ @@ -43,6 +44,7 @@ const Bar = ({ isDuel, isMultiMissingFluid, weekdays, + clickable = true, }: BarProps) => { const dispatch = useDispatch<Dispatch<AppActionsTypes>>() const { selectedDate } = useSelector((state: AppStore) => state.ecolyo.chart) @@ -55,7 +57,7 @@ const Bar = ({ fluidType === FluidType.MULTIFLUID ? 'MULTIFLUID' : FluidType[fluidType] const handleClick = () => { - if (!isSwitching && !isDuel) { + if (!isSwitching && !isDuel && clickable) { setClicked(true) dispatch(setSelectedDate(dataload.date)) } @@ -94,6 +96,7 @@ const Bar = ({ const edgeBrowser = browser && browser.name !== 'edge' const selected = isSelectedDate ? ' selected' : '' const bounce = edgeBrowser ? '1' : '3' + const disabled = `${clickable ? '' : 'disabled'}` const getBarClass = () => { const bounceDelay = ` bounce-${bounce} delay--${delayIndex}` @@ -102,9 +105,9 @@ const Bar = ({ if (clicked) { return `${fluidWeekdays}${selected} bounce-2 delay` } else if (animationEnded) { - return `${fluidWeekdays}${selected}` + return `${fluidWeekdays}${selected} ${disabled}` } - return `${fluidWeekdays}${bounceDelay}${selected}` + return `${fluidWeekdays}${bounceDelay}${selected} ${disabled}` } const getCompareBarClass = () => { @@ -186,7 +189,7 @@ const Bar = ({ {height > 0 && ( <g transform={`translate(${xScaleValue}, -40)`} - className="barContainer" + className={`barContainer ${disabled}`} > <rect onClick={!weekdays ? handleClick : () => undefined} diff --git a/src/components/Charts/BarChart.tsx b/src/components/Charts/BarChart.tsx index 487e359f0..9483e03da 100644 --- a/src/components/Charts/BarChart.tsx +++ b/src/components/Charts/BarChart.tsx @@ -22,6 +22,7 @@ export interface BarChartProps { marginTop?: number marginBottom?: number isSwitching: boolean + clickable?: boolean } const BarChart = ({ @@ -35,6 +36,7 @@ const BarChart = ({ marginTop = 20, marginBottom = 50, isSwitching, + clickable = true, }: BarChartProps) => { const { showCompare } = useSelector((state: AppStore) => state.ecolyo.chart) const getContentWidth = () => { @@ -112,6 +114,7 @@ const BarChart = ({ ? true : false } + clickable={clickable} /> ))} </g> diff --git a/src/components/DateNavigator/DateNavigator.tsx b/src/components/DateNavigator/DateNavigator.tsx index 6dc00f9d9..97a2a39ec 100644 --- a/src/components/DateNavigator/DateNavigator.tsx +++ b/src/components/DateNavigator/DateNavigator.tsx @@ -12,20 +12,20 @@ import React, { Dispatch } from 'react' import { useDispatch, useSelector } from 'react-redux' import DateChartService from 'services/dateChart.service' import { AppActionsTypes, AppStore } from 'store' +import { setAnalysisMonth } from 'store/analysis/analysis.slice' import { setCurrentIndex, setSelectedDate } from 'store/chart/chart.slice' import { isLastDateReached } from 'utils/date' import { isKonnectorActive } from 'utils/utils' import './datenavigator.scss' interface DateNavigatorProps { + /** If a value is given, Navigator is for Analysis view */ currentAnalysisDate?: DateTime - setCurrentAnalysisDate?: React.Dispatch<React.SetStateAction<DateTime>> inlineDateDisplay?: boolean } const DateNavigator = ({ currentAnalysisDate, - setCurrentAnalysisDate, inlineDateDisplay = false, }: DateNavigatorProps) => { const { t } = useI18n() @@ -46,9 +46,10 @@ const DateNavigator = ({ const dateChartService = new DateChartService() + /** Handle date navigation from ConsumptionView and Analysis view, not ideal though */ const handleClickMove = (increment: number) => { if (!currentAnalysisDate) { - const updatedDate: DateTime = dateChartService.incrementeDate( + const updatedDate: DateTime = dateChartService.incrementDate( currentTimeStep, selectedDate, increment @@ -60,12 +61,12 @@ const DateNavigator = ({ dispatch(setSelectedDate(updatedDate)) dispatch(setCurrentIndex(updatedIndex)) } else { - const updatedDate: DateTime = dateChartService.incrementeDate( + const updatedDate: DateTime = dateChartService.incrementDate( TimeStep.MONTH, currentAnalysisDate, increment ) - setCurrentAnalysisDate?.(updatedDate) + dispatch(setAnalysisMonth(updatedDate)) } } diff --git a/src/components/FluidChart/FluidChartSwipe.tsx b/src/components/FluidChart/FluidChartSwipe.tsx index 3d5ddee72..2505001ed 100644 --- a/src/components/FluidChart/FluidChartSwipe.tsx +++ b/src/components/FluidChart/FluidChartSwipe.tsx @@ -42,7 +42,7 @@ const FluidChartSwipe = ({ fluidType, setActive }: FluidChartSwipeProps) => { currentIndex ) } - const updatedDate: DateTime = dateChartService.incrementeDate( + const updatedDate: DateTime = dateChartService.incrementDate( currentTimeStep, selectedDate, increment diff --git a/src/components/Splash/SplashRoot.tsx b/src/components/Splash/SplashRoot.tsx index ead877e8d..655955003 100644 --- a/src/components/Splash/SplashRoot.tsx +++ b/src/components/Splash/SplashRoot.tsx @@ -39,6 +39,7 @@ import PartnersInfoService from 'services/partnersInfo.service' import ProfileTypeEntityService from 'services/profileTypeEntity.service' import UsageEventService from 'services/usageEvent.service' import { AppActionsTypes } from 'store' +import { setAnalysisMonth } from 'store/analysis/analysis.slice' import { setChallengeConsumption, setUserChallengeList, @@ -251,6 +252,7 @@ const SplashRoot = ({ fadeTimer = 1000, children }: SplashRootProps) => { haveSeenLastAnalysis: analysisResult.haveSeenLastAnalysis, } dispatch(updateProfile(updatedProfile)) + dispatch(setAnalysisMonth(analysisResult.monthlyAnalysisDate)) if (profileType) { await loadProfileType(profileType) } diff --git a/src/locales/fr.json b/src/locales/fr.json index 668cd81d6..823bb73b4 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -116,7 +116,7 @@ }, "analysis_pie": { "total": "Conso totale", - "month": "Au mois de ", + "month": "Au mois\u00a0", "estimation": "Comment sont estimés", "estimation2": "les prix\u00a0?" }, diff --git a/src/models/analysis.model.ts b/src/models/analysis.model.ts index ce7da6eec..62a9391f4 100644 --- a/src/models/analysis.model.ts +++ b/src/models/analysis.model.ts @@ -1,11 +1,6 @@ import { DateTime } from 'luxon' -export interface AnalysisAttributes { - sendAnalysisNotification: boolean - haveSeenLastAnalysis: boolean - monthlyAnalysisDate: DateTime -} - export interface AnalysisState { period: 'month' | 'year' + analysisMonth: DateTime } diff --git a/src/services/dateChart.service.spec.ts b/src/services/dateChart.service.spec.ts index 83449b87a..393f02a12 100644 --- a/src/services/dateChart.service.spec.ts +++ b/src/services/dateChart.service.spec.ts @@ -969,7 +969,7 @@ describe('dateChart service', () => { expect(result).toBe(76) }) - it('should return 0 for unkonwn time step', () => { + it('should return 0 for unknown time step', () => { const selectedDate: DateTime = DateTime.fromISO( '2020-10-09T09:30:00.000Z', { @@ -985,7 +985,7 @@ describe('dateChart service', () => { }) }) - describe('incrementeDate method', () => { + describe('incrementDate method', () => { const dateChartService = new DateChartService() const selectedDate: DateTime = DateTime.fromISO( '2020-10-31T00:00:00.000Z', @@ -1001,7 +1001,7 @@ describe('dateChart service', () => { zone: 'utc', } ) - const result = dateChartService.incrementeDate( + const result = dateChartService.incrementDate( TimeStep.YEAR, selectedDate, 1 @@ -1016,7 +1016,7 @@ describe('dateChart service', () => { zone: 'utc', } ) - const result = dateChartService.incrementeDate( + const result = dateChartService.incrementDate( TimeStep.MONTH, selectedDate, 1 @@ -1031,7 +1031,7 @@ describe('dateChart service', () => { zone: 'utc', } ) - const result = dateChartService.incrementeDate( + const result = dateChartService.incrementDate( TimeStep.DAY, selectedDate, 1 @@ -1046,7 +1046,7 @@ describe('dateChart service', () => { zone: 'utc', } ) - const result = dateChartService.incrementeDate( + const result = dateChartService.incrementDate( TimeStep.WEEK, selectedDate, 1 @@ -1061,7 +1061,7 @@ describe('dateChart service', () => { zone: 'utc', } ) - const result = dateChartService.incrementeDate( + const result = dateChartService.incrementDate( TimeStep.HALF_AN_HOUR, selectedDate, 1 @@ -1070,7 +1070,7 @@ describe('dateChart service', () => { }) it('should return selected date for unknown time step', () => { - const result = dateChartService.incrementeDate( + const result = dateChartService.incrementDate( unknownTimeStep, selectedDate, 1 diff --git a/src/services/dateChart.service.ts b/src/services/dateChart.service.ts index 8e97757b0..199666c05 100644 --- a/src/services/dateChart.service.ts +++ b/src/services/dateChart.service.ts @@ -233,7 +233,7 @@ export default class DateChartService { * @param {number} increment - increment * @returns {DateTime} - incremented date */ - public incrementeDate( + public incrementDate( timeStep: TimeStep, selectedDate: DateTime, increment: number diff --git a/src/store/analysis/analysis.slice.spec.ts b/src/store/analysis/analysis.slice.spec.ts index c1799dd98..cfe316189 100644 --- a/src/store/analysis/analysis.slice.spec.ts +++ b/src/store/analysis/analysis.slice.spec.ts @@ -1,23 +1,37 @@ -import { mockInitialAnalysisState } from '../../../tests/__mocks__/store' -import { analysisSlice, setPeriod } from './analysis.slice' +import { DateTime } from 'luxon' +import { mockAnalysisState } from '../../../tests/__mocks__/store' +import { analysisSlice, setAnalysisMonth, setPeriod } from './analysis.slice' describe('analysis reducer', () => { it('should return the initial state', () => { const initialState = analysisSlice.reducer(undefined, { type: undefined, }) - expect(initialState).toEqual(mockInitialAnalysisState) + expect(initialState).toEqual({ + ...mockAnalysisState, + analysisMonth: DateTime.local().minus({ months: 1 }).startOf('day'), + }) }) describe('setPeriod', () => { - it('should handle setPeriod with payload', () => { + it('should handle setPeriod', () => { + const state = analysisSlice.reducer(mockAnalysisState, setPeriod('year')) + expect(state).toEqual({ + ...mockAnalysisState, + period: 'year', + }) + }) + }) + + describe('setAnalysisMonth', () => { + it('should handle setAnalysisMonth', () => { const state = analysisSlice.reducer( - mockInitialAnalysisState, - setPeriod('year') + mockAnalysisState, + setAnalysisMonth(DateTime.local(2023, 2, 1)) ) expect(state).toEqual({ - ...mockInitialAnalysisState, - period: 'year', + ...mockAnalysisState, + analysisMonth: DateTime.local(2023, 2, 1), }) }) }) diff --git a/src/store/analysis/analysis.slice.ts b/src/store/analysis/analysis.slice.ts index ffd088e00..b3fdf4b33 100644 --- a/src/store/analysis/analysis.slice.ts +++ b/src/store/analysis/analysis.slice.ts @@ -1,12 +1,16 @@ import { PayloadAction, createSlice } from '@reduxjs/toolkit' +import { DateTime } from 'luxon' import { AnalysisState } from 'models' const initialState: AnalysisState = { period: 'month', + analysisMonth: DateTime.local().minus({ months: 1 }).startOf('day'), } type SetPeriodAction = PayloadAction<'month' | 'year'> -export type AnalysisActionTypes = SetPeriodAction +type SetSelectedMonthAction = PayloadAction<DateTime> + +export type AnalysisActionTypes = SetPeriodAction | SetSelectedMonthAction export const analysisSlice = createSlice({ name: 'analysis', @@ -15,7 +19,10 @@ export const analysisSlice = createSlice({ setPeriod: (state, action: SetPeriodAction) => { state.period = action.payload }, + setAnalysisMonth: (state, action: SetSelectedMonthAction) => { + state.analysisMonth = action.payload + }, }, }) -export const { setPeriod } = analysisSlice.actions +export const { setPeriod, setAnalysisMonth } = analysisSlice.actions diff --git a/src/styles/components/_barchart.scss b/src/styles/components/_barchart.scss index 9cd721113..7097e1166 100644 --- a/src/styles/components/_barchart.scss +++ b/src/styles/components/_barchart.scss @@ -21,8 +21,12 @@ fill: $grey-bright; } } + .barContainer, .barFill { + &.disabled * { + cursor: default !important; + } &:hover { cursor: pointer; } @@ -46,6 +50,9 @@ .bar-ELECTRICITY { &:hover { cursor: pointer; + &.disabled { + cursor: default; + } } } .bar-ELECTRICITY { diff --git a/src/utils/date.spec.ts b/src/utils/date.spec.ts index b7c8df668..a3ab46f2f 100644 --- a/src/utils/date.spec.ts +++ b/src/utils/date.spec.ts @@ -5,7 +5,6 @@ import { Dataload } from 'models' import { graphData } from '../../tests/__mocks__/chartData.mock' import { compareDates, - convertDateToMonthString, convertDateToMonthYearString, convertDateToShortDateString, getActualAnalysisDate, @@ -403,24 +402,6 @@ describe('date utils', () => { }) }) - describe('convertDateToMonthString test', () => { - it('should return the name of the month with " de " ', () => { - const date = DateTime.fromISO('2020-11-29T23:59:59.999Z', { - zone: 'utc', - }) - const result = convertDateToMonthString(date) - expect(result).toBe(' de novembre') - }) - - it('should return the name of the month with " d\'" ', () => { - const date = DateTime.fromISO('2020-10-29T23:59:59.999Z', { - zone: 'utc', - }) - const result = convertDateToMonthString(date) - expect(result).toBe(" d'octobre") - }) - }) - describe('convertDateToMonthYearString test', () => { it('should return the name of the month and the year', () => { const date = DateTime.fromISO('2020-11-29T23:59:59.999Z', { diff --git a/src/utils/date.ts b/src/utils/date.ts index 60374d002..805c50a8d 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -126,14 +126,6 @@ export const convertDateToShortDateString = ( } } -export const convertDateToMonthString = (date: DateTime): string => { - if (date.month !== 4 && date.month != 8 && date.month != 10) { - return ' de ' + date.setLocale('fr-FR').toLocaleString({ month: 'long' }) - } else { - return " d'" + date.setLocale('fr-FR').toLocaleString({ month: 'long' }) - } -} - export const convertDateToMonthYearString = (date: DateTime): string => { return date.setLocale('fr-FR').toLocaleString({ month: 'long', diff --git a/src/utils/utils.spec.ts b/src/utils/utils.spec.ts index 218e783cd..139492298 100644 --- a/src/utils/utils.spec.ts +++ b/src/utils/utils.spec.ts @@ -7,6 +7,7 @@ import { getChallengeTitleWithLineReturn, getFluidType, getKonnectorSlug, + getMonthNameWithPrep, getSeason, } from './utils' @@ -104,4 +105,15 @@ describe('utils test', () => { expect(getChallengeTitleWithLineReturn('CHALLENGE0000')).toBe(undefined) }) }) + describe('getMonthNameWithPrep', () => { + it('should return the name of the month with " de " ', () => { + const date = DateTime.fromISO('2020-11-29T23:59:59.999Z') + expect(getMonthNameWithPrep(date)).toBe('de novembre') + }) + + it('should return the name of the month with " d\'" ', () => { + const date = DateTime.fromISO('2020-10-29T23:59:59.999Z') + expect(getMonthNameWithPrep(date)).toBe('d’octobre') + }) + }) }) diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 507d4c4eb..146fa9263 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -159,6 +159,7 @@ export const getMonthFullName = (month: number) => { /** * Return month string according to month index + * @Note Equivalent to date.monthLong * @param date - DateTime * @returns month in french */ diff --git a/tests/__mocks__/store.ts b/tests/__mocks__/store.ts index 02fac1214..d3540b60b 100644 --- a/tests/__mocks__/store.ts +++ b/tests/__mocks__/store.ts @@ -298,12 +298,13 @@ export const mockInitialChallengeState: ChallengeState = { currentDataload: [], } -export const mockInitialAnalysisState: AnalysisState = { +export const mockAnalysisState: AnalysisState = { period: 'month', + analysisMonth: DateTime.local(2023, 1, 1).startOf('day'), } export const mockInitialEcolyoState: EcolyoState = { - analysis: mockInitialAnalysisState, + analysis: mockAnalysisState, challenge: mockInitialChallengeState, chart: mockInitialChartState, global: mockInitialGlobalState, -- GitLab