diff --git a/.vscode/settings.json b/.vscode/settings.json index 471272b3125f5203b5ad76980906e1113e308e7d..c142123b64be691c10498efdc7cf9cc159d3e3e1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -100,6 +100,7 @@ "grdfgrandlyon", "Hypervitesse", "késako", + "kmodal", "Konnected", "konnector", "konnectors", diff --git a/src/components/Konnector/KonnectorModal.tsx b/src/components/Konnector/KonnectorModal.tsx index c5ebc3ba8a5bd98feda2f6cb770acbd2018b23c0..75e829613132b59e821af46fee0f448d0ca61f6a 100644 --- a/src/components/Konnector/KonnectorModal.tsx +++ b/src/components/Konnector/KonnectorModal.tsx @@ -17,6 +17,7 @@ import { FluidType, KonnectorError } from 'enums' import { shuffle } from 'lodash' import { Account } from 'models' import React, { useCallback, useEffect, useMemo, useState } from 'react' +import { getFluidName } from 'utils/utils' import KonnectorModalFooter from './KonnectorModalFooter' import './konnectorModal.scss' @@ -45,19 +46,21 @@ const KonnectorModal = ({ account, }: KonnectorModalProps) => { const { t } = useI18n() - const fluidName: string = FluidType[fluidType] + const fluidName = getFluidName(fluidType) const [index, setIndex] = useState<number>(0) + /** Only used for enedis to see common errors */ + const [showCommonErrors, setShowCommonErrors] = useState(false) + const shuffledWaitingTexts = useMemo(() => { - if (fluidType) { + if (fluidType !== FluidType.ELECTRICITY) { return shuffle(connectionWaitingText) - } else { - return connectionWaitingText } + return connectionWaitingText }, [fluidType]) + const firstConnectionWaitingTexts = firstConnectionWaitingText.concat( ...shuffledWaitingTexts ) - const [showCommonErrors, setShowCommonErrors] = useState(false) const getUpdatingText = useCallback(() => { return ( @@ -103,14 +106,14 @@ const KonnectorModal = ({ const connectionSuccessContent = () => ( <div className="konnector-config"> <Icon icon={successIcon} size={48} /> - <div className="kcs-picto-txt text-20-bold"> + <div className="headerSuccess text-20-bold"> {t(`konnector_modal.success_${isUpdating ? 'update_' : ''}txt`)} </div> <b> {t( `konnector_modal.success_data_${ isUpdating ? 'update_' : '' - }${fluidName.toLowerCase()}` + }${fluidName}` )} </b> <p @@ -119,7 +122,7 @@ const KonnectorModal = ({ __html: t( `konnector_modal.success_data_additional_${ isUpdating ? 'update_' : '' - }${fluidName.toLowerCase()}` + }${fluidName}` ), }} /> @@ -156,11 +159,13 @@ const KonnectorModal = ({ {t('konnector_modal.accessibility.window_title')} </div> <div className="kmodal-content"> - {open && !state ? ( + {/* NEITHER ERROR NOR SUCCESS => LOADING */} + {!state ? ( <> <Loader fluidType={fluidType} /> {!isLogging && ( - <div className="kmodal-content-text kmodal-content-text-center text-16-normal"> + <div className="kmodal-content-text text-16-normal"> + {/* TODO remove kc-wait */} <div className="kc-wait text-16-bold"> {t( `konnector_modal.loading_data${isUpdating ? '_update' : ''}` @@ -173,6 +178,7 @@ const KonnectorModal = ({ </> ) : ( <> + {/* ERROR OR SUCCESS */} <div className="kmodal-info"> {state === ERROR_EVENT && ( <> @@ -180,35 +186,34 @@ const KonnectorModal = ({ // LOGIN FAILED FOR ENEDIS AND EGL <div className="konnector-config"> <Icon icon={errorIcon} size={48} /> - <div className="kce-picto-txt text-20-bold"> + <div className="headerError text-20-bold"> {t('konnector_modal.error_txt')} </div> <div> {t( `konnector_modal.error_credentials_${ isUpdating ? 'update_' : '' - }${fluidName.toLowerCase()}` + }${fluidName}` )} </div> {fluidType === FluidType.ELECTRICITY && !isUpdating && ( <div className="elec-fail"> {t( - `konnector_modal.error_credentials_${fluidName.toLowerCase()}_2` + `konnector_modal.error_credentials_${fluidName}_2` )} </div> )} - {/* Show common errors */} + {/* Show common errors for enedis */} {fluidType === FluidType.ELECTRICITY && ( <> - {!showCommonErrors && ( + {!showCommonErrors ? ( <Button - className="btnText commonErrors" + className="btnText" onClick={() => setShowCommonErrors(true)} > {t('konnector_modal.show_common_error')} </Button> - )} - {showCommonErrors && ( + ) : ( <div className="commonErrorsList" dangerouslySetInnerHTML={{ @@ -226,7 +231,8 @@ const KonnectorModal = ({ isUpdating && fluidType === FluidType.ELECTRICITY && ( // MISMATCH UPDATE ERROR ENEDIS - <div className="kce-picto-txt konnector-config mismatch"> + // WEIRD STYLES + <div className="headerError konnector-config mismatch"> <Icon icon={EnedisIcon} width={120} height={80} /> <div className="title text-20-bold"> {t('konnector_modal.mismatch.title')} @@ -242,24 +248,19 @@ const KonnectorModal = ({ </div> </div> )} - {error === KonnectorError.CHALLENGE_ASKED && + {error === + KonnectorError.USER_ACTION_NEEDED_ACCOUNT_REMOVED && fluidType === FluidType.GAS && ( - // CONSENT FORM ERROR GRDF <div className="konnector-config"> <Icon icon={errorIcon} size={48} /> - <div className="kce-picto-txt text-20-bold"> + <div className="headerError text-20-bold"> {t('konnector_modal.error_txt')} </div> <div className="title text-20-bold"> {t('konnector_modal.error_consent_form_gas_title')} </div> <div className="err-data-2"> - {t('konnector_modal.error_consent_form_gas_content')} - </div> - <div className="err-data-2"> - {t( - 'konnector_modal.error_consent_form_gas_content_2' - )} + {t('konnector_modal.error_consent_form_gas_report')} </div> </div> )} @@ -269,14 +270,14 @@ const KonnectorModal = ({ // DEFAULT CASE <div className="konnector-config"> <Icon icon={errorIcon} size={48} /> - <div className="kce-picto-txt text-20-bold"> + <div className="headerError text-20-bold"> {t('konnector_modal.error_txt')} </div> <div> {t( `konnector_modal.error_data_${ isUpdating ? 'update_' : '' - }${fluidName.toLowerCase()}` + }${fluidName}` )} </div> <div className="err-data-2"> diff --git a/src/components/Konnector/KonnectorModalFooter.tsx b/src/components/Konnector/KonnectorModalFooter.tsx index dbc539dcf5f8877dc1f24f100cc78ecaaf9206b0..f7466eea9e9cbc0f2d0df7b6866cf5d266ae90c2 100644 --- a/src/components/Konnector/KonnectorModalFooter.tsx +++ b/src/components/Konnector/KonnectorModalFooter.tsx @@ -1,15 +1,14 @@ import Button from '@material-ui/core/Button' import { useClient } from 'cozy-client' -import { - ERROR_EVENT, - SUCCESS_EVENT, -} from 'cozy-harvest-lib/dist/models/flowEvents' +import { SUCCESS_EVENT } from 'cozy-harvest-lib/dist/models/flowEvents' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { KonnectorError } from 'enums' import { Account } from 'models' import React, { useCallback } from 'react' import { useNavigate } from 'react-router-dom' import AccountService from 'services/account.service' +import { useAppDispatch } from 'store/hooks' +import { openFeedbackModal } from 'store/modal/modal.slice' import './konnectorModal.scss' interface KonnectorModalFooterProps { @@ -32,6 +31,8 @@ const KonnectorModalFooter = ({ const { t } = useI18n() const client = useClient() const navigate = useNavigate() + const dispatch = useAppDispatch() + const handleSGELoginRetry = useCallback(() => { handleCloseClick(state === SUCCESS_EVENT) navigate('/connect/electricity') @@ -46,73 +47,75 @@ const KonnectorModalFooter = ({ } }, [account, client, handleAccountDeletion, navigate]) - const defaultButton = ( - <Button - aria-label={t('konnector_modal.accessibility.button_close')} - onClick={() => handleCloseClick(state === SUCCESS_EVENT)} - className="btnPrimary" - > - <div>{t('konnector_modal.button_validate')}</div> - </Button> - ) - - const errorButtons = () => { - switch (error) { - case KonnectorError.USER_ACTION_NEEDED: - // INSEE CODE ERROR ENEDIS - return ( + switch (error) { + case KonnectorError.USER_ACTION_NEEDED: + // INSEE CODE ERROR ENEDIS + return ( + <Button + aria-label={t('konnector_modal.accessibility.button_close')} + onClick={() => handleCloseClick(state === SUCCESS_EVENT)} + className="btnPrimary" + > + <div>{t('konnector_modal.button_understood')}</div> + </Button> + ) + case KonnectorError.LOGIN_FAILED: + case KonnectorError.CHALLENGE_ASKED: + // INCOMPLETE CONSENT FORM - GRDF + return ( + <Button + aria-label={t('konnector_modal.accessibility.button_close')} + onClick={() => handleCloseClick(state === SUCCESS_EVENT)} + className="btnPrimary" + > + <div>{t('konnector_modal.button_try_again')}</div> + </Button> + ) + case KonnectorError.TERMS_VERSION_MISMATCH: + return ( + <div className="buttons"> <Button aria-label={t('konnector_modal.accessibility.button_close')} onClick={() => handleCloseClick(state === SUCCESS_EVENT)} - className="btnPrimary" + className="btnSecondary" > - <div>{t('konnector_modal.button_understood')}</div> + <div>{t('konnector_modal.button_later')}</div> </Button> - ) - case KonnectorError.LOGIN_FAILED: - case KonnectorError.CHALLENGE_ASKED: - // INCOMPLETE CONSENT FORM - GRDF - return ( <Button aria-label={t('konnector_modal.accessibility.button_close')} - onClick={() => handleCloseClick(state === SUCCESS_EVENT)} + onClick={!isUpdating ? handleSGELoginRetry : handleResetSGEAccount} className="btnPrimary" > - <div>{t('konnector_modal.button_try_again')}</div> + <div> + {!isUpdating + ? t('konnector_modal.button_check_info') + : t('konnector_modal.button_go')} + </div> </Button> - ) - case KonnectorError.TERMS_VERSION_MISMATCH: - return ( - <div className="buttons"> - <Button - aria-label={t('konnector_modal.accessibility.button_close')} - onClick={() => handleCloseClick(state === SUCCESS_EVENT)} - className="btnSecondary" - > - <div>{t('konnector_modal.button_later')}</div> - </Button> - <Button - aria-label={t('konnector_modal.accessibility.button_close')} - onClick={ - !isUpdating ? handleSGELoginRetry : handleResetSGEAccount - } - className="btnPrimary" - > - <div> - {!isUpdating - ? t('konnector_modal.button_check_info') - : t('konnector_modal.button_go')} - </div> - </Button> - </div> - ) - default: - // DEFAULT FOOTER BUTTONS - return defaultButton - } + </div> + ) + case KonnectorError.USER_ACTION_NEEDED_ACCOUNT_REMOVED: + return ( + <Button + aria-label={t('konnector_modal.accessibility.button_close')} + onClick={() => dispatch(openFeedbackModal(true))} + className="btnPrimary" + > + <div>{t('konnector_modal.button_contact')}</div> + </Button> + ) + default: + // DEFAULT FOOTER BUTTONS + return ( + <Button + aria-label={t('konnector_modal.accessibility.button_close')} + onClick={() => handleCloseClick(state === SUCCESS_EVENT)} + className="btnPrimary" + > + <div>{t('konnector_modal.button_validate')}</div> + </Button> + ) } - - return <>{state === ERROR_EVENT ? errorButtons() : defaultButton}</> } export default KonnectorModalFooter diff --git a/src/components/Konnector/KonnectorViewerCard.tsx b/src/components/Konnector/KonnectorViewerCard.tsx index 9c0de9fd00b07b86b85b8a48c2132b84b93c7937..0fc7116326d3154ebb26085cf7af70f6fd7101ad 100644 --- a/src/components/Konnector/KonnectorViewerCard.tsx +++ b/src/components/Konnector/KonnectorViewerCard.tsx @@ -192,24 +192,19 @@ const KonnectorViewerCard = ({ if (shouldDeleteAccount) { // KEEP LAST LOGIN FOR EPGL - let lastEpglLogin = '' if ( fluidSlug === FluidSlugType.WATER && currentFluidStatus.connection.account?.auth ) { const auth = currentFluidStatus.connection.account .auth as AccountEGLData - lastEpglLogin = auth.login + const lastEpglLogin = auth.login + dispatch(setLastEpglLogin(lastEpglLogin)) } // DELETE ACCOUNT const accountService = new AccountService(client) await accountService.deleteAccount(account) await handleAccountDeletion() - - // RESTORE LAST KNOWN CREDENTIALS - if (lastEpglLogin) { - dispatch(setLastEpglLogin(lastEpglLogin)) - } } else { const updatedFluidStatus = await fluidService.getFluidStatus(partnersInfo) @@ -491,6 +486,7 @@ const KonnectorViewerCard = ({ return ( <div className="konnector-section-root"> {isDisconnected && ( + // Why render accordion details without accordion ? <AccordionDetails>{getConnectionCard()}</AccordionDetails> )} {!isDisconnected && ( @@ -498,13 +494,12 @@ const KonnectorViewerCard = ({ expanded={active} onChange={toggleAccordion} classes={{ - root: `expansion-panel-root ${ - !currentFluidStatus.maintenance && - (currentFluidStatus.status === FluidState.ERROR || - currentFluidStatus.status === FluidState.ERROR_LOGIN_FAILED) - ? 'red-border' - : '' - }`, + root: classNames('expansion-panel-root', { + ['red-border']: + !currentFluidStatus.maintenance && + (currentFluidStatus.status === FluidState.ERROR || + currentFluidStatus.status === FluidState.ERROR_LOGIN_FAILED), + }), }} > <AccordionSummary @@ -539,13 +534,14 @@ const KonnectorViewerCard = ({ </AccordionDetails> </Accordion> )} + {/* Why render this if !isDisconnected => isConnected. Might be necessary */} <KonnectorModal open={openModal} isUpdating={isUpdating} isLogging={isLogging} state={konnectorState} error={konnectorErrorDescription} - fluidType={currentFluidStatus.fluidType} + fluidType={fluidType} handleCloseClick={handleConnectionEnd} handleAccountDeletion={handleAccountDeletion} account={account} diff --git a/src/components/Konnector/konnectorModal.scss b/src/components/Konnector/konnectorModal.scss index 1c27774962d8e9e7537666e13136f84ba5fc309b..0e8f5d3ac90a361ff6af1ed1f765d78dc3c248c9 100644 --- a/src/components/Konnector/konnectorModal.scss +++ b/src/components/Konnector/konnectorModal.scss @@ -38,16 +38,14 @@ } } - .kmodal-content-text-center { - text-align: center; - } - .kmodal-info { padding: 1rem; - text-align: center; + display: flex; + flex-direction: column; + gap: 1rem; .buttons { display: flex; - gap: 0.825rem; + gap: 1rem; } .konnector-config { align-items: center; @@ -58,12 +56,12 @@ gap: 1rem; .elec-fail { color: $grey-bright; - margin-top: 1rem; } &.mismatch { .title { color: $orange; } + // TO REMOVE div { margin-bottom: 1rem; } @@ -71,19 +69,13 @@ color: $grey-bright; } } - .kce-picto-txt { - color: $red-primary; - } - - .kcs-picto-txt { + .headerSuccess { color: $multi-color; } - - .commonErrors { - text-decoration: underline; - cursor: pointer; - margin: 1rem auto 0.5rem; + .headerError { + color: $red-primary; } + .commonErrorsList { text-align: left; span { @@ -104,10 +96,6 @@ } } } - - button { - margin-top: 1rem; - } } } diff --git a/src/enums/konnectorStatus.enum.ts b/src/enums/konnectorStatus.enum.ts index 4e8543316bd8de24d6cf05a1843e1e2a88371467..d708ed49ef1eafc8a9dd43087384b2e2d0ebdc8e 100644 --- a/src/enums/konnectorStatus.enum.ts +++ b/src/enums/konnectorStatus.enum.ts @@ -6,6 +6,7 @@ export enum KonnectorError { UNKNOWN_ERROR = 'UNKNOWN_ERROR', CRITICAL = 'exit status 1', MISSING_SECRET = "Cannot read property 'secret' of null", + USER_ACTION_NEEDED_ACCOUNT_REMOVED = 'USER_ACTION_NEEDED_ACCOUNT_REMOVED', } export enum KonnectorUpdate { diff --git a/src/locales/fr.json b/src/locales/fr.json index 9fa6b95a7b4337c81dbd5f57bb756097fab886ee..bc0838f4acfcf3a3ec77a5a33d6f2855eec900ca 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -819,18 +819,17 @@ "text2": "Reconfigurer mon connecteur\u00a0?", "text3": "La reconfiguration de votre connecteur passe par sa suppression et sa nouvelle installation. Vos données seront conservées." }, - "error_data_electricity": "Un problème est survenu. Vos données de consommation d’électricité ne seront pas chargées.", - "error_data_water": "Un problème est survenu. Vos données de consommation d’eau ne seront pas chargées.", "error_credentials_water": "Une erreur s'est glissée dans vos identifiants de connexion. Veuillez vérifier ces éléments et tenter de vous reconnecter. L'identifiant est un numéro à 7 chiffres (différent de votre numéro de contrat).", "error_credentials_electricity": "Il semblerait que les nom(s) et adresse ne concordent pas avec le numéro de votre compteur.", "error_credentials_electricity_2": "Nous ne pouvons vous donner accès aux données de consommation.", "error_credentials_update_water": "Une erreur s'est glissée dans vos identifiants de connexion. Veuillez vérifier ces éléments et tenter de vous reconnecter.", "error_credentials_update_electricity": "Un problème a lieu lors de la récupération de vos données. Merci de supprimer votre connecteur et vous reconnecter.", "error_credentials_update_gas": "Un problème a lieu lors de la récupération de vos données. Merci de supprimer votre connecteur et vous reconnecter.", - "error_data_gas": "Un problème est survenu. Vos données de consommation de gaz ne seront pas chargées.", - "error_consent_form_gas_title": "Nous n'avons pas pu connecter vos données de consommation de gaz à Ecolyo.", - "error_consent_form_gas_content": "En effet, le partage de vos données de consommation de gaz \"informatives\" doit être accepté.", - "error_consent_form_gas_content_2": "Merci de cocher \"OUI\" au partage de vos données de consommation de gaz, et à \"Autoriser l'accès à mes données informatives\".", + "error_consent_form_gas_title": "L'accès à vos données a été bloqué par GRDF suite à un consentement précédemment supprimé par vos soins.", + "error_consent_form_gas_report": "Merci de nous signaler le problème.", + "error_data_electricity": "Un problème est survenu. Vos données de consommation d’électricité ne seront pas chargées.", + "error_data_water": "Un problème est survenu. Vos données de consommation d’eau ne seront pas chargées.", + "error_data_gas": "Il semblerait que le service de connexion à vos données de gaz soit momentanément en panne.", "error_data_update_electricity": "Un problème est survenu. Vos données de consommation d’électricité n’ont pas été mises à jour.", "error_data_update_water": "Un problème est survenu. Vos données de consommation d’eau n’ont pas été mises à jour.", "error_data_update_gas": "Un problème est survenu. Vos données de consommation de gaz n’ont pas été mises à jour.", @@ -838,6 +837,7 @@ "button_validate": "Ok", "button_understood": "J'ai compris", "button_try_again": "Réessayer", + "button_contact": "Nous contacter", "button_check_info": "Vérifier les infos", "button_go": "J'y vais", "button_later": "Plus tard", diff --git a/src/store/global/global.slice.ts b/src/store/global/global.slice.ts index f251ba453ab9d70ae6008aa7156ba6695c49aa98..cc6228136820adca2d32ff907f7cf1dc8c372e5a 100644 --- a/src/store/global/global.slice.ts +++ b/src/store/global/global.slice.ts @@ -200,6 +200,7 @@ export const globalSlice = createSlice({ ) => { state.fluidStatus[fluidType].connection = fluidConnection }, + /** Restore last known credentials */ setLastEpglLogin: (state, action: PayloadAction<string>) => { state.lastEpglLogin = action.payload },