diff --git a/.gitignore b/.gitignore index 226f552a47c09ed3e1c567473615c83856e6212f..978c511aa1f259d53646f17e9551b88dcc11eb5f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules/ npm-debug.log yarn-error.log +.yarn # Scripts scripts/AAAA* diff --git a/CHANGELOG.md b/CHANGELOG.md index a7362598a3610a1d895a3a8d43671cd13f9e0ccc..8f78810e523bf43212c623e57b45fa298d757c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,30 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.2.1](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/compare/v2.2.0...v2.2.1) (2023-02-03) + + +### Bug Fixes + +* **alert:** partners date error on empty profiles ([0d9a71d](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/commit/0d9a71db99229c6d5e2241b73bdda077cabdce00)) + +## [2.2.0](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/compare/v2.1.1...v2.2.0) (2023-02-02) + + +### Features + +* **konnector:** added info text to konnector card on arrival time of data ([157ba76](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/commit/157ba76e4eb2a6c22d4d62e1646457dae89f5bd0)) +* updated onboarding for EPGL and GRDF ([43a0c15](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/commit/43a0c1566ade5465651e693d79f9918bbf33b9d6)) + + +### Bug Fixes + +* **deps:** update dependency react-router-dom to v6 ([96ef8ca](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/commit/96ef8ca49e680464f63c4b7ac292c720d208ad83)) +* **header:** title hidden on large screens ([c49298c](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/commit/c49298c4e4f6c199a5d51bfc47bc2a317d8d4d46)) +* onboarding for EPGL and GRDF (QA feedback) ([3486385](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/commit/348638514e1a02ee274835bc1314adff846ab477)) +* **partners:** re-activate partners maintenance once a day ([43b0a84](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/commit/43b0a844daaee0bf472f2f9e3838d161081bf3e3)) +* rename Enedis SGE -> Enedis ([ba692fc](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/commit/ba692fc1f328b0e042ec545e67b4063b6e33ba19)) + ### [2.1.1](https://forge.grandlyon.com/web-et-numerique/factory/llle_project/ecolyo/compare/v2.1.0...v2.1.1) (2023-01-17) diff --git a/manifest.webapp b/manifest.webapp index d1320b073317010b97503c4ca0c098bdac1697f8..1cdcfaa21a4585dba47fb00d103b10f80d36df88 100644 --- a/manifest.webapp +++ b/manifest.webapp @@ -3,7 +3,7 @@ "slug": "ecolyo", "icon": "icon.svg", "categories": ["energy"], - "version": "2.1.1", + "version": "2.2.1", "licence": "AGPL-3.0", "editor": "Métropole de Lyon", "default_locale": "fr", diff --git a/package.json b/package.json index 932f0c84bc9ccecff6bb7f290ef38123e6222f70..608ce972b4129527c28a4ab6f734e1a4c2a4f6d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ecolyo", - "version": "2.1.1", + "version": "2.2.1", "scripts": { "build": "yarn run build:css && yarn run build:browser", "build:browser": "cs build --browser ", diff --git a/src/components/Connection/ConnectionResult.tsx b/src/components/Connection/ConnectionResult.tsx index 60740d100b40f11a1c9ea0ef2548fdf3b4922d45..214dadb432b29f2b133e84845c040769ecd0b501 100644 --- a/src/components/Connection/ConnectionResult.tsx +++ b/src/components/Connection/ConnectionResult.tsx @@ -11,7 +11,7 @@ import { AccountSgeData, FluidConnection, FluidStatus, - Trigger, + Trigger } from 'models' import React, { useCallback, useEffect, useState } from 'react' import { useDispatch } from 'react-redux' @@ -21,7 +21,7 @@ import TriggerService from 'services/triggers.service' import { setShouldRefreshConsent, updatedFluidConnection, - updateSgeStore, + updateSgeStore } from 'store/global/global.actions' import { getKonnectorUpdateError } from 'utils/utils' import './connectionResult.scss' @@ -165,6 +165,17 @@ const ConnectionResult: React.FC<ConnectionResultProps> = ({ } }, [fluidStatus.connection.triggerState, isOutdated]) + const getFluidTypeTranslation = (fluidType: FluidType) => { + switch (fluidType) { + case FluidType.GAS: + return 'de gaz' + case FluidType.ELECTRICITY: + return "d'électricité" + default: + return "d'eau" + } + } + return ( <div className="connection-update-result"> <div @@ -185,93 +196,24 @@ const ConnectionResult: React.FC<ConnectionResultProps> = ({ </div> ) : status === 'errored' ? ( // Else check if konnector is in error state - <div className="connection-caption-errored warning-white text-16-normal"> - <StyledIcon - icon={warningWhite} - size={36} - className="warning-icon" - role="img" - title="Attention" - ariaHidden={false} - /> - {konnectorError === 'login_failed' ? ( - <div className="text-16-normal"> - {t('konnector_form.login_failed')} - </div> - ) : ( - <div className="text-16-normal"> - {t(`konnector_form.${konnectorError}`, { - fluid: - fluidType === FluidType.GAS - ? 'de gaz' - : fluidType === FluidType.ELECTRICITY - ? "d'électricité" - : "d'eau", - })} - {konnectorError !== 'error_update_oauth' && ( - <> - <div className="connection-caption"> - {t('konnector_form.label_updated_at')} - </div> - <div className="text-16-bold"> - {lastExecutionDate.toLocaleString()} - </div> - </> - )} - </div> - )} - </div> + <DisplayKonnectorErrorState + konnectorError={konnectorError} + lastExecutionDate={lastExecutionDate} + fluidConcerned={getFluidTypeTranslation(fluidType)} + /> ) : outDatedDataDays ? ( // Else check if data is outdated - <div className="connection-caption text-16-normal"> - <div className="text-16-normal"> - <> - {hasUpdatedToday() === true ? ( - // If user has already ran an update today, display a message about energy provider issue - <> - <div className="connection-caption"> - {t('konnector_form.label_updated_at')} - </div> - <div className="text-16-bold"> - {lastExecutionDate.toLocaleString()} - </div> - <div> - {fluidStatus?.connection?.konnector && - t('konnector_form.issue') + - ' ' + - fluidStatus.connection.konnector.name + - '.'} - </div> - </> - ) : ( - // Otherwise tells user to run a manual update - <div className="connection-caption-errored connection-update-errored warning-white text-16-normal"> - <StyledIcon - icon={warningWhite} - size={36} - className="warning-icon" - role="img" - title="Attention" - ariaHidden={false} - /> - <div className="text-16-normal"> - {t('konnector_form.resolve')} - </div> - </div> - )} - </> - </div> - </div> + <DisplayDataOutdated + fluidStatus={fluidStatus} + fluidType={fluidType} + lastExecutionDate={lastExecutionDate} + hasUpdatedToday={hasUpdatedToday()} + /> ) : ( - //If no partner error nor konnector error, display the last update date - <div> - <div className="connection-caption text-16-normal"> - {t('konnector_form.label_updated_at')} - </div> - <div className="text-16-bold"> - {lastExecutionDate.toLocaleString()} - </div> - </div> + <DisplayLastUpdateDate + fluidType={fluidType} + lastExecutionDate={lastExecutionDate} + /> )} </div> <div className="inline-buttons"> @@ -288,8 +230,9 @@ const ConnectionResult: React.FC<ConnectionResultProps> = ({ label: 'text-16-normal', }} > - {deleting && <Loader fluidType={fluidType} />} - {!deleting && t('konnector_form.button_delete')} + {deleting + ? t('konnector_form.loading') + : t('konnector_form.button_delete')} </Button> <Button aria-label={t('konnector_form.accessibility.button_update')} @@ -325,4 +268,157 @@ const ConnectionResult: React.FC<ConnectionResultProps> = ({ ) } +const DisplayKonnectorErrorState = ({ + konnectorError, + lastExecutionDate, + fluidConcerned, +}: { + konnectorError: string + lastExecutionDate: string | DateTime + fluidConcerned: string +}) => { + const { t } = useI18n() + return ( + <div className="connection-caption-errored warning-white text-16-normal"> + <StyledIcon + icon={warningWhite} + size={36} + className="warning-icon" + role="img" + title="Attention" + ariaHidden={false} + /> + {konnectorError === 'login_failed' ? ( + <div className="text-16-normal">{t('konnector_form.login_failed')}</div> + ) : ( + <div className="text-16-normal"> + {t(`konnector_form.${konnectorError}`, { + fluid: fluidConcerned, + })} + {konnectorError !== 'error_update_oauth' && ( + <> + <div className="connection-caption"> + {t('konnector_form.label_updated_at')} + </div> + <div className="text-16-bold"> + {lastExecutionDate.toLocaleString()} + </div> + </> + )} + </div> + )} + </div> + ) +} + +const DisplayDataOutdated = ({ + fluidStatus, + fluidType, + lastExecutionDate, + hasUpdatedToday, +}: { + fluidStatus: FluidStatus + fluidType: FluidType + lastExecutionDate: string | DateTime + hasUpdatedToday: boolean +}) => { + return ( + <div className="connection-caption text-16-normal"> + <div className="text-16-normal"> + <> + {hasUpdatedToday ? ( + // If user has already ran an update today, display a message about energy provider issue + <DisplayAlreadyUpdatedToday + fluidStatus={fluidStatus} + fluidType={fluidType} + lastExecutionDate={lastExecutionDate.toLocaleString()} + /> + ) : ( + <DisplayManualUpdate /> + )} + </> + </div> + </div> + ) +} + +/** If user has already ran an update today, display a message about energy provider issue */ +const DisplayAlreadyUpdatedToday = ({ + fluidStatus, + fluidType, + lastExecutionDate, +}: { + fluidStatus: FluidStatus + fluidType: FluidType + lastExecutionDate: string | DateTime +}) => { + const { t } = useI18n() + + const getFluidTypeKonnectorTranslation = (fluidType: FluidType) => { + switch (fluidType) { + case FluidType.GAS: + return 'GRDF' + case FluidType.ELECTRICITY: + return 'Enedis' + default: + return 'Eau Publique du Grand Lyon' + } + } + return ( + <> + <div className="connection-caption"> + {t('konnector_form.label_updated_at')} + </div> + <div className="text-16-bold">{lastExecutionDate.toLocaleString()}</div> + <div> + {fluidStatus?.connection?.konnector && + t('konnector_form.issue') + + ' ' + + getFluidTypeKonnectorTranslation(fluidType) + + '.'} + </div> + </> + ) +} + +/** Tells user to run a manual update */ +const DisplayManualUpdate = () => { + const { t } = useI18n() + return ( + <div className="connection-caption-errored connection-update-errored warning-white text-16-normal"> + <StyledIcon + icon={warningWhite} + size={36} + className="warning-icon" + role="img" + title="Attention" + ariaHidden={false} + /> + <div className="text-16-normal">{t('konnector_form.resolve')}</div> + </div> + ) +} + +const DisplayLastUpdateDate = ({ + lastExecutionDate, + fluidType, +}: { + lastExecutionDate: string | DateTime + fluidType: FluidType +}) => { + const { t } = useI18n() + return ( + <div> + <div className="connection-caption text-16-normal"> + {t('konnector_form.label_updated_at')} + </div> + <div className="text-16-bold">{lastExecutionDate.toLocaleString()}</div> + <br /> + <div className="text-14-normal delta-caption"> + {t(`konnector_form.konnector_delta.${FluidType[fluidType]}`)} + </div> + </div> + ) +} + export default ConnectionResult diff --git a/src/components/Connection/connectionResult.scss b/src/components/Connection/connectionResult.scss index 49753c53b3b5c33342bbe943a914d506426f9e15..6d46ca06831d704e396c84b8be352823cfe9cb19 100644 --- a/src/components/Connection/connectionResult.scss +++ b/src/components/Connection/connectionResult.scss @@ -31,6 +31,10 @@ } } +.delta-caption { + color: $soft-grey; +} + .inline-buttons { display: flex; flex-flow: row nowrap; diff --git a/src/locales/fr.json b/src/locales/fr.json index 80b8133f3b3cec645cb65bdbf72b2e3a4c12e20c..522a8737951f6fd0052ebf074ec86943c909e5a6 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -728,6 +728,7 @@ "konnector_form": { "label_updated_at": "Dernière mise-à-jour le", "button_update": "Mettre à jour", + "loading": "Chargement...", "wait_end_issue": "Attendre la fin de la maintenance avant de mettre à jour.", "button_delete": "Supprimer", "issue": "Le problème semble venir de ", @@ -742,6 +743,11 @@ "error_update_oauth": "Votre autorisation pour afficher vos données %{fluid} a expiré.", "button_oauth_reload": "Redonner mon consentement", "OK": "Ok", + "konnector_delta": { + "ELECTRICITY": "La donnée de consommation électrique arrive normalement à J+1. Un retard d'un ou deux jours est parfois constaté.", + "GAS": "La donnée de consommation de gaz arrive normalement à J+3. Un retard d'un ou deux jours est parfois constaté.", + "WATER": "La donnée de consommation d'eau arrive normalement à J+3. Un retard d'un ou deux jours est parfois constaté." + }, "accessibility": { "button_install": "Installer le connecteur", "button_update": "Mettre à jour votre connexion", diff --git a/src/models/profile.model.ts b/src/models/profile.model.ts index 8950b1d2292130b18ae1a21ed4d42b31524a0c50..c041fb23a5f8acd5a20d4cf83dc48a518374bad1 100644 --- a/src/models/profile.model.ts +++ b/src/models/profile.model.ts @@ -23,9 +23,9 @@ export interface ProfileEntity { onboarding: Onboarding mailToken: string partnersIssueSeenDate: { - enedis: string - egl: string - grdf: string + enedis?: string + egl?: string + grdf?: string } haveSeenEcogestureModal: boolean activateHalfHourDate: string diff --git a/src/services/profile.service.ts b/src/services/profile.service.ts index 06f7d644e8277baba9382c98aeb1508902187241..64bf5dc792d3d18564472b50773c10ee527240d3 100644 --- a/src/services/profile.service.ts +++ b/src/services/profile.service.ts @@ -10,7 +10,10 @@ export default class ProfileService { this._client = _client } - private getDate(date: string): DateTime { + private getDate(date: string | undefined): DateTime { + if (!date) { + return DateTime.local().minus({ day: 1 }).startOf('day') + } return DateTime.fromISO(date, { zone: 'utc', }) @@ -30,9 +33,9 @@ export default class ProfileService { : profileEntity.monthlyAnalysisDate, lastConnectionDate: this.getDate(profileEntity.lastConnectionDate), partnersIssueSeenDate: { - enedis: this.getDate(profileEntity.partnersIssueSeenDate.enedis), - egl: this.getDate(profileEntity.partnersIssueSeenDate.egl), - grdf: this.getDate(profileEntity.partnersIssueSeenDate.grdf), + enedis: this.getDate(profileEntity.partnersIssueSeenDate?.enedis), + egl: this.getDate(profileEntity.partnersIssueSeenDate?.egl), + grdf: this.getDate(profileEntity.partnersIssueSeenDate?.grdf), }, activateHalfHourDate: this.getDate(profileEntity.activateHalfHourDate), customPopupDate: this.getDate(profileEntity.customPopupDate),