From 041e56d6f4bd50d1797c95f8aa1374f96e742274 Mon Sep 17 00:00:00 2001 From: Bastien DUMONT <bdumont@grandlyon.com> Date: Wed, 27 Sep 2023 15:10:39 +0000 Subject: [PATCH] fix(analysis): modal no konnector --- src/components/Analysis/MonthlyAnalysis.tsx | 18 +- .../NoKonnector/NoAnalysisModal.spec.tsx | 13 + .../Analysis/NoKonnector/NoAnalysisModal.tsx | 77 ++++ .../Analysis/NoKonnector/noAnalysisModal.scss | 31 ++ .../MonthlyAnalysis.spec.tsx.snap | 372 ++++++++++++++++++ src/locales/fr.json | 11 + 6 files changed, 520 insertions(+), 2 deletions(-) create mode 100644 src/components/Analysis/NoKonnector/NoAnalysisModal.spec.tsx create mode 100644 src/components/Analysis/NoKonnector/NoAnalysisModal.tsx create mode 100644 src/components/Analysis/NoKonnector/noAnalysisModal.scss diff --git a/src/components/Analysis/MonthlyAnalysis.tsx b/src/components/Analysis/MonthlyAnalysis.tsx index a5e3e2e0b..90e2d0253 100644 --- a/src/components/Analysis/MonthlyAnalysis.tsx +++ b/src/components/Analysis/MonthlyAnalysis.tsx @@ -1,7 +1,7 @@ import { Fade } from '@material-ui/core' import Loader from 'components/Loader/Loader' import { useClient } from 'cozy-client' -import { FluidType, TimeStep } from 'enums' +import { FluidState, FluidType, TimeStep } from 'enums' import { PerformanceIndicator, TimePeriod } from 'models' import React, { useEffect, useMemo, useState } from 'react' import ConsumptionService from 'services/consumption.service' @@ -11,6 +11,7 @@ import Comparison from './Comparison/Comparison' import ElecHalfHourMonthlyAnalysis from './ElecHalfHourMonthlyAnalysis/ElecHalfHourMonthlyAnalysis' import IncompleteDataWarning from './IncompleteDataWarning/IncompleteDataWarning' import MaxConsumptionCard from './MaxConsumptionCard/MaxConsumptionCard' +import NoAnalysisModal from './NoKonnector/NoAnalysisModal' import ProfileComparator from './ProfileComparator/ProfileComparator' import TotalAnalysisChart from './TotalAnalysisChart/TotalAnalysisChart' import './monthlyanalysis.scss' @@ -25,7 +26,10 @@ const MonthlyAnalysis = ({ scrollPosition, }: MonthlyAnalysisProps) => { const client = useClient() - const { analysisMonth } = useAppSelector(state => state.ecolyo.analysis) + const { + analysis: { analysisMonth }, + global: { fluidStatus }, + } = useAppSelector(state => state.ecolyo) const consumptionService = useMemo( () => new ConsumptionService(client), @@ -36,7 +40,12 @@ const MonthlyAnalysis = ({ [] ) + const allKonnectorDisconnected = fluidStatus.some( + fluid => fluid.status === FluidState.NOT_CONNECTED + ) + const [loadAnalysis, setLoadAnalysis] = useState<boolean>(true) + const [openNoDataModal, setOpenNoDataModal] = useState(false) const [fluidsWithData, setFluidsWithData] = useState<FluidType[]>([]) const [incompleteDataFluids, setIncompleteDataFluids] = useState<FluidType[]>( [] @@ -95,6 +104,9 @@ const MonthlyAnalysis = ({ ) ) } + if (resultFluids.length === 0 && allKonnectorDisconnected) { + setOpenNoDataModal(true) + } setFluidsWithData(resultFluids) setLoadAnalysis(false) } @@ -108,6 +120,7 @@ const MonthlyAnalysis = ({ subscribed = false } }, [ + allKonnectorDisconnected, analysisMonth, consumptionService, performanceIndicatorService, @@ -129,6 +142,7 @@ const MonthlyAnalysis = ({ <Loader /> </div> )} + <NoAnalysisModal open={openNoDataModal} onClose={setOpenNoDataModal} /> {!loadAnalysis && ( <Fade in={!loadAnalysis}> <div className="analysis-root"> diff --git a/src/components/Analysis/NoKonnector/NoAnalysisModal.spec.tsx b/src/components/Analysis/NoKonnector/NoAnalysisModal.spec.tsx new file mode 100644 index 000000000..fd629dca0 --- /dev/null +++ b/src/components/Analysis/NoKonnector/NoAnalysisModal.spec.tsx @@ -0,0 +1,13 @@ +import Button from '@material-ui/core/Button' +import Dialog from '@material-ui/core/Dialog' +import { mount } from 'enzyme' +import React from 'react' +import NoAnalysisModal from './NoAnalysisModal' + +describe('NoAnalysisModal component', () => { + it('should be rendered correctly', () => { + const wrapper = mount(<NoAnalysisModal open={true} onClose={jest.fn()} />) + expect(wrapper.find(Dialog).exists()).toBeTruthy() + expect(wrapper.find(Button).exists()).toBeTruthy() + }) +}) diff --git a/src/components/Analysis/NoKonnector/NoAnalysisModal.tsx b/src/components/Analysis/NoKonnector/NoAnalysisModal.tsx new file mode 100644 index 000000000..09e0b8c74 --- /dev/null +++ b/src/components/Analysis/NoKonnector/NoAnalysisModal.tsx @@ -0,0 +1,77 @@ +import Button from '@material-ui/core/Button' +import Dialog from '@material-ui/core/Dialog' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import React from 'react' +import { useNavigate } from 'react-router-dom' +import './noAnalysisModal.scss' + +const NoAnalysisModal = ({ + open, + onClose, +}: { + open: boolean + onClose: React.Dispatch<React.SetStateAction<boolean>> +}) => { + const { t } = useI18n() + const navigate = useNavigate() + + const goToConsumption = () => { + navigate('/consumption') + } + + const close = () => { + onClose(false) + } + + return ( + <Dialog + open={open} + onClose={close} + aria-labelledby="accessibility-title" + classes={{ + root: 'modal-root', + paper: 'modal-paper', + }} + > + <div id="accessibility-title"> + {t('analysis_error_modal.accessibility.window_title')} + </div> + <div className="em-root analysis-error-container"> + <div className="analysis-error-title text-20-bold"> + {t('analysis_error_modal.title')} + </div> + <div className="analysis-error-message text-16-normal"> + {t('analysis_error_modal.message')} + </div> + <div className="analysis-error-button"> + <Button + aria-label={t( + 'analysis_error_modal.accessibility.button_understood' + )} + onClick={close} + classes={{ + root: 'btn-secondary-positive', + label: 'text-16-normal', + }} + > + {t('analysis_error_modal.understood')} + </Button> + <Button + aria-label={t( + 'analysis_error_modal.accessibility.button_goto_konnector' + )} + onClick={goToConsumption} + classes={{ + root: 'btn-highlight', + label: 'text-16-bold', + }} + > + {t('analysis_error_modal.go_to_conso')} + </Button> + </div> + </div> + </Dialog> + ) +} + +export default NoAnalysisModal diff --git a/src/components/Analysis/NoKonnector/noAnalysisModal.scss b/src/components/Analysis/NoKonnector/noAnalysisModal.scss new file mode 100644 index 000000000..4f89990b3 --- /dev/null +++ b/src/components/Analysis/NoKonnector/noAnalysisModal.scss @@ -0,0 +1,31 @@ +@import 'src/styles/base/color'; +@import 'src/styles/base/breakpoint'; + +.analysis-error-container { + color: $grey-bright; + text-align: center; + display: flex; + flex-direction: column; + gap: 1.5rem; + .analysis-error-title { + color: $gold-shadow; + } + .analysis-error-button { + display: flex; + justify-content: space-between; + gap: 1rem; + button { + margin: 0; + height: 45px; + &.btn-secondary-positive { + margin-bottom: 0; + } + } + @media #{$large-phone} { + flex-direction: column-reverse; + button { + width: 100%; + } + } + } +} diff --git a/src/components/Analysis/__snapshots__/MonthlyAnalysis.spec.tsx.snap b/src/components/Analysis/__snapshots__/MonthlyAnalysis.spec.tsx.snap index 70fef5256..e6d74e926 100644 --- a/src/components/Analysis/__snapshots__/MonthlyAnalysis.spec.tsx.snap +++ b/src/components/Analysis/__snapshots__/MonthlyAnalysis.spec.tsx.snap @@ -48,6 +48,378 @@ exports[`MonthlyAnalysis component should be rendered correctly 1`] = ` saveLastScrollPosition={[MockFunction]} scrollPosition={0} > + <NoAnalysisModal + onClose={[Function]} + open={false} + > + <WithStyles(ForwardRef(Dialog)) + aria-labelledby="accessibility-title" + classes={ + Object { + "paper": "modal-paper", + "root": "modal-root", + } + } + onClose={[Function]} + open={false} + > + <ForwardRef(Dialog) + aria-labelledby="accessibility-title" + classes={ + Object { + "container": "MuiDialog-container", + "paper": "MuiDialog-paper modal-paper", + "paperFullScreen": "MuiDialog-paperFullScreen", + "paperFullWidth": "MuiDialog-paperFullWidth", + "paperScrollBody": "MuiDialog-paperScrollBody", + "paperScrollPaper": "MuiDialog-paperScrollPaper", + "paperWidthFalse": "MuiDialog-paperWidthFalse", + "paperWidthLg": "MuiDialog-paperWidthLg", + "paperWidthMd": "MuiDialog-paperWidthMd", + "paperWidthSm": "MuiDialog-paperWidthSm", + "paperWidthXl": "MuiDialog-paperWidthXl", + "paperWidthXs": "MuiDialog-paperWidthXs", + "root": "MuiDialog-root modal-root", + "scrollBody": "MuiDialog-scrollBody", + "scrollPaper": "MuiDialog-scrollPaper", + } + } + onClose={[Function]} + open={false} + > + <ForwardRef(Modal) + BackdropComponent={ + Object { + "$$typeof": Symbol(react.forward_ref), + "Naked": Object { + "$$typeof": Symbol(react.forward_ref), + "propTypes": Object { + "children": [Function], + "className": [Function], + "classes": [Function], + "invisible": [Function], + "open": [Function], + "transitionDuration": [Function], + }, + "render": [Function], + }, + "displayName": "WithStyles(ForwardRef(Backdrop))", + "options": Object { + "defaultTheme": Object { + "breakpoints": Object { + "between": [Function], + "down": [Function], + "keys": Array [ + "xs", + "sm", + "md", + "lg", + "xl", + ], + "only": [Function], + "up": [Function], + "values": Object { + "lg": 1280, + "md": 960, + "sm": 600, + "xl": 1920, + "xs": 0, + }, + "width": [Function], + }, + "direction": "ltr", + "mixins": Object { + "gutters": [Function], + "toolbar": Object { + "@media (min-width:0px) and (orientation: landscape)": Object { + "minHeight": 48, + }, + "@media (min-width:600px)": Object { + "minHeight": 64, + }, + "minHeight": 56, + }, + }, + "overrides": Object {}, + "palette": Object { + "action": Object { + "activatedOpacity": 0.12, + "active": "rgba(0, 0, 0, 0.54)", + "disabled": "rgba(0, 0, 0, 0.26)", + "disabledBackground": "rgba(0, 0, 0, 0.12)", + "disabledOpacity": 0.38, + "focus": "rgba(0, 0, 0, 0.12)", + "focusOpacity": 0.12, + "hover": "rgba(0, 0, 0, 0.04)", + "hoverOpacity": 0.04, + "selected": "rgba(0, 0, 0, 0.08)", + "selectedOpacity": 0.08, + }, + "augmentColor": [Function], + "background": Object { + "default": "#fafafa", + "paper": "#fff", + }, + "common": Object { + "black": "#000", + "white": "#fff", + }, + "contrastThreshold": 3, + "divider": "rgba(0, 0, 0, 0.12)", + "error": Object { + "contrastText": "#fff", + "dark": "#d32f2f", + "light": "#e57373", + "main": "#f44336", + }, + "getContrastText": [Function], + "grey": Object { + "100": "#f5f5f5", + "200": "#eeeeee", + "300": "#e0e0e0", + "400": "#bdbdbd", + "50": "#fafafa", + "500": "#9e9e9e", + "600": "#757575", + "700": "#616161", + "800": "#424242", + "900": "#212121", + "A100": "#d5d5d5", + "A200": "#aaaaaa", + "A400": "#303030", + "A700": "#616161", + }, + "info": Object { + "contrastText": "#fff", + "dark": "#1976d2", + "light": "#64b5f6", + "main": "#2196f3", + }, + "primary": Object { + "contrastText": "#fff", + "dark": "#303f9f", + "light": "#7986cb", + "main": "#3f51b5", + }, + "secondary": Object { + "contrastText": "#fff", + "dark": "#c51162", + "light": "#ff4081", + "main": "#f50057", + }, + "success": Object { + "contrastText": "rgba(0, 0, 0, 0.87)", + "dark": "#388e3c", + "light": "#81c784", + "main": "#4caf50", + }, + "text": Object { + "disabled": "rgba(0, 0, 0, 0.38)", + "hint": "rgba(0, 0, 0, 0.38)", + "primary": "rgba(0, 0, 0, 0.87)", + "secondary": "rgba(0, 0, 0, 0.54)", + }, + "tonalOffset": 0.2, + "type": "light", + "warning": Object { + "contrastText": "rgba(0, 0, 0, 0.87)", + "dark": "#f57c00", + "light": "#ffb74d", + "main": "#ff9800", + }, + }, + "props": Object {}, + "shadows": Array [ + "none", + "0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)", + "0px 3px 1px -2px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 1px 5px 0px rgba(0,0,0,0.12)", + "0px 3px 3px -2px rgba(0,0,0,0.2),0px 3px 4px 0px rgba(0,0,0,0.14),0px 1px 8px 0px rgba(0,0,0,0.12)", + "0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)", + "0px 3px 5px -1px rgba(0,0,0,0.2),0px 5px 8px 0px rgba(0,0,0,0.14),0px 1px 14px 0px rgba(0,0,0,0.12)", + "0px 3px 5px -1px rgba(0,0,0,0.2),0px 6px 10px 0px rgba(0,0,0,0.14),0px 1px 18px 0px rgba(0,0,0,0.12)", + "0px 4px 5px -2px rgba(0,0,0,0.2),0px 7px 10px 1px rgba(0,0,0,0.14),0px 2px 16px 1px rgba(0,0,0,0.12)", + "0px 5px 5px -3px rgba(0,0,0,0.2),0px 8px 10px 1px rgba(0,0,0,0.14),0px 3px 14px 2px rgba(0,0,0,0.12)", + "0px 5px 6px -3px rgba(0,0,0,0.2),0px 9px 12px 1px rgba(0,0,0,0.14),0px 3px 16px 2px rgba(0,0,0,0.12)", + "0px 6px 6px -3px rgba(0,0,0,0.2),0px 10px 14px 1px rgba(0,0,0,0.14),0px 4px 18px 3px rgba(0,0,0,0.12)", + "0px 6px 7px -4px rgba(0,0,0,0.2),0px 11px 15px 1px rgba(0,0,0,0.14),0px 4px 20px 3px rgba(0,0,0,0.12)", + "0px 7px 8px -4px rgba(0,0,0,0.2),0px 12px 17px 2px rgba(0,0,0,0.14),0px 5px 22px 4px rgba(0,0,0,0.12)", + "0px 7px 8px -4px rgba(0,0,0,0.2),0px 13px 19px 2px rgba(0,0,0,0.14),0px 5px 24px 4px rgba(0,0,0,0.12)", + "0px 7px 9px -4px rgba(0,0,0,0.2),0px 14px 21px 2px rgba(0,0,0,0.14),0px 5px 26px 4px rgba(0,0,0,0.12)", + "0px 8px 9px -5px rgba(0,0,0,0.2),0px 15px 22px 2px rgba(0,0,0,0.14),0px 6px 28px 5px rgba(0,0,0,0.12)", + "0px 8px 10px -5px rgba(0,0,0,0.2),0px 16px 24px 2px rgba(0,0,0,0.14),0px 6px 30px 5px rgba(0,0,0,0.12)", + "0px 8px 11px -5px rgba(0,0,0,0.2),0px 17px 26px 2px rgba(0,0,0,0.14),0px 6px 32px 5px rgba(0,0,0,0.12)", + "0px 9px 11px -5px rgba(0,0,0,0.2),0px 18px 28px 2px rgba(0,0,0,0.14),0px 7px 34px 6px rgba(0,0,0,0.12)", + "0px 9px 12px -6px rgba(0,0,0,0.2),0px 19px 29px 2px rgba(0,0,0,0.14),0px 7px 36px 6px rgba(0,0,0,0.12)", + "0px 10px 13px -6px rgba(0,0,0,0.2),0px 20px 31px 3px rgba(0,0,0,0.14),0px 8px 38px 7px rgba(0,0,0,0.12)", + "0px 10px 13px -6px rgba(0,0,0,0.2),0px 21px 33px 3px rgba(0,0,0,0.14),0px 8px 40px 7px rgba(0,0,0,0.12)", + "0px 10px 14px -6px rgba(0,0,0,0.2),0px 22px 35px 3px rgba(0,0,0,0.14),0px 8px 42px 7px rgba(0,0,0,0.12)", + "0px 11px 14px -7px rgba(0,0,0,0.2),0px 23px 36px 3px rgba(0,0,0,0.14),0px 9px 44px 8px rgba(0,0,0,0.12)", + "0px 11px 15px -7px rgba(0,0,0,0.2),0px 24px 38px 3px rgba(0,0,0,0.14),0px 9px 46px 8px rgba(0,0,0,0.12)", + ], + "shape": Object { + "borderRadius": 4, + }, + "spacing": [Function], + "transitions": Object { + "create": [Function], + "duration": Object { + "complex": 375, + "enteringScreen": 225, + "leavingScreen": 195, + "short": 250, + "shorter": 200, + "shortest": 150, + "standard": 300, + }, + "easing": Object { + "easeIn": "cubic-bezier(0.4, 0, 1, 1)", + "easeInOut": "cubic-bezier(0.4, 0, 0.2, 1)", + "easeOut": "cubic-bezier(0.0, 0, 0.2, 1)", + "sharp": "cubic-bezier(0.4, 0, 0.6, 1)", + }, + "getAutoHeightDuration": [Function], + }, + "typography": Object { + "body1": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "1rem", + "fontWeight": 400, + "letterSpacing": "0.00938em", + "lineHeight": 1.5, + }, + "body2": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "0.875rem", + "fontWeight": 400, + "letterSpacing": "0.01071em", + "lineHeight": 1.43, + }, + "button": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "0.875rem", + "fontWeight": 500, + "letterSpacing": "0.02857em", + "lineHeight": 1.75, + "textTransform": "uppercase", + }, + "caption": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "0.75rem", + "fontWeight": 400, + "letterSpacing": "0.03333em", + "lineHeight": 1.66, + }, + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": 14, + "fontWeightBold": 700, + "fontWeightLight": 300, + "fontWeightMedium": 500, + "fontWeightRegular": 400, + "h1": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "6rem", + "fontWeight": 300, + "letterSpacing": "-0.01562em", + "lineHeight": 1.167, + }, + "h2": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "3.75rem", + "fontWeight": 300, + "letterSpacing": "-0.00833em", + "lineHeight": 1.2, + }, + "h3": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "3rem", + "fontWeight": 400, + "letterSpacing": "0em", + "lineHeight": 1.167, + }, + "h4": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "2.125rem", + "fontWeight": 400, + "letterSpacing": "0.00735em", + "lineHeight": 1.235, + }, + "h5": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "1.5rem", + "fontWeight": 400, + "letterSpacing": "0em", + "lineHeight": 1.334, + }, + "h6": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "1.25rem", + "fontWeight": 500, + "letterSpacing": "0.0075em", + "lineHeight": 1.6, + }, + "htmlFontSize": 16, + "overline": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "0.75rem", + "fontWeight": 400, + "letterSpacing": "0.08333em", + "lineHeight": 2.66, + "textTransform": "uppercase", + }, + "pxToRem": [Function], + "round": [Function], + "subtitle1": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "1rem", + "fontWeight": 400, + "letterSpacing": "0.00938em", + "lineHeight": 1.75, + }, + "subtitle2": Object { + "fontFamily": "\\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif", + "fontSize": "0.875rem", + "fontWeight": 500, + "letterSpacing": "0.00714em", + "lineHeight": 1.57, + }, + }, + "zIndex": Object { + "appBar": 1100, + "drawer": 1200, + "mobileStepper": 1000, + "modal": 1300, + "snackbar": 1400, + "speedDial": 1050, + "tooltip": 1500, + }, + }, + "name": "MuiBackdrop", + }, + "propTypes": Object { + "classes": [Function], + "innerRef": [Function], + }, + "render": [Function], + "useStyles": [Function], + } + } + BackdropProps={ + Object { + "transitionDuration": Object { + "enter": 225, + "exit": 195, + }, + } + } + className="MuiDialog-root modal-root" + closeAfterTransition={true} + disableEscapeKeyDown={false} + onClose={[Function]} + open={false} + /> + </ForwardRef(Dialog)> + </WithStyles(ForwardRef(Dialog))> + </NoAnalysisModal> <ForwardRef(Fade) in={true} > diff --git a/src/locales/fr.json b/src/locales/fr.json index 5d60f9633..409eb54ad 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -106,6 +106,17 @@ "year_tab": "Comparer à l'année dernière" } }, + "analysis_error_modal": { + "title": "Aucune analyse", + "message": "Pour profiter d’une analyse de vos consommations, connectez au moins un de vos compteurs.", + "go_to_conso": "Je connecte mes compteurs", + "understood": "J'ai compris", + "accessibility": { + "window_title": "Fenêtre d'erreur", + "button_understood": "J'ai compris", + "button_goto_konnector": "Aller aux connecteurs" + } + }, "analysis_pie": { "total": "Conso totale", "month": "Au mois\u00a0", -- GitLab