Skip to content
Snippets Groups Projects
ConnectionResult.tsx 12.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Bastien DUMONT's avatar
    Bastien DUMONT committed
    import Button from '@material-ui/core/Button'
    import warningWhite from 'assets/icons/ico/warning-white.svg'
    import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import Loader from 'components/Loader/Loader'
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
    import { useClient } from 'cozy-client'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import { useI18n } from 'cozy-ui/transpiled/react/I18n'
    
    import { FluidType } from 'enum/fluid.enum'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import { DateTime } from 'luxon'
    
    import {
      Account,
      AccountSgeData,
      FluidConnection,
      FluidStatus,
      Trigger,
    } from 'models'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import React, { useCallback, useEffect, useState } from 'react'
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
    import { useDispatch } from 'react-redux'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import AccountService from 'services/account.service'
    import DateChartService from 'services/dateChart.service'
    import TriggerService from 'services/triggers.service'
    
    import {
      setShouldRefreshConsent,
      updatedFluidConnection,
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
    import { getKonnectorUpdateError } from 'utils/utils'
    import './connectionResult.scss'
    
    import DeleteGRDFAccountModal from './DeleteGRDFAccountModal'
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
    
    interface ConnectionResultProps {
      fluidStatus: FluidStatus
      handleAccountDeletion: Function
      fluidType: FluidType
    }
    
    const ConnectionResult: React.FC<ConnectionResultProps> = ({
      fluidStatus,
      handleAccountDeletion,
      fluidType,
    }: ConnectionResultProps) => {
      const { t } = useI18n()
      const client = useClient()
      const dispatch = useDispatch()
      const account: Account | null = fluidStatus.connection.account
    
    
      const [deleting, setDeleting] = useState<boolean>(false)
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
      const [updating, setUpdating] = useState<boolean>(false)
      const [lastExecutionDate, setLastExecutionDate] = useState<string | DateTime>(
        '-'
      )
      const [konnectorError, setKonnectorError] = useState<string>('')
      const [status, setStatus] = useState<string>('')
      const [outDatedDataDays, setOutDatedDataDays] = useState<number | null>(null)
    
      const [openGRDFDeletionModal, setOpenGRDFDeletionModal] =
        useState<boolean>(false)
    
      const toggleGRDFDeletionModal = useCallback(() => {
        setOpenGRDFDeletionModal(prev => !prev)
      }, [])
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
    
      const updateKonnector = async () => {
        setUpdating(true)
        setStatus('')
        setLastExecutionDate('-')
        setKonnectorError('')
        const updatedConnection: FluidConnection = {
          ...fluidStatus.connection,
          shouldLaunchKonnector: true,
          isUpdating: true,
        }
        dispatch(updatedFluidConnection(fluidStatus.fluidType, updatedConnection))
        setUpdating(false)
      }
    
    
      const deleteAccountsAndTriggers = useCallback(async () => {
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
        try {
          if (account) {
            const accountService = new AccountService(client)
    
            const triggerService = new TriggerService(client)
            const accounts: Account[] = await accountService.getAccountsByType(
              account.account_type
            )
            for (const _account of accounts) {
    
              const trigger: Trigger | null =
                await triggerService.getTriggerForAccount(_account)
    
              if (trigger) await triggerService.deleteTrigger(trigger)
              await accountService.deleteAccount(_account)
            }
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
            await handleAccountDeletion()
          }
        } catch (error) {
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
        }
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
      const isOutdated = useCallback(() => {
        const dateChartService = new DateChartService()
        return dateChartService.isDataOutdated(fluidStatus.lastDataDate, fluidType)
      }, [fluidStatus, fluidType])
    
      const hasUpdatedToday = useCallback(() => {
        const todayDate = DateTime.local()
          .setZone('utc', {
            keepLocalTime: true,
          })
          .minus({ days: 1 })
          .toLocaleString()
        if (lastExecutionDate === '-') {
          return false
        } else if (lastExecutionDate > todayDate) {
          return false
        } else {
          return true
        }
      }, [lastExecutionDate])
    
    
      const handleRefreshConsent = useCallback(
        (fluidType: FluidType) => {
          if (fluidType == FluidType.ELECTRICITY) {
            const accountData = fluidStatus.connection.account
              ?.auth as AccountSgeData
            //store the previous account data since the onDelete will remove account from DB
            dispatch(
              updateSgeStore({
                currentStep: 0,
                firstName: accountData.firstname,
                lastName: accountData.lastname,
                pdl: parseInt(accountData.pointId),
                address: accountData.address,
                zipCode: parseInt(accountData.postalCode),
                city: accountData.city,
                dataConsent: true,
                pdlConfirm: true,
                shouldLaunchAccount: true,
              })
            )
    
            dispatch(setShouldRefreshConsent(true))
    
          } else {
            deleteAccountsAndTriggers()
          }
        },
        [deleteAccountsAndTriggers, dispatch, fluidStatus.connection.account?.auth]
      )
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
      useEffect(() => {
    
        if (fluidStatus.connection.triggerState?.last_success) {
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
          const result = DateTime.fromISO(
            fluidStatus.connection.triggerState.last_success
          )
          setLastExecutionDate(result)
        } else {
          setLastExecutionDate('-')
        }
        if (
    
          fluidStatus.connection.triggerState?.status === 'errored' &&
          fluidStatus.connection.triggerState?.last_error
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
        ) {
          setStatus('errored')
          setKonnectorError(
            getKonnectorUpdateError(fluidStatus.connection.triggerState.last_error)
          )
        }
        if (isOutdated()) {
          setOutDatedDataDays(isOutdated())
        }
      }, [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"
        }
      }
    
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
      return (
        <div className="connection-update-result">
          <div
            className={
    
              status === 'errored' && !hasUpdatedToday() && !fluidStatus.maintenance
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
                ? 'connection-update-errored'
                : ''
            }
          >
    
            {fluidStatus.maintenance ? (
    
              // First check if there is partner error from backoffice
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
              <div className="connection-caption text-16-normal">
                <div className="text-16-normal">
    
                  <div className="connection-caption">
                    {t('konnector_form.wait_end_issue')}
                  </div>
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
                </div>
              </div>
            ) : status === 'errored' ? (
    
              <DisplayKonnectorErrorState
                konnectorError={konnectorError}
                lastExecutionDate={lastExecutionDate}
                fluidConcerned={getFluidTypeTranslation(fluidType)}
              />
    
            ) : outDatedDataDays ? (
              // Else check if data is outdated
    
              <DisplayDataOutdated
                fluidStatus={fluidStatus}
                fluidType={fluidType}
                lastExecutionDate={lastExecutionDate}
                hasUpdatedToday={hasUpdatedToday()}
              />
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
            ) : (
    
              <DisplayLastUpdateDate lastExecutionDate={lastExecutionDate} />
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
            )}
          </div>
          <div className="inline-buttons">
            <Button
              aria-label={t('konnector_form.accessibility.button_delete')}
    
              onClick={
                fluidType === FluidType.GAS
                  ? toggleGRDFDeletionModal
                  : deleteAccountsAndTriggers
              }
    
              disabled={updating || deleting}
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
              classes={{
                root: 'btn-secondary-positive',
                label: 'text-16-normal',
              }}
            >
    
              {deleting
                ? t('konnector_form.loading')
                : t('konnector_form.button_delete')}
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
            </Button>
            <Button
              aria-label={t('konnector_form.accessibility.button_update')}
    
                  ? () => handleRefreshConsent(fluidType)
    
              disabled={updating || deleting}
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
              classes={{
                root: 'btn-highlight',
                label: 'text-16-bold',
              }}
            >
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
              {updating && <Loader color="black" />}
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
              {!updating && (
    
                <div>
                  {konnectorError === 'error_update_oauth'
                    ? t('konnector_form.button_oauth_reload')
                    : t('konnector_form.button_update')}
                </div>
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
              )}
            </Button>
          </div>
    
          {fluidType === FluidType.GAS && (
            <DeleteGRDFAccountModal
              open={openGRDFDeletionModal}
              handleCloseClick={toggleGRDFDeletionModal}
              deleteAccount={deleteAccountsAndTriggers}
            />
          )}
    
    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,
    }: {
      lastExecutionDate: string | DateTime
    }) => {
      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>
        </div>
      )
    }
    
    
    Hugo SUBTIL's avatar
    Hugo SUBTIL committed
    export default ConnectionResult