diff --git a/config.json b/config.json index be2d5bd472ac148867bdcbd45401e3b688cea88e..dc13d0c45ae0c91d46104a4010ef22c20ffb3663 100644 --- a/config.json +++ b/config.json @@ -9,7 +9,8 @@ "name": "Enedis", "type": "ELECTRICITY", "oauth": true, - "slug": "enedisgrandlyon" + "slug": "enedisgrandlyon", + "cron": "0 30 8 * * *" }, "siteLink": "https://mon-compte-client.enedis.fr/" }, @@ -22,7 +23,8 @@ "name": "Eau du Grand Lyon", "type": "WATER", "oauth": false, - "slug": "eglgrandlyon" + "slug": "eglgrandlyon", + "cron": "0 30 8 * * *" }, "siteLink": "https://agence.eaudugrandlyon.com/inscription.aspx" }, @@ -35,7 +37,8 @@ "name": "GRDF", "type": "GAS", "oauth": false, - "slug": "grdfgrandlyon" + "slug": "grdfgrandlyon", + "cron": "0 30 8 * * *" }, "siteLink": "https://monespace.grdf.fr/monespace/connexion" } diff --git a/manifest.webapp b/manifest.webapp index 620578ab4191080c76345fe9792f2d6232356d6f..55d877fe577a25a3709b923ae387c556633b8262 100644 --- a/manifest.webapp +++ b/manifest.webapp @@ -3,7 +3,7 @@ "slug": "ecolyo", "icon": "icon.svg", "categories": ["energy"], - "version": "0.1.4", + "version": "0.1.6", "licence": "AGPL-3.0", "editor": "Métropole de Lyon", "default_locale": "fr", diff --git a/package.json b/package.json index 35fc72bef446d891eec2824a148b3261fd3a6579..a7875687fa3301a91b601da8c92b3fabf0bba464 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ecolyo", - "version": "0.1.4", + "version": "0.1.6", "scripts": { "tx": "tx pull --all || true", "lint": "yarn lint:js && yarn lint:styles", diff --git a/src/assets/icons/ico/legal.svg b/src/assets/icons/ico/legal-notice.svg similarity index 100% rename from src/assets/icons/ico/legal.svg rename to src/assets/icons/ico/legal-notice.svg diff --git a/src/assets/png/badges/CHA00000001-1.png b/src/assets/png/badges/CHA00000001-1.png index bfe6fb3c24e6cfa36b612e68e75681c03e738e51..2ed44e2c252a4b699f71c5d2210f6b6b196d7f03 100644 Binary files a/src/assets/png/badges/CHA00000001-1.png and b/src/assets/png/badges/CHA00000001-1.png differ diff --git a/src/assets/png/badges/CHA00000002-0.png b/src/assets/png/badges/CHA00000002-0.png index bad09c3516c15277abd6b9adc44b4653ec40abb2..a2a54f3678e00e6c821f9c0a40781c1e7050b0f7 100644 Binary files a/src/assets/png/badges/CHA00000002-0.png and b/src/assets/png/badges/CHA00000002-0.png differ diff --git a/src/assets/png/badges/CHA00000002-1.png b/src/assets/png/badges/CHA00000002-1.png index e16bba433ca3ece0ba70d5f0435443e57bcba00b..348e046b322e9f0a0bfc24e51cbb44b969283b55 100644 Binary files a/src/assets/png/badges/CHA00000002-1.png and b/src/assets/png/badges/CHA00000002-1.png differ diff --git a/src/assets/png/badges/CHA00000003-0.png b/src/assets/png/badges/CHA00000003-0.png index f71ef47156b5c0a10f247dd9a0dcc08dfe5b515e..cbe825edfe5ed9aebd57bb2a75e69df5f35718d6 100644 Binary files a/src/assets/png/badges/CHA00000003-0.png and b/src/assets/png/badges/CHA00000003-0.png differ diff --git a/src/assets/png/badges/CHA00000003-1.png b/src/assets/png/badges/CHA00000003-1.png index 043748e2ba4c3f9ff731d28f8bfe542c0fb6cbdb..ead289b704c16f432aec12f8135d9781de2ff466 100644 Binary files a/src/assets/png/badges/CHA00000003-1.png and b/src/assets/png/badges/CHA00000003-1.png differ diff --git a/src/components/CommonKit/Button/StyledButtonValid.tsx b/src/components/CommonKit/Button/StyledButtonValid.tsx index b7383291700898a61d99b2f753493fb241d58f1b..349cc268d2dbb2c4a1470a33b5b938c67957081d 100644 --- a/src/components/CommonKit/Button/StyledButtonValid.tsx +++ b/src/components/CommonKit/Button/StyledButtonValid.tsx @@ -45,6 +45,9 @@ const BaseButton = withStyles({ fontSize: '1rem', lineHeight: '120%', }, + disabled: { + opacity: '0.4', + }, })(MuiButton) const PrimaryButton = withStyles({ @@ -55,6 +58,9 @@ const PrimaryButton = withStyles({ color: 'var(--textBlack)', fontWeight: 'bold', }, + disabled: { + background: 'var(--blueBackground) !important', + }, })(BaseButton) const SecondaryButton = withStyles({ diff --git a/src/components/CommonKit/Card/StyledCard.tsx b/src/components/CommonKit/Card/StyledCard.tsx index b5d825a00429bcd1ebc53c9f09a2775362d72637..eb900a5fe7d363a888afa9bd96e9457bce2ecdc1 100644 --- a/src/components/CommonKit/Card/StyledCard.tsx +++ b/src/components/CommonKit/Card/StyledCard.tsx @@ -9,12 +9,10 @@ import { FluidType } from 'enum/fluid.enum' const CardBase = withStyles({ root: { background: 'linear-gradient(180deg, #323339 0%, #25262B 100%)', - border: '1px solid', boxSizing: 'border-box', boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.75)', borderRadius: '4px', margin: '10px 0px 20px 0px', - //maxWidth: '155px', }, })(CardActionArea) diff --git a/src/components/ContainerComponents/LegalContainer/LegalContainer.tsx b/src/components/ContainerComponents/LegalContainer/LegalContainer.tsx deleted file mode 100644 index 8ee630bd2c5ff1663274f23334e3b516757cf031..0000000000000000000000000000000000000000 --- a/src/components/ContainerComponents/LegalContainer/LegalContainer.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react' -import { NavLink } from 'react-router-dom' -import { translate } from 'cozy-ui/react/I18n' - -import StyledCard from 'components/CommonKit/Card/StyledCard' -import StyledIcon from 'components/CommonKit/Icon/StyledIcon' -import LegalIcon from 'assets/icons/ico/legal.svg' - -interface LegalContainerProps { - t: Function -} - -const LegalContainer: React.FC<LegalContainerProps> = ({ - t, -}: LegalContainerProps) => { - return ( - <div className="legal-root"> - <div className="legal-content"> - <div className="legal-header text-14-normal-uppercase"> - {t('LEGAL.TITLE_LEGAL')} - </div> - <NavLink className="legal-card-link" to="parameters/Legal"> - <StyledCard> - <div className="legal-card"> - <div className="legal-card-content"> - <StyledIcon - className="legal-card-content-icon" - icon={LegalIcon} - size={50} - /> - <div className="legal-card-content-title"> - {t('LEGAL.READ_LEGAL')} - </div> - </div> - </div> - </StyledCard> - </NavLink> - </div> - </div> - ) -} - -export default translate()(LegalContainer) diff --git a/src/components/ContainerComponents/LegalNoticeContainer/LegalNoticeContainer.tsx b/src/components/ContainerComponents/LegalNoticeContainer/LegalNoticeContainer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e5d82a9e0d5dc47587a176c492b86287056d115a --- /dev/null +++ b/src/components/ContainerComponents/LegalNoticeContainer/LegalNoticeContainer.tsx @@ -0,0 +1,43 @@ +import React from 'react' +import { NavLink } from 'react-router-dom' +import { translate } from 'cozy-ui/react/I18n' + +import StyledCard from 'components/CommonKit/Card/StyledCard' +import StyledIcon from 'components/CommonKit/Icon/StyledIcon' +import LegalNoticeIcon from 'assets/icons/ico/legal-notice.svg' + +interface LegalNoticeContainerProps { + t: Function +} + +const LegalNoticeContainer: React.FC<LegalNoticeContainerProps> = ({ + t, +}: LegalNoticeContainerProps) => { + return ( + <div className="legal-notice-root"> + <div className="legal-notice-content"> + <div className="legal-notice-header text-14-normal-uppercase"> + {t('LEGAL.TITLE_LEGAL')} + </div> + <NavLink className="legal-notice-card-link" to="parameters/legalnotice"> + <StyledCard> + <div className="legal-notice-card"> + <div className="legal-notice-card-content"> + <StyledIcon + className="legal-notice-card-content-icon" + icon={LegalNoticeIcon} + size={50} + /> + <div className="legal-notice-card-content-title"> + {t('LEGAL.READ_LEGAL')} + </div> + </div> + </div> + </StyledCard> + </NavLink> + </div> + </div> + ) +} + +export default translate()(LegalNoticeContainer) diff --git a/src/components/ContainerComponents/ViewContainer/AvailableChallengeDetailsViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/AvailableChallengeDetailsViewContainer.tsx index d9c4c0deaae82aa8034a97e6256581ddef929a50..7152d937d96a2337d0b608dc2ac24db390f83ad8 100644 --- a/src/components/ContainerComponents/ViewContainer/AvailableChallengeDetailsViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/AvailableChallengeDetailsViewContainer.tsx @@ -13,6 +13,8 @@ import { Client, withClient } from 'cozy-client' import StyledButtonValid from 'components/CommonKit/Button/StyledButtonValid' import { ScreenType } from 'enum/screen.enum' import AvailableChallengeIcon from 'assets/png/badges/available-big.png' +import ConsumptionDataManager from 'services/consumptionDataManagerService' +import { DateTime } from 'luxon' interface AvailableChallengeDetailsViewProps { location: any @@ -27,11 +29,16 @@ const AvailableChallengeDetailsViewContainer: React.FC<AvailableChallengeDetails const t = props.t const client = props.client const challengeManager = new ChallengeManager(client) - const { refreshCurrentChallenge, screenType } = useContext(AppContext) + const { refreshCurrentChallenge, screenType, fluidTypes } = useContext( + AppContext + ) const [redirect, setRedirect] = useState(false) const [challenge, setChallenge] = useState<ChallengeType | null>(null) const [userChallenge, setUserChallenge] = useState<UserChallenge | null>(null) const [headerHeight, setHeaderHeight] = useState<number>(0) + const [lackOfDataForChallenge, setLackOfDataForChallenge] = useState<boolean>( + false + ) const defineHeaderHeight = (height: number) => { setHeaderHeight(height) @@ -41,7 +48,7 @@ const AvailableChallengeDetailsViewContainer: React.FC<AvailableChallengeDetails if (challenge) { const data = await challengeManager.startChallenge( challenge, - [0, 1, 2], + fluidTypes, challenge.availableEcogestures ) const chal = await challengeManager.getCurrentChallenge() @@ -69,9 +76,40 @@ const AvailableChallengeDetailsViewContainer: React.FC<AvailableChallengeDetails } useEffect(() => { + let subscribed = true + const lag = challengeManager.getLagDays(fluidTypes) + + const timePeriod = { + startDate: DateTime.local() + .plus({ days: -6 }) + .startOf('day'), + endDate: DateTime.local() + .plus({ days: -lag }) + .endOf('day'), + } + + async function checkPreviousData() { + const cdm = new ConsumptionDataManager(client) + const fetchedPerformanceIndicators = await cdm.getPerformanceIndicators( + timePeriod, + 20, + fluidTypes + ) + if (fetchedPerformanceIndicators && subscribed) { + fetchedPerformanceIndicators.forEach(element => { + !element.value ? setLackOfDataForChallenge(true) : null + }) + } else { + setLackOfDataForChallenge(true) + } + } + checkPreviousData() if (props.location.state) { setChallenge(props && props.location.state.challenge) } + return () => { + subscribed = false + } }, []) return ( @@ -114,6 +152,7 @@ const AvailableChallengeDetailsViewContainer: React.FC<AvailableChallengeDetails </div> <div className="cp-right-button"> <StyledButtonValid + disabled={lackOfDataForChallenge} color="primary" onClick={handleStartClick} > @@ -122,6 +161,11 @@ const AvailableChallengeDetailsViewContainer: React.FC<AvailableChallengeDetails </StyledButtonValid> </div> </div> + <div className="lack-of-data-challenge"> + {lackOfDataForChallenge + ? t('CHALLENGE.LACK_OF_DATA') + : null} + </div> </div> </div> </div> diff --git a/src/components/ContainerComponents/ViewContainer/FinishedChallengeDetailsViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/FinishedChallengeDetailsViewContainer.tsx index 8be12a7ec3835b5dfed627bff6ad3143bb26e2b8..4dfb491d83fb89d088fb65e6c880b78be957c64f 100644 --- a/src/components/ContainerComponents/ViewContainer/FinishedChallengeDetailsViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/FinishedChallengeDetailsViewContainer.tsx @@ -34,7 +34,7 @@ const FinishedChallengeDetailsViewContainer: React.FC<FinishedChallengeDetailsVi const [challenge, setChallenge] = useState<UserChallenge | null>(null) const [headerHeight, setHeaderHeight] = useState<number>(0) const [badgeIcon, setBadgeIcon] = useState<any | null>(null) - const { screenType } = useContext(AppContext) + const { screenType, toggleBackgroundScroll } = useContext(AppContext) async function importRightBadge(id: string, badgeStatus: number) { // Les png doivent être au format idchallenge-badgestate.png @@ -56,10 +56,12 @@ const FinishedChallengeDetailsViewContainer: React.FC<FinishedChallengeDetailsVi const handleClick = (index: number) => { setChallengeEcogesture(index) setOpenEcogestureModal(true) + toggleBackgroundScroll() } const handleCloseClick = () => { setOpenEcogestureModal(false) + toggleBackgroundScroll() } useEffect(() => { @@ -164,6 +166,7 @@ const FinishedChallengeDetailsViewContainer: React.FC<FinishedChallengeDetailsVi opened={openEcogestureModal} ecogesture={challenge.selectedEcogestures[challengeEcogesture]} handleCloseClick={handleCloseClick} + unlockedEcogesture={true} /> </> )} diff --git a/src/components/ContainerComponents/ViewContainer/HomeViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/HomeViewContainer.tsx index c1a4e4a76488678ad88bc5977307d59c2d02e0be..b17278c282a74a8a5893064dcca49042eb6791df 100644 --- a/src/components/ContainerComponents/ViewContainer/HomeViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/HomeViewContainer.tsx @@ -1,4 +1,4 @@ -import React, { useState, useContext } from 'react' +import React, { useState, useContext, useEffect } from 'react' import StyledSpinner from 'components/CommonKit/Spinner/StyledSpinner' import CozyBar from 'components/ContainerComponents/CozyBar/CozyBar' import Header from 'components/ContainerComponents/Header/Header' @@ -12,9 +12,12 @@ import ChallengeCardContainer from 'components/ContainerComponents/ChallengeCard import KonnectorViewerContainer from 'components/ContainerComponents/KonnectorViewerContainer/KonnectorViewerContainer' const HomeViewContainer: React.FC = () => { - const { fluidTypes, previousTimeStep, setPreviousTimeStep } = useContext( - AppContext - ) + const { + fluidTypes, + previousTimeStep, + setPreviousTimeStep, + chartIsLoaded, + } = useContext(AppContext) const [timeStep, setTimeStep] = useState<TimeStep>( previousTimeStep && previousTimeStep !== TimeStep.HALF_AN_HOUR ? previousTimeStep @@ -65,24 +68,32 @@ const HomeViewContainer: React.FC = () => { /> </Header> <Content height={headerHeight}> - {(isChartLoading || isIndicatorsLoading) && ( + {(isChartLoading || isIndicatorsLoading || !chartIsLoaded) && ( <div className="content-view-loading"> <StyledSpinner size="5em" /> </div> )} - <FluidContainer - timeStep={timeStep} - fluidTypes={fluidTypes} - resetReferenceDate={resetRefenceDate} - multiFluid={true} - handleClickTimeStep={handleClickTimeStepForFluidContainer} - setChartLoaded={setChartLoaded} - /> - <MultliFluidIndicatorsContainer - timeStep={timeStep} - setIndicatorsLoaded={setIndicatorsLoaded} - /> - <ChallengeCardContainer /> + <div + className={`${ + isChartLoading || isIndicatorsLoading || !chartIsLoaded + ? 'chart-indicator-none' + : 'chart-indicator-block' + }`} + > + <FluidContainer + timeStep={timeStep} + fluidTypes={fluidTypes} + resetReferenceDate={resetRefenceDate} + multiFluid={true} + handleClickTimeStep={handleClickTimeStepForFluidContainer} + setChartLoaded={setChartLoaded} + /> + <MultliFluidIndicatorsContainer + timeStep={timeStep} + setIndicatorsLoaded={setIndicatorsLoaded} + /> + <ChallengeCardContainer /> + </div> </Content> </> ) : ( diff --git a/src/components/ContainerComponents/ViewContainer/LegalNoticeViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/LegalNoticeViewContainer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ac17fa31bf2cda767983b26c2f47f1e085828871 --- /dev/null +++ b/src/components/ContainerComponents/ViewContainer/LegalNoticeViewContainer.tsx @@ -0,0 +1,331 @@ +import React, { useState } from 'react' +import CozyBar from 'components/ContainerComponents/CozyBar/CozyBar' +import Header from 'components/ContainerComponents/Header/Header' +import Content from 'components/ContainerComponents/Content/Content' + +const LegalNoticeViewContainer: React.FC = () => { + const [headerHeight, setHeaderHeight] = useState<number>(0) + const defineHeaderHeight = (height: number) => { + setHeaderHeight(height) + } + + const url = window.location.hostname + + return ( + <React.Fragment> + <CozyBar titleKey={'COMMON.APP_LEGAL_TITLE'} displayBackArrow={true} /> + <Header + setHeaderHeight={defineHeaderHeight} + desktopTitleKey={'COMMON.APP_LEGAL_TITLE'} + displayBackArrow={true} + ></Header> + <Content height={headerHeight}> + <div className="legal-notice-root"> + <div className="legal-notice-content"> + <h2>Mentions légales de Ecolyo</h2> + <p>Site du service Ecolyo : {url}</p> + <p> + Métropole de Lyon - 20, rue du Lac – CS 33569 - 69505 Lyon cedex + 03{' '} + </p> + <p>Tél : (33) 4 78 63 40 40</p>{' '} + <p className="ln-contact">contact(at)grandlyon.com </p> + <div className="text-16-normal"> + <div className="legal-notice-oneline"> + <span className="text-16-bold">Directeur de publication</span> : + Blandine MELAY + </div>{' '} + <div className="legal-notice-oneline"> + <span className="text-16-bold"> + Animation éditoriale, gestion et mise à jour + </span>{' '} + : Marion BERTHOLON, Maria Inés LEAL + </div>{' '} + <div className="legal-notice-oneline"> + <span className="text-16-bold">Photographies</span> : sauf + mention contraire, les photos sont la propriété de la Métropole + de Lyon + </div>{' '} + <div className="legal-notice-oneline"> + <span className="text-16-bold"> + Conception et Charte graphique + </span>{' '} + : Florent Dufier -{' '} + <a + href="https://florentdufier.myportfolio.com/" + target="_blank" + rel="noopener noreferrer" + > + https://florentdufier.myportfolio.com/ + </a> + </div> + <div className="legal-notice-oneline"> + <span className="text-16-bold">Réalisation technique</span> : + Métropole de Lyon, Sopra Steria, Cozy Cloud, Clever Age + </div> + <div className="legal-notice-oneline"> + <span className="text-16-bold">Maintenance technique</span> : + Délégation Développement économique, emploi & savoirs - + Innovation numérique & systèmes d’information - Usages et + services numériques - Développement des services numériques + </div>{' '} + <div className="legal-notice-part"> + <h3>Crédits</h3> + <p> + Ce site est le résultat de développements spécifiques réalisés + dans les langages Go, TypeScript, HTML et Sass. Les + développements s’appuient sur plusieurs bibliothèques et + frameworks libres : axios, cozy-bar, cozy-client, + cozy-harvest-lib, cozy-scripts, cozy-ui, d3, detect-browser, + eslint-config-cozy-app, global, lodash, luxon, moment, + moment-timezone, node-sass, object-hash, react, react-dom, + react-lottie, react-router-dom, react-swipeable-views, + sass-loader. La pile technique intègre également les + applications Cozy stack, Yarn, Docker, ACH. Les déploiements + sont réalisés sur le registre hébergé chez Cozy. L’équipe de + réalisation utilise au quotidien les applications GitLab, + IceScrum, RocketChat, Sonarqube. + </p> + </div> + <div className="legal-notice-part"> + <h3> + Traitement des données personnelles et droit d’accès, de + modification et de suppression + </h3> + <p> + Conformément à la réglementation en vigueur en matière de + protection des données personnelles, le service Ecolyo a fait + l’objet d’une inscription au registre des traitements de la + Métropole de Lyon. Ecolyo fait partie de l’écosystème de + services orientés « self data » déployés par la Métropole de + Lyon avec l’ambition d’offrir aux usagers métropolitains les + outils et les services leur permettant d’exercer directement + leur droit à la portabilité, dans un cadre apte à garantir + aussi bien la transparence et le contrôle sur l’usage de leurs + données personnelles que l’exploitation directe du contenu de + ces données selon leurs libres choix. Le self data est en + effet selon la Fondation Internet Nouvelle Génération (FING) + « “la production, l’exploitation et le partage de données + personnelles par les individus, sous leur contrôle et à leurs + propres fins ». Au sein de cet environnement self data, la + gestion des données s’appuie sur l’organisation suivante des + rôles et responsabilités associées : + </p> + <ul> + <li> + Les partenaires du service Ecolyo – Enedis, GRDF et Eau du + Grand Lyon sont responsable exclusivements des selis + traitements de Données Personnelles relatifs à la collecte + des données de consommation de l’usager et à leur + transmission sur la plateforme de cloud personnel, après + consentement de l’usager ; + </li> + <li> + La Métropole de Lyon est responsable de traitement sur le + périmètre du service Ecolyo qu’elle propose à l’usager, + ainsi que les traitements nécessaires à la fourniture de la + plateforme de cloud personnel qu’elle met à disposition de + l’usager pour accéder au service Ecolyo. En sa qualité de + responsable de ces traitements, elle collecte et traite : + <ul> + <li> + Les données de compte de l’usager sur la plateforme de + cloud personnel à des fins de gestion du compte et de + communication avec l’usager ; + </li> + <li> + Les données privées de consommation d’énergie dont la + récupération, la sauvegarde, le stockage, la + synchronisation et le partage sur la plateforme de cloud + personnel sont initiés par l’usager sans visibilité de + la Métropole de Lyon sur leur contenu. + </li> + </ul> + </li> + <li> + L’usager, seul décisionnaire des finalités d’utilisation + qu’il souhaite définir pour le traitement de ses données + personnelles, à la suite de leur transmission par les + partenaires du service Ecolyo sur son cloud personnel. + </li> + </ul> + </div> + <div className="legal-notice-part"> + <p> + Ainsi, dans le cadre de l’utilisation d’Ecolyo, l’usager ne + recevra les données des partenaires du service : Enedis, GRDF + et Eau du Grand Lyon seulement qu’à sa demande expresse après + la saisie de ses identifiants. + </p> + <p>L’utilisateur est donc le seul à accéder :</p> + <ul> + <li> + À ses données de consommation d’électricité horaires, + journalières, hebdomadaires, mensuelles et annuelles. + </li> + <li> + À ses données de consommation de gaz journalières, + hebdomadaires, mensuelles et annuelles. + </li> + <li> + À ses données de consommation eau journalières, + hebdomadaires, mensuelles et annuelles. + </li> + </ul> + <p> + Les engagements et responsabilités de la Métropole de Lyon + concernant la protection des données et la confidentialité des + données Ecolyo sont précisés dans les Mentions légales et les + conditions d’utilisation du cloud personnel Grand Lyon qui + accueille aujourd’hui le service Ecolyo et sans lequel le + service ne peut pas fonctionner. Conformément à la loi 78-17 + du 6 janvier 1978 modifiée relative à l’information, aux + fichiers et aux libertés, vous disposez d’un droit d’accès, de + rectification et d’opposition au traitement de vos données à + caractère personnel. Votre cloud personnel vous permet + d’exercer ces droits directement dans cet espace sur vos + données de compte et sur vos données privées de consommation + dans le cadre de votre utilisation du service Ecolyo. + </p> + <p> + Vous pouvez également si vous avez des questions à ce sujet, + vous adresser par courrier postal à : + </p> + <p> + Métropole de Lyon - Direction des Affaires Juridiques et de la + Commande Publique - 20, rue du Lac – CS 33569 - 69505 Lyon + Cedex 03. + </p> + <p> + L’exercice des droits d’accès et de rectification de vos + données nominatives auprès de la Métropole de Lyon concerne + exclusivement celles de « Ecolyo ». + </p> + </div> + <div className="legal-notice-part"> + <h3>Dispositions légales</h3> + <p> + Les divers éléments du site web (la forme, la mise en page, le + fonds, la structure …) sont protégés par le droit des dessins + et modèles, le droit d’auteur, le droit des marques ainsi que + le droit à l’image et ils ne peuvent être copiés ou imités en + tout ou partie sauf autorisation expresse de la Métropole de + Lyon. + </p>{' '} + <p> + Toute personne ne respectant pas les dispositions légales + applicables se rend coupable du délit de contrefaçon et est + passible des sanctions pénales prévues par la loi. + </p> + </div> + <div className="legal-notice-part"> + <h3>Droits d’auteurs</h3> + <p> + Les photographies, textes, logos, pictogrammes, ainsi que + toutes œuvres intégrées dans le site sont la propriété de la + "Métropole de Lyon" ou de tiers ayant autorisé la + "Métropole de Lyon" à les utiliser. + </p> + <p> + Les reproductions, les transmissions, les modifications, les + réutilisations, sur un support papier ou informatique, dudit + site et des œuvres qui y sont reproduites ne sont autorisées + que pour un usage personnel et privé conforme aux dispositions + de l’article L 122-5 du Code de la Propriété Intellectuelle. + Ces reproductions devront ainsi notamment indiquer clairement + la source et l’auteur du site et/ou de ces œuvres multimédias. + En aucun cas ces reproductions ne sauraient porter préjudice + aux droits des tiers. Les reproductions, les transmissions, + les modifications, les réutilisations à des fins + publicitaires, commerciales ou d’information, de tout ou + partie du site, sont totalement interdites. + </p> + </div> + <div className="legal-notice-part"> + <h3>Logo de la Métropole de Lyon et d’Ecolyo</h3> + <p> + Les logos de la Métropole de Lyon et d’Ecolyo ne peuvent pas + être modifiés – proportions, couleurs, éléments, constituants + – et ne peuvent pas être sujet à aucune transformation, + animation ou tout autre processus. Les logos de la Métropole + de Lyon et d’Ecolyo ne peuvent être utilisés et associés + qu’aux seules informations vérifiables. Ils ne peuvent être + notamment utilisés que pour illustrer des relations avec la + Métropole de Lyon ou des actions de sponsorings dûment + établies. Quel que soit le cas d’espèce, la Métropole de Lyon + se réserve le droit d’approuver ou de désapprouver toute + utilisation des logos de la Métropole de Lyon et d’Ecolyo, + pour assurer son utilisation correcte, conformément à + l’éthique, la morale et aux intérêts de la Métropole de Lyon. + Les conditions susmentionnées s’appliquent dans le cadre de + pages web, elles ne font pas référence à l’utilisation des + logos dans tout autre document. La Métropole de Lyon se + réserve le droit de modifier les conditions d’utilisation des + logos de la Métropole de Lyon et d’Ecolyo à tout moment et + sans préavis. + </p> + </div> + <div className="legal-notice-part"> + <h3>Droit des bases de données</h3> + <p> + Les bases de données sont protégées par la loi du 1er juillet + 1998 et le régime français du droit d’auteur. + </p> + </div> + <div className="legal-notice-part"> + <h3>Établissements de liens vers le site Ecolyo</h3> + <p> + La Métropole de Lyon autorise la mise en place d’un lien + hypertexte vers son service Ecolyo pour tous les sites + internet, à l’exclusion de ceux diffusant des informations à + caractère polémique, pornographique, xénophobe ou pouvant, + dans une plus large mesure porter atteinte à la sensibilité du + plus grand nombre. + </p> + <p> + Le lien doit aboutir à la page d’accueil du service et le + service Ecolyo doit apparaître dans une nouvelle fenêtre. Les + pages du service Ecolyo ne doivent en aucun cas être intégrées + à l’intérieur des pages d’un autre site (iframe). + </p> + <p> + Dans tous les cas d’espèce, La Métropole de Lyon se réserve le + droit de demander la suppression d’un lien si elle estime que + le site cible ne respecte pas les règles ainsi définies.{' '} + </p> + </div> + <div className="legal-notice-part"> + <h3>Liens vers des sites tiers depuis le site Ecolyo</h3> + <p> + Les liens hypertextes mis en œuvre au sein du site en + direction d’autres sites et/ou de pages personnelles et d’une + manière générale vers toutes ressources existantes sur + internet ne sauraient engager la responsabilité de la + Métropole de Lyon quant aux liens qu’ils contiennent ou aux + changements ou mises à jour qui leur sont apportés. + </p> + </div> + <div className="legal-notice-part"> + <h3>Mise en garde générale</h3> + <p> + Nos services mettent tout en œuvre pour offrir aux visiteurs + de ce site web des informations fiables et vérifiées. + Cependant, malgré tous les soins apportés, le site peut + comporter des inexactitudes, des défauts de mise à jour ou des + erreurs. + </p> + <p> + Nous remercions les utilisateurs du site de nous faire part + d’éventuelles omissions, erreurs ou corrections par mail sur + la boite aux lettres du webmestre. + </p> + </div> + </div> + </div> + </div> + </Content> + </React.Fragment> + ) +} + +export default LegalNoticeViewContainer diff --git a/src/components/ContainerComponents/ViewContainer/LegalViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/LegalViewContainer.tsx deleted file mode 100644 index 4217088a268bfbc1602b90c63b90ed95324c0eea..0000000000000000000000000000000000000000 --- a/src/components/ContainerComponents/ViewContainer/LegalViewContainer.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { useState } from 'react' -import CozyBar from 'components/ContainerComponents/CozyBar/CozyBar' -import Header from 'components/ContainerComponents/Header/Header' -import Content from 'components/ContainerComponents/Content/Content' - -const LegalViewContainer: React.FC = () => { - const [headerHeight, setHeaderHeight] = useState<number>(0) - const defineHeaderHeight = (height: number) => { - setHeaderHeight(height) - } - return ( - <React.Fragment> - <CozyBar titleKey={'COMMON.APP_LEGAL_TITLE'} displayBackArrow={true} /> - <Header - setHeaderHeight={defineHeaderHeight} - desktopTitleKey={'COMMON.APP_LEGAL_TITLE'} - displayBackArrow={true} - ></Header> - <Content height={headerHeight}> - <p>Mentions légales à venir</p> - </Content> - </React.Fragment> - ) -} - -export default LegalViewContainer diff --git a/src/components/ContainerComponents/ViewContainer/OngoingChallengeDetailsViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/OngoingChallengeDetailsViewContainer.tsx index 5dccdf94b7fd70d0f3a5c3f31f6b2e86a4436301..1d95f0e63226c34a36b4ce9ea9dc563c0a6931d5 100644 --- a/src/components/ContainerComponents/ViewContainer/OngoingChallengeDetailsViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/OngoingChallengeDetailsViewContainer.tsx @@ -38,7 +38,11 @@ const OngoingChallengeDetailsViewContainer: React.FC<OngoingChallengeDetailsView const client = props.client const challengeManager = new ChallengeManager(client) - const { refreshCurrentChallenge, screenType } = useContext(AppContext) + const { + refreshCurrentChallenge, + screenType, + toggleBackgroundScroll, + } = useContext(AppContext) const [challengeEcogesture, setChallengeEcogesture] = useState<number>(0) const [openEcogestureModal, setOpenEcogestureModal] = useState(false) const [challenge, setChallenge] = useState<UserChallenge | null>(null) @@ -63,10 +67,12 @@ const OngoingChallengeDetailsViewContainer: React.FC<OngoingChallengeDetailsView const handleClick = (index: number) => { setChallengeEcogesture(index) setOpenEcogestureModal(true) + toggleBackgroundScroll() } const handleCloseClick = () => { setOpenEcogestureModal(false) + toggleBackgroundScroll() } useEffect(() => { diff --git a/src/components/ContainerComponents/ViewContainer/ParametersViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/ParametersViewContainer.tsx index b2fe9942ce102237b1129f1c9d70afb935dd83e5..ceaac1930755cece69883ccb913e0faead4c7c5a 100644 --- a/src/components/ContainerComponents/ViewContainer/ParametersViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/ParametersViewContainer.tsx @@ -4,7 +4,7 @@ import Header from 'components/ContainerComponents/Header/Header' import Content from 'components/ContainerComponents/Content/Content' import KonnectorViewerContainer from 'components/ContainerComponents/KonnectorViewerContainer/KonnectorViewerContainer' import FAQContainer from 'components/ContainerComponents/FAQContainer/FAQContainer' -import LegalContainer from 'components/ContainerComponents/LegalContainer/LegalContainer' +import LegalNoticeContainer from 'components/ContainerComponents/LegalNoticeContainer/LegalNoticeContainer' const ParametersViewContainer: React.FC = () => { const [headerHeight, setHeaderHeight] = useState<number>(0) @@ -21,7 +21,7 @@ const ParametersViewContainer: React.FC = () => { <Content height={headerHeight}> <FAQContainer /> <KonnectorViewerContainer isParam={true} /> - <LegalContainer /> + <LegalNoticeContainer /> </Content> </React.Fragment> ) diff --git a/src/components/ContainerComponents/ViewContainer/SingleFluidViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/SingleFluidViewContainer.tsx index 882df7e31342b3200032309439f28d202850df3b..9394f64f5f368eb045273a8ba4d913ff97c63d99 100644 --- a/src/components/ContainerComponents/ViewContainer/SingleFluidViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/SingleFluidViewContainer.tsx @@ -16,7 +16,9 @@ interface SingleFluidViewContainerProps { const SingleFluidViewContainer: React.FC<SingleFluidViewContainerProps> = ({ fluidTypes, }: SingleFluidViewContainerProps) => { - const { setPreviousTimeStep, previousTimeStep } = useContext(AppContext) + const { setPreviousTimeStep, previousTimeStep, chartIsLoaded } = useContext( + AppContext + ) const [timeStep, setTimeStep] = useState<TimeStep | null>( previousTimeStep && previousTimeStep !== TimeStep.HALF_AN_HOUR @@ -71,14 +73,16 @@ const SingleFluidViewContainer: React.FC<SingleFluidViewContainerProps> = ({ /> </Header> <Content height={headerHeight}> - {(isChartLoading || isIndicatorsLoading) && ( + {(isChartLoading || isIndicatorsLoading || !chartIsLoaded) && ( <div className="content-view-loading"> <StyledSpinner size="5em" fluidTypes={fluidTypes} /> </div> )} <div className={`${ - isChartLoading ? 'chart-indicator-none' : 'chart-indicator-display' + isChartLoading || isIndicatorsLoading || !chartIsLoaded + ? 'chart-indicator-none' + : 'chart-indicator-block' }`} > <FluidChartContainer diff --git a/src/components/ContainerComponents/ViewContainer/ViewContainer.tsx b/src/components/ContainerComponents/ViewContainer/ViewContainer.tsx index c700cf5eb79198e2b6a9e274608a11ad628fc47c..500f05b0f2d9cb9c7d0e884b581581da88bd7002 100644 --- a/src/components/ContainerComponents/ViewContainer/ViewContainer.tsx +++ b/src/components/ContainerComponents/ViewContainer/ViewContainer.tsx @@ -18,7 +18,7 @@ import OngoingChallengeDetailsViewContainer from './OngoingChallengeDetailsViewC import LockedChallengeDetailsViewContainer from './LockedChallengeDetailsViewContainer' import AvailableChallengeDetailsViewContainer from './AvailableChallengeDetailsViewContainer' import SplashContainer from 'components/ContainerComponents/SplashContainer/SplashContainer' -import LegalViewContainer from './LegalViewContainer' +import LegalNoticeViewContainer from './LegalNoticeViewContainer' export const history = createBrowserHistory() @@ -105,8 +105,8 @@ export const ViewContainer = () => { component={FAQViewContainer} /> <Route - path={`${url}/legal`} - component={LegalViewContainer} + path={`${url}/legalnotice`} + component={LegalNoticeViewContainer} /> <Route path={`${url}/`} diff --git a/src/components/ContentComponents/Challenge/FollowChallengeTimeline.tsx b/src/components/ContentComponents/Challenge/FollowChallengeTimeline.tsx index e8b3edc7b58b0113e22d46c298786d4273144daa..6cc015ff90dd0bd569bd5f4722b1b7eaf68e7ccd 100644 --- a/src/components/ContentComponents/Challenge/FollowChallengeTimeline.tsx +++ b/src/components/ContentComponents/Challenge/FollowChallengeTimeline.tsx @@ -23,7 +23,7 @@ const FollowChallengeTimeline: React.FC<FollowChallengeTimelineViewProps> = ({ if (challenge && challenge.challengeType) { const startingDate = challenge.startingDate return startingDate.plus({ - days: challengeManager.getLagDays(fluidTypes), + days: challengeManager.getLagDays(challenge.fluidTypes), }) } else { return DateTime.local() diff --git a/src/components/ContentComponents/Challenge/OngoingChallengeViewingDate.tsx b/src/components/ContentComponents/Challenge/OngoingChallengeViewingDate.tsx index f6f20ed409d389a3bafa7d01c8969388e0fe36a6..85dd1f3734738666cd4be57f06e1e93d81b275fb 100644 --- a/src/components/ContentComponents/Challenge/OngoingChallengeViewingDate.tsx +++ b/src/components/ContentComponents/Challenge/OngoingChallengeViewingDate.tsx @@ -28,7 +28,7 @@ const OngoingChallengeViewingDate: React.FC<OngoingChallengeViewingDateProps> = useEffect(() => { if (challenge) { - const lag = challengeManager.getLagDays(fluidTypes) + const lag = challengeManager.getLagDays(challenge.fluidTypes) setFirstDateWithData( challenge.startingDate.plus({ days: lag, diff --git a/src/components/ContentComponents/ChallengeList/ChallengesList.tsx b/src/components/ContentComponents/ChallengeList/ChallengesList.tsx index 46c71e6a2a1b9d196c22ba3ec965492e0fbc1269..523800f93fe5089e461984bd08ddd168acd4d957 100644 --- a/src/components/ContentComponents/ChallengeList/ChallengesList.tsx +++ b/src/components/ContentComponents/ChallengeList/ChallengesList.tsx @@ -19,14 +19,21 @@ interface ChallengesListProps { const ChallengesList: React.FC<ChallengesListProps> = ({ client, }: ChallengesListProps) => { - const { fluidTypes, refreshCurrentChallenge, screenType } = useContext( - AppContext - ) + const { + fluidTypes, + refreshCurrentChallenge, + screenType, + toggleBackgroundScroll, + } = useContext(AppContext) const challengeManager = new ChallengeManager(client) const [ ongoingChallenge, setOngoingChallenge, ] = useState<UserChallenge | null>(null) + const [ + ongoingChallengeModal, + setOngoingChallengeModal, + ] = useState<UserChallenge | null>(null) const [challengesType, setChallengesType] = useState<ChallengeType[] | null>( null ) @@ -41,6 +48,8 @@ const ChallengesList: React.FC<ChallengesListProps> = ({ const handleCloseClick = () => { setOngoingChallenge(null) setOpenChallengeModal(false) + setOngoingChallengeModal(null) + toggleBackgroundScroll() } const setRightChallengeInTheMiddle = ( @@ -97,6 +106,7 @@ const ChallengesList: React.FC<ChallengesListProps> = ({ setChallengesType(dataAllCT) setUserChallenges(dataAllUC) setOngoingChallenge(ongoingChallengeTmp) + setOngoingChallengeModal(ongoingChallengeTmp) setUserLevel(levelOfUser) if ( await challengeManager.isChallengeOver( @@ -105,8 +115,10 @@ const ChallengesList: React.FC<ChallengesListProps> = ({ ) ) { setOpenChallengeModal(true) + toggleBackgroundScroll() await challengeManager.endChallenge(ongoingChallengeTmp, fluidTypes) await refreshCurrentChallenge() + setOngoingChallenge(null) } } } @@ -200,9 +212,9 @@ const ChallengesList: React.FC<ChallengesListProps> = ({ )} <ChallengeModal opened={openChallengeModal} - challenge={ongoingChallenge} + challenge={ongoingChallengeModal} handleCloseClick={handleCloseClick} - badgeStatus={ongoingChallenge && ongoingChallenge.badge} + badgeStatus={ongoingChallengeModal && ongoingChallengeModal.badge} /> </> ) diff --git a/src/components/ContentComponents/ChallengeModal/ChallengeModal.tsx b/src/components/ContentComponents/ChallengeModal/ChallengeModal.tsx index a428aa8ab05a9fbfd502f0f1e49f9aaabee45c87..13cec17b7b28a4957a181419413da42d2ec7257c 100644 --- a/src/components/ContentComponents/ChallengeModal/ChallengeModal.tsx +++ b/src/components/ContentComponents/ChallengeModal/ChallengeModal.tsx @@ -90,7 +90,7 @@ const ChallengeModal: React.FC<ChallengeModalProps> = ({ {' '} {challengeId === 'CHA00000001' ? ( <> - <div className="text-18-medium"> + <div className="text-18-normal"> {t('CHALLENGE.WIN_TEXT_ECOLYO')} </div> <div className="cm-text-new-available text-18-bold"> diff --git a/src/components/ContentComponents/Charts/BarChart.tsx b/src/components/ContentComponents/Charts/BarChart.tsx index 093c301c0b6c434c3a5dcf4b34c9ad950633701b..1e2fd9ab7a39fb104abbe77498f6765f65eec53d 100644 --- a/src/components/ContentComponents/Charts/BarChart.tsx +++ b/src/components/ContentComponents/Charts/BarChart.tsx @@ -76,7 +76,7 @@ const BarChart: React.FC<BarChartProps> = (props: BarChartProps) => { const maxCompare = chartData.comparisonData ? Math.max(...chartData.comparisonData.map(d => d.value)) : 0 - max = max === -1 ? 15 : max + max = max <= 0 ? 15 : max return showCompare ? Math.max(max, maxCompare) : max } diff --git a/src/components/ContentComponents/ConsumptionVisualizer/ConsumptionVisualizer.tsx b/src/components/ContentComponents/ConsumptionVisualizer/ConsumptionVisualizer.tsx index ab843acfb16bceb318ecbacba84e15f584d1681e..e42661db7164ba5feff30e178928ef70796f0ca1 100644 --- a/src/components/ContentComponents/ConsumptionVisualizer/ConsumptionVisualizer.tsx +++ b/src/components/ContentComponents/ConsumptionVisualizer/ConsumptionVisualizer.tsx @@ -93,14 +93,16 @@ const ConsumptionVisualizer = ({ /> )} {!dataload || - (dataload && dataload.value === -1 && ( - <LastDataConsumptionVisualizer - fluidTypes={fluidTypes} - timeStep={timeStep} - multiFluid={multiFluid} - handleChangeIndex={handleChangeIndex} - /> - ))} + (dataload && + dataload.value === -1 && + dataload.date > lastDataDate && ( + <LastDataConsumptionVisualizer + fluidTypes={fluidTypes} + timeStep={timeStep} + multiFluid={multiFluid} + handleChangeIndex={handleChangeIndex} + /> + ))} </div> </div> ) diff --git a/src/components/ContentComponents/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx b/src/components/ContentComponents/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx index fcf15ef5827175ef6879579d082a2c42d07654c5..af8a52377b7de0e8461aaf060f9f38b9e7b820f3 100644 --- a/src/components/ContentComponents/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx +++ b/src/components/ContentComponents/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useState, useEffect } from 'react' import { translate } from 'cozy-ui/react/I18n' import { NavLink } from 'react-router-dom' import { FluidType } from 'enum/fluid.enum' @@ -33,22 +33,42 @@ const DataloadConsumptionVisualizer = ({ t, }: DataloadConsumptionVisualizerProps) => { const ltcc = new LoadToCurrencyConverter() + const [hasData, setHasData] = useState(false) + const [hasCompareData, setHasCompareData] = useState(false) const fluidStyle = multiFluid ? 'MULTIFLUID' : fluidTypes.length > 1 ? 'MULTIFLUID' : FluidType[fluidTypes[0]] - const hasData = dataload && dataload.value && dataload.value >= 0 - const hasCompareData = - showCompare && - !multiFluid && - compareDataload && - compareDataload.value && - compareDataload.value >= 0 + + useEffect(() => { + if ((dataload && dataload.value) || (dataload && dataload.value === 0)) { + if (dataload.value === -1) { + setHasData(false) + } else { + setHasData(true) + } + } else { + setHasData(false) + } + if ( + compareDataload && + compareDataload.value && + compareDataload.value === -1 + ) { + setHasCompareData(false) + } else { + if (showCompare && !multiFluid) { + setHasCompareData(true) + } else { + setHasCompareData(false) + } + } + }, [dataload, showCompare]) return ( <div className="cv-load"> - {!isLoaded ? null : hasData ? ( + {isLoaded && hasData ? ( <div className="cv-load-content"> {hasCompareData && ( <div className="cv-load-section cv-load-section-left"> @@ -56,7 +76,7 @@ const DataloadConsumptionVisualizer = ({ className={`cv-load-value ${fluidStyle.toLowerCase()}-compare chart-result`} > {formatNumberValues(compareDataload.value)} - <span className="text-18-medium">{`${t( + <span className="text-18-normal">{`${t( 'FLUID.' + fluidStyle + '.UNIT' )}`}</span> </div> @@ -84,7 +104,7 @@ const DataloadConsumptionVisualizer = ({ ltcc.Convert(dataload.value, fluidTypes[0]) ) : formatNumberValues(dataload.value)} - <span className="text-18-medium">{`${t( + <span className="text-18-normal">{`${t( 'FLUID.' + fluidStyle + '.UNIT' )}`}</span> </div> @@ -161,11 +181,23 @@ const DataloadConsumptionVisualizer = ({ </div> </div> ) : ( - <div - className={`cv-load-content ${fluidStyle.toLowerCase()} text-22-normal`} - > - {`${t('COMMON.LASTDATA')} : ${lastDataDate.toFormat("dd'/'MM'/'yy")}`} - </div> + <> + {dataload && dataload.date > lastDataDate ? ( + <div + className={`cv-load-content ${fluidStyle.toLowerCase()} text-22-normal`} + > + {`${t('COMMON.LASTDATA')} : ${lastDataDate.toFormat( + "dd'/'MM'/'yy" + )}`} + </div> + ) : ( + <div + className={`cv-load-content ${fluidStyle.toLowerCase()} text-22-normal`} + > + {`${t('COMMON.NODATA')}`} + </div> + )} + </> )} </div> ) diff --git a/src/components/ContentComponents/EcogestureList/EcogestureList.tsx b/src/components/ContentComponents/EcogestureList/EcogestureList.tsx index 9b7b21f4d1800e772c7b87db9b192cc1bfc053d0..1a436e9da851397ba6633ce3bb16f66d9c621930 100644 --- a/src/components/ContentComponents/EcogestureList/EcogestureList.tsx +++ b/src/components/ContentComponents/EcogestureList/EcogestureList.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' +import React, { useState, useEffect, useContext } from 'react' import { withClient, Client } from 'cozy-client' import StyledChallengeSpinner from 'components/CommonKit/Spinner/StyledChallengeSpinner' import EcogestureCard from '../EcogestureCard/EcogestureCard' @@ -8,6 +8,7 @@ import { EcogestureType } from 'services/dataChallengeContracts' import ChallengeManager from 'services/challengeDataManagerService' import StyledClearGreyButton from 'components/CommonKit/Button/StyledClearGreyButton' import { translate } from 'cozy-ui/react/I18n' +import { AppContext } from 'components/Contexts/AppContextProvider' interface EcogesturesListProps { t: Function @@ -22,7 +23,7 @@ const EcogesturesList: React.FC<EcogesturesListProps> = ({ selectedEcogesture, setSelectedEcogesture, ] = useState<EcogestureType | null>(null) - + const { toggleBackgroundScroll } = useContext(AppContext) const [ecogestures, setEcogestures] = useState<EcogestureType[] | null>(null) const [openEcogestureModal, setOpenEcogestureModal] = useState(false) const [openNegaWattModal, setOpenNegaWattModal] = useState(false) @@ -31,18 +32,22 @@ const EcogesturesList: React.FC<EcogesturesListProps> = ({ const handleClick = (ecogesture: EcogestureType) => { setSelectedEcogesture(ecogesture) setOpenEcogestureModal(true) + toggleBackgroundScroll() } const handleCloseClick = () => { setOpenEcogestureModal(false) + toggleBackgroundScroll() } const handleNegaWattClick = () => { setOpenNegaWattModal(true) + toggleBackgroundScroll() } const handleNegaWattCloseClick = () => { setOpenNegaWattModal(false) + toggleBackgroundScroll() } useEffect(() => { diff --git a/src/components/ContentComponents/EcogestureModal/EcogestureModal.tsx b/src/components/ContentComponents/EcogestureModal/EcogestureModal.tsx index 7bb2d7635f8f701a83d88551d90e4324b5542cdd..4d9d01fc788c4db55a6fa042102d686926dd0d4e 100644 --- a/src/components/ContentComponents/EcogestureModal/EcogestureModal.tsx +++ b/src/components/ContentComponents/EcogestureModal/EcogestureModal.tsx @@ -55,17 +55,16 @@ const EcogestureModal: React.FC<EcogestureModalProps> = ({ <div className="em-header text-14-normal-uppercase"> {t('ECOGESTURE.TITLE_ECOGESTURE')} </div> - <div className="em-content-box"> - <div className="em-content-box-text"> - <div className="em-content-box-text-header"> - {(ecogesture.unlocked || unlockedEcogesture) && ecogestureIcon && ( - <div> + <div className="em-root"> + <div className="em-content"> + <div className="em-content-box-img"> + {(ecogesture.unlocked || unlockedEcogesture) && + ecogestureIcon && ( <Icon className="icon" icon={ecogestureIcon} size={140} /> - </div> - )} - <div className="em-title text-24-bold "> - {ecogesture.shortName} - </div> + )} + </div> + <div className="em-title text-24-bold "> + {ecogesture.shortName} </div> <div className="em-detail"> <div className="em-detail-nwh"> diff --git a/src/components/ContentComponents/FluidChart/FluidChartSlide.tsx b/src/components/ContentComponents/FluidChart/FluidChartSlide.tsx index 22ff38c90b2fda2f0ac2772ad92d62d50f13b718..09bddac34d0973f6863c5b4286c1848de2ab4f9e 100644 --- a/src/components/ContentComponents/FluidChart/FluidChartSlide.tsx +++ b/src/components/ContentComponents/FluidChart/FluidChartSlide.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react' +import React, { useState, useEffect, useContext } from 'react' import { DateTime } from 'luxon' import { IConsumptionDataManager, @@ -12,6 +12,7 @@ import { defineTimePeriod } from 'services/dateChartService' import { FluidType } from 'enum/fluid.enum' import BarChart from 'components/ContentComponents/Charts/BarChart' +import { AppContext } from 'components/Contexts/AppContextProvider' interface FluidChartSlideProps { index: number @@ -48,9 +49,10 @@ const FluidChartSlide: React.FC<FluidChartSlideProps> = ({ const [chartData, setChartData] = useState<IChartData>(new ChartData([])) const [isLoaded, setIsLoaded] = useState<boolean>(false) + const { setChartIsLoaded, chartIsLoaded } = useContext(AppContext) + useEffect(() => { let subscribed = true - async function loadData() { const [timePeriod, compareTimePeriod] = await Promise.all([ defineTimePeriod(referenceDate, timeStep, index), @@ -65,9 +67,11 @@ const FluidChartSlide: React.FC<FluidChartSlideProps> = ({ if (subscribed && graphData && graphData.actualData.length > 0) { setChartData(graphData) setIsLoaded(true) + setChartIsLoaded(true) } } setIsLoaded(false) + loadData() return () => { subscribed = false diff --git a/src/components/ContentComponents/Konnector/KonnectorLaunch.tsx b/src/components/ContentComponents/Konnector/KonnectorLaunch.tsx index a967b39588a26f4d24775200504741a72515ee1b..69470f3abebe21f769a60987bb540f5843c101cf 100644 --- a/src/components/ContentComponents/Konnector/KonnectorLaunch.tsx +++ b/src/components/ContentComponents/Konnector/KonnectorLaunch.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState, useContext } from 'react' import { withClient, Client } from 'cozy-client' import { translate } from 'cozy-ui/react/I18n' @@ -18,6 +18,7 @@ import Modal from 'components/CommonKit/Modal/Modal' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import errorIcon from 'assets/icons/visu/data-nok.svg' import successIcon from 'assets/icons/visu/data-ok.svg' +import { AppContext } from 'components/Contexts/AppContextProvider' const loadingOptions = { loop: true, @@ -45,7 +46,7 @@ const KonnectorLaunch: React.FC<KonnectorLaunchProps> = ({ }: KonnectorLaunchProps) => { const [state, setState] = useState<string | null>(null) const [openModal, setOpenModal] = useState(false) - + const { toggleBackgroundScroll } = useContext(AppContext) const callbackResponse = (_state: string) => { setState(_state) } @@ -53,6 +54,7 @@ const KonnectorLaunch: React.FC<KonnectorLaunchProps> = ({ const handleCloseClick = () => { handleKonnectorLaunch() setOpenModal(false) + toggleBackgroundScroll() } useEffect(() => { @@ -71,6 +73,7 @@ const KonnectorLaunch: React.FC<KonnectorLaunchProps> = ({ callbackResponse(SUCCESS_EVENT) }) setOpenModal(true) + toggleBackgroundScroll() } } getData() diff --git a/src/components/ContentComponents/Konnector/KonnectorResult.tsx b/src/components/ContentComponents/Konnector/KonnectorResult.tsx index b9f30f6410cfd8889e667c0e9f047c72fa438428..fdb201e67fbf157bfc16751c951777dc3f4059d1 100644 --- a/src/components/ContentComponents/Konnector/KonnectorResult.tsx +++ b/src/components/ContentComponents/Konnector/KonnectorResult.tsx @@ -41,10 +41,15 @@ const KonnectorResult: React.FC<KonnectorResultProps> = ({ const updateState = async (trigger: Trigger) => { const triggerState = await TriggerService.fetchTriggerState(client, trigger) + if (triggerState) { - setLastExecutionDate( - new Date(triggerState.last_execution).toLocaleString() - ) + if (triggerState.last_success) { + setLastExecutionDate( + new Date(triggerState.last_success).toLocaleString() + ) + } else { + setLastExecutionDate('-') + } setStatus(triggerState.status) handleJobState(triggerState.status) await context.refreshFluidTypes() diff --git a/src/components/Contexts/AppContextProvider.tsx b/src/components/Contexts/AppContextProvider.tsx index 0e3f7f2206e44bea3cc1b6a18f843eb309a948e8..a69164e65347b7aa6fe6dc463b381ad0b1ec9c03 100644 --- a/src/components/Contexts/AppContextProvider.tsx +++ b/src/components/Contexts/AppContextProvider.tsx @@ -29,6 +29,9 @@ interface AppContextProps { setPreviousTimeStep: Function userProfile: UserProfile | null setWelcomeModalViewed: Function + chartIsLoaded: boolean + setChartIsLoaded: Function + toggleBackgroundScroll: Function } export const AppContext = React.createContext<AppContextProps>({ @@ -52,6 +55,9 @@ export const AppContext = React.createContext<AppContextProps>({ userProfile: null, setPreviousTimeStep: () => null, setWelcomeModalViewed: () => null, + chartIsLoaded: false, + setChartIsLoaded: () => null, + toggleBackgroundScroll: () => null, }) interface AppContextProviderProps { @@ -63,6 +69,7 @@ const AppContextProvider: React.FC<AppContextProviderProps> = ({ children, client, }: AppContextProviderProps) => { + const [chartIsLoaded, setChartIsLoaded] = useState<boolean>(false) const [isIndexesLoading, setIndexesLoading] = useState<boolean>(false) const [isIndexesLoadingSuccess, setIndexesLoadingSuccess] = useState< boolean | null @@ -109,9 +116,9 @@ const AppContextProvider: React.FC<AppContextProviderProps> = ({ const [userProfile, setUserProfile] = useState<UserProfile | null>(null) const defineScreenType = () => { - if (screen.width <= 768) { + if (innerWidth <= 768) { setScreenType(ScreenType.MOBILE) - } else if (screen.width <= 1024) { + } else if (innerWidth <= 1024) { setScreenType(ScreenType.TABLET) } else { setScreenType(ScreenType.DESKTOP) @@ -149,12 +156,13 @@ const AppContextProvider: React.FC<AppContextProviderProps> = ({ const refreshCurrentChallenge = async (): Promise<boolean> => { const im = new InitDataManager(client) try { - const resultUpdateChallenge = await im.checkCurrentChallenge(fluidTypes) + const resultUpdateChallenge = await im.checkCurrentChallenge() setCurrentChallenge(resultUpdateChallenge) + if (resultUpdateChallenge) { const resultNotificationChallenge = await im.isCurrentChallengeOver( resultUpdateChallenge, - fluidTypes + resultUpdateChallenge.fluidTypes ) if (resultNotificationChallenge) { setChallengeNotification(resultNotificationChallenge) @@ -170,6 +178,25 @@ const AppContextProvider: React.FC<AppContextProviderProps> = ({ } } + const toggleBackgroundScroll = () => { + const backgroundDesktop = document.querySelector( + '.app-content' + ) as HTMLElement + const backgroundMobile = document.querySelector('html') as HTMLElement + const bgStyleDesktop = backgroundDesktop && backgroundDesktop.style + const bgStyleMobile = backgroundMobile && backgroundMobile.style + if ( + bgStyleDesktop.overflowY === 'hidden' || + bgStyleMobile.overflowY === 'hidden' + ) { + bgStyleDesktop.overflowY = 'unset' + bgStyleMobile.overflowY = 'unset' + } else { + bgStyleDesktop.overflowY = 'hidden' + bgStyleMobile.overflowY = 'hidden' + } + } + useEffect(() => { let subscribed = true function handleResize() { @@ -224,13 +251,13 @@ const AppContextProvider: React.FC<AppContextProviderProps> = ({ // Update current challenge if exists setCurrentChallengeUpdateLoading(true) try { - const resultUpdateChallenge = await im.checkCurrentChallenge( - resultFluidTypes ? resultFluidTypes : [] - ) + const resultUpdateChallenge = await im.checkCurrentChallenge() if (subscribed && resultUpdateChallenge) { const resultNotificationChallenge = await im.isCurrentChallengeOver( resultUpdateChallenge, - resultFluidTypes ? resultFluidTypes : [] + resultUpdateChallenge.fluidTypes + ? resultUpdateChallenge.fluidTypes + : [] ) if (subscribed && resultNotificationChallenge) { setChallengeNotification(resultNotificationChallenge) @@ -317,6 +344,9 @@ const AppContextProvider: React.FC<AppContextProviderProps> = ({ previousTimeStep, setPreviousTimeStep, setWelcomeModalViewed, + setChartIsLoaded, + chartIsLoaded, + toggleBackgroundScroll, }} > {children} diff --git a/src/db/challengeTypeData.json b/src/db/challengeTypeData.json index 033efa5b0f104616fead022f75b38c5ba7bbe069..e83ed61d1365bb9a7b8daca1b855d1690cb2abff 100644 --- a/src/db/challengeTypeData.json +++ b/src/db/challengeTypeData.json @@ -20,7 +20,7 @@ "_id": "CHA00000002", "type": 0, "title": "Coques en stock", - "description": "Et si dans les 7 prochains jours vous réussissiez à consommer moins que dans les 7 derniers", + "description": "Et si dans les 7 prochains jours vous réussissiez à consommer moins que dans les 7 derniers ?", "level": 2, "duration": { "days": 7 }, "fluidTypes": [0, 1, 2], @@ -49,7 +49,7 @@ "_id": "CHA00000003", "type": 0, "title": "Méga Coques en stock", - "description": "Et si dans les 4 prochaines semaines vous réussissiez à consommer moins que dans les 4 dernières", + "description": "Et si dans les 4 prochaines semaines vous réussissiez à consommer moins que dans les 4 dernières ?", "level": 3, "duration": { "days": 28 }, "fluidTypes": [0, 1, 2], diff --git a/src/locales/fr.json b/src/locales/fr.json index 0359e37a5ab4da14e66e6f6c05c982786c11f736..de2006ddc82fcd6d9549940413b37aa6439672c5 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -168,6 +168,7 @@ "FULLY_LOCKED": "Ce défi sera disponible dans une prochaine mise à jour", "START": "Allons-y !", "NOT_NOW": "Pas maintenant !", + "LACK_OF_DATA": "Il manque des données de comparaison pour lancer ce défi", "STOP": "Arrêter le défi", "BACK": "I'll be back", "ECOGESTURE": "Voir l'écogeste", @@ -197,7 +198,7 @@ "TITLE_NEGAWATT": "NégaWatt", "QUESTION": "Que sont les nWh ? (néga Watt heure)", "ANSWER": { - "BASE": "Le néga Watt heure (ou nWh) sert d'unité de mesure de vos économies d'énergie.", + "BASE": "Le néga Watt heure (ou nWh) sert d'unité de mesure de vos économies d'énergie. Cette unité est purement indicative et arbitraire.", "STRONG": "10 nWh : économie importante", "MEDIUM": "3 nWh : économie moyenne", "WEAK": "1 nWh : économie faible" diff --git a/src/services/IFluidConfig.ts b/src/services/IFluidConfig.ts index 8cb83c90cfde02a631039baf377044f5d1ba03dc..9147fe93e0769d6e6c99ac5170e40d1e828a1c24 100644 --- a/src/services/IFluidConfig.ts +++ b/src/services/IFluidConfig.ts @@ -3,7 +3,7 @@ interface KonnectorConfig { type: string oauth: boolean slug: string - id: string + cron?: string } // eslint-disable-next-line @typescript-eslint/interface-name-prefix diff --git a/src/services/challengeDataManagerService.ts b/src/services/challengeDataManagerService.ts index 6da8ff4b80846bb72f731101bafd4d876e1f866d..00a32f05a680fb0ac96fcfe03fef4ef3b0cfd381 100644 --- a/src/services/challengeDataManagerService.ts +++ b/src/services/challengeDataManagerService.ts @@ -38,7 +38,7 @@ export default class ChallengeManager implements IChallengeManager { public async getMaxEnergy( challenge: UserChallenge, client: Client, - fluidTypes: FluidType[] + challengeFluidTypes: FluidType[] ) { const cdm = new ConsumptionDataManager(client) let durationTimeStep = '' @@ -55,7 +55,7 @@ export default class ChallengeManager implements IChallengeManager { const fetchedPerformanceIndicators = await cdm.getPerformanceIndicators( period, TimeStep.DAY, - fluidTypes + challengeFluidTypes ) const maxEnergy = PerformanceIndicatorAggregateCalculator.aggregatePerformanceIndicators( fetchedPerformanceIndicators @@ -69,7 +69,7 @@ export default class ChallengeManager implements IChallengeManager { public async setMaxEnergy( challenge: UserChallenge, client: Client, - fluidTypes: FluidType[] + challengeFluidTypes: FluidType[] ): Promise<number> { const cdm = new ConsumptionDataManager(client) let durationTimeStep = '' @@ -86,7 +86,7 @@ export default class ChallengeManager implements IChallengeManager { const fetchedPerformanceIndicators = await cdm.getPerformanceIndicators( period, TimeStep.DAY, - fluidTypes + challengeFluidTypes ) const maxEnergy = PerformanceIndicatorAggregateCalculator.aggregatePerformanceIndicators( fetchedPerformanceIndicators @@ -119,16 +119,16 @@ export default class ChallengeManager implements IChallengeManager { public async getSpentEnergy( challenge: UserChallenge, client: Client, - fluidTypes: FluidType[] + challengeFluidTypes: FluidType[] ) { - const lagDays = this.getLagDays(fluidTypes) + const lagDays = this.getLagDays(challengeFluidTypes) if (DateTime.local() > challenge.startingDate.plus({ days: lagDays })) { const cdm = new ConsumptionDataManager(client) const startDate = challenge.startingDate let endDate = DateTime.local() .plus({ days: -lagDays }) .endOf('day') - if (await this.isChallengeOver(challenge, fluidTypes)) { + if (await this.isChallengeOver(challenge, challengeFluidTypes)) { endDate = challenge.endingDate.plus({ days: -1 }).endOf('day') } const period = { startDate, endDate } @@ -136,7 +136,7 @@ export default class ChallengeManager implements IChallengeManager { const fetchedPerformanceIndicators = await cdm.getPerformanceIndicators( period, TimeStep.DAY, - fluidTypes + challengeFluidTypes ) const spentEnergy = PerformanceIndicatorAggregateCalculator.aggregatePerformanceIndicators( @@ -154,16 +154,16 @@ export default class ChallengeManager implements IChallengeManager { public async setSpentEnergy( challenge: UserChallenge, client: Client, - fluidTypes: FluidType[] + challengeFluidTypes: FluidType[] ): Promise<number> { - const lagDays = this.getLagDays(fluidTypes) + const lagDays = this.getLagDays(challengeFluidTypes) if (DateTime.local() > challenge.startingDate.plus({ days: lagDays })) { const cdm = new ConsumptionDataManager(client) const startDate = challenge.startingDate let endDate = DateTime.local() .plus({ days: -lagDays }) .endOf('day') - if (await this.isChallengeOver(challenge, fluidTypes)) { + if (await this.isChallengeOver(challenge, challengeFluidTypes)) { endDate = challenge.endingDate.plus({ days: -1 }).endOf('day') } const period = { startDate, endDate } @@ -171,7 +171,7 @@ export default class ChallengeManager implements IChallengeManager { const fetchedPerformanceIndicators = await cdm.getPerformanceIndicators( period, TimeStep.DAY, - fluidTypes + challengeFluidTypes ) const spentEnergy = PerformanceIndicatorAggregateCalculator.aggregatePerformanceIndicators( fetchedPerformanceIndicators @@ -340,13 +340,10 @@ export default class ChallengeManager implements IChallengeManager { * Return the date of the first day for which * we can calculate the data in function of configured fluidTypes */ - public getViewingDate = ( - challenge: UserChallenge, - fluidTypes: FluidType[] - ): DateTime => { + public getViewingDate = (challenge: UserChallenge): DateTime => { const startingDate = challenge.startingDate - if (fluidTypes.length > 0) { - const lagDays = this.getLagDays(fluidTypes) + if (challenge && challenge.fluidTypes && challenge.fluidTypes.length > 0) { + const lagDays = this.getLagDays(challenge.fluidTypes) return startingDate.plus({ days: lagDays }) } else { return startingDate @@ -358,9 +355,17 @@ export default class ChallengeManager implements IChallengeManager { * the possible calculation of data based on configured fluidTypes */ public getLagDays = (fluidTypes: FluidType[]): number => { - if (fluidTypes.includes(FluidType.WATER)) { + if ( + fluidTypes && + fluidTypes.length > 0 && + fluidTypes.includes(FluidType.WATER) + ) { return 3 - } else if (fluidTypes.includes(FluidType.GAS)) { + } else if ( + fluidTypes && + fluidTypes.length > 0 && + fluidTypes.includes(FluidType.GAS) + ) { return 2 } else { return 1 @@ -473,7 +478,8 @@ export default class ChallengeManager implements IChallengeManager { challenge, -1, -1, - -1 + -1, + fluidTypes ) const resultUserChallenge = await this._client.create( diff --git a/src/services/challengeDataMapperService.ts b/src/services/challengeDataMapperService.ts index 198957092047d279716b35bd75c3cc5b7ecea59b..c865ca78e1c5646085e235aa3f5203af44745433 100644 --- a/src/services/challengeDataMapperService.ts +++ b/src/services/challengeDataMapperService.ts @@ -19,6 +19,7 @@ export class UserChallengeEntity { currentEnergy: number badge: BadgeState relationships: any + fluidTypes: FluidType[] constructor( startingDate: string, @@ -28,6 +29,7 @@ export class UserChallengeEntity { currentEnergy: number, badge: BadgeState, relationships: any, + fluidTypes: FluidType[], _id?: string ) { this.startingDate = startingDate @@ -38,6 +40,7 @@ export class UserChallengeEntity { this.badge = badge this._id = _id this.relationships = relationships + this.fluidTypes = fluidTypes } } @@ -89,6 +92,7 @@ export default class ChallengeDataMapper { state: userChallenge.state, maxEnergy: userChallenge.maxEnergy, currentEnergy: userChallenge.currentEnergy, + fluidTypes: userChallenge.fluidTypes, relationships: { selectedEcogestures: { data: mappedEcogestures, @@ -141,6 +145,7 @@ export default class ChallengeDataMapper { userChallengeEntity.maxEnergy, userChallengeEntity.currentEnergy, userChallengeEntity.badge, + userChallengeEntity.fluidTypes, userChallengeEntity._id ) return mappedUserChallenge @@ -149,8 +154,8 @@ export default class ChallengeDataMapper { public mapToChallengeType( challengeEntity: ChallengeTypeEntity, challengeTypeEntityRelationships?: any | null, - unlockedEcogestures?: string[], - fluidTypes?: FluidType[] + unlockedEcogestures?: string[] + // fluidTypes?: FluidType[] ): ChallengeType { const completeAvailableEcogestures: any[] = [] @@ -219,15 +224,15 @@ export default class ChallengeDataMapper { public mapToChallengeTypes( challengeEntities: ChallengeTypeEntity[], challengeTypeEntityRelationships?: any | null, - unlockedEcogestures?: any, - fluidTypes?: FluidType[] + unlockedEcogestures?: any + // fluidTypes?: FluidType[] ): ChallengeType[] { return challengeEntities.map(challengeEntity => this.mapToChallengeType( challengeEntity, challengeTypeEntityRelationships, - unlockedEcogestures, - fluidTypes + unlockedEcogestures + // fluidTypes ) ) } diff --git a/src/services/dataChallengeContracts.ts b/src/services/dataChallengeContracts.ts index 1300951bc537335ef6e6d5848509c49d999c4a1a..766705ac0ab45e6866a32107b6866e7f24c871f9 100644 --- a/src/services/dataChallengeContracts.ts +++ b/src/services/dataChallengeContracts.ts @@ -103,6 +103,7 @@ export class UserChallenge { maxEnergy: number currentEnergy: number badge: BadgeState | null + fluidTypes: FluidType[] constructor( startingDate: DateTime, @@ -113,6 +114,7 @@ export class UserChallenge { maxEnergy: number, currentEnergy: number, badge: BadgeState | null, + fluidTypes: FluidType[], id?: string ) { this.startingDate = startingDate @@ -123,6 +125,7 @@ export class UserChallenge { this.maxEnergy = maxEnergy this.currentEnergy = currentEnergy this.badge = badge + this.fluidTypes = fluidTypes this.id = id } @@ -164,7 +167,7 @@ export interface IChallengeManager { ): Promise<UserChallenge | null> cancelChallenge(id: string): Promise<UserChallenge | null> - fulfillChallenge(challenge: UserChallenge): Promise<UserChallenge | null> + // fulfillChallenge(challenge: UserChallenge): Promise<UserChallenge | null> startChallenge( challenge: ChallengeType, @@ -176,7 +179,7 @@ export interface IChallengeManager { fluidTypes?: FluidType[] ): Promise<ChallengeType[] | null> - getUnlockedBadges(): Promise<ChallengeType[] | null> + // getUnlockedBadges(): Promise<ChallengeType[] | null> getUnlockedEcogestures(): Promise<EcogestureType[] | null> } diff --git a/src/services/fluidConfigService.ts b/src/services/fluidConfigService.ts index 8e814c291d94d6fdea9e89d05b2abfcc52eebb04..8f132a17eda43e43d8f4eeb1390841464c3ef29e 100644 --- a/src/services/fluidConfigService.ts +++ b/src/services/fluidConfigService.ts @@ -11,4 +11,13 @@ export default class FluidConfig { public getFluidConfig(): IFluidConfig[] { return this._fluidConfig } + + public getCronArgs(slug: string): string { + const filtered = this._fluidConfig.filter( + fluidConfig => fluidConfig.konnectorConfig.slug === slug + ) + return filtered.length > 0 && filtered[0].konnectorConfig.cron + ? filtered[0].konnectorConfig.cron + : '0 30 8 * * *' + } } diff --git a/src/services/initDataManagerService.ts b/src/services/initDataManagerService.ts index 3b30f88b80b03ee277e91d413adbcc9e704a9e08..68dcf4b1bd2f240f8a2b981f7ab397902910da39 100644 --- a/src/services/initDataManagerService.ts +++ b/src/services/initDataManagerService.ts @@ -405,9 +405,7 @@ export default class InitDataManager { * Search for a current challenge * and update it if found */ - public async checkCurrentChallenge( - fluidTypes: FluidType[] - ): Promise<UserChallenge | null> { + public async checkCurrentChallenge(): Promise<UserChallenge | null> { try { // get the current challenge const cdm = new ChallengeDataManager(this._client) @@ -422,13 +420,16 @@ export default class InitDataManager { currentChallenge.challengeType.type === TypeChallenge.CHALLENGE ) { // Check if we are in the viewing timezone for current challenge - const viewingDate = cdm.getViewingDate(currentChallenge, fluidTypes) - if (DateTime.local() >= viewingDate) { - if (currentChallenge.maxEnergy === -1) { + const viewingDate = cdm.getViewingDate(currentChallenge) + if (DateTime.local() >= viewingDate && currentChallenge.fluidTypes) { + if ( + currentChallenge.maxEnergy === -1 && + currentChallenge.fluidTypes + ) { const maxEnergyResult = await cdm.setMaxEnergy( currentChallenge, this._client, - fluidTypes + currentChallenge.fluidTypes ) if (maxEnergyResult > 0) { currentChallenge.maxEnergy = maxEnergyResult @@ -437,7 +438,7 @@ export default class InitDataManager { const currentEnergyResult = await cdm.setSpentEnergy( currentChallenge, this._client, - fluidTypes + currentChallenge.fluidTypes ) if (currentEnergyResult) { currentChallenge.currentEnergy = currentEnergyResult diff --git a/src/services/triggersService.ts b/src/services/triggersService.ts index b132f34700c8f848efd3bd2705023ede1b171231..6115785efeeea8fdaddfd4ff9d0d4778dbf75656 100644 --- a/src/services/triggersService.ts +++ b/src/services/triggersService.ts @@ -1,6 +1,7 @@ import { Client } from 'cozy-client' import { Account, Konnector, Trigger } from 'doctypes' import triggersMutations from 'cozy-harvest-lib/dist/connections/triggers' +import FluidConfig from 'services/fluidConfigService' import { DateTime } from 'luxon' export interface TriggerAttributes { @@ -46,9 +47,11 @@ export class TriggerService { 'TriggersServices : createTriggerAttribute - account _id or konnector slug not found' ) } + const fluidConfigService = new FluidConfig() + const cronArgs = fluidConfigService.getCronArgs(this._konnector.slug) const triggerAttributes: TriggerAttributes = { type: '@cron', - arguments: '0 0 4 * * *', + arguments: cronArgs, worker: 'konnector', message: { account: this._account._id, diff --git a/src/styles/base/_typography.scss b/src/styles/base/_typography.scss index a65a2f753f28ddfdf0a9bbead7f459c8999c5dfc..4eb61d770ee73594e87456c93b9d360efdab7536 100644 --- a/src/styles/base/_typography.scss +++ b/src/styles/base/_typography.scss @@ -93,7 +93,7 @@ p { font-size: 1rem; line-height: 120%; } -.text-18-medium { +.text-18-normal { font-family: $text-font; font-style: normal; font-weight: 500; diff --git a/src/styles/components/_challenges.scss b/src/styles/components/_challenges.scss index 293c7f6d9c5bb6a1d487de9d1bfbc4f6b45d4efc..0d270b0263529ce98cfcaa0d321978ee15ead4d1 100644 --- a/src/styles/components/_challenges.scss +++ b/src/styles/components/_challenges.scss @@ -98,7 +98,7 @@ margin: 0.5rem 0; z-index: 5; width: 62px; - height: 100%; + height: 62px; } .cli-content-title { color: $text-white; @@ -238,7 +238,7 @@ align-items: center; justify-items: center; position: absolute; - top: 15px; + top: 52px; @media #{$large-phone} { top: 65px; } @@ -511,6 +511,10 @@ width: 100%; } } + .lack-of-data-challenge { + text-align: center; + width: 90%; + } } .cp-follow { width: 90%; diff --git a/src/styles/components/_ecogesture.scss b/src/styles/components/_ecogesture.scss index 57fae3085bdfc637411ac54f45b386913e8e39b7..2ad62004c9347d69bdf66da2662eb8b1a82d6700 100644 --- a/src/styles/components/_ecogesture.scss +++ b/src/styles/components/_ecogesture.scss @@ -8,13 +8,13 @@ align-items: center; justify-content: center; padding: 1rem 1.5rem 2.5rem 1.5rem; - .negawatt-button-content{ - width: calc(53rem - 2%); - margin: 0 1%; - @media #{$large-phone} { + .negawatt-button-content { + max-width: 52rem; + width: 97%; + @media #{$tablet} { width: 97%; } - @media #{$tablet} { + @media #{$large-phone} { width: 97%; } } @@ -22,11 +22,11 @@ display: flex; flex-wrap: wrap; justify-content: space-around; - width: 53rem; - @media #{$large-phone} { + max-width: 53rem; + @media #{$tablet} { width: 100%; } - @media #{$tablet} { + @media #{$large-phone} { width: 100%; } .ecogesture-list-item { @@ -61,7 +61,7 @@ &.ec-content-challenge { padding: 0.5rem 0; } - .ec-content-icon{ + .ec-content-icon { min-height: 50px; } .ec-content-short-name { @@ -98,37 +98,8 @@ display: flex; justify-content: center; } -.em-icon { - margin-bottom: 1rem; -} -.em-title { - margin-bottom: 0; -} -.em-detail { - display: flex; - flex-direction: row; - margin: 0.5em 0; - .em-detail-nwh { - display: flex; - flex: 1; - align-self: flex-start; - margin-top: 0.65rem; - color: var(--textDark); - .em-detail-nwh-unit { - margin-left: 0.2rem; - } - } - .em-picto-flow { - display: flex; - align-self: flex-end; - .em-pic-content { - margin: 0.3em; - } - } -} -.em-content-box { - max-height: 25rem; +.em-root { overflow: auto; width: 100%; display: flex; @@ -145,15 +116,14 @@ &::-webkit-scrollbar-thumb { background: #6f7074; } - .em-content-box-text { - display: flex; - flex-direction: column; - padding: 1.5rem 1.5rem; - width: 22.125rem; + .em-content { + padding: 1.5rem 2.5rem 0; + @media #{$large-phone} { - width: 100%; + width: 100%; + padding: 1.5rem 1.5rem 0; } - .em-content-box-text-header { + .em-content-box-img { display: flex; flex-direction: column; justify-content: center; @@ -162,19 +132,60 @@ margin-bottom: 1rem; } } + .em-detail { + display: flex; + flex-direction: row; + margin: 0.5em 0; + .em-detail-nwh { + display: flex; + flex: 1; + align-self: flex-start; + margin-top: 0.65rem; + color: var(--textDark); + .em-detail-nwh-unit { + margin-left: 0.2rem; + } + } + .em-picto-flow { + display: flex; + align-self: flex-end; + .em-pic-content { + margin: 0.3em; + } + } + } + .em-title { + margin-bottom: 0; + text-align: center; + } + .long-name { font-weight: bold; line-height: 150%; margin: 0.5rem 0 1rem; } .em-description { - padding-bottom: 2.5rem; - } - .em-description-2 { - padding-top: 2rem; - } - .em-description-3 { - padding-bottom: 0.5rem; + padding-bottom: 2rem; } } } +.em-content-box-text { + display: flex; + flex-direction: column; + padding: 1.5rem 2.5rem; + @media #{$large-phone} { + padding: 1.5rem 1.5rem; + } + .em-description { + padding-bottom: 2rem; + } + .em-description-2 { + padding-top: 2rem; + } + .em-description-3 { + padding-bottom: 0.5rem; + } + .em-title { + margin-bottom: 0; + } +} diff --git a/src/styles/components/_faq.scss b/src/styles/components/_faq.scss index 29f2e1c3dc44eeececd675484caab87d1ad6797e..6a2872239e6c0a128c2f23f7df3a6f0daa554c52 100644 --- a/src/styles/components/_faq.scss +++ b/src/styles/components/_faq.scss @@ -11,6 +11,9 @@ .faq-content { margin-bottom: -1rem; width: 45.75rem; + a { + text-decoration: none; + } @media #{$large-phone} { width: 100%; } @@ -42,6 +45,7 @@ .faq-card-content-title { margin: 0 1rem; align-self: center; + text-decoration: none; } } } diff --git a/src/styles/components/_fluid.scss b/src/styles/components/_fluid.scss index a1c53b76daf79a2a717da446cc17b23e0b03e8f1..270a37b2c3f0af3a11a3ec8c1b3a2cb52ef47b1a 100644 --- a/src/styles/components/_fluid.scss +++ b/src/styles/components/_fluid.scss @@ -112,10 +112,12 @@ } } .chart-indicator-none { - display: none; + visibility: hidden; + height: 0; } .chart-indicator-block { - display: block; + visibility: visible; + height: auto; } diff --git a/src/styles/components/_legal.scss b/src/styles/components/_legal.scss deleted file mode 100644 index 678e663b11093d9d4f220d10856350981101b4ea..0000000000000000000000000000000000000000 --- a/src/styles/components/_legal.scss +++ /dev/null @@ -1,76 +0,0 @@ -@import '../base/color'; -@import '../base/breakpoint'; - -// FAQContainer -.legal-root { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 0 1.5rem 1.5rem; - .legal-content { - margin-bottom: -1rem; - width: 45.75rem; - @media #{$large-phone} { - width: 100%; - } - .legal-header { - margin-top: 2rem; - margin-bottom: 1.25rem; - color: $text-bright; - } - } -} - -.legal-card-link { - color: black; -} -.legal-card { - display: flex; - flex-direction: row; - margin: -0.75rem 0; - width: 100%; - @media #{$large-phone} { - width: 100%; - } - .legal-card-content { - display: flex; - flex-direction: row; - .legal-card-content-icon { - margin: 0.5rem 0; - } - .legal-card-content-title { - margin: 0 1rem; - align-self: center; - } - } -} - -// FAQ -.legal-view-root { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 1rem 0; - margin-top: 1.5rem; - .legal-view-content { - width: 45.75rem; - @media #{$large-phone} { - width: 100%; - } - .legal-content-detail { - padding-bottom: 0.6rem; - .text-bold { - font-weight: bold; - } - .text-underline { - text-decoration: underline; - } - .spaceline { - height: 0.6rem; - display: block; - } - } - } -} diff --git a/src/styles/components/_legalnotice.scss b/src/styles/components/_legalnotice.scss new file mode 100644 index 0000000000000000000000000000000000000000..f87acfd3d41dca7549f4012a0c58aac15f47b29f --- /dev/null +++ b/src/styles/components/_legalnotice.scss @@ -0,0 +1,62 @@ +@import '../base/color'; +@import '../base/breakpoint'; + +// LegalContainer +.legal-notice-root { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: $text-white; + padding: 1.5rem; + .legal-notice-content { + p { + color: $text-white; + } + a { + color: $text-white; + text-decoration: none; + } + li { + margin: 1rem 0; + } + h2 { + color: $text-white; + } + h3 { + color: $text-white; + margin: 2.5rem 0 1rem; + } + .ln-contact { + color: $multi-color; + } + width: 45.75rem; + @media #{$large-phone} { + width: 100%; + } + } +} + +.legal-notice-card-link { + color: black; +} +.legal-notice-card { + display: flex; + flex-direction: row; + margin: -0.75rem 0; + width: 100%; + @media #{$large-phone} { + width: 100%; + } + .legal-notice-card-content { + display: flex; + flex-direction: row; + .legal-notice-card-content-icon { + margin: 0.5rem 0; + } + .legal-notice-card-content-title { + margin: 0 1rem; + align-self: center; + } + } +} diff --git a/src/styles/components/_modal.scss b/src/styles/components/_modal.scss index 72ec865ef3aefd4bbac2aeb1a4c037f50511375a..a4b55f813b4b12f17e839617c87e431114aac9df 100644 --- a/src/styles/components/_modal.scss +++ b/src/styles/components/_modal.scss @@ -19,22 +19,20 @@ .modal-box { background: $grey-background; position: fixed; - top: 50%; - left: 50%; width: 36rem; - @media #{$large-phone} { - width: 20rem; - } max-width: 100%; - max-height: 30rem; - @media #{$large-phone} { - max-height: unset; - } - transform: translate(-50%, -50%); + max-height: 80vh; padding: 1rem 0 0 0; box-sizing: border-box; box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.55); border-radius: 4px; + @media #{$tablet} { + width: 35rem; + } + @media #{$large-phone} { + width: 85%; + max-width: 35rem; + } &.modal-box-bordered { border: 1px solid $blue-40; } @@ -46,8 +44,10 @@ top: 0.5rem; right: 0.5rem; padding: 5px 5px; + z-index: 10; } .modal-content { + max-height: 75vh; display: flex; flex-direction: column; align-items: center; diff --git a/src/styles/index.scss b/src/styles/index.scss index 4b50efdb1f7c163cff57f377abf0d3838d142198..550656551ae776974982a55fea7c4b7d1061bfcf 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -21,7 +21,7 @@ @import 'components/ecogesture'; @import 'components/modal'; @import 'components/faq'; -@import 'components/legal'; +@import 'components/legalnotice'; @import 'components/splash'; @import 'components/auth'; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 1eb5346e23684c8f1a681c0f97a3ee36f8c7d3dd..befddaeb59d4853e54140fc792277f01e929dee6 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -115,10 +115,14 @@ export function getFuildType(type: string) { } } export function formatNumberValues(value: number) { - return value.toLocaleString('fr-FR', { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }) + if (value || value === 0) { + return value.toLocaleString('fr-FR', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }) + } else { + return '--,--' + } } export function compareDates(dateA: DateTime, dateB: DateTime) {