import ExpiredConsentModal from 'components/Connection/ExpiredConsentModal/ExpiredConsentModal' import { GrdfWaitConsentModal } from 'components/Connection/GRDFConnect/GrdfWaitConsent' import Content from 'components/Content/Content' import CustomPopupModal from 'components/CustomPopup/CustomPopupModal' import DateNavigator from 'components/DateNavigator/DateNavigator' import FluidChart from 'components/FluidChart/FluidChart' import CozyBar from 'components/Header/CozyBar' import Header from 'components/Header/Header' import KonnectorViewerCard from 'components/Konnector/KonnectorViewerCard' import KonnectorViewerList from 'components/Konnector/KonnectorViewerList' import PartnerIssueModal from 'components/PartnerIssue/PartnerIssueModal' import ReleaseNotesModal from 'components/ReleaseNotesModal/ReleaseNotesModal' import { useClient } from 'cozy-client' import { FluidState, FluidType, TimeStep } from 'enums' import { DateTime } from 'luxon' import React, { useCallback, useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import DateChartService from 'services/dateChart.service' import ProfileService from 'services/profile.service' import { setCurrentIndex, setCurrentTimeStep, setSelectedDate, setShowOfflineData, } from 'store/chart/chart.slice' import { showReleaseNotes } from 'store/global/global.slice' import { useAppDispatch, useAppSelector } from 'store/hooks' import { openPartnersModal, setCustomPopup } from 'store/modal/modal.slice' import { isLastDateReached } from 'utils/date' import { getKonnectorUpdateError, getTodayDate, isKonnectorActive, } from 'utils/utils' import ConsumptionDetails from './ConsumptionDetails/ConsumptionDetails' import FluidButtons from './FluidButtons/FluidButtons' const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => { const navigate = useNavigate() const client = useClient() const dispatch = useAppDispatch() const isMulti = fluidType === FluidType.MULTIFLUID const { chart: { currentTimeStep, showOfflineData, selectedDate, currentIndex }, global: { fluidStatus, releaseNotes }, modal: { partnersIssueModal, customPopupModal }, } = useAppSelector(state => state.ecolyo) const currentFluidStatus = fluidStatus[fluidType] const dateChartService = new DateChartService() const isWaitingForConsent = fluidType === FluidType.GAS && currentFluidStatus.status === FluidState.CHALLENGE_ASKED const [waitConsent, setWaitConsent] = useState(isWaitingForConsent) const [openExpiredConsentModal, setOpenExpiredConsentModal] = useState(true) const [openReleaseNoteModal, setOpenReleaseNoteModal] = useState<boolean>( releaseNotes.show ) const [consentExpiredFluids, setConsentExpiredFluids] = useState<FluidType[]>( [] ) /** Show wait consent modal when navigating and consent is "A valider" */ useEffect(() => { setWaitConsent(isWaitingForConsent) }, [isWaitingForConsent]) const updateKey = fluidType !== FluidType.MULTIFLUID && currentFluidStatus.lastDataDate ? `${currentFluidStatus.lastDataDate.toLocaleString()} + ${ currentFluidStatus.status + fluidType }` : '' const lastDataDateKey = fluidType !== FluidType.MULTIFLUID && currentFluidStatus.lastDataDate ? `${currentFluidStatus.lastDataDate.toLocaleString() + fluidType}` : '' const getPartnerKey = (fluidType: FluidType): 'enedis' | 'egl' | 'grdf' => { switch (fluidType) { case FluidType.ELECTRICITY: return 'enedis' case FluidType.WATER: return 'egl' case FluidType.GAS: return 'grdf' default: throw new Error('unknown fluidtype') } } const handleCloseReleaseNoteModal = useCallback(() => { setOpenReleaseNoteModal(false) dispatch( showReleaseNotes({ show: false, notes: releaseNotes.notes, redirectLink: releaseNotes.redirectLink, }) ) if (releaseNotes.redirectLink) { navigate(releaseNotes.redirectLink) } }, [dispatch, navigate, releaseNotes.notes, releaseNotes.redirectLink]) const handleClosePartnerIssueModal = useCallback( async (fluidType: FluidType) => { const profileService = new ProfileService(client) const profileValues = await profileService.getProfile() if (profileValues) { const updatedProfile = await profileService.updateProfile({ partnersIssueSeenDate: { ...profileValues.partnersIssueSeenDate, [getPartnerKey(fluidType)]: getTodayDate(), }, }) if (updatedProfile) { dispatch( openPartnersModal({ ...partnersIssueModal, [getPartnerKey(fluidType)]: false, }) ) } } }, [client, dispatch, partnersIssueModal] ) const handleCloseCustomPopupModal = async () => { const profileService = new ProfileService(client) const updatedProfile = await profileService.updateProfile({ customPopupDate: getTodayDate(), }) if (updatedProfile) { dispatch( setCustomPopup({ ...customPopupModal, popupEnabled: false, }) ) } } useEffect( /** Reset half-hour timestep for water & gas & multifluid */ function setDefaultTimeStep() { if ( fluidType !== FluidType.ELECTRICITY && currentTimeStep == TimeStep.HALF_AN_HOUR ) { dispatch(setCurrentTimeStep(TimeStep.WEEK)) } }, [dispatch, fluidType, currentTimeStep] ) /** * If fluid is not connected, display Connect components * If fluid is connected, dispatch FluidChart */ useEffect(() => { const isFluidConnected = isKonnectorActive(fluidStatus, fluidType) dispatch(setShowOfflineData(isFluidConnected)) }, [dispatch, fluidStatus, fluidType]) /** Check if some fluids have expired consent error */ useEffect(() => { let subscribed = true const expiredConsents: FluidType[] = [] for (const fluid of fluidStatus) { const error = fluid.connection.triggerState?.last_error if (error && getKonnectorUpdateError(error) === 'error_update_oauth') { expiredConsents.push(fluid.fluidType) } } if (subscribed) setConsentExpiredFluids(expiredConsents) return () => { subscribed = false } }, [fluidStatus]) const disablePrev = selectedDate < DateTime.local(0, 1, 1).setZone('utc', { keepLocalTime: true, }) && !isKonnectorActive(fluidStatus, FluidType.MULTIFLUID) const getIncrement = (next: boolean) => next ? dateChartService.defineIncrementForNextIndex( currentTimeStep, selectedDate, currentIndex ) : dateChartService.defineIncrementForPreviousIndex( currentTimeStep, selectedDate, currentIndex ) const handleClickMove = (next: boolean) => { const increment = getIncrement(next) const updatedDate = dateChartService.incrementDate( currentTimeStep, selectedDate, increment ) const updatedIndex = dateChartService.defineDateIndex( currentTimeStep, updatedDate ) dispatch(setSelectedDate(updatedDate)) dispatch(setCurrentIndex(updatedIndex)) } return ( <> <CozyBar titleKey="common.title_consumption" /> <Header desktopTitleKey="common.title_consumption"> <DateNavigator disableNext={isLastDateReached(selectedDate, currentTimeStep)} disablePrev={disablePrev} handleNextDate={() => handleClickMove(true)} handlePrevDate={() => handleClickMove(false)} navigatorDate={selectedDate} timeStep={currentTimeStep} /> </Header> <Content> <FluidButtons activeFluid={fluidType} key={updateKey} /> {showOfflineData && ( <> <FluidChart fluidType={fluidType} key={lastDataDateKey} /> <ConsumptionDetails fluidType={fluidType} /> </> )} {/* TODO maybe move this inside block above */} {!isMulti && <KonnectorViewerCard fluidType={fluidType} />} {isMulti && !showOfflineData && <KonnectorViewerList />} </Content> {/* MODALS */} {openReleaseNoteModal && ( <ReleaseNotesModal open={openReleaseNoteModal} handleCloseClick={handleCloseReleaseNoteModal} /> )} {/* Partner issue modals for individual fluids */} {fluidStatus .filter(fluid => fluid.maintenance) .filter(fluid => fluid.fluidType === fluidType) .map(issuedFluid => ( <PartnerIssueModal key={issuedFluid.fluidType} issuedFluid={issuedFluid.fluidType} open={partnersIssueModal[getPartnerKey(issuedFluid.fluidType)]} handleCloseClick={handleClosePartnerIssueModal} /> ))} <CustomPopupModal customPopup={customPopupModal} handleCloseClick={handleCloseCustomPopupModal} /> {Boolean(consentExpiredFluids.length) && consentExpiredFluids.map(fluid => ( <ExpiredConsentModal key={fluid} open={openExpiredConsentModal} handleCloseClick={() => setOpenExpiredConsentModal(false)} fluidType={fluid} toggleModal={() => setOpenExpiredConsentModal(prev => !prev)} /> ))} {/* GRDF Waiting screen */} <GrdfWaitConsentModal open={waitConsent} setOpen={setWaitConsent} /> </> ) } export default ConsumptionView