diff --git a/src/components/Ecogesture/EcogestureView.tsx b/src/components/Ecogesture/EcogestureView.tsx index 45179a9baba4bb8093c0e25337bbf2d9db20bdb7..d34d84ef85b811daa0666b6793470ce9b8568a5a 100644 --- a/src/components/Ecogesture/EcogestureView.tsx +++ b/src/components/Ecogesture/EcogestureView.tsx @@ -109,7 +109,6 @@ const EcogestureView: React.FC = () => { const handleChange = useCallback( (event: React.ChangeEvent<object>, newValue: number) => { event.preventDefault() - console.log(event) const params = new URLSearchParams() params.append('tab', newValue.toString()) navigate({ search: params.toString() }) diff --git a/src/components/EcogestureForm/EcogestureFormEquipment.spec.tsx b/src/components/EcogestureForm/EcogestureFormEquipment.spec.tsx index 714c6ee62f1f2d6aaf04dea7fcaaf757dab9ace4..d1e633a61649020325f81e7f25198d55309ddf44 100644 --- a/src/components/EcogestureForm/EcogestureFormEquipment.spec.tsx +++ b/src/components/EcogestureForm/EcogestureFormEquipment.spec.tsx @@ -23,6 +23,7 @@ jest.mock('cozy-ui/transpiled/react/I18n', () => { }) const mockStore = configureStore([]) const mockSetPreviousStep = jest.fn() +const mockSetNextStep = jest.fn() const useSelectorSpy = jest.spyOn(reactRedux, 'useSelector') const mockHistoryPush = jest.fn() @@ -33,7 +34,6 @@ jest.mock('react-router-dom', () => ({ }), })) jest.mock('./EquipmentIcon', () => 'mock-equipment-icon') -const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') describe('EcogestureFormEquipment component', () => { useSelectorSpy.mockReturnValue({ @@ -50,9 +50,9 @@ describe('EcogestureFormEquipment component', () => { const wrapper = mount( <Provider store={store}> <EcogestureFormEquipment - profileEcogesture={mockProfileEcogesture} + currentProfileEcogesture={mockProfileEcogesture} setPreviousStep={mockSetPreviousStep} - setNextStep={jest.fn()} + setNextStepEcogestureForm={mockSetNextStep} step={0} /> </Provider> @@ -60,29 +60,7 @@ describe('EcogestureFormEquipment component', () => { await waitForComponentToPaint(wrapper) expect(toJson(wrapper)).toMatchSnapshot() }) - it('should finish the form', async () => { - const store = mockStore({ - ecolyo: { - global: globalStateData, - challenge: challengeStateData, - }, - }) - mockUseDispatch.mockReturnValue(jest.fn()) - const wrapper = mount( - <Provider store={store}> - <EcogestureFormEquipment - profileEcogesture={mockProfileEcogesture} - setPreviousStep={mockSetPreviousStep} - setNextStep={jest.fn()} - step={0} - /> - </Provider> - ) - await waitForComponentToPaint(wrapper) - wrapper.find(Button).at(1).simulate('click') - expect(mockUseDispatch).toHaveBeenCalledTimes(2) - }) it('should select equipment and unselect it', async () => { const store = mockStore({ ecolyo: { @@ -94,9 +72,9 @@ describe('EcogestureFormEquipment component', () => { const wrapper = mount( <Provider store={store}> <EcogestureFormEquipment - profileEcogesture={mockProfileEcogesture} + currentProfileEcogesture={mockProfileEcogesture} setPreviousStep={mockSetPreviousStep} - setNextStep={jest.fn()} + setNextStepEcogestureForm={mockSetNextStep} step={0} /> </Provider> @@ -121,9 +99,9 @@ describe('EcogestureFormEquipment component', () => { const wrapper = mount( <Provider store={store}> <EcogestureFormEquipment - profileEcogesture={mockProfileEcogesture} + currentProfileEcogesture={mockProfileEcogesture} setPreviousStep={mockSetPreviousStep} - setNextStep={jest.fn()} + setNextStepEcogestureForm={mockSetNextStep} step={0} /> </Provider> diff --git a/src/components/EcogestureForm/EcogestureFormEquipment.tsx b/src/components/EcogestureForm/EcogestureFormEquipment.tsx index 841883c608e0404344e2d41346e8206715a02d29..39f6dd56a825df1276da55a3ae6cba88dca0f692 100644 --- a/src/components/EcogestureForm/EcogestureFormEquipment.tsx +++ b/src/components/EcogestureForm/EcogestureFormEquipment.tsx @@ -5,72 +5,70 @@ import { EquipmentType } from 'enum/ecogesture.enum' import { EcogestureStepForm } from 'enum/ecogestureForm.enum' import { ProfileTypeStepForm } from 'enum/profileType.enum' import { ProfileEcogesture, ProfileType } from 'models' -import React, { Dispatch, useCallback, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { useNavigate } from 'react-router-dom' -import { AppActionsTypes, AppStore } from 'store' -import { updateProfile } from 'store/profile/profile.actions' -import { - newProfileEcogestureEntry, - updateProfileEcogesture, -} from 'store/profileEcogesture/profileEcogesture.actions' +import React, { useCallback, useState } from 'react' +import { useSelector } from 'react-redux' +import { AppStore } from 'store' import EquipmentIcon from './EquipmentIcon' import './ecogestureFormEquipment.scss' interface EcogestureFormEquipmentProps { - profileEcogesture: ProfileEcogesture - setPreviousStep: (_profileEcogesture: ProfileEcogesture) => void - setNextStep?: (_profileType?: ProfileType) => void + currentProfileEcogesture?: ProfileEcogesture + currentProfileType?: ProfileType + setPreviousStep: () => void + setNextStepEcogestureForm?: (_profileEcogesture: ProfileEcogesture) => void + setNextStepProfileForm?: (_profileType: ProfileType) => void step: ProfileTypeStepForm | EcogestureStepForm } const EcogestureFormEquipment: React.FC<EcogestureFormEquipmentProps> = ({ - profileEcogesture, + currentProfileEcogesture, + currentProfileType, setPreviousStep, - setNextStep, + setNextStepEcogestureForm, + setNextStepProfileForm, step, }: EcogestureFormEquipmentProps) => { const { t } = useI18n() - const dispatch: Dispatch<AppActionsTypes> = useDispatch() - const navigate = useNavigate() const { isProfileEcogestureCompleted } = useSelector( (state: AppStore) => state.ecolyo.profile ) - const [answer, setAnswer] = useState<string[]>( - isProfileEcogestureCompleted ? profileEcogesture.equipments : [] + const previousEquipments = + currentProfileType?.equipments || currentProfileEcogesture?.equipments || [] + const [answer, setAnswer] = useState<EquipmentType[]>( + isProfileEcogestureCompleted ? previousEquipments : [] ) const handlePrevious = useCallback(() => { - setPreviousStep(profileEcogesture) - }, [profileEcogesture, setPreviousStep]) + setPreviousStep() + }, [setPreviousStep]) const handleNext = useCallback(() => { - profileEcogesture.equipments = answer as EquipmentType[] - // Check if gestureForm is used from Big profile or small profile - if (setNextStep) { - setNextStep() - dispatch(updateProfileEcogesture(profileEcogesture)) - } else { - dispatch(newProfileEcogestureEntry(profileEcogesture)) - dispatch(updateProfile({ isProfileEcogestureCompleted: true })) - navigate('/ecogesture-selection') + if (setNextStepProfileForm && currentProfileType) { + setNextStepProfileForm({ + ...currentProfileType, + equipments: answer, + }) + } else if (setNextStepEcogestureForm && currentProfileEcogesture) { + setNextStepEcogestureForm({ + ...currentProfileEcogesture, + equipments: answer, + }) } - }, [profileEcogesture, setNextStep, answer, dispatch, navigate]) + }, [ + setNextStepProfileForm, + currentProfileType, + setNextStepEcogestureForm, + currentProfileEcogesture, + answer, + ]) - const isChecked = useCallback( - (value: string): boolean => { - if (answer.includes(value)) { - return true - } else { - return false - } - }, - [answer] - ) + const isChecked = (value: EquipmentType): boolean => { + return answer.includes(value) + } const handleChange = useCallback( - (value: string) => { - const tempAnswer: string[] = [...answer] + (value: EquipmentType) => { + const tempAnswer = [...answer] if (tempAnswer.includes(value)) { const index = tempAnswer.indexOf(value) if (index > -1) tempAnswer.splice(index, 1) diff --git a/src/components/EcogestureForm/EcogestureFormSingleChoice.spec.tsx b/src/components/EcogestureForm/EcogestureFormSingleChoice.spec.tsx index 1f961c4a03fcb1dbd74113a1d55ba8b9c1c8f734..1b6515c429eb7c8dbf22cd7520635df02720b308 100644 --- a/src/components/EcogestureForm/EcogestureFormSingleChoice.spec.tsx +++ b/src/components/EcogestureForm/EcogestureFormSingleChoice.spec.tsx @@ -3,6 +3,7 @@ import { Button } from '@material-ui/core' import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import React from 'react' +import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import { mockEcogestureAnswer, @@ -12,6 +13,8 @@ import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import { waitForComponentToPaint } from '../../../tests/__mocks__/testUtils' import EcogestureFormSingleChoice from './EcogestureFormSingleChoice' +const useSelectorSpy = jest.spyOn(reactRedux, 'useSelector') + jest.mock('cozy-ui/transpiled/react/I18n', () => { return { useI18n: jest.fn(() => { @@ -48,7 +51,7 @@ describe('EcogestureFormSingleChoice component', () => { viewedStep={1} setNextStep={mockHandleNextStep} setPreviousStep={mockHandlePreviousStep} - profileEcogesture={mockProfileEcogesture} + currentProfileEcogesture={mockProfileEcogesture} answerType={mockEcogestureAnswer} /> </Provider> @@ -64,7 +67,7 @@ describe('EcogestureFormSingleChoice component', () => { viewedStep={1} setNextStep={mockHandleNextStep} setPreviousStep={mockHandlePreviousStep} - profileEcogesture={mockProfileEcogesture} + currentProfileEcogesture={mockProfileEcogesture} answerType={mockEcogestureAnswer} /> </Provider> @@ -82,7 +85,7 @@ describe('EcogestureFormSingleChoice component', () => { viewedStep={1} setNextStep={mockHandleNextStep} setPreviousStep={mockHandlePreviousStep} - profileEcogesture={mockProfileEcogesture} + currentProfileEcogesture={mockProfileEcogesture} answerType={mockEcogestureAnswer} /> </Provider> @@ -102,7 +105,7 @@ describe('EcogestureFormSingleChoice component', () => { viewedStep={0} setNextStep={mockHandleNextStep} setPreviousStep={mockHandlePreviousStep} - profileEcogesture={mockProfileEcogesture} + currentProfileEcogesture={mockProfileEcogesture} answerType={mockEcogestureAnswer} /> </Provider> @@ -112,6 +115,11 @@ describe('EcogestureFormSingleChoice component', () => { expect(mockHandlePreviousStep).toHaveBeenCalledTimes(1) }) it('should keep previous answer', async () => { + useSelectorSpy.mockReturnValue({ + profile: { + isProfileEcogestureCompleted: true, + }, + }) const wrapper = mount( <Provider store={store}> <EcogestureFormSingleChoice @@ -119,7 +127,7 @@ describe('EcogestureFormSingleChoice component', () => { viewedStep={1} setNextStep={mockHandleNextStep} setPreviousStep={mockHandlePreviousStep} - profileEcogesture={mockProfileEcogesture} + currentProfileEcogesture={mockProfileEcogesture} answerType={mockEcogestureAnswer} /> </Provider> diff --git a/src/components/EcogestureForm/EcogestureFormSingleChoice.tsx b/src/components/EcogestureForm/EcogestureFormSingleChoice.tsx index be00ef2afd0dcb148cdc2bbe0bc3cbcd106c7550..81d52efc34fa68bb6af730cc57dcaa06ad426f05 100644 --- a/src/components/EcogestureForm/EcogestureFormSingleChoice.tsx +++ b/src/components/EcogestureForm/EcogestureFormSingleChoice.tsx @@ -6,25 +6,25 @@ import { EcogestureStepForm } from 'enum/ecogestureForm.enum' import { ProfileEcogesture, ProfileEcogestureAnswer, - ProfileEcogestureAnswerChoices, + ProfileEcogestureValues, } from 'models/profileEcogesture.model' -import React, { useCallback, useEffect, useState } from 'react' +import React, { useCallback, useState } from 'react' import { useSelector } from 'react-redux' import { AppStore } from 'store' import './ecogestureFormSingleChoice.scss' interface EcogestureFormSingleChoiceProps { step: EcogestureStepForm viewedStep: EcogestureStepForm - profileEcogesture: ProfileEcogesture + currentProfileEcogesture: ProfileEcogesture answerType: ProfileEcogestureAnswer setNextStep: (_profileEcogesture: ProfileEcogesture) => void - setPreviousStep: (_profileEcogesture: ProfileEcogesture) => void + setPreviousStep: () => void } const EcogestureFormSingleChoice: React.FC<EcogestureFormSingleChoiceProps> = ({ step, viewedStep, - profileEcogesture, + currentProfileEcogesture, answerType, setNextStep, setPreviousStep, @@ -33,28 +33,19 @@ const EcogestureFormSingleChoice: React.FC<EcogestureFormSingleChoiceProps> = ({ const { isProfileEcogestureCompleted } = useSelector( (state: AppStore) => state.ecolyo.profile ) - const [answer, setAnswer] = useState<ProfileEcogestureAnswerChoices>( - isProfileEcogestureCompleted ? profileEcogesture[answerType.attribute] : '' + const [answer, setAnswer] = useState<ProfileEcogestureValues>( + isProfileEcogestureCompleted || step < viewedStep + ? currentProfileEcogesture[answerType.attribute] + : null ) const handlePrevious = useCallback(() => { - setPreviousStep(profileEcogesture) - }, [profileEcogesture, setPreviousStep]) - - const handleAnswer = useCallback((value: ProfileEcogestureAnswerChoices) => { - setAnswer(value) - }, []) + setPreviousStep() + }, [setPreviousStep]) const handleNext = useCallback(() => { - profileEcogesture[answerType.attribute] = answer - setNextStep(profileEcogesture) - }, [profileEcogesture, setNextStep, answer, answerType.attribute]) - - useEffect(() => { - if (step < viewedStep) { - setAnswer(profileEcogesture[answerType.attribute]) - } - }, [step, viewedStep, profileEcogesture, answerType]) + setNextStep({ ...currentProfileEcogesture, [answerType.attribute]: answer }) + }, [currentProfileEcogesture, setNextStep, answer, answerType.attribute]) return ( <div className="ecogesture-profile-container"> @@ -64,40 +55,39 @@ const EcogestureFormSingleChoice: React.FC<EcogestureFormSingleChoiceProps> = ({ `ecogesture_form.${EcogestureStepForm[step].toLowerCase()}.question` )} </div> - {answerType.choices.map( - (value: ProfileEcogestureAnswerChoices, index: number) => { - return value || value === 0 ? ( - <label - key={index} - className={classNames({ - ['radio_short']: answerType.choices.length < 5, - ['radio_long']: answerType.choices.length > 4, - ['answer-checked']: answer === value, - })} - > - <input - type={'radio'} - value={value} - name={value.toString()} - onChange={() => handleAnswer(value)} - checked={answer === value ? true : false} - className={answer === value ? 'checked-input' : ''} - /> - {t( - `ecogesture_form.${EcogestureStepForm[ - step - ].toLowerCase()}.${value}` - )} - </label> - ) : null - } - )} + {answerType.choices.map(value => { + if (!value) return null + return ( + <label + key={value.toString()} + className={classNames({ + ['radio_short']: answerType.choices.length < 5, + ['radio_long']: answerType.choices.length > 4, + ['answer-checked']: answer === value, + })} + > + <input + type={'radio'} + value={value} + name={value.toString()} + onChange={() => setAnswer(value)} + checked={answer === value} + className={answer === value ? 'checked-input' : ''} + /> + {t( + `ecogesture_form.${EcogestureStepForm[ + step + ].toLowerCase()}.${value}` + )} + </label> + ) + })} </div> <FormNavigation step={step} handlePrevious={handlePrevious} handleNext={handleNext} - disableNextButton={answer === ''} + disableNextButton={answer === null} isEcogesture={true} /> </div> diff --git a/src/components/EcogestureForm/EcogestureFormView.spec.tsx b/src/components/EcogestureForm/EcogestureFormView.spec.tsx index d8588e3e67171fccb5620bbee49c33052cacb65d..78474a832ea8baadda173e0f28f88adbf309b580 100644 --- a/src/components/EcogestureForm/EcogestureFormView.spec.tsx +++ b/src/components/EcogestureForm/EcogestureFormView.spec.tsx @@ -4,6 +4,7 @@ import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import { Profile } from 'models' import React from 'react' +import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import { createMockEcolyoStore, @@ -28,6 +29,7 @@ jest.mock( 'components/EcogestureForm/EcogestureLaunchFormModal', () => 'mock-ecogesturelaunchmodal' ) +const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') jest.mock('components/Content/Content', () => 'mock-content') const mockedNavigate = jest.fn() jest.mock('react-router-dom', () => ({ @@ -44,6 +46,7 @@ describe('EcogestureFormView component', () => { const store = createMockEcolyoStore() beforeEach(() => { store.clearActions() + mockUseDispatch.mockClear() }) it('should be rendered correctly', async () => { @@ -107,4 +110,19 @@ describe('EcogestureFormView component', () => { wrapper.find(Button).first().simulate('click') expect(wrapper.find('.ecogesture-form-single').exists()).toBeTruthy() }) + + it('should handle form end', async () => { + mockUseDispatch.mockReturnValue(jest.fn()) + jest + .spyOn(React, 'useState') + .mockImplementationOnce(() => [0, () => null]) + .mockImplementationOnce(() => [4, () => null]) + const wrapper = mount( + <Provider store={store}> + <EcogestureFormView /> + </Provider> + ) + await waitForComponentToPaint(wrapper) + expect(mockUseDispatch).toHaveBeenCalledTimes(2) + }) }) diff --git a/src/components/EcogestureForm/EcogestureFormView.tsx b/src/components/EcogestureForm/EcogestureFormView.tsx index b2fbe1779d04ecc9f6135e2fa64572d20df9dc0a..a92a6a2688eef625612a21211d50cc6e39563660 100644 --- a/src/components/EcogestureForm/EcogestureFormView.tsx +++ b/src/components/EcogestureForm/EcogestureFormView.tsx @@ -14,21 +14,25 @@ import { ProfileEcogesture, ProfileEcogestureAnswer, } from 'models/profileEcogesture.model' -import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' -import { useLocation } from 'react-router-dom' +import React, { Dispatch, useCallback, useEffect, useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { useLocation, useNavigate } from 'react-router-dom' import ProfileEcogestureFormService from 'services/profileEcogestureForm.service' -import { AppStore } from 'store' +import { AppActionsTypes, AppStore } from 'store' +import { updateProfile } from 'store/profile/profile.actions' +import { newProfileEcogestureEntry } from 'store/profileEcogesture/profileEcogesture.actions' const EcogestureFormView: React.FC = () => { + const dispatch: Dispatch<AppActionsTypes> = useDispatch() + const { + profile: { isProfileTypeCompleted }, + profileEcogesture, + } = useSelector((state: AppStore) => state.ecolyo) + const navigate = useNavigate() const [headerHeight, setHeaderHeight] = useState<number>(0) const defineHeaderHeight = (height: number) => { setHeaderHeight(height) } - const { - profile: { isProfileTypeCompleted }, - profileEcogesture: curProfileEcogesture, - } = useSelector((state: AppStore) => state.ecolyo) const shouldOpenModal = new URLSearchParams(useLocation().search).get('modal') const [step, setStep] = useState<EcogestureStepForm>( @@ -36,7 +40,7 @@ const EcogestureFormView: React.FC = () => { ) const [answerType, setAnswerType] = useState<ProfileEcogestureAnswer>({ type: ProfileEcogestureAnswerType.SINGLE_CHOICE, - attribute: '', + attribute: 'heating', choices: [], }) @@ -45,44 +49,50 @@ const EcogestureFormView: React.FC = () => { shouldOpenModal !== 'false' ? true : false ) const [viewedStep, setViewedStep] = useState<number>(-1) - const [profileEcogesture, setProfileEcogesture] = - useState<ProfileEcogesture>(curProfileEcogesture) + const [currentProfileEcogesture, setCurrentProfileEcogesture] = + useState<ProfileEcogesture>(profileEcogesture) const setNextStep = useCallback( (_profileEcogesture: ProfileEcogesture) => { - setProfileEcogesture(_profileEcogesture) + setCurrentProfileEcogesture(_profileEcogesture) const pefs = new ProfileEcogestureFormService(_profileEcogesture) - const nextStep: EcogestureStepForm = pefs.getNextFormStep(step) + const nextStep = pefs.getNextFormStep(step) setIsLoading(true) if (nextStep > viewedStep) { setViewedStep(nextStep) } - const _answerType: ProfileEcogestureAnswer = + const _answerType = ProfileEcogestureFormService.getAnswerForStep(nextStep) setAnswerType(_answerType) setStep(nextStep) }, [step, viewedStep] ) - const setPreviousStep = useCallback( - (_profileEcogesture: ProfileEcogesture) => { - setProfileEcogesture(_profileEcogesture) - const pefs = new ProfileEcogestureFormService(_profileEcogesture) - const previousStep: EcogestureStepForm = pefs.getPreviousFormStep(step) - setIsLoading(true) - const _answerType: ProfileEcogestureAnswer = - ProfileEcogestureFormService.getAnswerForStep(previousStep) - setAnswerType(_answerType) - setStep(previousStep) - }, - [step] - ) + const setPreviousStep = useCallback(() => { + const pefs = new ProfileEcogestureFormService(currentProfileEcogesture) + const previousStep = pefs.getPreviousFormStep(step) + setIsLoading(true) + const _answerType = + ProfileEcogestureFormService.getAnswerForStep(previousStep) + setAnswerType(_answerType) + setStep(previousStep) + }, [currentProfileEcogesture, step]) + + const handleEndForm = useCallback(() => { + dispatch(newProfileEcogestureEntry(currentProfileEcogesture)) + dispatch(updateProfile({ isProfileEcogestureCompleted: true })) + navigate('/ecogesture-selection') + }, [currentProfileEcogesture, dispatch, navigate]) useEffect(() => { - const _answerType: ProfileEcogestureAnswer = - ProfileEcogestureFormService.getAnswerForStep(step) + const _answerType = ProfileEcogestureFormService.getAnswerForStep(step) setAnswerType(_answerType) setIsLoading(false) - }, [step]) + + // handle end of form + if (step === EcogestureStepForm.END) { + handleEndForm() + } + }, [handleEndForm, step]) if (isLoading) { return ( @@ -106,7 +116,8 @@ const EcogestureFormView: React.FC = () => { {step === EcogestureStepForm.EQUIPMENTS && ( <EcogestureFormEquipment step={EcogestureStepForm.EQUIPMENTS} - profileEcogesture={profileEcogesture} + currentProfileEcogesture={currentProfileEcogesture} + setNextStepEcogestureForm={setNextStep} setPreviousStep={setPreviousStep} /> )} @@ -114,7 +125,7 @@ const EcogestureFormView: React.FC = () => { <EcogestureFormSingleChoice step={step} viewedStep={viewedStep} - profileEcogesture={profileEcogesture} + currentProfileEcogesture={currentProfileEcogesture} answerType={answerType} setNextStep={setNextStep} setPreviousStep={setPreviousStep} diff --git a/src/components/EcogestureForm/__snapshots__/EcogestureFormEquipment.spec.tsx.snap b/src/components/EcogestureForm/__snapshots__/EcogestureFormEquipment.spec.tsx.snap index 3d438e9efa10df387fc2ea4872131b93bc90c85a..541a06390317a40266289902a1be72a383bcfa3b 100644 --- a/src/components/EcogestureForm/__snapshots__/EcogestureFormEquipment.spec.tsx.snap +++ b/src/components/EcogestureForm/__snapshots__/EcogestureFormEquipment.spec.tsx.snap @@ -14,7 +14,7 @@ exports[`EcogestureFormEquipment component should be rendered correctly 1`] = ` } > <EcogestureFormEquipment - profileEcogesture={ + currentProfileEcogesture={ Object { "equipments": Array [], "heating": "individual", @@ -22,7 +22,7 @@ exports[`EcogestureFormEquipment component should be rendered correctly 1`] = ` "warmingFluid": 0, } } - setNextStep={[MockFunction]} + setNextStepEcogestureForm={[MockFunction]} setPreviousStep={[MockFunction]} step={0} > diff --git a/src/components/EcogestureForm/__snapshots__/EcogestureFormSingleChoice.spec.tsx.snap b/src/components/EcogestureForm/__snapshots__/EcogestureFormSingleChoice.spec.tsx.snap index 8dfe3731ddfe10627e288544994e65b4ae2fdd5a..3fc0bc2066d0d83a06be85c5e7e8d8c7b2c3154d 100644 --- a/src/components/EcogestureForm/__snapshots__/EcogestureFormSingleChoice.spec.tsx.snap +++ b/src/components/EcogestureForm/__snapshots__/EcogestureFormSingleChoice.spec.tsx.snap @@ -24,7 +24,7 @@ exports[`EcogestureFormSingleChoice component should be rendered correctly 1`] = "type": 0, } } - profileEcogesture={ + currentProfileEcogesture={ Object { "equipments": Array [], "heating": "individual", @@ -50,7 +50,7 @@ exports[`EcogestureFormSingleChoice component should be rendered correctly 1`] = </div> <label className="radio_short answer-checked" - key="0" + key="individual" > <input checked={true} @@ -64,7 +64,7 @@ exports[`EcogestureFormSingleChoice component should be rendered correctly 1`] = </label> <label className="radio_short" - key="1" + key="collective" > <input checked={false} diff --git a/src/components/EcogestureForm/__snapshots__/EcogestureFormView.spec.tsx.snap b/src/components/EcogestureForm/__snapshots__/EcogestureFormView.spec.tsx.snap index 05718102ded39cce7cef10b601ffc5083d50b91f..304c1fdddbaa8721f125a341cc1ab6837f54a958 100644 --- a/src/components/EcogestureForm/__snapshots__/EcogestureFormView.spec.tsx.snap +++ b/src/components/EcogestureForm/__snapshots__/EcogestureFormView.spec.tsx.snap @@ -35,7 +35,7 @@ exports[`EcogestureFormView component should be rendered correctly 1`] = ` "type": 0, } } - profileEcogesture={ + currentProfileEcogesture={ Object { "equipments": Array [], "heating": "individual", @@ -61,7 +61,7 @@ exports[`EcogestureFormView component should be rendered correctly 1`] = ` </div> <label className="radio_short" - key="0" + key="individual" > <input checked={false} @@ -75,7 +75,7 @@ exports[`EcogestureFormView component should be rendered correctly 1`] = ` </label> <label className="radio_short" - key="1" + key="collective" > <input checked={false} diff --git a/src/components/ProfileType/ProfileTypeFinished.tsx b/src/components/ProfileType/ProfileTypeFinished.tsx index 2e7cd4b2ab35e80dacfd93e9b0998280b2058fd1..56b7def6f600f81cc901450434de32f905998102 100644 --- a/src/components/ProfileType/ProfileTypeFinished.tsx +++ b/src/components/ProfileType/ProfileTypeFinished.tsx @@ -47,7 +47,7 @@ const ProfileTypeFinished: React.FC<{ profileType: ProfileType }> = ({ useEffect(() => { async function checkForExistingProfileType() { - const consistentProfileType: ProfileType = + const consistentProfileType = ProfileTypeService.checkConsistency(profileType) const chosenPeriod: TimePeriod = { startDate: profileType.updateDate.setZone('utc', { diff --git a/src/components/ProfileType/ProfileTypeFormDateSelection.tsx b/src/components/ProfileType/ProfileTypeFormDateSelection.tsx index 59186b342d46dc8799d83fab1540030926596843..ff4334beb9c393739cb813fa4bd5c2caa2e746ec 100644 --- a/src/components/ProfileType/ProfileTypeFormDateSelection.tsx +++ b/src/components/ProfileType/ProfileTypeFormDateSelection.tsx @@ -8,22 +8,21 @@ import { DateTime } from 'luxon' import { ProfileType, ProfileTypeAnswer, - ProfileTypeAnswerChoices, + ProfileTypeValues, } from 'models/profileType.model' -import React, { useCallback, useEffect, useState } from 'react' +import React, { useCallback, useState } from 'react' +import { getMonthFullName } from 'utils/utils' interface ProfileTypeFormDateSelectionProps { step: ProfileTypeStepForm - viewedStep: ProfileTypeStepForm profileType: ProfileType answerType: ProfileTypeAnswer - setNextStep: (_profileType?: ProfileType) => void - setPreviousStep: (_profileType: ProfileType) => void - isProfileTypeComplete: boolean + setNextStep: (_profileType: ProfileType) => void + setPreviousStep: () => void } interface SelectionMonth { - label: string | null + label: string value: string } @@ -31,20 +30,23 @@ const ProfileTypeFormDateSelection: React.FC< ProfileTypeFormDateSelectionProps > = ({ step, - viewedStep, profileType, answerType, setNextStep, setPreviousStep, - isProfileTypeComplete, }: ProfileTypeFormDateSelectionProps) => { const { t } = useI18n() - const [selectedMonth, setSelectedMonth] = useState<any>({ + const [selectedYear, setSelectedYear] = useState<number>(DateTime.now().year) + const [selectedMonth, setSelectedMonth] = useState<SelectionMonth>({ label: DateTime.now().toLocaleString({ month: 'long' }), value: DateTime.now().month.toString().padStart(2, '0'), // Date.getMonth starts at 0 }) - const [selectedYear, setSelectedYear] = useState<number>(DateTime.now().year) - const [answer, setAnswer] = useState<ProfileTypeAnswerChoices>('') + const buildISODate = (year: string, month: string) => + DateTime.fromISO(`${year}-${month}-01`) + + const [answer, setAnswer] = useState<ProfileTypeValues>( + buildISODate(selectedYear.toString(), selectedMonth.value) + ) const selectMonths: SelectionMonth[] = [ { @@ -76,7 +78,7 @@ const ProfileTypeFormDateSelection: React.FC< value: '07', }, { - label: 'Aout', + label: 'Août', value: '08', }, { @@ -105,75 +107,32 @@ const ProfileTypeFormDateSelection: React.FC< selectYears.push(i) } - function getMonthFullName(month: number) { - switch (month) { - case 1: - return 'Janvier' - case 2: - return 'Février' - case 3: - return 'Mars' - case 4: - return 'Avril' - case 5: - return 'Mai' - case 6: - return 'Juin' - case 7: - return 'Juillet' - case 8: - return 'Aout' - case 9: - return 'Septembre' - case 10: - return 'Octobre' - case 11: - return 'Novembre' - case 12: - return 'Décembre' - default: - return null - } - } - const handlePrevious = useCallback(() => { - setPreviousStep(profileType) - }, [profileType, setPreviousStep]) + setPreviousStep() + }, [setPreviousStep]) - const handleNext = useCallback(() => { + const handleNext = () => { setNextStep({ ...profileType, [answerType.attribute]: answer }) - }, [profileType, setNextStep, answer, answerType.attribute]) + } function handleSelectMonth(event: any) { setSelectedMonth({ value: event.target.value, label: getMonthFullName(parseInt(event.target.value)), }) - const isoString: string = selectedYear + '-' + selectedMonth.value + '-01' - setAnswer(DateTime.fromISO(isoString)) + setAnswer(buildISODate(selectedYear.toString(), event.target.value)) } function handleSelectYear(event: any) { setSelectedYear(parseInt(event.target.value)) - const isoString: string = selectedYear + '-' + selectedMonth.value + '-01' - setAnswer(DateTime.fromISO(isoString)) + setAnswer(buildISODate(event.target.value, selectedMonth.value)) } - useEffect(() => { - if (step < viewedStep || isProfileTypeComplete) { - const isoString: string = selectedYear + '-' + selectedMonth.value + '-01' - setAnswer(DateTime.fromISO(isoString)) - profileType[answerType.attribute] = DateTime.fromISO(isoString) - } - }, [ - step, - viewedStep, - profileType, - answerType, - isProfileTypeComplete, - selectedYear, - selectedMonth.value, - ]) + /** If current year, only show past and present months else show full months */ + const renderMonths = + selectedYear === DateTime.now().year + ? selectMonths.slice(0, DateTime.now().month) + : selectMonths return ( <> @@ -184,13 +143,14 @@ const ProfileTypeFormDateSelection: React.FC< `profile_type.${ProfileTypeStepForm[step].toLowerCase()}.question` )} </div> - {answer !== null ? ( + {answer !== null && ( <div className="select-container"> <div className="date-select"> <Select labelId="selectYearDate" className="year" defaultValue={selectedYear} + value={selectedYear} onChange={e => handleSelectYear(e)} > {selectYears.map(year => ( @@ -202,36 +162,25 @@ const ProfileTypeFormDateSelection: React.FC< </div> <div className="date-select"> <Select - native={false} labelId="selectMonthDate" className="month" defaultValue={selectedMonth.value} + value={selectedMonth.value} onChange={e => handleSelectMonth(e)} > - {/* if current year, only show past and present months else show full months */} - {selectedYear === DateTime.now().year - ? selectMonths.slice(0, DateTime.now().month).map(month => ( - <MenuItem - value={month.value} - key={month.value} - className="date-option" - > - {month.label} - </MenuItem> - )) - : selectMonths.map(month => ( - <MenuItem - value={month.value} - key={month.value} - className="date-option" - > - {month.label} - </MenuItem> - ))} + {renderMonths.map(month => ( + <MenuItem + value={month.value} + key={month.value} + className="date-option" + > + {month.label} + </MenuItem> + ))} </Select> </div> </div> - ) : null} + )} </div> <FormNavigation step={step} diff --git a/src/components/ProfileType/ProfileTypeFormMultiChoice.spec.tsx b/src/components/ProfileType/ProfileTypeFormMultiChoice.spec.tsx index 8b5019a070025c88702e74b651764ab955b94029..5d4160e1f38963231dc63c36589da244c8c7713d 100644 --- a/src/components/ProfileType/ProfileTypeFormMultiChoice.spec.tsx +++ b/src/components/ProfileType/ProfileTypeFormMultiChoice.spec.tsx @@ -39,11 +39,10 @@ describe('ProfileTypeFormMultiChoice component', () => { <ProfileTypeFormMultiChoice step={ProfileTypeStepForm.COOKING_FLUID} viewedStep={ProfileTypeStepForm.AREA} - profileType={mockProfileType} + currentProfileType={mockProfileType} answerType={mockProfileTypeAnswers[1]} setNextStep={mockhandlePrevious} setPreviousStep={mockhandleNext} - isProfileTypeComplete={false} /> </Provider> ) diff --git a/src/components/ProfileType/ProfileTypeFormMultiChoice.tsx b/src/components/ProfileType/ProfileTypeFormMultiChoice.tsx index 46cec7784e22ae7d7284917d0bc49088137e3914..e6e3d8c085c66f520d32e8eabc411128f6cfc6cd 100644 --- a/src/components/ProfileType/ProfileTypeFormMultiChoice.tsx +++ b/src/components/ProfileType/ProfileTypeFormMultiChoice.tsx @@ -3,41 +3,37 @@ import FormNavigation from 'components/FormGlobal/FormNavigation' import FormProgress from 'components/FormGlobal/FormProgress' import 'components/ProfileType/profileTypeForm.scss' import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import { - IndividualInsulationWork, - ProfileTypeStepForm, -} from 'enum/profileType.enum' +import { ProfileTypeStepForm } from 'enum/profileType.enum' import { remove } from 'lodash' -import { - ProfileType, - ProfileTypeAnswer, - ProfileTypeAnswerChoices, -} from 'models/profileType.model' +import { ProfileType, ProfileTypeAnswer } from 'models/profileType.model' import React, { useCallback, useEffect, useState } from 'react' +import { useSelector } from 'react-redux' +import { AppStore } from 'store' interface ProfileTypeFormMultiChoiceProps { step: ProfileTypeStepForm viewedStep: ProfileTypeStepForm - profileType: ProfileType + currentProfileType: ProfileType answerType: ProfileTypeAnswer - setNextStep: (_profileType?: ProfileType) => void - setPreviousStep: (_profileType: ProfileType) => void - isProfileTypeComplete: boolean + setNextStep: (_profileType: ProfileType) => void + setPreviousStep: () => void } const ProfileTypeFormMultiChoice: React.FC<ProfileTypeFormMultiChoiceProps> = ({ step, viewedStep, - profileType, + currentProfileType, answerType, setNextStep, setPreviousStep, - isProfileTypeComplete, }: ProfileTypeFormMultiChoiceProps) => { const { t } = useI18n() - const [answer, setAnswer] = useState<ProfileTypeAnswerChoices[]>([]) + const { isProfileTypeCompleted } = useSelector( + (state: AppStore) => state.ecolyo.profile + ) + const [answer, setAnswer] = useState<string[]>([]) - const handleChange = (value: ProfileTypeAnswerChoices) => { + const handleChange = (value: string) => { let tempAnswer = [...answer] if (value === 'none' && !tempAnswer.includes(value)) { tempAnswer = [value] @@ -54,33 +50,27 @@ const ProfileTypeFormMultiChoice: React.FC<ProfileTypeFormMultiChoiceProps> = ({ setAnswer(tempAnswer) } - const isChecked = (value: ProfileTypeAnswerChoices): boolean => { - if (answer.includes(value)) { - return true - } else { - return false - } + const isChecked = (value: string): boolean => { + return answer.includes(value) } const handlePrevious = useCallback(() => { - setPreviousStep(profileType) - }, [profileType, setPreviousStep]) + setPreviousStep() + }, [setPreviousStep]) const handleNext = useCallback(() => { setNextStep({ - ...profileType, - [answerType.attribute]: answer as IndividualInsulationWork[], + ...currentProfileType, + [answerType.attribute]: answer, }) - }, [profileType, setNextStep, answer, answerType.attribute]) + }, [currentProfileType, setNextStep, answer, answerType.attribute]) useEffect(() => { - const attribute = profileType[ - answerType.attribute - ] as IndividualInsulationWork[] - if (step < viewedStep || isProfileTypeComplete) { - setAnswer(attribute) + // Set answer if profileType is completed + if (step < viewedStep || isProfileTypeCompleted) { + setAnswer(currentProfileType[answerType.attribute] as string[]) } - }, [step, viewedStep, profileType, answerType, isProfileTypeComplete]) + }, [step, viewedStep, currentProfileType, answerType, isProfileTypeCompleted]) return ( <> @@ -94,31 +84,31 @@ const ProfileTypeFormMultiChoice: React.FC<ProfileTypeFormMultiChoiceProps> = ({ <span className="profile-question-hint"> {t('profile_type.multi_choices')} </span> - {answerType.choices.map( - (value: ProfileTypeAnswerChoices, index: number) => { - return value ? ( - <label - key={index} - className={classNames('checkbox', { - ['answer-checked']: answer.includes(value), - })} - > - <input - type={'checkbox'} - value={value} - name={value.toString()} - onChange={() => handleChange(value)} - checked={isChecked(value)} - /> - {t( - `profile_type.${ProfileTypeStepForm[ - step - ].toLowerCase()}.${value}` - )} - </label> - ) : null - } - )} + {answerType.choices.map((value, index) => { + if (!value) return null + const stringValue = value.toString() + return ( + <label + key={index} + className={classNames('checkbox', { + ['answer-checked']: answer.includes(stringValue), + })} + > + <input + type={'checkbox'} + value={stringValue} + name={stringValue} + onChange={() => handleChange(stringValue)} + checked={isChecked(stringValue)} + /> + {t( + `profile_type.${ProfileTypeStepForm[ + step + ].toLowerCase()}.${value}` + )} + </label> + ) + })} </div> <FormNavigation step={step} diff --git a/src/components/ProfileType/ProfileTypeFormNumber.spec.tsx b/src/components/ProfileType/ProfileTypeFormNumber.spec.tsx index 2631a9fa3567f9464eeabe6c15dec384bd6c7d15..c9b0f6aea21672cb10525809e8994263ce43e914 100644 --- a/src/components/ProfileType/ProfileTypeFormNumber.spec.tsx +++ b/src/components/ProfileType/ProfileTypeFormNumber.spec.tsx @@ -38,11 +38,10 @@ describe('ProfileTypeFormNumber component', () => { <ProfileTypeFormNumber step={ProfileTypeStepForm.COOKING_FLUID} viewedStep={ProfileTypeStepForm.AREA} - profileType={mockProfileType} + currentProfileType={mockProfileType} answerType={mockProfileTypeAnswers[1]} setNextStep={mockhandlePrevious} setPreviousStep={mockhandleNext} - isProfileTypeComplete={false} /> </Provider> ) diff --git a/src/components/ProfileType/ProfileTypeFormNumber.tsx b/src/components/ProfileType/ProfileTypeFormNumber.tsx index c46ac1924aa835385cdc270112a35c7b8305b79d..269c8e074120622dcd786cce27f4fbf2bc5e63c0 100644 --- a/src/components/ProfileType/ProfileTypeFormNumber.tsx +++ b/src/components/ProfileType/ProfileTypeFormNumber.tsx @@ -6,45 +6,48 @@ import { ProfileTypeStepForm } from 'enum/profileType.enum' import { ProfileType, ProfileTypeAnswer, - ProfileTypeAnswerChoices, + ProfileTypeValues, } from 'models/profileType.model' import React, { useCallback, useEffect, useState } from 'react' +import { useSelector } from 'react-redux' +import { AppStore } from 'store' interface ProfileTypeFormNumberProps { step: ProfileTypeStepForm viewedStep: ProfileTypeStepForm - profileType: ProfileType + currentProfileType: ProfileType answerType: ProfileTypeAnswer - setNextStep: (_profileType?: ProfileType) => void - setPreviousStep: (_profileType: ProfileType) => void - isProfileTypeComplete: boolean + setNextStep: (_profileType: ProfileType) => void + setPreviousStep: () => void } const ProfileTypeFormNumber: React.FC<ProfileTypeFormNumberProps> = ({ step, viewedStep, - profileType, + currentProfileType, answerType, setNextStep, setPreviousStep, - isProfileTypeComplete, }: ProfileTypeFormNumberProps) => { const { t } = useI18n() - const [answer, setAnswer] = useState<ProfileTypeAnswerChoices>('') + const { isProfileTypeCompleted } = useSelector( + (state: AppStore) => state.ecolyo.profile + ) + const [answer, setAnswer] = useState<ProfileTypeValues>('') const handlePrevious = useCallback(() => { - setPreviousStep(profileType) - }, [profileType, setPreviousStep]) + setPreviousStep() + }, [setPreviousStep]) const handleNext = useCallback(() => { - setNextStep({ ...profileType, [answerType.attribute]: answer }) - }, [profileType, setNextStep, answer, answerType.attribute]) + setNextStep({ ...currentProfileType, [answerType.attribute]: answer }) + }, [currentProfileType, setNextStep, answer, answerType.attribute]) useEffect(() => { - if (step < viewedStep || isProfileTypeComplete) { - setAnswer(profileType[answerType.attribute]) + if (step < viewedStep || isProfileTypeCompleted) { + setAnswer(currentProfileType[answerType.attribute]) } - }, [step, viewedStep, profileType, answerType, isProfileTypeComplete]) + }, [step, viewedStep, currentProfileType, answerType, isProfileTypeCompleted]) return ( <> @@ -55,18 +58,18 @@ const ProfileTypeFormNumber: React.FC<ProfileTypeFormNumberProps> = ({ `profile_type.${ProfileTypeStepForm[step].toLowerCase()}.question` )} </div> - {answer != null ? ( + {answer != null && ( <label className={'text'}> <input type={'number'} - value={answer} + value={answer.toString()} name={answerType.attribute} onChange={e => setAnswer(e.target.value)} autoFocus /> m² </label> - ) : null} + )} </div> <FormNavigation step={step} diff --git a/src/components/ProfileType/ProfileTypeFormNumberSelection.spec.tsx b/src/components/ProfileType/ProfileTypeFormNumberSelection.spec.tsx index e6cf7e1c3a13c3cc2a5ee9df31a53de051ee2dc7..ce1e6210ab9d44ea75f54a0981d53c83622ee59e 100644 --- a/src/components/ProfileType/ProfileTypeFormNumberSelection.spec.tsx +++ b/src/components/ProfileType/ProfileTypeFormNumberSelection.spec.tsx @@ -38,11 +38,10 @@ describe('ProfileTypeFormNumberSelection component', () => { <ProfileTypeFormNumberSelection step={ProfileTypeStepForm.COOKING_FLUID} viewedStep={ProfileTypeStepForm.AREA} - profileType={mockProfileType} + currentProfileType={mockProfileType} answerType={mockProfileTypeAnswers[3]} setNextStep={mockhandlePrevious} setPreviousStep={mockhandleNext} - isProfileTypeComplete={false} /> </Provider> ) diff --git a/src/components/ProfileType/ProfileTypeFormNumberSelection.tsx b/src/components/ProfileType/ProfileTypeFormNumberSelection.tsx index d40309fa13cd78ab5b95cb5a6b94498a3b323cb7..efc734ea7041dc1f24676726a5892f014fb9c427 100644 --- a/src/components/ProfileType/ProfileTypeFormNumberSelection.tsx +++ b/src/components/ProfileType/ProfileTypeFormNumberSelection.tsx @@ -7,18 +7,19 @@ import { ProfileTypeStepForm } from 'enum/profileType.enum' import { ProfileType, ProfileTypeAnswer, - ProfileTypeAnswerChoices, + ProfileTypeValues, } from 'models/profileType.model' import React, { useCallback, useEffect, useState } from 'react' +import { useSelector } from 'react-redux' +import { AppStore } from 'store' interface ProfileTypeFormNumberSelectionProps { step: ProfileTypeStepForm viewedStep: ProfileTypeStepForm - profileType: ProfileType + currentProfileType: ProfileType answerType: ProfileTypeAnswer - setNextStep: (_profileType?: ProfileType) => void - setPreviousStep: (_profileType: ProfileType) => void - isProfileTypeComplete: boolean + setNextStep: (_profileType: ProfileType) => void + setPreviousStep: () => void } const ProfileTypeFormNumberSelection: React.FC< @@ -26,14 +27,16 @@ const ProfileTypeFormNumberSelection: React.FC< > = ({ step, viewedStep, - profileType, + currentProfileType, answerType, setNextStep, setPreviousStep, - isProfileTypeComplete, }: ProfileTypeFormNumberSelectionProps) => { const { t } = useI18n() - const [answer, setAnswer] = useState<ProfileTypeAnswerChoices>('') + const { isProfileTypeCompleted } = useSelector( + (state: AppStore) => state.ecolyo.profile + ) + const [answer, setAnswer] = useState<ProfileTypeValues>('') const [index, setIndex] = useState<number>(0) const decrement = () => { @@ -47,24 +50,25 @@ const ProfileTypeFormNumberSelection: React.FC< } const handlePrevious = useCallback(() => { - setPreviousStep(profileType) - }, [profileType, setPreviousStep]) + setPreviousStep() + }, [setPreviousStep]) const handleNext = useCallback(() => { - setNextStep({ ...profileType, [answerType.attribute]: answer }) - }, [profileType, setNextStep, answer, answerType.attribute]) + setNextStep({ ...currentProfileType, [answerType.attribute]: answer }) + }, [currentProfileType, setNextStep, answer, answerType.attribute]) useEffect(() => { - if (step < viewedStep || isProfileTypeComplete) { + if (step < viewedStep || isProfileTypeCompleted) { const foundIndex = answerType.choices.findIndex( - element => element && element === profileType[answerType.attribute] + element => + element && element === currentProfileType[answerType.attribute] ) foundIndex > -1 && setIndex(foundIndex) - setAnswer(profileType[answerType.attribute]) + setAnswer(currentProfileType[answerType.attribute]) } else { setAnswer(answerType.choices[0]) } - }, [step, viewedStep, profileType, answerType, isProfileTypeComplete]) + }, [step, viewedStep, currentProfileType, answerType, isProfileTypeCompleted]) return ( <> @@ -87,7 +91,7 @@ const ProfileTypeFormNumberSelection: React.FC< <label className={'number'}> <input type={'text'} - value={answer} + value={answer.toString()} name={answerType.attribute} disabled={true} /> diff --git a/src/components/ProfileType/ProfileTypeFormSingleChoice.spec.tsx b/src/components/ProfileType/ProfileTypeFormSingleChoice.spec.tsx index f8743a938df9351e4ba42963d8062b2b04a373ae..f4c4e4e8ae30882f352bca71897d48d8a4547d29 100644 --- a/src/components/ProfileType/ProfileTypeFormSingleChoice.spec.tsx +++ b/src/components/ProfileType/ProfileTypeFormSingleChoice.spec.tsx @@ -38,11 +38,10 @@ describe('ProfileTypeFormSingleChoice component', () => { <ProfileTypeFormSingleChoice step={ProfileTypeStepForm.COOKING_FLUID} viewedStep={ProfileTypeStepForm.AREA} - profileType={mockProfileType} + currentProfileType={mockProfileType} answerType={mockProfileTypeAnswers[1]} setNextStep={mockhandlePrevious} setPreviousStep={mockhandleNext} - isProfileTypeComplete={false} /> </Provider> ) diff --git a/src/components/ProfileType/ProfileTypeFormSingleChoice.tsx b/src/components/ProfileType/ProfileTypeFormSingleChoice.tsx index b8ec1909a60440312be59cf795e14a5f8a9d1ec6..51c8d8d7e657f15a1a6b68144d7470d5010e2eaa 100644 --- a/src/components/ProfileType/ProfileTypeFormSingleChoice.tsx +++ b/src/components/ProfileType/ProfileTypeFormSingleChoice.tsx @@ -4,10 +4,11 @@ import FormProgress from 'components/FormGlobal/FormProgress' import 'components/ProfileType/profileTypeForm.scss' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { ProfileTypeStepForm } from 'enum/profileType.enum' +import { ProfileEcogesture } from 'models' import { ProfileType, ProfileTypeAnswer, - ProfileTypeAnswerChoices, + ProfileTypeValues, } from 'models/profileType.model' import React, { useCallback, useEffect, useState } from 'react' import { useSelector } from 'react-redux' @@ -16,11 +17,10 @@ import { AppStore } from 'store' interface ProfileTypeFormSingleChoiceProps { step: ProfileTypeStepForm viewedStep: ProfileTypeStepForm - profileType: ProfileType + currentProfileType: ProfileType answerType: ProfileTypeAnswer - setNextStep: (_profileType?: ProfileType) => void - setPreviousStep: (_profileType: ProfileType) => void - isProfileTypeComplete: boolean + setNextStep: (_profileType: ProfileType) => void + setPreviousStep: () => void } const ProfileTypeFormSingleChoice: React.FC< @@ -28,39 +28,47 @@ const ProfileTypeFormSingleChoice: React.FC< > = ({ step, viewedStep, - profileType, + currentProfileType, answerType, setNextStep, setPreviousStep, - isProfileTypeComplete, }: ProfileTypeFormSingleChoiceProps) => { const { t } = useI18n() - const [answer, setAnswer] = useState<ProfileTypeAnswerChoices>('') - const { - profile: { isProfileEcogestureCompleted }, + profile: { isProfileTypeCompleted, isProfileEcogestureCompleted }, profileEcogesture, } = useSelector((state: AppStore) => state.ecolyo) + + const [answer, setAnswer] = useState<ProfileTypeValues>('') const handlePrevious = useCallback(() => { - setPreviousStep(profileType) - }, [profileType, setPreviousStep]) + setPreviousStep() + }, [setPreviousStep]) const handleNext = useCallback(() => { - setNextStep({ ...profileType, [answerType.attribute]: answer }) - }, [profileType, setNextStep, answer, answerType.attribute]) + setNextStep({ ...currentProfileType, [answerType.attribute]: answer }) + }, [currentProfileType, setNextStep, answer, answerType.attribute]) useEffect(() => { // Set answer if profileType is completed - if (step < viewedStep || isProfileTypeComplete) { - setAnswer(profileType[answerType.attribute]) + if (step < viewedStep || isProfileTypeCompleted) { + setAnswer(currentProfileType[answerType.attribute]) return } // Set answer if ecogestureProfile is completed if (isProfileEcogestureCompleted) { - setAnswer(profileEcogesture[answerType.attribute]) - return + setAnswer( + profileEcogesture[answerType.attribute as keyof ProfileEcogesture] + ) } - }, [step, viewedStep, profileType, answerType, isProfileTypeComplete]) + }, [ + step, + viewedStep, + currentProfileType, + answerType, + isProfileTypeCompleted, + isProfileEcogestureCompleted, + profileEcogesture, + ]) return ( <> @@ -71,33 +79,33 @@ const ProfileTypeFormSingleChoice: React.FC< `profile_type.${ProfileTypeStepForm[step].toLowerCase()}.question` )} </div> - {answerType.choices.map( - (value: ProfileTypeAnswerChoices, index: number) => { - return value || value === 0 ? ( - <label - key={index} - className={classNames({ - ['radio_short']: answerType.choices.length < 5, - ['radio_long']: answerType.choices.length > 4, - ['answer-checked']: answer === value, - })} - > - <input - type={'radio'} - value={value.toString()} - name={value.toString()} - onChange={() => setAnswer(value)} - checked={answer === value ? true : false} - /> - {t( - `profile_type.${ProfileTypeStepForm[ - step - ].toLowerCase()}.${value}` - )} - </label> - ) : null - } - )} + {answerType.choices.map((value, index) => { + if (!value && value !== 0) return null + + return ( + <label + key={index} + className={classNames({ + ['radio_short']: answerType.choices.length < 5, + ['radio_long']: answerType.choices.length > 4, + ['answer-checked']: answer === value, + })} + > + <input + type={'radio'} + value={value.toString()} + name={value.toString()} + onChange={() => setAnswer(value)} + checked={answer === value ? true : false} + /> + {t( + `profile_type.${ProfileTypeStepForm[ + step + ].toLowerCase()}.${value}` + )} + </label> + ) + })} </div> <FormNavigation step={step} diff --git a/src/components/ProfileType/ProfileTypeView.tsx b/src/components/ProfileType/ProfileTypeView.tsx index aedfcb8ad9d2b8a1674513323c90c022a9ee4c68..646110a21d59454e1f0839ddee7d036cd26d43af 100644 --- a/src/components/ProfileType/ProfileTypeView.tsx +++ b/src/components/ProfileType/ProfileTypeView.tsx @@ -25,7 +25,7 @@ import { WarmingType, } from 'enum/profileType.enum' import { DateTime } from 'luxon' -import { ProfileType, ProfileTypeAnswer } from 'models/profileType.model' +import { ProfileType, ProfileTypeAnswer } from 'models' import React, { useCallback, useEffect, useState } from 'react' import { useSelector } from 'react-redux' import ProfileTypeFormService from 'services/profileTypeForm.service' @@ -33,14 +33,12 @@ import { AppStore } from 'store' import ProfileTypeFormDateSelection from './ProfileTypeFormDateSelection' const ProfileTypeView: React.FC = () => { - const { - profile, - profileType: curProfileType, - profileEcogesture: curProfileEcogesture, - } = useSelector((state: AppStore) => state.ecolyo) + const { profile, profileType, profileEcogesture } = useSelector( + (state: AppStore) => state.ecolyo + ) const [headerHeight, setHeaderHeight] = useState<number>(0) - const [profileType, setProfileType] = useState<ProfileType>({ + const [currentProfileType, setCurrentProfileType] = useState<ProfileType>({ updateDate: DateTime.local() .setZone('utc', { keepLocalTime: true, @@ -69,7 +67,7 @@ const ProfileTypeView: React.FC = () => { ) const [answerType, setAnswerType] = useState<ProfileTypeAnswer>({ type: ProfileTypeFormType.SINGLE_CHOICE, - attribute: '', + attribute: 'housingType', choices: [], }) @@ -83,55 +81,83 @@ const ProfileTypeView: React.FC = () => { // if ecogesture profile is completed, update default profileType useEffect(() => { if (profile.isProfileEcogestureCompleted) { - setProfileType({ + setCurrentProfileType({ ...profileType, - hotWater: curProfileEcogesture.hotWater, - heating: curProfileEcogesture.heating, - warmingFluid: curProfileEcogesture.warmingFluid, - equipments: curProfileEcogesture.equipments, + hotWater: profileEcogesture.hotWater, + heating: profileEcogesture.heating, + warmingFluid: profileEcogesture.warmingFluid, + equipments: profileEcogesture.equipments, }) } - }, [profile.isProfileEcogestureCompleted]) + }, [ + profile.isProfileEcogestureCompleted, + profileEcogesture.equipments, + profileEcogesture.heating, + profileEcogesture.hotWater, + profileEcogesture.warmingFluid, + profileType, + ]) const setNextStep = useCallback( - (_profileType?: ProfileType) => { + (_profileType: ProfileType) => { let profileTypeFormService: ProfileTypeFormService if (_profileType) { - setProfileType(_profileType) + setCurrentProfileType(_profileType) profileTypeFormService = new ProfileTypeFormService(_profileType) } else { - // if equipments are updated, keep profileType as it is + // if equipments are updated, keep currentProfileType as it is profileTypeFormService = new ProfileTypeFormService({ - ...profileType, + ...currentProfileType, }) } - const nextStep: ProfileTypeStepForm = - profileTypeFormService.getNextFormStep( - step, - !profile.isProfileTypeCompleted - ) + const nextStep = profileTypeFormService.getNextFormStep( + step, + !profile.isProfileTypeCompleted + ) setIsLoading(true) if (nextStep > viewedStep) { setViewedStep(nextStep) } setStep(nextStep) }, - [ - curProfileEcogesture, - profile.isProfileTypeCompleted, - profileType, - step, - viewedStep, - ] + [profile.isProfileTypeCompleted, currentProfileType, step, viewedStep] ) const setPreviousStep = useCallback(() => { - const profileTypeFormService = new ProfileTypeFormService(profileType) + const profileTypeFormService = new ProfileTypeFormService( + currentProfileType + ) const previousStep: ProfileTypeStepForm = profileTypeFormService.getPreviousFormStep(step) setIsLoading(true) setStep(previousStep) - }, [profileType, step]) + }, [currentProfileType, step]) + + useEffect(() => { + const _answerType = ProfileTypeFormService.getAnswerForStep(step) + setAnswerType(_answerType) + setIsLoading(false) + }, [step]) + + /** If profileType OR ecogestureProfile is completed, apply it to local state */ + useEffect(() => { + if (profile.isProfileTypeCompleted) { + setCurrentProfileType({ ...profileType }) + return + } + + if (profile.isProfileEcogestureCompleted) { + // Default state + ecogestureProfile + setCurrentProfileType({ + ...currentProfileType, + hotWater: profileEcogesture.hotWater, + heating: profileEcogesture.heating, + warmingFluid: profileEcogesture.warmingFluid, + equipments: profileEcogesture.equipments, + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [profileType, profile, profile.isProfileEcogestureCompleted]) const selectForm = () => { if (answerType.type === ProfileTypeFormType.SINGLE_CHOICE) { @@ -139,10 +165,9 @@ const ProfileTypeView: React.FC = () => { <ProfileTypeFormSingleChoice step={step} viewedStep={viewedStep} - profileType={profileType} + currentProfileType={currentProfileType} answerType={answerType} setNextStep={setNextStep} - isProfileTypeComplete={profile.isProfileTypeCompleted} setPreviousStep={setPreviousStep} /> ) @@ -151,11 +176,10 @@ const ProfileTypeView: React.FC = () => { <ProfileTypeFormMultiChoice step={step} viewedStep={viewedStep} - profileType={profileType} + currentProfileType={currentProfileType} answerType={answerType} setNextStep={setNextStep} setPreviousStep={setPreviousStep} - isProfileTypeComplete={profile.isProfileTypeCompleted} /> ) } else if (answerType.type === ProfileTypeFormType.NUMBER) { @@ -163,10 +187,9 @@ const ProfileTypeView: React.FC = () => { <ProfileTypeFormNumber step={step} viewedStep={viewedStep} - profileType={profileType} + currentProfileType={currentProfileType} answerType={answerType} setNextStep={setNextStep} - isProfileTypeComplete={profile.isProfileTypeCompleted} setPreviousStep={setPreviousStep} /> ) @@ -175,10 +198,9 @@ const ProfileTypeView: React.FC = () => { <ProfileTypeFormNumberSelection step={step} viewedStep={viewedStep} - profileType={profileType} + currentProfileType={currentProfileType} answerType={answerType} setNextStep={setNextStep} - isProfileTypeComplete={profile.isProfileTypeCompleted} setPreviousStep={setPreviousStep} /> ) @@ -186,11 +208,9 @@ const ProfileTypeView: React.FC = () => { return ( <ProfileTypeFormDateSelection step={step} - viewedStep={viewedStep} - profileType={profileType} + profileType={currentProfileType} answerType={answerType} setNextStep={setNextStep} - isProfileTypeComplete={profile.isProfileTypeCompleted} setPreviousStep={setPreviousStep} /> ) @@ -198,27 +218,14 @@ const ProfileTypeView: React.FC = () => { return ( <EcogestureFormEquipment step={step} - profileEcogesture={curProfileEcogesture} - setNextStep={setNextStep} + currentProfileType={currentProfileType} + setNextStepProfileForm={setNextStep} setPreviousStep={setPreviousStep} /> ) } } - useEffect(() => { - const _answerType: ProfileTypeAnswer = - ProfileTypeFormService.getAnswerForStep(step) - setAnswerType(_answerType) - setIsLoading(false) - }, [step]) - - useEffect(() => { - if (profile.isProfileTypeCompleted) { - setProfileType({ ...curProfileType }) - } - }, [curProfileType, profile]) - return ( <> <CozyBar titleKey={'common.title_profiletype'} displayBackArrow={true} /> @@ -234,7 +241,7 @@ const ProfileTypeView: React.FC = () => { <> {step !== ProfileTypeStepForm.END && selectForm()} {step === ProfileTypeStepForm.END && ( - <ProfileTypeFinished profileType={profileType} /> + <ProfileTypeFinished profileType={currentProfileType} /> )} </> )} diff --git a/src/enum/ecogestureForm.enum.ts b/src/enum/ecogestureForm.enum.ts index 556793f5060848b704ba271f4e2f528cd5b0c29d..3e847a32533a511af712846712647aa6a2af3bf5 100644 --- a/src/enum/ecogestureForm.enum.ts +++ b/src/enum/ecogestureForm.enum.ts @@ -3,6 +3,7 @@ export enum EcogestureStepForm { WARMING_FLUID = 1, HOT_WATER_TYPE = 2, EQUIPMENTS = 3, + END, } export enum ProfileEcogestureAnswerType { SINGLE_CHOICE = 0, diff --git a/src/models/profileEcogesture.model.ts b/src/models/profileEcogesture.model.ts index 90164e3125e9ea7e4bac68ac6cd7eb730a3f189e..217f67a7030951283c9c80238898c1bcf8c6532e 100644 --- a/src/models/profileEcogesture.model.ts +++ b/src/models/profileEcogesture.model.ts @@ -1,41 +1,15 @@ -import { EquipmentType } from 'enum/ecogesture.enum' import { ProfileEcogestureAnswerType } from 'enum/ecogestureForm.enum' -import { FluidType } from 'enum/fluid.enum' -import { - HotWaterEquipment, - IndividualOrCollective, - WarmingType, -} from 'enum/profileType.enum' +import { ProfileType } from './profileType.model' -interface ProfileEcogestureIndexableTypes { - [key: string]: - | IndividualOrCollective - | FluidType - | WarmingType - | EquipmentType[] - | ProfileEcogesture - | string - | null -} -export type ProfileEcogestureAnswerChoices = - | string - | IndividualOrCollective - | HotWaterEquipment - | FluidType - | WarmingType - | EquipmentType[] - | number - | null +export type ProfileEcogesture = Pick< + ProfileType, + 'heating' | 'warmingFluid' | 'hotWater' | 'equipments' +> + +export type ProfileEcogestureValues = ProfileEcogesture[keyof ProfileEcogesture] export interface ProfileEcogestureAnswer { type: ProfileEcogestureAnswerType - attribute: string - choices: ProfileEcogestureAnswerChoices[] -} - -export interface ProfileEcogesture extends ProfileEcogestureIndexableTypes { - heating: IndividualOrCollective - warmingFluid: WarmingType | null - hotWater: IndividualOrCollective - equipments: EquipmentType[] + attribute: keyof ProfileEcogesture + choices: ProfileEcogestureValues[] } diff --git a/src/models/profileType.model.ts b/src/models/profileType.model.ts index adf82054779dd32507d2199170ee09b769a817a0..e3184314d77fd3f5f2c1e9737c3e8a846458a1fa 100644 --- a/src/models/profileType.model.ts +++ b/src/models/profileType.model.ts @@ -14,47 +14,30 @@ import { WarmingType, } from 'enum/profileType.enum' import { DateTime } from 'luxon' -import { ProfileEcogesture } from './profileEcogesture.model' -interface ProfileTypeIndexableTypes { - [key: string]: - | HousingType - | ConstructionYear - | OutsideFacingWalls - | Floor - | IndividualOrCollective - | IndividualInsulationWork[] - | ThreeChoicesAnswer - | HotWaterEquipment - | FluidType - | DateTime - | EquipmentType[] - | number - | string - | string[] - | null -} -export interface ProfileType extends ProfileTypeIndexableTypes { - housingType: HousingType - constructionYear: ConstructionYear +export interface ProfileType { area: string - occupantsNumber: number - outsideFacingWalls: OutsideFacingWalls - floor: Floor - heating: IndividualOrCollective coldWater: IndividualOrCollective - hotWater: IndividualOrCollective - individualInsulationWork: IndividualInsulationWork[] + constructionYear: ConstructionYear + cookingFluid: FluidType + equipments: EquipmentType[] + floor: Floor hasInstalledVentilation: ThreeChoicesAnswer hasReplacedHeater: ThreeChoicesAnswer + heating: IndividualOrCollective + hotWater: IndividualOrCollective hotWaterEquipment: HotWaterEquipment - warmingFluid: WarmingType | null hotWaterFluid: HotWaterFluid | null - cookingFluid: FluidType + housingType: HousingType + individualInsulationWork: IndividualInsulationWork[] + occupantsNumber: number + outsideFacingWalls: OutsideFacingWalls updateDate: DateTime - equipments: EquipmentType[] + warmingFluid: WarmingType | null } +export type ProfileTypeValues = ProfileType[keyof ProfileType] + export interface MonthlyForecast { month: number fluidForecast: FluidForecast[] @@ -74,25 +57,8 @@ export interface DetailsMonthlyForecast { coldWaterConsumption: number | null } -export type ProfileTypeAnswerChoices = - | string - | string[] - | number - | HousingType - | ConstructionYear - | OutsideFacingWalls - | Floor - | IndividualOrCollective - | IndividualInsulationWork[] - | ThreeChoicesAnswer - | HotWaterEquipment - | FluidType - | DateTime - | EquipmentType[] - | ProfileEcogesture - | null export interface ProfileTypeAnswer { type: ProfileTypeFormType - attribute: string - choices: ProfileTypeAnswerChoices[] + attribute: keyof ProfileType + choices: ProfileTypeValues[] } diff --git a/src/services/profileEcogestureForm.service.ts b/src/services/profileEcogestureForm.service.ts index 9cbe8fa36e381787e695ceda5023642c680533be..0da0d9ead5ad4974b4fb06fa4a4809cabe62a547 100644 --- a/src/services/profileEcogestureForm.service.ts +++ b/src/services/profileEcogestureForm.service.ts @@ -7,6 +7,7 @@ import { IndividualOrCollective, WarmingType } from 'enum/profileType.enum' import { ProfileEcogesture, ProfileEcogestureAnswer, + ProfileEcogestureValues, } from 'models/profileEcogesture.model' export default class ProfileEcogestureFormService { @@ -32,6 +33,8 @@ export default class ProfileEcogestureFormService { return EcogestureStepForm.HOT_WATER_TYPE case EcogestureStepForm.HOT_WATER_TYPE: return EcogestureStepForm.EQUIPMENTS + case EcogestureStepForm.EQUIPMENTS: + return EcogestureStepForm.END default: return EcogestureStepForm.HEATING_TYPE } @@ -86,7 +89,7 @@ export default class ProfileEcogestureFormService { return { type: ProfileEcogestureAnswerType.MULTI_CHOICE, attribute: 'equipments', - choices: Object.keys(EquipmentType), + choices: Object.keys(EquipmentType) as ProfileEcogestureValues[], } case EcogestureStepForm.HEATING_TYPE: default: diff --git a/src/utils/utils.ts b/src/utils/utils.ts index e24d187a25f5e84fc98f436f3c1434fd60aade92..507d4c4eb70cf26e4f88793f4bcc9f61c4e2039e 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -90,7 +90,6 @@ export function formatNumberValues( /** * Get one relation in doc - * * @param {object} doc - DocumentEntity * @param {string} relName - Name of the relation */ @@ -100,7 +99,6 @@ export function getRelationship<D>(doc: D, relName: string): Relation { /** * Get array of items in one relation in doc - * * @param {object} doc - DocumentEntity * @param {string} relName - Name of the relation */ @@ -110,7 +108,6 @@ export function getRelationshipHasMany<D>(doc: D, relName: string): Relation[] { /** * Get many relations in doc - * * @param {object} doc - DocumentEntity * @param {Array<[relName: string]: Array<Relation>>} relNameList - Array of name of the relations */ @@ -124,7 +121,6 @@ export function getRelationships<D>( } /** - * * @param id * @param pathType */ @@ -142,6 +138,25 @@ export const importIconById = async (id: string, pathType: string) => { } } +export const getMonthFullName = (month: number) => { + const monthNames = [ + 'Janvier', + 'Février', + 'Mars', + 'Avril', + 'Mai', + 'Juin', + 'Juillet', + 'Août', + 'Septembre', + 'Octobre', + 'Novembre', + 'Décembre', + ] as const + if (month < 1 || month > 12) throw new Error('Invalid month') + return monthNames[month - 1] +} + /** * Return month string according to month index * @param date - DateTime