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
Select Git revision
  • build
  • build-dev
  • build-test
  • dev
  • lint/testing-libraby-plugin
  • master
  • renovate/copy-webpack-plugin-13.x
  • renovate/couchdb-3.x
  • renovate/cozy-client-49.x
  • renovate/cozy-device-helper-3.x
  • renovate/cozy-flags-4.x
  • renovate/cozy-harvest-lib-32.x
  • renovate/cozy-harvest-lib-9.x
  • renovate/cozy-realtime-5.x
  • renovate/cozy-scripts-8.x
  • renovate/devdependencies-(non-major)
  • renovate/eslint-9.x
  • renovate/eslint-config-prettier-10.x
  • renovate/eslint-plugin-testing-library-7.x
  • renovate/major-react-monorepo
  • renovate/major-react-router-monorepo
  • renovate/major-typescript-eslint-monorepo
  • renovate/react-19.x
  • renovate/react-dom-19.x
  • renovate/react-inspector-6.x
  • renovate/sass-loader-16.x
  • 0.1.0
  • 0.1.1
  • 0.1.2
  • 0.1.6
  • 0.1.7
  • 1.0.2
  • 1.0.3
  • 1.0.5
  • 1.0.6
  • 1.0.7
  • 1.0.8
  • 1.0.9
  • 1.1.0
  • 1.2.0
  • 1.2.1
  • 1.2.4-beta.1
  • V1.11.0
  • v1.10.0
  • v1.10.1
  • v1.10.2
  • v1.12.0
  • v1.12.1
  • v1.2.0
  • v1.2.1
  • v1.2.2
  • v1.2.3
  • v1.2.4
  • v1.2.4-beta.1
  • v1.3.0
  • v1.4.0
  • v1.4.1
  • v1.4.2
  • v1.4.3
  • v1.4.4
  • v1.5.0
  • v1.5.1
  • v1.6.0
  • v1.6.1
  • v1.6.2
  • v1.6.3
  • v1.6.4
  • v1.6.5
  • v1.7.0
  • v1.7.1
  • v1.7.2
  • v1.7.3
  • v1.7.4
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.8.3
  • v1.9.0
  • v1.9.1
  • v1.9.2
  • v1.9.3
  • v1.9.4
  • v2.0.0
  • v2.0.1
  • v2.0.2
  • v2.1.0
  • v2.1.1
  • v2.2.0
  • v2.2.1
  • v2.2.2
  • v2.3.0
  • v2.3.1
  • v2.4.0
  • v2.5.0
  • v2.5.1
  • v2.6.0
  • v2.7.0
  • v2.7.1
  • v2.7.2
  • v2.8.0
  • v3.0.0
  • v3.1.0
  • v3.1.1
103 results

Target

Select target project
  • web-et-numerique/factory/llle_project/ecolyo
1 result
Select Git revision
  • build
  • build-dev
  • build-test
  • dev
  • lint/testing-libraby-plugin
  • master
  • renovate/copy-webpack-plugin-13.x
  • renovate/couchdb-3.x
  • renovate/cozy-client-49.x
  • renovate/cozy-device-helper-3.x
  • renovate/cozy-flags-4.x
  • renovate/cozy-harvest-lib-32.x
  • renovate/cozy-harvest-lib-9.x
  • renovate/cozy-realtime-5.x
  • renovate/cozy-scripts-8.x
  • renovate/devdependencies-(non-major)
  • renovate/eslint-9.x
  • renovate/eslint-config-prettier-10.x
  • renovate/eslint-plugin-testing-library-7.x
  • renovate/major-react-monorepo
  • renovate/major-react-router-monorepo
  • renovate/major-typescript-eslint-monorepo
  • renovate/react-19.x
  • renovate/react-dom-19.x
  • renovate/react-inspector-6.x
  • renovate/sass-loader-16.x
  • 0.1.0
  • 0.1.1
  • 0.1.2
  • 0.1.6
  • 0.1.7
  • 1.0.2
  • 1.0.3
  • 1.0.5
  • 1.0.6
  • 1.0.7
  • 1.0.8
  • 1.0.9
  • 1.1.0
  • 1.2.0
  • 1.2.1
  • 1.2.4-beta.1
  • V1.11.0
  • v1.10.0
  • v1.10.1
  • v1.10.2
  • v1.12.0
  • v1.12.1
  • v1.2.0
  • v1.2.1
  • v1.2.2
  • v1.2.3
  • v1.2.4
  • v1.2.4-beta.1
  • v1.3.0
  • v1.4.0
  • v1.4.1
  • v1.4.2
  • v1.4.3
  • v1.4.4
  • v1.5.0
  • v1.5.1
  • v1.6.0
  • v1.6.1
  • v1.6.2
  • v1.6.3
  • v1.6.4
  • v1.6.5
  • v1.7.0
  • v1.7.1
  • v1.7.2
  • v1.7.3
  • v1.7.4
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.8.3
  • v1.9.0
  • v1.9.1
  • v1.9.2
  • v1.9.3
  • v1.9.4
  • v2.0.0
  • v2.0.1
  • v2.0.2
  • v2.1.0
  • v2.1.1
  • v2.2.0
  • v2.2.1
  • v2.2.2
  • v2.3.0
  • v2.3.1
  • v2.4.0
  • v2.5.0
  • v2.5.1
  • v2.6.0
  • v2.7.0
  • v2.7.1
  • v2.7.2
  • v2.8.0
  • v3.0.0
  • v3.1.0
  • v3.1.1
