Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • web-et-numerique/factory/llle_project/ecolyo
1 result
Show changes
Commits on Source (56)
Showing
with 434 additions and 401 deletions
......@@ -100,6 +100,7 @@
"grdfgrandlyon",
"Hypervitesse",
"késako",
"kmodal",
"Konnected",
"konnector",
"konnectors",
......
......@@ -19,22 +19,25 @@ module.exports = {
__STACK_ASSETS__: target !== 'mobile',
__PIWIK_TRACKER_URL__: JSON.stringify('https://statweb.grandlyon.com/'),
__PIWIK_SITEID__: 117,
__SENTRY_DSN__: JSON.stringify(
'https://c868f6010f3f431d95be8f70d7f37666@grandlyon.errors.cozycloud.cc/6'
),
__SAU_LINK__: JSON.stringify(
'https://portail-citoyen-sau.guichet-recette.grandlyon.com/ecolyo/'
),
__SAU_IDEA_DIRECT_LINK__: JSON.stringify(
'https://demarches-sau.guichet-recette.grandlyon.com/retour-ecolyo/ecolyo-une-idee/'
),
__SAU_ISSUE_DIRECT_LINK__: JSON.stringify(
'https://demarches-sau.guichet-recette.grandlyon.com/retour-ecolyo/ecolyo-un-probleme/'
),
__SENTRY_DSN__: JSON.stringify(
'https://c868f6010f3f431d95be8f70d7f37666@grandlyon.errors.cozycloud.cc/6'
),
}),
],
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
//To fix a SAfari 10 bug : https://github.com/zeit/next.js/issues/5630
// To fix a SAfari 10 bug : https://github.com/zeit/next.js/issues/5630
terserOptions: {
safari10: true,
},
......
......@@ -29,6 +29,9 @@ const stackProvidedLibsConfig = {
__SAU_IDEA_DIRECT_LINK__: JSON.stringify(
'https://demarches-sau.guichet-recette.grandlyon.com/retour-ecolyo/ecolyo-une-idee/'
),
__SAU_ISSUE_DIRECT_LINK__: JSON.stringify(
'https://demarches-sau.guichet-recette.grandlyon.com/retour-ecolyo/ecolyo-un-probleme/'
),
__SENTRY_DSN__: JSON.stringify(
'https://c868f6010f3f431d95be8f70d7f37666@grandlyon.errors.cozycloud.cc/6'
),
......
......@@ -23,6 +23,9 @@ module.exports = {
__SAU_IDEA_DIRECT_LINK__: JSON.stringify(
'https://demarches-support.grandlyon.com/retour-ecolyo/ecolyo-une-idee/'
),
__SAU_ISSUE_DIRECT_LINK__: JSON.stringify(
'https://demarches-support.grandlyon.com/retour-ecolyo/ecolyo-un-probleme/'
),
__SENTRY_DSN__: JSON.stringify(
'https://c868f6010f3f431d95be8f70d7f37666@grandlyon.errors.cozycloud.cc/6'
),
......@@ -32,7 +35,7 @@ module.exports = {
minimizer: [
new TerserPlugin({
parallel: true,
//To fix a SAfari 10 bug : https://github.com/zeit/next.js/issues/5630
// To fix a SAfari 10 bug : https://github.com/zeit/next.js/issues/5630
terserOptions: {
safari10: true,
},
......
......@@ -20,15 +20,14 @@ export enum GrdfStep {
}
export const GrdfConnectView = () => {
const [launchConnection, setLaunchConnection] = useState(false)
const navigate = useNavigate()
const { data: instanceSettings } = useUserInstanceSettings()
const { fluidStatus } = useAppSelector(state => state.ecolyo.global)
const currentFluidStatus = fluidStatus[FluidType.GAS]
const account = currentFluidStatus.connection.account
const { data: instanceSettings } = useUserInstanceSettings()
const [launchConnection, setLaunchConnection] = useState(false)
const [currentStep, setCurrentStep] = useState<GrdfStep>(GrdfStep.Identity)
const [formData, setFormData] = useState<AccountGRDFData>({
lastname: '',
firstname: '',
......
......@@ -4,15 +4,16 @@
margin: auto;
display: flex;
flex-direction: column;
gap: 1rem;
gap: 0.5rem;
align-items: center;
text-align: center;
padding-inline: 1rem;
.green {
color: var(--gasColor);
}
.emailContainer span {
.emailContainer {
color: $gold-shadow;
font-weight: 700;
}
......
import { Button } from '@material-ui/core'
import { Button, Dialog, IconButton } from '@material-ui/core'
import CloseIcon from 'assets/icons/ico/close.svg'
import GRDFMail from 'assets/icons/visu/onboarding/grdf-mail.svg'
import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
import useUserInstanceSettings from 'components/Hooks/useUserInstanceSettings'
import { useI18n } from 'cozy-ui/transpiled/react/I18n'
import React from 'react'
import Icon from 'cozy-ui/transpiled/react/Icon'
import { FluidType } from 'enums'
import { FluidConnection } from 'models'
import React, { useEffect, useState } from 'react'
import { updateFluidConnection } from 'store/global/global.slice'
import { useAppDispatch, useAppSelector } from 'store/hooks'
import './GrdfWaitConsent.scss'
export const GrdfWaitConsent = () => {
const { t } = useI18n()
const dispatch = useAppDispatch()
const { fluidStatus } = useAppSelector(state => state.ecolyo.global)
const { data: instanceSettings } = useUserInstanceSettings()
const [emailSentOn, setEmailSentOn] = useState('')
const currentFluidStatus = fluidStatus[FluidType.GAS]
useEffect(() => {
setEmailSentOn(instanceSettings.email || '')
}, [instanceSettings])
const updateKonnector = async () => {
const updatedConnection: FluidConnection = {
...currentFluidStatus.connection,
// TODO : investigate is this is duplicate ?
shouldLaunchKonnector: true,
isUpdating: true,
}
dispatch(
updateFluidConnection({
fluidType: currentFluidStatus.fluidType,
fluidConnection: updatedConnection,
})
)
}
return (
<div className="grdfWait">
<div
className="text-18-normal emailContainer"
dangerouslySetInnerHTML={{
__html: t('auth.grdfgrandlyon.waiting.mailSent', {
email: 'test@test.com',
}),
}}
/>
<div className="text-18-normal">
{t('auth.grdfgrandlyon.waiting.mailSent')}
</div>
<div className="text-16-normal">
{t('auth.grdfgrandlyon.waiting.mailDelay')}
</div>
<span className="emailContainer">{emailSentOn}</span>
<StyledIcon size={80} icon={GRDFMail} />
<div className="text-18-normal">
<span className="text-18-bold green">
......@@ -25,9 +56,43 @@ export const GrdfWaitConsent = () => {
<br />
<span>{t('auth.grdfgrandlyon.waiting.comeback')}</span>
</div>
<Button className="btnPrimary">
<Button className="btnPrimary" onClick={updateKonnector}>
{t('auth.grdfgrandlyon.waiting.button_done')}
</Button>
</div>
)
}
export const GrdfWaitConsentModal = ({
open,
setOpen,
}: {
open: boolean
setOpen: React.Dispatch<React.SetStateAction<boolean>>
}) => {
const { t } = useI18n()
return (
<Dialog
open={open}
onClose={() => setOpen(false)}
aria-labelledby="accessibility-title"
classes={{
root: 'modal-root',
paper: 'modal-paper',
}}
>
<div id="accessibility-title">
{t('consumption_visualizer.modal.window_title')}
</div>
<IconButton
aria-label={t('consumption_visualizer.modal.close')}
className="modal-paper-close-button"
onClick={() => setOpen(false)}
>
<Icon icon={CloseIcon} size={16} />
</IconButton>
<GrdfWaitConsent />
</Dialog>
)
}
import ExpiredConsentModal from 'components/Connection/ExpiredConsentModal/ExpiredConsentModal'
import { GrdfWaitConsentModal } from 'components/Connection/GRDFConnect/GrdfWaitConsent'
import Content from 'components/Content/Content'
import CustomPopupModal from 'components/CustomPopup/CustomPopupModal'
import DateNavigator from 'components/DateNavigator/DateNavigator'
......@@ -10,7 +11,7 @@ import KonnectorViewerList from 'components/Konnector/KonnectorViewerList'
import PartnerIssueModal from 'components/PartnerIssue/PartnerIssueModal'
import ReleaseNotesModal from 'components/ReleaseNotesModal/ReleaseNotesModal'
import { useClient } from 'cozy-client'
import { FluidType, TimeStep } from 'enums'
import { FluidState, FluidType, TimeStep } from 'enums'
import { DateTime } from 'luxon'
import React, { useCallback, useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
......@@ -45,29 +46,51 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => {
modal: { partnersIssueModal, customPopupModal },
} = useAppSelector(state => state.ecolyo)
const currentFluidStatus = fluidStatus[fluidType]
const dateChartService = new DateChartService()
const isWaitingForConsent =
fluidType === FluidType.GAS &&
currentFluidStatus.status === FluidState.CHALLENGE_ASKED
const [waitConsent, setWaitConsent] = useState(isWaitingForConsent)
const [openExpiredConsentModal, setOpenExpiredConsentModal] = useState(true)
const [openReleaseNoteModal, setOpenReleaseNoteModal] = useState<boolean>(
releaseNotes.show
)
const [openExpiredConsentModal, setOpenExpiredConsentModal] =
useState<boolean>(true)
const [consentExpiredFluids, setConsentExpiredFluids] = useState<FluidType[]>(
[]
)
/** Show wait consent modal when navigating and consent is "A valider" */
useEffect(() => {
setWaitConsent(isWaitingForConsent)
}, [isWaitingForConsent])
const updateKey =
fluidType !== FluidType.MULTIFLUID && fluidStatus[fluidType].lastDataDate
? `${fluidStatus[fluidType].lastDataDate!.toLocaleString()} + ${
fluidStatus[fluidType].status + fluidType
fluidType !== FluidType.MULTIFLUID && currentFluidStatus.lastDataDate
? `${currentFluidStatus.lastDataDate.toLocaleString()} + ${
currentFluidStatus.status + fluidType
}`
: ''
const lastDataDateKey =
fluidType !== FluidType.MULTIFLUID && fluidStatus[fluidType].lastDataDate
? `${fluidStatus[fluidType].lastDataDate!.toLocaleString() + fluidType}`
fluidType !== FluidType.MULTIFLUID && currentFluidStatus.lastDataDate
? `${currentFluidStatus.lastDataDate.toLocaleString() + fluidType}`
: ''
const getPartnerKey = (fluidType: FluidType): 'enedis' | 'egl' | 'grdf' => {
switch (fluidType) {
case FluidType.ELECTRICITY:
return 'enedis'
case FluidType.WATER:
return 'egl'
case FluidType.GAS:
return 'grdf'
default:
throw new Error('unknown fluidtype')
}
}
const handleCloseReleaseNoteModal = useCallback(() => {
setOpenReleaseNoteModal(false)
dispatch(
......@@ -82,19 +105,6 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => {
}
}, [dispatch, navigate, releaseNotes.notes, releaseNotes.redirectLink])
const getPartnerKey = (fluidType: FluidType): 'enedis' | 'egl' | 'grdf' => {
switch (fluidType) {
case FluidType.ELECTRICITY:
return 'enedis'
case FluidType.WATER:
return 'egl'
case FluidType.GAS:
return 'grdf'
default:
throw new Error('unknown fluidtype')
}
}
const handleClosePartnerIssueModal = useCallback(
async (fluidType: FluidType) => {
const profileService = new ProfileService(client)
......@@ -134,15 +144,18 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => {
}
}
/** Handle time change */
useEffect(() => {
if (
fluidType !== FluidType.ELECTRICITY &&
currentTimeStep == TimeStep.HALF_AN_HOUR
) {
dispatch(setCurrentTimeStep(TimeStep.WEEK))
}
}, [dispatch, fluidType, currentTimeStep])
useEffect(
/** Reset half-hour timestep for water & gas & multifluid */
function setDefaultTimeStep() {
if (
fluidType !== FluidType.ELECTRICITY &&
currentTimeStep == TimeStep.HALF_AN_HOUR
) {
dispatch(setCurrentTimeStep(TimeStep.WEEK))
}
},
[dispatch, fluidType, currentTimeStep]
)
/**
* If fluid is not connected, display Connect components
......@@ -153,10 +166,11 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => {
dispatch(setShowOfflineData(isFluidConnected))
}, [dispatch, fluidStatus, fluidType])
/** Check if some fluids have expired consent error */
useEffect(() => {
let subscribed = true
const expiredConsents: FluidType[] = []
// Check if some fluids have expired consent error
// TODO only electricity can have expired consent error -> refactor this for of loop
for (const fluid of fluidStatus) {
const error = fluid.connection.triggerState?.last_error
if (error && getKonnectorUpdateError(error) === 'error_update_oauth') {
......@@ -220,38 +234,24 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => {
<Content>
<FluidButtons activeFluid={fluidType} key={updateKey} />
{openReleaseNoteModal && (
<ReleaseNotesModal
open={openReleaseNoteModal}
handleCloseClick={handleCloseReleaseNoteModal}
/>
)}
{showOfflineData && (
<>
<FluidChart fluidType={fluidType} key={lastDataDateKey} />
<ConsumptionDetails fluidType={fluidType} />
{!isMulti && (
<KonnectorViewerCard
fluidType={fluidType}
showOfflineData={true}
key={fluidType}
/>
)}
</>
)}
{!showOfflineData && (
<div className="konnector-section">
{isMulti ? (
<KonnectorViewerList />
) : (
<KonnectorViewerCard
fluidType={fluidType}
showOfflineData={false}
/>
)}
</div>
)}
{!isMulti && <KonnectorViewerCard fluidType={fluidType} />}
{isMulti && !showOfflineData && <KonnectorViewerList />}
</Content>
{/* MODALS */}
{openReleaseNoteModal && (
<ReleaseNotesModal
open={openReleaseNoteModal}
handleCloseClick={handleCloseReleaseNoteModal}
/>
)}
{/* Partner issue modals for individual fluids */}
{fluidStatus
.filter(fluid => fluid.maintenance)
......@@ -269,17 +269,18 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => {
handleCloseClick={handleCloseCustomPopupModal}
/>
{Boolean(consentExpiredFluids.length) &&
consentExpiredFluids.map(fluid => {
return (
<ExpiredConsentModal
key={fluid}
open={openExpiredConsentModal}
handleCloseClick={() => setOpenExpiredConsentModal(false)}
fluidType={fluid}
toggleModal={() => setOpenExpiredConsentModal(prev => !prev)}
/>
)
})}
consentExpiredFluids.map(fluid => (
<ExpiredConsentModal
key={fluid}
open={openExpiredConsentModal}
handleCloseClick={() => setOpenExpiredConsentModal(false)}
fluidType={fluid}
toggleModal={() => setOpenExpiredConsentModal(prev => !prev)}
/>
))}
{/* GRDF Waiting screen */}
<GrdfWaitConsentModal open={waitConsent} setOpen={setWaitConsent} />
</>
)
}
......
......@@ -32,7 +32,7 @@ const FluidButton = ({ fluidType, isActive }: FluidButtonProps) => {
(fluidType !== FluidType.MULTIFLUID &&
fluidStatus[fluidType].status === FluidState.ERROR) ||
(fluidType !== FluidType.WATER &&
fluidStatus[fluidType].status === FluidState.ERROR_LOGIN_FAILED)
fluidStatus[fluidType].status === FluidState.LOGIN_FAILED)
) {
return true
}
......
......@@ -60,7 +60,6 @@ const formatAuthData = ({
}
const useKonnectorAuth = (
// not needed ?
fluidType: FluidType,
options: {
eglAuthData?: AccountEGLData
......
......@@ -5,7 +5,7 @@ import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
import Loader from 'components/Loader/Loader'
import { useClient } from 'cozy-client'
import { useI18n } from 'cozy-ui/transpiled/react/I18n'
import { FluidType, KonnectorUpdate } from 'enums'
import { FluidState, FluidType, KonnectorUpdate } from 'enums'
import { DateTime } from 'luxon'
import { Account, AccountSgeData, FluidConnection, FluidStatus } from 'models'
import React, { useCallback, useEffect, useState } from 'react'
......@@ -38,6 +38,7 @@ const ConnectionResult = ({
const account: Account | null = currentFluidStatus.connection.account
const [deleting, setDeleting] = useState<boolean>(false)
// todo remove updating state
const [updating, setUpdating] = useState<boolean>(false)
const [lastExecutionDate, setLastExecutionDate] = useState<string | DateTime>(
'-'
......@@ -46,6 +47,10 @@ const ConnectionResult = ({
const [status, setStatus] = useState<string>('')
const [outDatedDataDays, setOutDatedDataDays] = useState<number | null>(null)
const isWaitingForConsent =
fluidType === FluidType.GAS &&
currentFluidStatus.status === FluidState.CHALLENGE_ASKED
const updateKonnector = async () => {
setUpdating(true)
setStatus('')
......@@ -110,37 +115,35 @@ const ConnectionResult = ({
}
}, [lastExecutionDate])
const handleRefreshConsent = useCallback(
(fluidType: FluidType) => {
if (fluidType == FluidType.ELECTRICITY) {
const accountData = currentFluidStatus.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,
currentFluidStatus.connection.account?.auth,
]
)
const handleRefreshConsent = useCallback(() => {
if (fluidType == FluidType.ELECTRICITY) {
const accountData = currentFluidStatus.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()
}
}, [
fluidType,
currentFluidStatus.connection.account?.auth,
dispatch,
deleteAccountsAndTriggers,
])
useEffect(() => {
if (currentFluidStatus.connection.triggerState?.last_success) {
......@@ -162,9 +165,8 @@ const ConnectionResult = ({
)
)
}
if (isOutdated()) {
setOutDatedDataDays(isOutdated())
}
const outdated = isOutdated()
if (outdated) setOutDatedDataDays(outdated)
}, [currentFluidStatus.connection.triggerState, isOutdated])
const getFluidTypeTranslation = (fluidType: FluidType) => {
......@@ -178,9 +180,7 @@ const ConnectionResult = ({
}
}
const consentError =
konnectorError === KonnectorUpdate.ERROR_CONSENT_FORM_GAS ||
konnectorError === KonnectorUpdate.ERROR_UPDATE_OAUTH
const consentError = konnectorError === KonnectorUpdate.ERROR_UPDATE_OAUTH
/**
* Get Konnector state, possible values:
......@@ -193,12 +193,17 @@ const ConnectionResult = ({
// First check if there is partner error from backoffice
if (currentFluidStatus.maintenance) {
return (
<div className="connection-caption text-16-normal">
<div className="text-16-normal">
<div className="connection-caption">
{t('konnector_form.wait_end_issue')}
</div>
</div>
<div className="connection-caption">
{t('konnector_form.wait_end_issue')}
</div>
)
}
if (isWaitingForConsent) {
return (
<div className="connection-caption">
<div>{t('auth.grdfgrandlyon.waiting.validate')}</div>
<div>{t('auth.grdfgrandlyon.waiting.comeback')}</div>
</div>
)
}
......@@ -233,19 +238,21 @@ const ConnectionResult = ({
)
}
const getErrorClassName = (): string => {
if (isWaitingForConsent) return ''
if (
status === 'errored' &&
!hasUpdatedToday() &&
!currentFluidStatus.maintenance
) {
return 'connection-update-errored'
}
return ''
}
return (
<div className="connection-update-result">
<div
className={
status === 'errored' &&
!hasUpdatedToday() &&
!currentFluidStatus.maintenance
? 'connection-update-errored'
: ''
}
>
{getConnectionStatus()}
</div>
<div className={getErrorClassName()}>{getConnectionStatus()}</div>
<div className="inline-buttons">
{!consentError && (
<Button
......@@ -254,18 +261,12 @@ const ConnectionResult = ({
disabled={updating || deleting}
className="btnSecondary"
>
{deleting
? t('konnector_form.loading')
: t('konnector_form.button_disconnect')}
{t(`konnector_form.${deleting ? 'loading' : 'button_disconnect'}`)}
</Button>
)}
<Button
aria-label={t('konnector_form.accessibility.button_update')}
onClick={
consentError
? () => handleRefreshConsent(fluidType)
: updateKonnector
}
onClick={consentError ? handleRefreshConsent : updateKonnector}
disabled={updating || deleting}
className="btnPrimary"
>
......
......@@ -23,7 +23,7 @@ describe('KonnectorModal component', () => {
error={null}
fluidType={FluidType.ELECTRICITY}
handleCloseClick={mockHandleCloseClick}
isLogging={false}
isVerifyingIdentity={false}
account={null}
handleAccountDeletion={jest.fn()}
/>
......@@ -41,7 +41,7 @@ describe('KonnectorModal component', () => {
error={null}
fluidType={FluidType.ELECTRICITY}
handleCloseClick={mockHandleCloseClick}
isLogging={false}
isVerifyingIdentity={false}
account={null}
handleAccountDeletion={jest.fn()}
/>
......@@ -61,7 +61,7 @@ describe('KonnectorModal component', () => {
error={null}
fluidType={FluidType.ELECTRICITY}
handleCloseClick={mockHandleCloseClick}
isLogging={false}
isVerifyingIdentity={false}
account={null}
handleAccountDeletion={jest.fn()}
/>
......@@ -80,14 +80,14 @@ describe('KonnectorModal component', () => {
error={KonnectorError.LOGIN_FAILED}
fluidType={FluidType.ELECTRICITY}
handleCloseClick={mockHandleCloseClick}
isLogging={false}
isVerifyingIdentity={false}
account={null}
handleAccountDeletion={jest.fn()}
/>
</Provider>
)
expect(
baseElement.getElementsByClassName('kce-picto-txt')[0]
baseElement.getElementsByClassName('headerError')[0]
).toBeInTheDocument()
})
it('should render unknown error', async () => {
......@@ -100,7 +100,7 @@ describe('KonnectorModal component', () => {
error={null}
fluidType={FluidType.ELECTRICITY}
handleCloseClick={mockHandleCloseClick}
isLogging={false}
isVerifyingIdentity={false}
account={null}
handleAccountDeletion={jest.fn()}
/>
......@@ -120,7 +120,7 @@ describe('KonnectorModal component', () => {
error={null}
fluidType={FluidType.WATER}
handleCloseClick={mockHandleCloseClick}
isLogging={false}
isVerifyingIdentity={false}
account={null}
handleAccountDeletion={jest.fn()}
/>
......
......@@ -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'
......@@ -24,7 +25,7 @@ interface KonnectorModalProps {
open: boolean
isUpdating: boolean
/** Only used for SGE when searching for user identity */
isLogging: boolean
isVerifyingIdentity: boolean
state: string | null
error: KonnectorError | null
fluidType: FluidType
......@@ -36,7 +37,7 @@ interface KonnectorModalProps {
const KonnectorModal = ({
open,
isUpdating,
isLogging,
isVerifyingIdentity,
state,
error,
fluidType,
......@@ -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 (
......@@ -80,7 +83,7 @@ const KonnectorModal = ({
const getConnectionText = useCallback(() => {
return (
<div className="kmodal-waiting-text text-18-italic">
{isLogging ? (
{isVerifyingIdentity ? (
<p className="text-18-white">{t('konnector_modal.logging_txt')}</p>
) : (
firstConnectionWaitingTexts.map((text, idx) => (
......@@ -97,20 +100,20 @@ const KonnectorModal = ({
)}
</div>
)
}, [firstConnectionWaitingTexts, index, isLogging, t])
}, [firstConnectionWaitingTexts, index, isVerifyingIdentity, t])
/** Returns connection success contents, depending on the fluid and update status */
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">
{!isVerifyingIdentity && (
<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,7 @@ const KonnectorModal = ({
isUpdating &&
fluidType === FluidType.ELECTRICITY && (
// MISMATCH UPDATE ERROR ENEDIS
<div className="kce-picto-txt konnector-config mismatch">
<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,41 +247,37 @@ 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">
<div className="title text-16-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>
)}
{error !== KonnectorError.LOGIN_FAILED &&
error !== KonnectorError.TERMS_VERSION_MISMATCH &&
error !== KonnectorError.CHALLENGE_ASKED && (
error !==
KonnectorError.USER_ACTION_NEEDED_ACCOUNT_REMOVED && (
// 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">
......
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'
......@@ -12,6 +9,8 @@ import { useNavigate } from 'react-router-dom'
import AccountService from 'services/account.service'
import './konnectorModal.scss'
declare let __SAU_ISSUE_DIRECT_LINK__: string
interface KonnectorModalFooterProps {
state: string | null
error: KonnectorError | null
......@@ -32,6 +31,7 @@ const KonnectorModalFooter = ({
const { t } = useI18n()
const client = useClient()
const navigate = useNavigate()
const handleSGELoginRetry = useCallback(() => {
handleCloseClick(state === SUCCESS_EVENT)
navigate('/connect/electricity')
......@@ -46,73 +46,87 @@ 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 (
<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="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>
if (error === 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>
)
} else if (error === KonnectorError.LOGIN_FAILED) {
// INCOMPLETE CONSENT FORM - GRDF // what is this comment ?
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>
)
} else if (error === KonnectorError.CHALLENGE_ASKED) {
return (
<Button
aria-label={t('konnector_modal.accessibility.button_close')}
onClick={() => handleCloseClick(state === SUCCESS_EVENT)}
className="btnPrimary"
>
<div>{t('konnector_modal.button_come_back_later')}</div>
</Button>
)
} else if (error === 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>
)
default:
// DEFAULT FOOTER BUTTONS
return defaultButton
}
</Button>
</div>
)
} else if (error === KonnectorError.USER_ACTION_NEEDED_ACCOUNT_REMOVED) {
return (
<Button
aria-label={t('konnector_modal.accessibility.button_close')}
onClick={() => {
window.open(
`${__SAU_ISSUE_DIRECT_LINK__}?version=${client.appMetadata.version}`
)
handleCloseClick(state === SUCCESS_EVENT)
}}
className="btnPrimary"
>
<div>{t('konnector_modal.button_contact')}</div>
</Button>
)
} else {
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
......@@ -12,7 +12,6 @@ import OfflinePicto from 'assets/icons/visu/offline-param.svg'
import classNames from 'classnames'
import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
import Connection from 'components/Connection/Connection'
import { GrdfWaitConsent } from 'components/Connection/GRDFConnect/GrdfWaitConsent'
import KonnectorModal from 'components/Konnector/KonnectorModal'
import { useClient } from 'cozy-client'
import { isKonnectorRunning } from 'cozy-harvest-lib/dist/helpers/triggers'
......@@ -54,27 +53,24 @@ import {
updateFluidConnection,
} from 'store/global/global.slice'
import { useAppDispatch, useAppSelector } from 'store/hooks'
import logApp from 'utils/logger'
import { getParamPicto } from 'utils/picto'
import { getKonnectorSlug } from 'utils/utils'
import ConnectionNotFound from './ConnectionNotFound/ConnectionNotFound'
import ConnectionResult from './ConnectionResult/ConnectionResult'
import './konnectorViewerCard.scss'
interface KonnectorViewerCardProps {
showOfflineData: boolean
fluidType: FluidType
}
const KonnectorViewerCard = ({
showOfflineData,
fluidType,
}: KonnectorViewerCardProps) => {
}: {
fluidType: Exclude<FluidType, FluidType.MULTIFLUID>
}) => {
const { t } = useI18n()
const client = useClient()
const navigate = useNavigate()
const dispatch = useAppDispatch()
const {
chart: { showConnectionDetails },
chart: { showConnectionDetails, showOfflineData },
challenge: { currentChallenge },
global: { fluidStatus, shouldRefreshConsent, partnersInfo },
} = useAppSelector(state => state.ecolyo)
......@@ -83,26 +79,27 @@ const KonnectorViewerCard = ({
const fluidState = currentFluidStatus.status
const { konnector, account, trigger } = currentFluidStatus.connection
const currentFluidName = FluidType[currentFluidStatus.fluidType].toLowerCase()
const [openModal, setOpenModal] = useState(false)
const [isUpdating, setIsUpdating] = useState(false)
const [isLogging, setIsLogging] = useState(
const [isVerifyingIdentity, setIsVerifyingIdentity] = useState(
fluidType === FluidType.ELECTRICITY
)
const [konnectorErrorDescription, setKonnectorErrorDescription] =
useState<KonnectorError | null>(null)
const [konnectorState, setKonnectorState] = useState<string | null>(null)
const [isOutdatedData, setIsOutdatedData] = useState<number | null>(null)
const isWaitingForConsent =
fluidType === FluidType.GAS &&
currentFluidStatus.status === FluidState.CHALLENGE_ASKED
const fluidService = useMemo(() => new FluidService(client), [client])
const partnersInfoService = useMemo(
() => new PartnersInfoService(client),
[client]
)
const lastDataDate =
fluidType !== FluidType.MULTIFLUID && currentFluidStatus.lastDataDate
? currentFluidStatus.lastDataDate.toLocaleString() + fluidType
: fluidType
const iconType = getParamPicto(currentFluidStatus.fluidType)
const toggleAccordion = () => {
......@@ -112,19 +109,19 @@ const KonnectorViewerCard = ({
const updateGlobalFluidStatus = useCallback(async (): Promise<
FluidStatus[]
> => {
const _updatedFluidStatus = await fluidService.getFluidStatus()
const updatedFluidStatus = await fluidService.getFluidStatus()
const refDate = DateTime.fromISO('0001-01-01')
let _lastDataDate = DateTime.fromISO('0001-01-01')
for (const fluid of _updatedFluidStatus) {
if (fluid?.lastDataDate && fluid?.lastDataDate > _lastDataDate) {
_lastDataDate = fluid.lastDataDate
let lastDataDate = DateTime.fromISO('0001-01-01')
for (const fluid of updatedFluidStatus) {
if (fluid?.lastDataDate && fluid?.lastDataDate > lastDataDate) {
lastDataDate = fluid.lastDataDate
}
}
if (_lastDataDate > refDate) {
dispatch(setSelectedDate(_lastDataDate))
if (lastDataDate > refDate) {
dispatch(setSelectedDate(lastDataDate))
}
return _updatedFluidStatus
return updatedFluidStatus
}, [dispatch, fluidService])
const refreshChallengeState = useCallback(async () => {
......@@ -173,9 +170,10 @@ const KonnectorViewerCard = ({
const isGlobalLoginFailed =
konnectorErrorDescription === KonnectorError.LOGIN_FAILED ||
konnectorErrorDescription === KonnectorError.UNKNOWN_ERROR ||
konnectorErrorDescription === KonnectorError.CHALLENGE_ASKED ||
konnectorErrorDescription === KonnectorError.CRITICAL ||
konnectorErrorDescription === KonnectorError.MISSING_SECRET
konnectorErrorDescription === KonnectorError.MISSING_SECRET ||
konnectorErrorDescription ===
KonnectorError.USER_ACTION_NEEDED_ACCOUNT_REMOVED
// CASE FOR ENEDIS CODE INSEE ERROR
const isEnedisCodeInseeError =
......@@ -189,25 +187,21 @@ const KonnectorViewerCard = ({
(isGlobalLoginFailed || isEnedisCodeInseeError)
if (shouldDeleteAccount) {
logApp('info', `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)
......@@ -249,12 +243,6 @@ const KonnectorViewerCard = ({
}, [dispatch, fluidType, navigate])
const getConnectionCard = useCallback(() => {
if (
fluidType === FluidType.GAS &&
fluidState === FluidState.CHALLENGE_ASKED
) {
return <GrdfWaitConsent />
}
if (showOfflineData && !account) {
return (
<Button className="btnPrimary" onClick={toggleModalConnection}>
......@@ -262,35 +250,32 @@ const KonnectorViewerCard = ({
</Button>
)
}
if (fluidState === FluidState.KONNECTOR_NOT_FOUND && !isUpdating) {
return <ConnectionNotFound konnectorSlug={fluidSlug} />
}
// Handle login failed for EGL
else if (
if (
(fluidType === FluidType.WATER &&
fluidState === FluidState.ERROR_LOGIN_FAILED) ||
fluidState === FluidState.LOGIN_FAILED) ||
(account && currentFluidStatus.status !== FluidState.NOT_CONNECTED)
) {
return (
<ConnectionResult
handleAccountDeletion={handleAccountDeletion}
fluidType={fluidType}
key={lastDataDate}
/>
)
} else {
return <Connection fluidType={currentFluidStatus.fluidType} />
}
}, [
account,
currentFluidStatus.fluidType,
currentFluidStatus.status,
fluidSlug,
fluidState,
fluidType,
handleAccountDeletion,
isUpdating,
lastDataDate,
showOfflineData,
t,
toggleModalConnection,
......@@ -325,63 +310,38 @@ const KonnectorViewerCard = ({
]
)
const getIconForStatus = (
status: FluidState,
maintenance: boolean,
connection: FluidConnection,
outdatedData: number | null
) => {
if (maintenance) {
return (
<StyledIcon
icon={PartnersIssueNotif}
size={24}
className="konnector-state-picto"
/>
)
}
/** Display konnector icon & its smaller status icon in upper left corner */
const displayKonnectorIcon = useCallback(() => {
const { maintenance, status, connection } = currentFluidStatus
let statusIcon = null
if (
(status === FluidState.ERROR ||
status === FluidState.ERROR_LOGIN_FAILED) &&
if (maintenance) {
statusIcon = PartnersIssueNotif
} else if (
(status === FluidState.ERROR || status === FluidState.LOGIN_FAILED) &&
connection.account
) {
return (
<StyledIcon
icon={ErrorNotif}
size={24}
className="konnector-state-picto"
/>
)
statusIcon = ErrorNotif
} else if (isWaitingForConsent || (isOutdatedData && isOutdatedData > 0)) {
statusIcon = WarningNotif
}
if (outdatedData && outdatedData > 0) {
return (
<StyledIcon
icon={WarningNotif}
size={24}
className="konnector-state-picto"
/>
)
}
}
const displayKonnectorIcon = useCallback(() => {
return (
<div className="konnector-icon">
<Icon
icon={currentFluidStatus.connection.account ? iconType : OfflinePicto}
size={49}
/>
{getIconForStatus(
currentFluidStatus.status,
currentFluidStatus.maintenance,
currentFluidStatus.connection,
isOutdatedData
{statusIcon && (
<StyledIcon
icon={statusIcon}
size={24}
className="konnector-state-picto"
/>
)}
</div>
)
}, [currentFluidStatus, iconType, isOutdatedData])
}, [currentFluidStatus, iconType, isOutdatedData, isWaitingForConsent])
const displayKonnectorHeader = useCallback(() => {
if (currentFluidStatus.maintenance) {
......@@ -452,7 +412,7 @@ const KonnectorViewerCard = ({
callbackResponse(ERROR_EVENT)
})
connectionFlow.jobWatcher.on(LOGIN_SUCCESS_EVENT, () => {
setIsLogging(false)
setIsVerifyingIdentity(false)
})
connectionFlow.jobWatcher.on(SUCCESS_EVENT, () => {
callbackResponse(SUCCESS_EVENT)
......@@ -486,21 +446,18 @@ const KonnectorViewerCard = ({
return (
<div className="konnector-section-root">
{!showOfflineData && (
<AccordionDetails>{getConnectionCard()}</AccordionDetails>
)}
{!showOfflineData && <Connection fluidType={fluidType} />}
{showOfflineData && (
<Accordion
expanded={showConnectionDetails}
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.LOGIN_FAILED),
}),
}}
>
<AccordionSummary
......@@ -538,10 +495,10 @@ const KonnectorViewerCard = ({
<KonnectorModal
open={openModal}
isUpdating={isUpdating}
isLogging={isLogging}
isVerifyingIdentity={isVerifyingIdentity}
state={konnectorState}
error={konnectorErrorDescription}
fluidType={currentFluidStatus.fluidType}
fluidType={fluidType}
handleCloseClick={handleConnectionEnd}
handleAccountDeletion={handleAccountDeletion}
account={account}
......
......@@ -11,8 +11,9 @@ import './konnectorViewerCard.scss'
const KonnectorViewerList = () => {
const { t } = useI18n()
const { fluidStatus } = useAppSelector(state => state.ecolyo.global)
const navigate = useNavigate()
const { fluidStatus } = useAppSelector(state => state.ecolyo.global)
const goToFluid = (fluidType: FluidType) => {
navigate(`/consumption/${getFluidName(fluidType)}`)
}
......
......@@ -62,7 +62,7 @@ exports[`KonnectorModal component should be rendered correctly 1`] = `
</div>
</div>
<div
class="kmodal-content-text kmodal-content-text-center text-16-normal"
class="kmodal-content-text text-16-normal"
>
<div
class="kc-wait text-16-bold"
......
......@@ -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,32 +56,22 @@
gap: 1rem;
.elec-fail {
color: $grey-bright;
margin-top: 1rem;
}
&.mismatch {
.title {
color: $orange;
}
div {
margin-bottom: 1rem;
}
.info {
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 +92,6 @@
}
}
}
button {
margin-top: 1rem;
}
}
}
......
......@@ -35,7 +35,7 @@ const ReportOptions = () => {
const isWaterConnected =
fluidStatus[FluidType.WATER].status !== FluidState.NOT_CONNECTED &&
fluidStatus[FluidType.WATER].status !== FluidState.KONNECTOR_NOT_FOUND &&
fluidStatus[FluidType.WATER].status !== FluidState.ERROR_LOGIN_FAILED
fluidStatus[FluidType.WATER].status !== FluidState.LOGIN_FAILED
const setWaterLimit = (
e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement, Element>
......
......@@ -6,10 +6,10 @@ export enum FluidType {
}
export enum FluidState {
KONNECTOR_NOT_FOUND = 0,
NOT_CONNECTED = 1,
DONE = 200,
ERROR = 300,
ERROR_LOGIN_FAILED = 301,
CHALLENGE_ASKED = 400,
KONNECTOR_NOT_FOUND = 'KONNECTOR_NOT_FOUND',
NOT_CONNECTED = 'NOT_CONNECTED',
DONE = 'DONE',
ERROR = 'ERROR',
LOGIN_FAILED = 'LOGIN_FAILED',
CHALLENGE_ASKED = 'CHALLENGE_ASKED',
}