103 results
Show changes
Showing
with 370 additions and 297 deletions
......@@ -127,7 +127,7 @@ exports[`BarChart component should render correctly 1`] = `
</lineargradient>
</defs>
<path
class="bar-ELECTRICITY undefined selected bounce-2 delay"
class="bar-ELECTRICITY selected bounce-2 delay"
d="
M0,4
a4,4 0 0 1 4,-4
......@@ -143,7 +143,7 @@ exports[`BarChart component should render correctly 1`] = `
>
<defs>
<lineargradient
class="bar-ELECTRICITY undefined selected bounce-2 delay"
class="bar-ELECTRICITY selected bounce-2 delay"
id="gradient"
x1="0"
x2="0"
......@@ -161,7 +161,7 @@ exports[`BarChart component should render correctly 1`] = `
</lineargradient>
</defs>
<path
class="bar-ELECTRICITY undefined selected bounce-2 delay"
class="bar-ELECTRICITY selected bounce-2 delay"
d="
M0,4
a4,4 0 0 1 4,-4
......@@ -210,7 +210,7 @@ exports[`BarChart component should render correctly 1`] = `
</lineargradient>
</defs>
<path
class="bar-ELECTRICITY undefined selected bounce-2 delay"
class="bar-ELECTRICITY selected bounce-2 delay"
d="
M0,4
a4,4 0 0 1 4,-4
......@@ -226,7 +226,7 @@ exports[`BarChart component should render correctly 1`] = `
>
<defs>
<lineargradient
class="bar-ELECTRICITY undefined selected bounce-2 delay"
class="bar-ELECTRICITY selected bounce-2 delay"
id="gradient"
x1="0"
x2="0"
......@@ -244,7 +244,7 @@ exports[`BarChart component should render correctly 1`] = `
</lineargradient>
</defs>
<path
class="bar-ELECTRICITY undefined selected bounce-2 delay"
class="bar-ELECTRICITY selected bounce-2 delay"
d="
M0,4
a4,4 0 0 1 4,-4
......@@ -293,7 +293,7 @@ exports[`BarChart component should render correctly 1`] = `
</lineargradient>
</defs>
<path
class="bar-ELECTRICITY bar-UNCOMING undefined selected bounce-2 delay"
class="bar-ELECTRICITY bar-UNCOMING selected bounce-2 delay"
d="
M0,4
a4,4 0 0 1 4,-4
......
......@@ -72,20 +72,19 @@ interface StyledSwitchProps extends SwitchProps {
}
const StyledSwitch = ({ fluidType, ...props }: StyledSwitchProps) => {
if (fluidType !== undefined) {
switch (fluidType) {
case FluidType.ELECTRICITY:
return <SwitchElec {...props} />
case FluidType.WATER:
return <SwitchWater {...props} />
case FluidType.GAS:
return <SwitchGas {...props} />
default:
return <SwitchBase {...props} />
}
} else {
if (fluidType === undefined) {
return <SwitchBase {...props} />
}
switch (fluidType) {
case FluidType.ELECTRICITY:
return <SwitchElec {...props} />
case FluidType.WATER:
return <SwitchWater {...props} />
case FluidType.GAS:
return <SwitchGas {...props} />
default:
return <SwitchBase {...props} />
}
}
export default StyledSwitch
......@@ -34,10 +34,9 @@ const EpglForm = ({ hasCreatedAccount }: { hasCreatedAccount: boolean }) => {
})
const changeLogin = (value: string) => {
if ((/\d/.test(value) && value.length <= 7) || value === '') {
setError('')
setLogin(value)
}
if (value.toString().length > 7) return
setError('')
setLogin(value)
}
const changePassword = (value: string) => {
......
......@@ -3,7 +3,7 @@
.expired-consent-modal {
display: flex;
flex-direction: column;
gap: 1rem;
gap: 24px;
color: $grey-bright;
.icon-main {
......
......@@ -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: '',
......
......@@ -2,17 +2,20 @@
.grdfWait {
margin: auto;
margin-top: 16px;
display: flex;
flex-direction: column;
gap: 1rem;
gap: 16px;
align-items: center;
text-align: center;
padding-inline: 1rem;
max-width: 600px;
.green {
color: var(--gasColor);
color: $gas-color;
}
.emailContainer span {
.emailContainer {
color: $gold-shadow;
font-weight: 700;
}
......
import { Button } from '@material-ui/core'
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 { 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,7 +54,7 @@ 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>
......
......@@ -2,7 +2,8 @@ import { render, screen } from '@testing-library/react'
import React from 'react'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import { createMockEcolyoStore } from 'tests/__mocks__/store'
import { SgeStatusWithAccount } from 'tests/__mocks__/fluidStatusData.mock'
import { createMockEcolyoStore, mockGlobalState } from 'tests/__mocks__/store'
import SgeConnectView from './SgeConnectView'
jest.mock('components/Content/Content', () => 'mock-content')
......@@ -10,6 +11,13 @@ jest.mock('components/Header/CozyBar', () => 'mock-cozybar')
const store = createMockEcolyoStore()
const mockConnect = jest.fn()
const mockUpdate = jest.fn()
jest.mock('components/Hooks/useKonnectorAuth', () =>
jest.fn(() => [mockConnect, mockUpdate])
)
describe('SgeConnectView component', () => {
beforeEach(() => {
jest.clearAllMocks()
......@@ -46,4 +54,43 @@ describe('SgeConnectView component', () => {
expect(prevButton).toBeDisabled()
expect(nextButton).toBeDisabled()
})
describe('should test methods from useKonnectorAuth hook', () => {
it('should launch account and trigger creation process', async () => {
const store = createMockEcolyoStore({
global: {
...mockGlobalState,
sgeConnect: {
...mockGlobalState.sgeConnect,
shouldLaunchAccount: true,
},
},
})
render(
<Provider store={store}>
<SgeConnectView />
</Provider>
)
expect(mockConnect).toHaveBeenCalled()
})
it('should launch existing account update process', async () => {
const store = createMockEcolyoStore({
global: {
...mockGlobalState,
fluidStatus: [SgeStatusWithAccount],
sgeConnect: {
...mockGlobalState.sgeConnect,
shouldLaunchAccount: true,
},
},
})
render(
<Provider store={store}>
<SgeConnectView />
</Provider>
)
expect(mockUpdate).toHaveBeenCalled()
})
})
})
......@@ -3,11 +3,15 @@ import FormProgress from 'components/CommonKit/FormProgress/FormProgress'
import Content from 'components/Content/Content'
import CozyBar from 'components/Header/CozyBar'
import Header from 'components/Header/Header'
import { SgeStep } from 'enums'
import useKonnectorAuth from 'components/Hooks/useKonnectorAuth'
import { FluidType, SgeStep } from 'enums'
import { SgeStore } from 'models'
import React, { useCallback, useState } from 'react'
import { useNavigate } from 'react-router'
import { updateSgeStore } from 'store/global/global.slice'
import React, { useCallback, useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import {
setShouldRefreshConsent,
updateSgeStore,
} from 'store/global/global.slice'
import { useAppDispatch, useAppSelector } from 'store/hooks'
import '../connection.scss'
import StepAddress from './StepAddress'
......@@ -33,6 +37,29 @@ const SgeConnectView = () => {
const [currentStep, setCurrentStep] = useState<SgeStep>(
SgeStep.IdentityAndPDL
)
const { fluidStatus } = useAppSelector(state => state.ecolyo.global)
const currentFluidStatus = fluidStatus[FluidType.ELECTRICITY]
const account = currentFluidStatus.connection.account
const [connect, update] = useKonnectorAuth(FluidType.ELECTRICITY, {
sgeAuthData: sgeConnect,
})
useEffect(() => {
async function launchConnect() {
if (sgeConnect.shouldLaunchAccount) {
dispatch(updateSgeStore({ ...sgeConnect, shouldLaunchAccount: false }))
dispatch(setShouldRefreshConsent(false))
if (!account) {
await connect()
} else {
await update()
}
navigate('/consumption/electricity')
}
}
launchConnect()
}, [account, connect, dispatch, navigate, sgeConnect, update])
const isNextValid = useCallback(() => {
switch (currentStep) {
......@@ -81,9 +108,8 @@ const SgeConnectView = () => {
}
setCurrentSgeState(updatedState)
dispatch(updateSgeStore(updatedState))
navigate('/consumption/electricity')
}
}, [currentStep, isLoading, dispatch, currentSgeState, navigate])
}, [currentStep, isLoading, dispatch, currentSgeState])
const handlePrev = useCallback(() => {
if (currentStep !== SgeStep.IdentityAndPDL) {
......@@ -98,18 +124,13 @@ const SgeConnectView = () => {
value: string | boolean | number,
maxLength?: number
) => {
// TODO duplicate with Form login input
if (
!maxLength ||
value === '' ||
(/\d/.test(value.toString()) && value.toString().length <= maxLength)
) {
const updatedState = {
...currentSgeState,
[key]: value,
}
setCurrentSgeState(updatedState)
if (maxLength && value.toString().length > maxLength) return
const updatedState = {
...currentSgeState,
[key]: value,
}
setCurrentSgeState(updatedState)
},
[currentSgeState]
)
......
......@@ -2,8 +2,7 @@ import { render, screen } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'
import React from 'react'
import { Provider } from 'react-redux'
import { SgeStatusWithAccount } from 'tests/__mocks__/fluidStatusData.mock'
import { createMockEcolyoStore, mockGlobalState } from 'tests/__mocks__/store'
import { createMockEcolyoStore } from 'tests/__mocks__/store'
import SgeInit from './SgeInit'
const mockedNavigate = jest.fn()
......@@ -12,13 +11,6 @@ jest.mock('react-router-dom', () => ({
useNavigate: () => mockedNavigate,
}))
const mockConnect = jest.fn()
const mockUpdate = jest.fn()
jest.mock('components/Hooks/useKonnectorAuth', () =>
jest.fn(() => [mockConnect, mockUpdate])
)
describe('SgeInit component', () => {
const store = createMockEcolyoStore()
it('should be rendered correctly', () => {
......@@ -38,40 +30,4 @@ describe('SgeInit component', () => {
await userEvent.click(screen.getAllByRole('button')[0])
expect(mockedNavigate).toHaveBeenCalled()
})
it('should launch account and trigger creation process', async () => {
const store = createMockEcolyoStore({
global: {
...mockGlobalState,
sgeConnect: {
...mockGlobalState.sgeConnect,
shouldLaunchAccount: true,
},
},
})
render(
<Provider store={store}>
<SgeInit />
</Provider>
)
expect(mockConnect).toHaveBeenCalled()
})
it('should launch existing account update process', async () => {
const store = createMockEcolyoStore({
global: {
...mockGlobalState,
fluidStatus: [SgeStatusWithAccount],
sgeConnect: {
...mockGlobalState.sgeConnect,
shouldLaunchAccount: true,
},
},
})
render(
<Provider store={store}>
<SgeInit />
</Provider>
)
expect(mockUpdate).toHaveBeenCalled()
})
})
import { Button } from '@material-ui/core'
import ElectricityBillIcon from 'assets/icons/visu/onboarding/electricity_bill.svg'
import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
import useKonnectorAuth from 'components/Hooks/useKonnectorAuth'
import { useI18n } from 'cozy-ui/transpiled/react/I18n'
import { FluidType } from 'enums'
import React, { useEffect } from 'react'
import React from 'react'
import { useNavigate } from 'react-router-dom'
import { setShowOfflineData } from 'store/chart/chart.slice'
import {
setShouldRefreshConsent,
updateSgeStore,
} from 'store/global/global.slice'
import { useAppDispatch, useAppSelector } from 'store/hooks'
const SgeInit = () => {
......@@ -18,27 +13,7 @@ const SgeInit = () => {
const navigate = useNavigate()
const { fluidStatus } = useAppSelector(state => state.ecolyo.global)
const currentFluidStatus = fluidStatus[FluidType.ELECTRICITY]
const account = currentFluidStatus.connection.account
const { sgeConnect } = useAppSelector(state => state.ecolyo.global)
const dispatch = useAppDispatch()
const [connect, update] = useKonnectorAuth(FluidType.ELECTRICITY, {
sgeAuthData: sgeConnect,
})
useEffect(() => {
async function launchConnect() {
if (sgeConnect.shouldLaunchAccount) {
dispatch(updateSgeStore({ ...sgeConnect, shouldLaunchAccount: false }))
dispatch(setShouldRefreshConsent(false))
if (!account) {
await connect()
} else {
await update()
}
}
}
launchConnect()
}, [account, connect, dispatch, sgeConnect, update])
return (
<div className="connection-form">
......
......@@ -24,6 +24,7 @@ const StepAddress = ({ sgeState, onChange }: StepAddressProps) => {
name="address"
value={sgeState.address}
onChange={e => onChange('address', e.target.value)}
required
/>
<TextField
label={t('auth.enedissgegrandlyon.zipCode')}
......@@ -33,6 +34,7 @@ const StepAddress = ({ sgeState, onChange }: StepAddressProps) => {
name="zipCode"
value={sgeState.zipCode ?? undefined}
onChange={e => onChange('zipCode', e.target.value, 5)}
required
/>
<TextField
label={t('auth.enedissgegrandlyon.city')}
......@@ -42,6 +44,7 @@ const StepAddress = ({ sgeState, onChange }: StepAddressProps) => {
name="city"
value={sgeState.city}
onChange={e => onChange('city', e.target.value)}
required
/>
</div>
)
......
......@@ -14,12 +14,19 @@ exports[`StepAddress component should be rendered correctly 1`] = `
class="MuiFormControl-root MuiTextField-root"
>
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
data-shrink="false"
for="address"
id="address-label"
>
auth.enedissgegrandlyon.address
<span
aria-hidden="true"
class="MuiFormLabel-asterisk MuiInputLabel-asterisk"
>
*
</span>
</label>
<div
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
......@@ -29,6 +36,7 @@ exports[`StepAddress component should be rendered correctly 1`] = `
class="MuiInputBase-input MuiOutlinedInput-input"
id="address"
name="address"
required=""
type="text"
value=""
/>
......@@ -41,6 +49,7 @@ exports[`StepAddress component should be rendered correctly 1`] = `
>
<span>
auth.enedissgegrandlyon.address
*
</span>
</legend>
</fieldset>
......@@ -50,12 +59,19 @@ exports[`StepAddress component should be rendered correctly 1`] = `
class="MuiFormControl-root MuiTextField-root"
>
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
data-shrink="false"
for="zipCode"
id="zipCode-label"
>
auth.enedissgegrandlyon.zipCode
<span
aria-hidden="true"
class="MuiFormLabel-asterisk MuiInputLabel-asterisk"
>
*
</span>
</label>
<div
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
......@@ -65,6 +81,7 @@ exports[`StepAddress component should be rendered correctly 1`] = `
class="MuiInputBase-input MuiOutlinedInput-input"
id="zipCode"
name="zipCode"
required=""
type="number"
value=""
/>
......@@ -77,6 +94,7 @@ exports[`StepAddress component should be rendered correctly 1`] = `
>
<span>
auth.enedissgegrandlyon.zipCode
*
</span>
</legend>
</fieldset>
......@@ -86,12 +104,19 @@ exports[`StepAddress component should be rendered correctly 1`] = `
class="MuiFormControl-root MuiTextField-root"
>
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined Mui-required Mui-required"
data-shrink="false"
for="city"
id="city-label"
>
auth.enedissgegrandlyon.city
<span
aria-hidden="true"
class="MuiFormLabel-asterisk MuiInputLabel-asterisk"
>
*
</span>
</label>
<div
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
......@@ -101,6 +126,7 @@ exports[`StepAddress component should be rendered correctly 1`] = `
class="MuiInputBase-input MuiOutlinedInput-input"
id="city"
name="city"
required=""
type="text"
value=""
/>
......@@ -113,6 +139,7 @@ exports[`StepAddress component should be rendered correctly 1`] = `
>
<span>
auth.enedissgegrandlyon.city
*
</span>
</legend>
</fieldset>
......
......@@ -168,28 +168,62 @@ describe('ConsumptionView component', () => {
).toBeInTheDocument()
})
// todo describe and add multiple fluids ?
it('should render partner issue Modal', async () => {
const updatedStatus = mockInitialEcolyoState.global.fluidStatus
updatedStatus[0] = mockExpiredElec
const store = createMockEcolyoStore({
chart: mockChartStateShowOffline,
global: {
fluidStatus: updatedStatus,
releaseNotes: mockInitialEcolyoState.global.releaseNotes,
},
modal: {
...mockModalState,
partnersIssueModal: { enedis: false, grdf: false, egl: true },
},
describe('Partner Issue Modal', () => {
it('should render partner issue Modal for electricity', async () => {
const updatedStatus = mockInitialEcolyoState.global.fluidStatus
updatedStatus[0] = { ...updatedStatus[0], maintenance: true }
const store = createMockEcolyoStore({
chart: mockChartStateShowOffline,
global: {
fluidStatus: updatedStatus,
releaseNotes: mockInitialEcolyoState.global.releaseNotes,
},
modal: {
...mockModalState,
partnersIssueModal: { enedis: true, grdf: false, egl: false },
},
})
mockUpdateProfile.mockResolvedValue(mockTestProfile1)
const { container } = render(
<Provider store={store}>
<ConsumptionView fluidType={FluidType.ELECTRICITY} />
</Provider>
)
await waitFor(() => null, { container })
expect(
screen.getByRole('presentation', {
name: 'consumption.partner_issue_modal.accessibility_title',
})
).toBeInTheDocument()
})
it('should render partner issue Modal for water', async () => {
const updatedStatus = mockInitialEcolyoState.global.fluidStatus
updatedStatus[1] = { ...updatedStatus[1], maintenance: true }
const store = createMockEcolyoStore({
chart: mockChartStateShowOffline,
global: {
fluidStatus: updatedStatus,
releaseNotes: mockInitialEcolyoState.global.releaseNotes,
},
modal: {
...mockModalState,
partnersIssueModal: { enedis: false, grdf: false, egl: true },
},
})
mockUpdateProfile.mockResolvedValue(mockTestProfile1)
const { container } = render(
<Provider store={store}>
<ConsumptionView fluidType={FluidType.WATER} />
</Provider>
)
await waitFor(() => null, { container })
expect(
screen.getByRole('presentation', {
name: 'consumption.partner_issue_modal.accessibility_title',
})
).toBeInTheDocument()
})
mockUpdateProfile.mockResolvedValue(mockTestProfile1)
render(
<Provider store={store}>
<ConsumptionView fluidType={FluidType.ELECTRICITY} />
</Provider>
)
expect(screen.getByRole('dialog')).toBeInTheDocument()
})
it('should show expired modal when a GRDF consent is expired', () => {
const updatedStatus = mockInitialEcolyoState.global.fluidStatus
......
import ExpiredConsentModal from 'components/Connection/ExpiredConsentModal/ExpiredConsentModal'
import { GrdfWaitConsent } 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'
......@@ -38,36 +39,53 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => {
const navigate = useNavigate()
const client = useClient()
const dispatch = useAppDispatch()
const isMulti = fluidType === FluidType.MULTIFLUID
const {
chart: { currentTimeStep, showOfflineData, selectedDate, currentIndex },
global: { fluidStatus, releaseNotes },
modal: { partnersIssueModal, customPopupModal },
} = useAppSelector(state => state.ecolyo)
const isMulti = fluidType === FluidType.MULTIFLUID
const currentFluidStatus = fluidStatus[fluidType]
const dateChartService = new DateChartService()
/** Show wait consent screen when consent is "A valider" */
const isWaitingForConsent =
fluidType === FluidType.GAS &&
currentFluidStatus.status === FluidState.CHALLENGE_ASKED
const [openExpiredConsentModal, setOpenExpiredConsentModal] = useState(true)
const [openReleaseNoteModal, setOpenReleaseNoteModal] = useState<boolean>(
releaseNotes.show
)
const [openExpiredConsentModal, setOpenExpiredConsentModal] =
useState<boolean>(true)
const [consentExpiredFluids, setConsentExpiredFluids] = useState<FluidType[]>(
[]
)
const updateKey =
fluidType !== FluidType.MULTIFLUID && fluidStatus[fluidType].lastDataDate
? `${fluidStatus[fluidType].lastDataDate!.toLocaleString()} + ${
fluidStatus[fluidType].status + fluidType
!isMulti && currentFluidStatus.lastDataDate
? `${currentFluidStatus.lastDataDate.toLocaleString()} + ${
currentFluidStatus.status + fluidType
}`
: ''
const lastDataDateKey =
fluidType !== FluidType.MULTIFLUID && fluidStatus[fluidType].lastDataDate
? `${fluidStatus[fluidType].lastDataDate!.toLocaleString() + fluidType}`
!isMulti && 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,42 +100,26 @@ 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)
const profileValues = await profileService.getProfile()
if (profileValues) {
const updatedProfile = await profileService.updateProfile({
partnersIssueSeenDate: {
...profileValues.partnersIssueSeenDate,
[getPartnerKey(fluidType)]: getTodayDate(),
},
})
if (updatedProfile) {
dispatch(
openPartnersModal({
...partnersIssueModal,
[getPartnerKey(fluidType)]: false,
})
)
}
const handleClosePartnerIssueModal = useCallback(async () => {
const profileService = new ProfileService(client)
const profileValues = await profileService.getProfile()
if (profileValues) {
const updatedProfile = await profileService.updateProfile({
partnersIssueSeenDate: {
...profileValues.partnersIssueSeenDate,
[getPartnerKey(fluidType)]: getTodayDate(),
},
})
if (updatedProfile) {
dispatch(
openPartnersModal({
...partnersIssueModal,
[getPartnerKey(fluidType)]: false,
})
)
}
},
[client, dispatch, partnersIssueModal]
)
}
}, [client, dispatch, fluidType, partnersIssueModal])
const handleCloseCustomPopupModal = async () => {
const profileService = new ProfileService(client)
......@@ -134,15 +136,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 +158,10 @@ 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
for (const fluid of fluidStatus) {
const error = fluid.connection.triggerState?.last_error
if (error && getKonnectorUpdateError(error) === 'error_update_oauth') {
......@@ -220,38 +225,30 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => {
<Content>
<FluidButtons activeFluid={fluidType} key={updateKey} />
{openReleaseNoteModal && (
<ReleaseNotesModal
open={openReleaseNoteModal}
handleCloseClick={handleCloseReleaseNoteModal}
/>
)}
{showOfflineData && (
{isWaitingForConsent ? (
<GrdfWaitConsent />
) : (
<>
<FluidChart fluidType={fluidType} key={lastDataDateKey} />
<ConsumptionDetails fluidType={fluidType} />
{!isMulti && (
<KonnectorViewerCard
fluidType={fluidType}
showOfflineData={true}
key={fluidType}
/>
{showOfflineData && (
<>
<FluidChart fluidType={fluidType} key={lastDataDateKey} />
<ConsumptionDetails fluidType={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 +266,15 @@ 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)}
/>
))}
</>
)
}
......
import { render } from '@testing-library/react'
import { render, screen } from '@testing-library/react'
import { FluidState, FluidType } from 'enums'
import { GlobalState } from 'models'
import React from 'react'
......@@ -19,15 +19,14 @@ describe('FluidButton component', () => {
})
it('should render multifluidButton', () => {
const { container } = render(
render(
<Provider store={store}>
<FluidButton fluidType={FluidType.MULTIFLUID} isActive={false} />
</Provider>
)
const element = container.getElementsByClassName('multifluid').item(0)
expect(element).toBeInTheDocument()
expect(screen.getByText('FLUID.MULTIFLUID.LABEL')).toBeInTheDocument()
})
it('should render active button', () => {
it('should render active gas button', () => {
const { container } = render(
<Provider store={store}>
<FluidButton fluidType={FluidType.GAS} isActive={true} />
......
import { IconButton } from '@material-ui/core'
import ErrorNotif from 'assets/icons/ico/notif_error.svg'
import PartnerIssueNotif from 'assets/icons/ico/notif_maintenance.svg'
import classNames from 'classnames'
import StyledIcon from 'components/CommonKit/Icon/StyledIcon'
import { useI18n } from 'cozy-ui/transpiled/react/I18n'
import { FluidState, FluidType } from 'enums'
......@@ -20,50 +21,42 @@ const FluidButton = ({ fluidType, isActive }: FluidButtonProps) => {
const navigate = useNavigate()
const { fluidStatus } = useAppSelector(state => state.ecolyo.global)
const [showError, setShowError] = useState<boolean>(false)
const isMulti = fluidType === FluidType.MULTIFLUID
const fluidName = getFluidName(fluidType)
const isConnected = useCallback(() => {
if (fluidType === FluidType.MULTIFLUID) {
return true
} else return isKonnectorActive(fluidStatus, fluidType)
}, [fluidStatus, fluidType])
if (isMulti) return true
return isKonnectorActive(fluidStatus, fluidType)
}, [fluidStatus, fluidType, isMulti])
const isErrored = useCallback(() => {
if (
(fluidType !== FluidType.MULTIFLUID &&
fluidStatus[fluidType].status === FluidState.ERROR) ||
(!isMulti && fluidStatus[fluidType].status === FluidState.ERROR) ||
(fluidType !== FluidType.WATER &&
fluidStatus[fluidType].status === FluidState.ERROR_LOGIN_FAILED)
fluidStatus[fluidType].status === FluidState.LOGIN_FAILED)
) {
return true
}
return false
}, [fluidStatus, fluidType])
}, [fluidStatus, fluidType, isMulti])
const iconType = getNavPicto(fluidType, isActive, isConnected())
const goToFluid = useCallback(async () => {
navigate(
fluidType === FluidType.MULTIFLUID
? '/consumption'
: `/consumption/${getFluidName(fluidType)}`
)
}, [navigate, fluidType])
navigate(isMulti ? '/consumption' : `/consumption/${fluidName}`)
}, [navigate, isMulti, fluidName])
const isFluidMaintenance = () => fluidStatus[fluidType]?.maintenance
useEffect(() => {
// Show errors only on connected konnectors that are in error, outdated, with no data (specific case), and not in multifluid
if (fluidType !== FluidType.MULTIFLUID && isConnected() && isErrored()) {
if (!isMulti && isConnected() && isErrored()) {
setShowError(true)
}
}, [fluidStatus, fluidType, isConnected, isErrored])
}, [fluidStatus, fluidType, isConnected, isErrored, isMulti])
return (
<IconButton
className={`fluid-title fluid-button ${FluidType[
fluidType
].toLowerCase()}`}
onClick={goToFluid}
>
<IconButton className="fluid-title fluid-button" onClick={goToFluid}>
<StyledIcon
className="fluid-icon"
icon={iconType}
......@@ -81,11 +74,11 @@ const FluidButton = ({ fluidType, isActive }: FluidButtonProps) => {
)
)}
<div
className={`fluid-title ${getFluidName(fluidType)} ${
isActive && 'active'
} text-14-normal`}
className={classNames('fluid-title text-14-normal', {
active: isActive,
})}
>
{t(`FLUID.${FluidType[fluidType]}.LABEL`)}
{t(`FLUID.${fluidName.toLocaleUpperCase()}.LABEL`)}
</div>
</IconButton>
)
......
......@@ -3,7 +3,7 @@
exports[`FluidButton component should be rendered correctly 1`] = `
<div>
<button
class="MuiButtonBase-root MuiIconButton-root fluid-title fluid-button electricity"
class="MuiButtonBase-root MuiIconButton-root fluid-title fluid-button"
tabindex="0"
type="button"
>
......@@ -21,7 +21,7 @@ exports[`FluidButton component should be rendered correctly 1`] = `
/>
</svg>
<div
class="fluid-title electricity false text-14-normal"
class="fluid-title text-14-normal"
>
FLUID.ELECTRICITY.LABEL
</div>
......
......@@ -9,7 +9,7 @@ exports[`FluidButtons component should be rendered correctly 1`] = `
class="content"
>
<button
class="MuiButtonBase-root MuiIconButton-root fluid-title fluid-button multifluid"
class="MuiButtonBase-root MuiIconButton-root fluid-title fluid-button"
tabindex="0"
type="button"
>
......@@ -27,7 +27,7 @@ exports[`FluidButtons component should be rendered correctly 1`] = `
/>
</svg>
<div
class="fluid-title multifluid false text-14-normal"
class="fluid-title text-14-normal"
>
FLUID.MULTIFLUID.LABEL
</div>
......@@ -37,7 +37,7 @@ exports[`FluidButtons component should be rendered correctly 1`] = `
/>
</button>
<button
class="MuiButtonBase-root MuiIconButton-root fluid-title fluid-button electricity"
class="MuiButtonBase-root MuiIconButton-root fluid-title fluid-button"
tabindex="0"
type="button"
>
......@@ -55,7 +55,7 @@ exports[`FluidButtons component should be rendered correctly 1`] = `
/>
</svg>
<div
class="fluid-title electricity active text-14-normal"
class="fluid-title text-14-normal active"
>
FLUID.ELECTRICITY.LABEL
</div>
......@@ -65,7 +65,7 @@ exports[`FluidButtons component should be rendered correctly 1`] = `
/>
</button>
<button
class="MuiButtonBase-root MuiIconButton-root fluid-title fluid-button water"
class="MuiButtonBase-root MuiIconButton-root fluid-title fluid-button"
tabindex="0"
type="button"
>
......@@ -83,7 +83,7 @@ exports[`FluidButtons component should be rendered correctly 1`] = `
/>
</svg>
<div
class="fluid-title water false text-14-normal"
class="fluid-title text-14-normal"
>
FLUID.WATER.LABEL
</div>
......@@ -93,7 +93,7 @@ exports[`FluidButtons component should be rendered correctly 1`] = `
/>
</button>
<button
class="MuiButtonBase-root MuiIconButton-root fluid-title fluid-button gas"
class="MuiButtonBase-root MuiIconButton-root fluid-title fluid-button"
tabindex="0"
type="button"
>
......@@ -111,7 +111,7 @@ exports[`FluidButtons component should be rendered correctly 1`] = `
/>
</svg>
<div
class="fluid-title gas false text-14-normal"
class="fluid-title text-14-normal"
>
FLUID.GAS.LABEL
</div>
......
......@@ -5,6 +5,7 @@ import { Dataload } from 'models'
import React, { useCallback } from 'react'
import { setShowConnectionDetails } from 'store/chart/chart.slice'
import { useAppDispatch } from 'store/hooks'
import { getFluidName } from 'utils/utils'
import './consumptionVisualizer.scss'
interface DataloadNoValueProps {
......@@ -15,6 +16,7 @@ interface DataloadNoValueProps {
const DataloadNoValue = ({ dataload, fluidType }: DataloadNoValueProps) => {
const { t } = useI18n()
const dispatch = useAppDispatch()
const fluidName = getFluidName(fluidType)
const handleToggleKonnectorCard = useCallback(() => {
dispatch(setShowConnectionDetails(true))
......@@ -45,11 +47,7 @@ const DataloadNoValue = ({ dataload, fluidType }: DataloadNoValueProps) => {
return (
<div className="dataloadvisualizer-content text-22-normal">
<div className="dataloadvisualizer-section">
<div
className={`dataloadvisualizer-value ${FluidType[
fluidType
].toLowerCase()} upper`}
>
<div className={`dataloadvisualizer-value ${fluidName} upper`}>
{t('consumption_visualizer.no_data')}
</div>
</div>
......@@ -74,11 +72,7 @@ const DataloadNoValue = ({ dataload, fluidType }: DataloadNoValueProps) => {
return (
<div className="dataloadvisualizer-content text-22-normal">
<div className="dataloadvisualizer-section">
<div
className={`dataloadvisualizer-value ${FluidType[
fluidType
].toLowerCase()} upper to-come`}
>
<div className={`dataloadvisualizer-value ${fluidName} upper to-come`}>
{t('consumption_visualizer.data_to_come')}
</div>
</div>
......