From 22880b9c15caca0a99721235a7fe8fc7f7f7bda1 Mon Sep 17 00:00:00 2001 From: Marc Itzenthaler Date: Tue, 19 Mar 2024 01:44:12 +0100 Subject: [PATCH] fix: registration data loading --- package.json | 4 +- src/api/apiAgencySelection.ts | 2 +- src/api/apiGetTopicGroups.ts | 4 +- src/components/input/input.tsx | 49 +- .../hooks/useAgenciesForRegistration.ts | 7 +- src/extensions/components/app/app.tsx | 51 -- .../ConsultingTypes.styles.scss | 63 --- .../consultingTypes/ConsultingTypes.tsx | 246 --------- ...ConsultingTypesAgencySelection.styles.scss | 41 -- .../ConsultingTypesAgencySelection.tsx | 169 ------ .../ConsultingTypesGroupChild.styles.scss | 55 -- .../ConsultingTypesGroupChild.tsx | 63 --- .../ConsultingTypesGroupChildDetails.tsx | 100 ---- .../ConsultingTypesOverlay.styles.scss | 12 - .../ConsultingTypesOverlay.tsx | 38 -- ...nsultingTypesGroupChildDetails.styles.scss | 40 -- .../components/registration/Registration.tsx | 360 ++++++------- .../registration/accountData/AccountData.tsx | 20 +- .../agencySelection/AgencyLanguages.tsx | 46 +- .../agencySelection/AgencySelection.tsx | 90 +--- .../AgencySelectionResults.tsx | 97 ++-- .../agencySelection/agencySelection.cy.tsx | 14 +- .../registration/infoDrawer/InfoDrawer.tsx | 268 +++++----- .../preselectionBox/PreselectionBox.tsx | 184 +++---- .../preselectionDrawer/preselectionDrawer.tsx | 83 +-- .../topicSelection/TopicSelection.tsx | 466 ++++++++-------- .../topicSelection/topicSelection.cy.tsx | 6 +- .../welcomeScreen/WelcomeScreen.tsx | 14 +- .../zipcodeInput/ZipcodeInput.tsx | 32 +- .../zipcodeInput/zipCodeInput.cy.tsx | 8 +- src/extensions/initApp.tsx | 80 ++- src/extensions/pages/app.html | 27 +- src/extensions/pages/under-construction.html | 2 +- .../interfaces/TopicsDataInterface.ts | 1 + .../provider/RegistrationProvider.tsx | 504 +++++++----------- .../provider/UrlParamsProvider.tsx | 28 +- src/resources/scripts/config.ts | 2 +- src/utils/useUrlParamsLoader.tsx | 45 +- 38 files changed, 1177 insertions(+), 2144 deletions(-) delete mode 100644 src/extensions/components/app/app.tsx delete mode 100644 src/extensions/components/consultingTypes/ConsultingTypes.styles.scss delete mode 100644 src/extensions/components/consultingTypes/ConsultingTypes.tsx delete mode 100644 src/extensions/components/consultingTypes/ConsultingTypesAgencySelection.styles.scss delete mode 100644 src/extensions/components/consultingTypes/ConsultingTypesAgencySelection.tsx delete mode 100644 src/extensions/components/consultingTypes/ConsultingTypesGroupChild.styles.scss delete mode 100644 src/extensions/components/consultingTypes/ConsultingTypesGroupChild.tsx delete mode 100644 src/extensions/components/consultingTypes/ConsultingTypesGroupChildDetails.tsx delete mode 100644 src/extensions/components/consultingTypes/ConsultingTypesOverlay.styles.scss delete mode 100644 src/extensions/components/consultingTypes/ConsultingTypesOverlay.tsx delete mode 100644 src/extensions/components/consultingTypes/consultingTypesGroupChildDetails.styles.scss diff --git a/package.json b/package.json index 81592df3d..1a42a9111 100644 --- a/package.json +++ b/package.json @@ -192,9 +192,9 @@ "dev": "node scripts/start.js", "dev:server": "nodemon", "build": "node scripts/build.js", - "test": "cross-env FAST_REFRESH=false BROWSER=none REACT_APP_API_URL=http://127.0.0.1:9001 HTTPS=0 PORT=9001 WDS_SOCKET_PORT=9001 concurrently --kill-others --success first \"npm run dev\" \"wait-on http://127.0.0.1:9001 && cypress run\"", + "test": "cross-env NODE_ENV=development FAST_REFRESH=false BROWSER=none REACT_APP_API_URL=http://127.0.0.1:9001 HTTPS=0 PORT=9001 WDS_SOCKET_PORT=9001 concurrently --kill-others --success first \"npm run dev\" \"wait-on http://127.0.0.1:9001 && cypress run\"", "test:components": "NODE_ENV=development cypress run --component --headed", - "test:build": "cross-env FAST_REFRESH=false BROWSER=none PORT=9001 CYPRESS_WS_URL=http://127.0.0.1:9002 REACT_APP_API_URL=http://127.0.0.1:9001 concurrently --kill-others --success first \"npm run start\" \"wait-on http://127.0.0.1:9001 && cypress run\"", + "test:build": "cross-env NODE_ENV=production FAST_REFRESH=false BROWSER=none PORT=9001 CYPRESS_WS_URL=http://127.0.0.1:9002 REACT_APP_API_URL=http://127.0.0.1:9001 concurrently --kill-others --success first \"npm run start\" \"wait-on http://127.0.0.1:9001 && cypress run\"", "release": "standard-version", "lint": "npm run lint:scripts && npm run lint:style", "lint:scripts": "eslint src && tsc", diff --git a/src/api/apiAgencySelection.ts b/src/api/apiAgencySelection.ts index 8f7acc972..9c89ad7b6 100644 --- a/src/api/apiAgencySelection.ts +++ b/src/api/apiAgencySelection.ts @@ -9,7 +9,7 @@ export const apiAgencySelection = async ( ...params }: { postcode?: string; - consultingType?: number | undefined; + consultingType?: number; topicId?: number; age?: number; gender?: string; diff --git a/src/api/apiGetTopicGroups.ts b/src/api/apiGetTopicGroups.ts index 334c17c9c..87fe0bb33 100644 --- a/src/api/apiGetTopicGroups.ts +++ b/src/api/apiGetTopicGroups.ts @@ -1,4 +1,4 @@ -import { TopicGroup } from '../globalState/interfaces/TopicGroups'; +import { TopicGroup } from '../globalState/interfaces'; import { endpoints } from '../resources/scripts/endpoints'; import { fetchData, FETCH_ERRORS, FETCH_METHODS } from './fetchData'; @@ -7,7 +7,7 @@ export const apiGetTopicGroups = async (): Promise<{ }> => { return fetchData({ url: endpoints.topicGroups, - responseHandling: [FETCH_ERRORS.EMPTY], + responseHandling: [FETCH_ERRORS.EMPTY, FETCH_ERRORS.NO_MATCH], method: FETCH_METHODS.GET }); }; diff --git a/src/components/input/input.tsx b/src/components/input/input.tsx index 55a1e349d..6344798cd 100644 --- a/src/components/input/input.tsx +++ b/src/components/input/input.tsx @@ -147,15 +147,15 @@ export const Input = ({ }} sx={{ '&[type=number]': { - '-moz-appearance': 'textfield' + MozAppearance: 'textfield' }, '&::-webkit-outer-spin-button': { - '-webkit-appearance': 'none', - 'margin': 0 + WebkitAppearance: 'none', + margin: 0 }, '&::-webkit-inner-spin-button': { - '-webkit-appearance': 'none', - 'margin': 0 + WebkitAppearance: 'none', + margin: 0 }, 'mt': '24px', '& legend': { @@ -205,7 +205,7 @@ export const Input = ({ setShrink(true); }} onBlur={handleBlur} - > + /> {info && !inputError && !showSuccessMessage && ( )} - {multipleCriteria && - multipleCriteria.map((criteria) => { - return ( - - {getMultipleCriteriaDesign(criteria).icon}{' '} - {t(criteria.info)} - - ); - })} + {multipleCriteria?.map((criteria) => ( + + {getMultipleCriteriaDesign(criteria).icon}{' '} + {t(criteria.info)} + + ))} ); }; diff --git a/src/containers/registration/hooks/useAgenciesForRegistration.ts b/src/containers/registration/hooks/useAgenciesForRegistration.ts index fbe24654c..918d428fa 100644 --- a/src/containers/registration/hooks/useAgenciesForRegistration.ts +++ b/src/containers/registration/hooks/useAgenciesForRegistration.ts @@ -13,7 +13,7 @@ import { UrlParamsContext } from '../../../globalState/provider/UrlParamsProvide import { VALID_POSTCODE_LENGTH } from '../../../components/agencySelection/agencySelectionHelpers'; interface AgenciesForRegistrationArgs { - consultingType: ConsultingTypeInterface; + consultingType?: ConsultingTypeInterface; postcode: string; topic: TopicsDataInterface; } @@ -59,7 +59,7 @@ export const useAgenciesForRegistration = ({ const allAgencies = useMemo(() => { // As long as no consulting type or topic is selected we can't show any agencies - if (!consultingType || topicsEnabledAndUnSelected) { + if (!consultingType && topicsEnabledAndUnSelected) { return []; } @@ -131,8 +131,9 @@ export const useAgenciesForRegistration = ({ apiAgencySelection( { ...(autoSelectPostcode ? {} : { postcode }), - consultingType: consultingType?.id, topicId: topic?.id, + // API will ignore consultingType if topicId isset but its required to be send + consultingType: topic?.id || consultingType?.id, fetchConsultingTypeDetails: true }, abortController.signal diff --git a/src/extensions/components/app/app.tsx b/src/extensions/components/app/app.tsx deleted file mode 100644 index 658d2cbd1..000000000 --- a/src/extensions/components/app/app.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import * as React from 'react'; -import { ThemeProvider } from '@mui/material'; -import { UrlParamsProvider } from '../../../globalState/provider/UrlParamsProvider'; -import { RegistrationProvider } from '../../../globalState'; -import { App as CoreApp, AppProps } from '../../../components/app/app'; -import { lazy } from 'react'; -import '../../../resources/styles/mui-variables-mapping'; -import theme from '../../theme'; - -const Registration = lazy(() => - import('../registration/Registration').then((m) => ({ - default: m.Registration - })) -); - -const NewRegistration = () => ( - - - - - -); - -export const App = (props: AppProps) => { - // The login is possible both at the root URL as well as with an - // optional resort name. Since resort names are dynamic, we have - // to find out if the provided path is a resort name. If not, we - // use the authenticated app as a catch-all fallback. - - const extraRoutes = [ - ...props.extraRoutes, - { - route: { - path: [ - '/registration', - '/registration/topic-selection', - '/registration/zipcode', - '/registration/account-data', - '/registration/agency-selection' - ] - }, - component: NewRegistration - } - ]; - - return ( - - - - ); -}; diff --git a/src/extensions/components/consultingTypes/ConsultingTypes.styles.scss b/src/extensions/components/consultingTypes/ConsultingTypes.styles.scss deleted file mode 100644 index c87469db5..000000000 --- a/src/extensions/components/consultingTypes/ConsultingTypes.styles.scss +++ /dev/null @@ -1,63 +0,0 @@ -.consultingTypes { - padding: 0; - - @include breakpoint($fromMedium) { - width: 490px; - } - - &__intro { - font-size: $font-size-h3; - line-height: 32px; - } - - &__groupSelect { - max-width: 100%; - margin: $grid-base-three 0; - padding: 0; - } - - &__children { - margin: $grid-base-six -#{$grid-base-three} 0; - } - - &__learnMore { - display: flex; - align-items: flex-start; - margin-top: $grid-base-eight; - appearance: none; - border: none; - background: transparent; - cursor: pointer; - text-decoration: underline; - padding-left: 0; - - &:hover { - color: var(--skin-color-primary-hover, $hover-primary); - } - } - - &__learnMoreIcon { - margin-right: $grid-base; - position: relative; - color: $primary; - } - - &__learnMoreText { - color: $primary; - &:hover { - color: var(--skin-color-primary-hover, $hover-primary); - } - } - - &__explanationOverlay .overlay__content { - text-align: left; - } - - .button-as-link { - &:focus-visible { - outline: $focus-outline; - outline-offset: 4px; - border-radius: $login-button-focus-border-radius; - } - } -} diff --git a/src/extensions/components/consultingTypes/ConsultingTypes.tsx b/src/extensions/components/consultingTypes/ConsultingTypes.tsx deleted file mode 100644 index 2ec79a776..000000000 --- a/src/extensions/components/consultingTypes/ConsultingTypes.tsx +++ /dev/null @@ -1,246 +0,0 @@ -import * as React from 'react'; -import { useEffect, useState, useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { apiGetConsultingTypeGroups } from '../../api/apiGetConsultingTypeGroups'; -import { ConsultingTypeGroupInterface } from '../../globalState/interfaces/ConsultingTypeGroupInterface'; -import { Stage } from '../stage/stage'; -import { ConsultingTypesGroupChild } from './ConsultingTypesGroupChild'; -import { ConsultingTypesOverlay } from './ConsultingTypesOverlay'; -import './ConsultingTypes.styles.scss'; -import useIsFirstVisit from '../../../utils/useIsFirstVisit'; -import { Headline } from '../../../components/headline/Headline'; -import { InfoIcon } from '../../../resources/img/icons'; -import { LoadingIndicator } from '../../../components/loadingIndicator/LoadingIndicator'; -import { SelectDropdown } from '../../../components/select/SelectDropdown'; -import { StageLayout } from '../../../components/stageLayout/StageLayout'; -import { Text } from '../../../components/text/Text'; - -export const ConsultingTypes = () => { - const { t: translate } = useTranslation(['common', 'consultingTypes']); - - const [loadedConsultingTypeGroups, setLoadedConsultingTypeGroups] = - useState([]); - - const [consultingTypeGroups, setConsultingTypeGroups] = useState< - (ConsultingTypeGroupInterface & { - id: string; - })[] - >([]); - - const [loading, setLoading] = useState(true); - const [selectedGroupId, setSelectedGroupId] = useState(null); - const [expandedConsultingTypeId, setExpandedConsultingTypeId] = - useState(null); - const [isExplanationOverlayOpen, setIsExplanationOverlayOpen] = - useState(false); - const [selectedGroup, setSelectedGroup] = useState(null); - - useEffect(() => { - if (selectedGroupId === null) { - return; - } - setSelectedGroup( - consultingTypeGroups.find((group) => group.id === selectedGroupId) - ); - }, [selectedGroupId, consultingTypeGroups]); - - const handleGroupSelect = useCallback((item) => { - setSelectedGroupId(item.value); - setExpandedConsultingTypeId(undefined); - }, []); - - const handleConsultingTypeToggle = useCallback( - (consultingTypeId) => { - setExpandedConsultingTypeId( - consultingTypeId === expandedConsultingTypeId - ? undefined - : consultingTypeId - ); - }, - [expandedConsultingTypeId] - ); - - const handleExplanationOverlayToggle = () => { - setIsExplanationOverlayOpen(!isExplanationOverlayOpen); - }; - - const sortConsultingTypeGroups = useCallback( - (groups: ConsultingTypeGroupInterface[]) => - groups - // Translate titles before sorting - .map( - ( - group - ): ConsultingTypeGroupInterface & { - id: string; - } => ({ - ...group, - id: group.consultingTypes - .map((consultingType) => consultingType.id) - .sort() - .join('-'), - title: translate( - [ - `consultingTypeGroup.${group.title}.title`, - group.title - ], - { ns: 'consultingTypes' } - ) - }) - ) - .sort((a, b) => (a.title < b.title ? -1 : 1)) - .map((group) => ({ - ...group, - consultingTypes: group.consultingTypes - .slice() - // Translate titles before sorting - .map((consultingType) => ({ - ...consultingType, - titles: { - ...consultingType.titles, - long: translate( - [ - `consultingType.${consultingType.id}.titles.long`, - consultingType.titles.long - ], - { ns: 'consultingTypes' } - ), - default: translate( - [ - `consultingType.${consultingType.id}.titles.default`, - consultingType.titles.default - ], - { ns: 'consultingTypes' } - ) - } - })) - .sort((a, b) => - a.titles.long < b.titles.long ? -1 : 1 - ) - })), - [translate] - ); - - useEffect(() => { - apiGetConsultingTypeGroups() - .then(setLoadedConsultingTypeGroups) - .catch((error) => { - console.log(error); - }); - }, []); - - useEffect(() => { - if (!loadedConsultingTypeGroups) { - return; - } - - setConsultingTypeGroups( - sortConsultingTypeGroups(loadedConsultingTypeGroups) - ); - setLoading(false); - - return () => { - setLoading(true); - }; - }, [loadedConsultingTypeGroups, sortConsultingTypeGroups]); - - const isFirstVisit = useIsFirstVisit(); - - if (loading) { - return null; - } - - return ( - - } - > - {consultingTypeGroups == null ? ( - - ) : ( -
- - - ({ - value: group.id, - label: group.title - }))} - selectInputLabel={translate( - 'consultingTypes.selectGroup' - )} - handleDropdownSelect={handleGroupSelect} - useIconOption={false} - isSearchable={false} - menuPlacement="bottom" - /> -
- {selectedGroup !== null && - selectedGroup.consultingTypes.map( - (consultingType) => ( - - handleConsultingTypeToggle( - consultingType.id - ) - } - groupChild={consultingType} - /> - ) - )} -
- -
- )} - - {isExplanationOverlayOpen && ( - - )} -
- ); -}; diff --git a/src/extensions/components/consultingTypes/ConsultingTypesAgencySelection.styles.scss b/src/extensions/components/consultingTypes/ConsultingTypesAgencySelection.styles.scss deleted file mode 100644 index c3d88ca86..000000000 --- a/src/extensions/components/consultingTypes/ConsultingTypesAgencySelection.styles.scss +++ /dev/null @@ -1,41 +0,0 @@ -.consultingTypesAgencySelection { - margin-top: $grid-base-three; - - &__innerWrapper { - @include breakpoint($xlarge) { - display: flex; - } - } - - &__postcode { - @include breakpoint($xlarge) { - max-width: 200px; - } - } - - &__register { - margin-top: $grid-base-three; - - button { - width: 100%; - } - - @include breakpoint($xlarge) { - margin-top: 0; - margin-left: $grid-base-two; - } - } - - &__postcodeFallback { - margin-top: $grid-base-three; - - p { - margin-bottom: 20px; - } - - a { - display: block; - text-decoration: underline; - } - } -} diff --git a/src/extensions/components/consultingTypes/ConsultingTypesAgencySelection.tsx b/src/extensions/components/consultingTypes/ConsultingTypesAgencySelection.tsx deleted file mode 100644 index 104fb1adb..000000000 --- a/src/extensions/components/consultingTypes/ConsultingTypesAgencySelection.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import * as React from 'react'; -import { useState, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import './ConsultingTypesAgencySelection.styles.scss'; -import { useAppConfig } from '../../../hooks/useAppConfig'; -import { VALID_POSTCODE_LENGTH } from '../../../components/agencySelection/agencySelectionHelpers'; -import { apiAgencySelection, FETCH_ERRORS } from '../../../api'; -import { parsePlaceholderString } from '../../../utils/parsePlaceholderString'; -import { InputField } from '../../../components/inputField/InputField'; -import { PinIcon } from '../../../resources/img/icons'; -import { Button, BUTTON_TYPES } from '../../../components/button/Button'; -import { Notice } from '../../../components/notice/Notice'; -import { - AgencyDataInterface, - ConsultingTypeInterface -} from '../../../globalState/interfaces'; -import { AskerRegistrationExternalAgencyOverlay } from '../../../components/profile/AskerRegistrationExternalAgencyOverlay'; -import { Text } from '../../../components/text/Text'; - -interface ConsultingTypesAgencySelectionProps { - consultingType: ConsultingTypeInterface; -} - -const ConsultingTypesAgencySelection = ({ - consultingType -}: ConsultingTypesAgencySelectionProps) => { - const settings = useAppConfig(); - const [postcode, setPostcode] = useState(''); - const [agency, setAgency] = useState(); - const [postcodeFallbackLink, setPostcodeFallbackLink] = useState(); - const [externalAgencyOverlayActive, setExternalAgencyOverlayActive] = - useState(null); - const { t: translate } = useTranslation(); - - const handlePostcodeInput = (e) => { - setPostcode(e.target.value); - setPostcodeFallbackLink(null); - }; - - const handleButton = () => { - if (agency.external) { - if (!agency.url) { - console.error( - "Agency is external but didn't provide a `url`. Failed to redirect." - ); - return; - } - setExternalAgencyOverlayActive(true); - } else { - window.location.href = `/${consultingType.slug}/registration?postcode=${postcode}`; - } - }; - - useEffect(() => { - setAgency(undefined); - - if (postcode.length !== VALID_POSTCODE_LENGTH) { - return; - } - - let isCanceled = false; - apiAgencySelection({ - postcode, - consultingType: consultingType.id - }) - .then((result) => { - if (isCanceled) return; - - if (!result || result.length === 0) { - console.error( - 'Agency selection returned an empty result. This should never happen.' - ); - return; - } - setAgency(result[0]); - }) - .catch((error) => { - if (error.message === FETCH_ERRORS.EMPTY) { - setPostcodeFallbackLink( - parsePlaceholderString(settings.postcodeFallbackUrl, { - url: consultingType.urls - .registrationPostcodeFallbackUrl, - postcode: postcode - }) - ); - } - - return null; - }); - - return () => { - isCanceled = true; - }; - }, [ - consultingType.id, - consultingType.urls.registrationPostcodeFallbackUrl, - postcode, - settings.postcodeFallbackUrl - ]); - - const handleExternalAgencyOverlayAction = () => { - setExternalAgencyOverlayActive(false); - }; - - return ( -
-
- - }} - inputHandle={handlePostcodeInput} - /> -
- {postcodeFallbackLink != null && ( - - - - {translate( - 'registration.agencySelection.postcode.search' - )} - - - )} - {externalAgencyOverlayActive && ( - - )} -
- ); -}; - -export default ConsultingTypesAgencySelection; diff --git a/src/extensions/components/consultingTypes/ConsultingTypesGroupChild.styles.scss b/src/extensions/components/consultingTypes/ConsultingTypesGroupChild.styles.scss deleted file mode 100644 index fa4ae97c6..000000000 --- a/src/extensions/components/consultingTypes/ConsultingTypesGroupChild.styles.scss +++ /dev/null @@ -1,55 +0,0 @@ -.consultingTypesGroupChild { - &:last-of-type:not(.consultingTypesGroupChild--expanded) { - border-bottom: 1px solid $grey-light; - } - - &:not(.consultingTypesGroupChild--expanded) { - border-top: 1px solid $grey-light; - } - - &:hover { - background-color: $background-lighter; - } - - &--expanded { - border: none; - background-color: $background-lighter; - - & + .consultingTypesGroupChild { - border-top: none; - } - - .consultingTypesGroupChild__titleLabel { - font-weight: $font-weight-bold; - color: $secondary; - } - } - - &__title { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - padding: $grid-base-three; - background: transparent; - appearance: none; - border: none; - cursor: pointer; - } - - &__titleLabel { - color: $primary; - } - - &__icon { - margin-left: $grid-base-three; - - path { - fill: $primary-4; - } - } - - &__details { - padding: 0 $grid-base-three $grid-base-three; - } -} diff --git a/src/extensions/components/consultingTypes/ConsultingTypesGroupChild.tsx b/src/extensions/components/consultingTypes/ConsultingTypesGroupChild.tsx deleted file mode 100644 index 69fd2a2c4..000000000 --- a/src/extensions/components/consultingTypes/ConsultingTypesGroupChild.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import clsx from 'clsx'; -import * as React from 'react'; -import { useTranslation } from 'react-i18next'; -import { ConsultingTypesGroupChildDetails } from './ConsultingTypesGroupChildDetails'; -import { ConsultingTypeGroupChildInterface } from '../../globalState/interfaces/ConsultingTypeGroupInterface'; -import { ArrowDownIcon, ArrowUpIcon } from '../../../resources/img/icons'; -import { Text } from '../../../components/text/Text'; -import './ConsultingTypesGroupChild.styles.scss'; - -interface ConsultingTypesGroupChildProps { - groupChild: ConsultingTypeGroupChildInterface; - handleToggleExpanded(): void; - isExpanded: boolean; -} - -export const ConsultingTypesGroupChild = ({ - groupChild, - handleToggleExpanded, - isExpanded -}: ConsultingTypesGroupChildProps) => { - const { t: translate } = useTranslation(['consultingTypes']); - const iconProps = { - width: 14, - className: 'consultingTypesGroupChild__icon' - }; - - return ( -
- - {isExpanded && ( - - )} -
- ); -}; diff --git a/src/extensions/components/consultingTypes/ConsultingTypesGroupChildDetails.tsx b/src/extensions/components/consultingTypes/ConsultingTypesGroupChildDetails.tsx deleted file mode 100644 index 960036f8b..000000000 --- a/src/extensions/components/consultingTypes/ConsultingTypesGroupChildDetails.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import * as React from 'react'; -import { useEffect, useState } from 'react'; -import ConsultingTypesAgencySelection from './ConsultingTypesAgencySelection'; -import './consultingTypesGroupChildDetails.styles.scss'; -import { useTranslation } from 'react-i18next'; -import { apiGetConsultingType } from '../../../api'; -import { LoadingIndicator } from '../../../components/loadingIndicator/LoadingIndicator'; -import { ConsultingTypeInterface } from '../../../globalState/interfaces'; -import { Text } from '../../../components/text/Text'; -import { ArrowRightIcon } from '../../../resources/img/icons'; -interface ConsultingTypesGroupChildDetailsProps { - className?: string; - consultingTypeId: number; -} - -export const ConsultingTypesGroupChildDetails = ({ - className, - consultingTypeId -}: ConsultingTypesGroupChildDetailsProps) => { - const [consultingType, setConsultingType] = - useState(); - const { t: translate } = useTranslation(['common', 'consultingTypes']); - - useEffect(() => { - let isCanceled; - - apiGetConsultingType({ consultingTypeId }).then((result) => { - if (isCanceled) return; - setConsultingType(result); - }); - return () => { - isCanceled = true; - }; - }, [consultingTypeId]); - - if (consultingType == null) { - return ( -
- -
- ); - } - - return ( -
- - {consultingType.furtherInformation.url && ( - - - - - )} - -
- {translate('consultingTypes.details.explanation.description') - .split('\n') - .map((part, index) => ( - - ))} -
- -
- ); -}; diff --git a/src/extensions/components/consultingTypes/ConsultingTypesOverlay.styles.scss b/src/extensions/components/consultingTypes/ConsultingTypesOverlay.styles.scss deleted file mode 100644 index 9bf15f8e2..000000000 --- a/src/extensions/components/consultingTypes/ConsultingTypesOverlay.styles.scss +++ /dev/null @@ -1,12 +0,0 @@ -.consultingTypesOverlay { - &__serviceExplanation { - display: grid; - grid-template-columns: auto; - padding: $grid-base-four 0 $grid-base; - row-gap: $grid-base-three; - - .text { - text-align: left; - } - } -} diff --git a/src/extensions/components/consultingTypes/ConsultingTypesOverlay.tsx b/src/extensions/components/consultingTypes/ConsultingTypesOverlay.tsx deleted file mode 100644 index 05448d865..000000000 --- a/src/extensions/components/consultingTypes/ConsultingTypesOverlay.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import './ConsultingTypesOverlay.styles.scss'; -import { - Overlay, - OVERLAY_FUNCTIONS -} from '../../../components/overlay/Overlay'; -import { ServiceExplanation } from '../../../components/serviceExplanation/ServiceExplanation'; -import { BUTTON_TYPES } from '../../../components/button/Button'; - -interface ConsultingTypesOverlayProps { - handleOverlay?(): void; -} - -export const ConsultingTypesOverlay = ({ - handleOverlay -}: ConsultingTypesOverlayProps) => { - const { t: translate } = useTranslation(); - return ( - - ), - buttonSet: [ - { - label: translate('consultingTypes.overlay.close'), - function: OVERLAY_FUNCTIONS.CLOSE, - type: BUTTON_TYPES.PRIMARY - } - ] - }} - handleOverlay={handleOverlay} - /> - ); -}; diff --git a/src/extensions/components/consultingTypes/consultingTypesGroupChildDetails.styles.scss b/src/extensions/components/consultingTypes/consultingTypesGroupChildDetails.styles.scss deleted file mode 100644 index d7abf504a..000000000 --- a/src/extensions/components/consultingTypes/consultingTypesGroupChildDetails.styles.scss +++ /dev/null @@ -1,40 +0,0 @@ -.consultingTypesGroupChildDetails { - &__loadingIndicator { - display: flex; - justify-content: center; - padding: $grid-base-three; - - // Reserve some extra space to avoid the layout shifting too much - min-height: 280px; - } - - &__details { - display: inline-flex; - align-items: flex-start; - margin-top: $grid-base-two; - } - - &__detailsIcon { - position: relative; - margin-right: $grid-base * 0.5; - top: -3px; // Align to baseline - } - - &__detailsLabel { - color: $primary; - } - - &__explanationTitle { - margin-top: $grid-base-three; - font-weight: $font-weight-bold; - } - - &__explanationDescription { - margin-top: $grid-base * 1.5; - white-space: pre-wrap; - } - - &__explanationDescriptionPart { - margin-top: $grid-base * 0.5; - } -} diff --git a/src/extensions/components/registration/Registration.tsx b/src/extensions/components/registration/Registration.tsx index e75ee0d13..b28bc4819 100644 --- a/src/extensions/components/registration/Registration.tsx +++ b/src/extensions/components/registration/Registration.tsx @@ -1,24 +1,30 @@ import { Typography, Link, Button, Box } from '@mui/material'; import * as React from 'react'; -import { useState, useEffect, useContext, useCallback } from 'react'; +import { useState, useEffect, useContext, useCallback, useMemo } from 'react'; +import { + Route, + Switch, + useHistory, + useLocation, + useParams, + useRouteMatch, + generatePath, + Link as RouterLink +} from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { Helmet } from 'react-helmet'; + import { StageLayout } from '../../../components/stageLayout/StageLayout'; import useIsFirstVisit from '../../../utils/useIsFirstVisit'; import { ReactComponent as HelloBannerIcon } from '../../../resources/img/illustrations/hello-banner.svg'; import { StepBar } from './stepBar/StepBar'; -import { AccountData } from './accountData/AccountData'; -import { ZipcodeInput } from './zipcodeInput/ZipcodeInput'; -import { AgencySelection } from './agencySelection/AgencySelection'; -import { TopicSelection } from './topicSelection/TopicSelection'; -import { useHistory, useLocation } from 'react-router-dom'; -import { useTranslation } from 'react-i18next'; -import { Link as RouterLink } from 'react-router-dom'; import { WelcomeScreen } from './welcomeScreen/WelcomeScreen'; import { RegistrationContext, TenantContext, - registrationSessionStorageKey + registrationSessionStorageKey, + RegistrationData } from '../../../globalState'; -import { Helmet } from 'react-helmet'; import { GlobalComponentContext } from '../../../globalState/provider/GlobalComponentContext'; import { OVERLAY_FUNCTIONS, @@ -32,29 +38,34 @@ import { endpoints } from '../../../resources/scripts/endpoints'; import { apiPostRegistration } from '../../../api'; import { useAppConfig } from '../../../hooks/useAppConfig'; import { REGISTRATION_DATA_VALIDATION } from './registrationDataValidation'; +import { UrlParamsContext } from '../../../globalState/provider/UrlParamsProvider'; export const Registration = () => { const { t } = useTranslation(['common', 'consultingTypes', 'agencies']); const settings = useAppConfig(); const isFirstVisit = useIsFirstVisit(); + const location = useLocation(); + const history = useHistory(); + const { path } = useRouteMatch(); + const { step, topicSlug } = useParams<{ + step: string; + topicSlug: string; + }>(); + const { Stage } = useContext(GlobalComponentContext); const { disabledNextButton, - setDisabledNextButton, - updateSessionStorageWithPreppedData, - refreshSessionStorageRegistrationData, - sessionStorageRegistrationData, - availableSteps, - dataPrepForSessionStorage, - consultant, - isConsultantLink + updateRegistrationData, + registrationData, + availableSteps } = useContext(RegistrationContext); + const { consultant: preselectedConsultant } = useContext(UrlParamsContext); const { tenant } = useContext(TenantContext); - const [currentStep, setCurrentStep] = useState(1); + + const [stepData, setStepData] = useState>({}); const [redirectOverlayActive, setRedirectOverlayActive] = useState(false); - const location = useLocation(); - const history = useHistory(); + const handleOverlayAction = (buttonFunction: string) => { if (buttonFunction === OVERLAY_FUNCTIONS.REDIRECT_WITH_BLUR) { redirectToApp(); @@ -74,27 +85,14 @@ export const Registration = () => { ] }; - const updateCurrentStep = () => { - const currentLocation = location?.pathname?.replace( - '/registration', - '' - ); - const step = availableSteps.findIndex( - (step) => step?.urlSuffix === currentLocation - ); - setCurrentStep(step === -1 ? 0 : step); - }; - - const checkForStepsWithMissingMandatoryFields = (): number[] => { - if (currentStep > 0) { + const checkForStepsWithMissingMandatoryFields = + useCallback((): number[] => { return availableSteps.reduce( (missingSteps, step, currentIndex) => { if ( step?.mandatoryFields?.some( (mandatoryField) => - sessionStorageRegistrationData?.[ - mandatoryField - ] === undefined + registrationData?.[mandatoryField] === undefined ) ) { return [...missingSteps, currentIndex]; @@ -103,62 +101,61 @@ export const Registration = () => { }, [] ); - } - return []; - }; + }, [availableSteps, registrationData]); - const onNextClick = () => { - updateSessionStorageWithPreppedData(); - }; + const onNextClick = useCallback(() => { + updateRegistrationData(stepData); + setStepData({}); + }, [updateRegistrationData, stepData]); - useEffect(() => { - setDisabledNextButton(true); - updateCurrentStep(); - refreshSessionStorageRegistrationData(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [location]); + const currStepIndex = useMemo( + () => availableSteps.findIndex(({ name }) => name === step), + [availableSteps, step] + ); useEffect(() => { // Check if mandatory fields from previous steps are missing const missingPreviousSteps = checkForStepsWithMissingMandatoryFields() .sort() - .filter((missingStep) => missingStep < currentStep); + .filter((missingStep) => missingStep < currStepIndex); if (missingPreviousSteps.length > 0) { history.push( - `/registration${ - availableSteps[missingPreviousSteps[0]]?.urlSuffix - }${location.search}` + `${generatePath(path, { topicSlug, step: availableSteps[missingPreviousSteps[0]]?.name })}${location.search}` ); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentStep, availableSteps, sessionStorageRegistrationData]); + }, [ + availableSteps, + checkForStepsWithMissingMandatoryFields, + history, + location.search, + currStepIndex, + path, + topicSlug + ]); const onRegisterClick = useCallback(() => { - const registrationData = { - ...sessionStorageRegistrationData, - ...dataPrepForSessionStorage, - agencyId: sessionStorageRegistrationData.agencyId.toString(), - postcode: sessionStorageRegistrationData.zipcode, + const data = { + ...registrationData, + ...stepData, + agencyId: registrationData.agency.id.toString(), + postcode: registrationData.zipcode, termsAccepted: 'true', preferredLanguage: 'de', - // ConsultingType and mainTopicId are identical for the MVP - consultingType: sessionStorageRegistrationData.mainTopicId, - ...(isConsultantLink - ? { consultantId: consultant?.consultantId } + consultingType: registrationData.agency.consultingType, + ...(preselectedConsultant + ? { consultantId: preselectedConsultant?.consultantId } : {}) }; if ( Object.keys(REGISTRATION_DATA_VALIDATION).every((item) => - REGISTRATION_DATA_VALIDATION[item].validation( - registrationData[item] - ) + REGISTRATION_DATA_VALIDATION[item].validation(data[item]) ) ) { apiPostRegistration( endpoints.registerAsker, - registrationData, + data, settings.multitenancyWithSingleDomainEnabled, tenant ).then(() => { @@ -167,14 +164,35 @@ export const Registration = () => { }); } }, [ - consultant?.consultantId, - dataPrepForSessionStorage, - isConsultantLink, - sessionStorageRegistrationData, + registrationData, + stepData, + preselectedConsultant, settings.multitenancyWithSingleDomainEnabled, tenant ]); + const [prevStepUrl, nextStepUrl] = useMemo( + () => [ + availableSteps[currStepIndex - 1] + ? `${generatePath(path, { topicSlug, step: availableSteps[currStepIndex - 1]?.name || 'welcome' })}${location.search}` + : null, + availableSteps[currStepIndex + 1] + ? `${generatePath(path, { topicSlug, step: availableSteps[currStepIndex + 1]?.name || 'welcome' })}${location.search}` + : null + ], + [availableSteps, currStepIndex, path, topicSlug, location.search] + ); + + const stepPaths = useMemo( + () => + availableSteps.reduce( + (acc, { name }) => + acc.concat(generatePath(path, { topicSlug, step: name })), + [] + ), + [availableSteps, path, topicSlug] + ); + return ( <> { width: '100%' }} > - {availableSteps[currentStep]?.component === 'welcome' ? ( - - ) : ( - <> + + @@ -210,125 +222,105 @@ export const Registration = () => { {t('registration.headline')}
- {} + - {availableSteps[currentStep]?.component === - 'topicSelection' && ( - - )} - {availableSteps[currentStep]?.component === - 'zipcode' && } - {availableSteps[currentStep]?.component === - 'agencySelection' && ( - - )} - {availableSteps[currentStep]?.component === - 'accountData' && } - - {availableSteps[currentStep]?.component !== - 'welcome' && ( + + {availableSteps.map( + ({ name, component: Component }) => ( + + + + ) + )} + + - - {currentStep > 0 && ( - - {t('registration.back')} - - )} - {currentStep === - availableSteps.length - 1 ? ( - - ) : ( - - )} - + {t('registration.back')} + + + {!nextStepUrl ? ( + + ) : ( + + )} - )} + - - )} + + + + + {redirectOverlayActive && ( diff --git a/src/extensions/components/registration/accountData/AccountData.tsx b/src/extensions/components/registration/accountData/AccountData.tsx index cf76ecaef..6544aa7cc 100644 --- a/src/extensions/components/registration/accountData/AccountData.tsx +++ b/src/extensions/components/registration/accountData/AccountData.tsx @@ -7,7 +7,14 @@ import { FormControlLabel } from '@mui/material'; import * as React from 'react'; -import { useState, useContext, useEffect } from 'react'; +import { + useState, + useContext, + useEffect, + VFC, + Dispatch, + SetStateAction +} from 'react'; import { useTranslation } from 'react-i18next'; import PersonIcon from '@mui/icons-material/Person'; import LockIcon from '@mui/icons-material/Lock'; @@ -19,7 +26,7 @@ import { hasSpecialChar } from '../../../../utils/validateInputValue'; import { LegalLinksContext } from '../../../../globalState/provider/LegalLinksProvider'; -import { RegistrationContext } from '../../../../globalState'; +import { RegistrationContext, RegistrationData } from '../../../../globalState'; import { apiGetIsUsernameAvailable } from '../../../../api/apiGetIsUsernameAvailable'; import { REGISTRATION_DATA_VALIDATION } from '../registrationDataValidation'; import LegalLinks from '../../../../components/legalLinks/LegalLinks'; @@ -43,7 +50,9 @@ export const passwordCriteria = [ } ]; -export const AccountData = () => { +export const AccountData: VFC<{ + onChange: Dispatch>>; +}> = ({ onChange }) => { const legalLinks = useContext(LegalLinksContext); const { t } = useTranslation(); const [password, setPassword] = useState(''); @@ -56,8 +65,7 @@ export const AccountData = () => { const [username, setUsername] = useState(''); const [isUsernameAvailable, setIsUsernameAvailable] = useState(true); - const { setDisabledNextButton, setDataForSessionStorage } = - useContext(RegistrationContext); + const { setDisabledNextButton } = useContext(RegistrationContext); useEffect(() => { if ( @@ -68,7 +76,7 @@ export const AccountData = () => { dataProtectionChecked ) { setDisabledNextButton(false); - setDataForSessionStorage({ username, password }); + onChange({ username, password }); } else { setDisabledNextButton(true); } diff --git a/src/extensions/components/registration/agencySelection/AgencyLanguages.tsx b/src/extensions/components/registration/agencySelection/AgencyLanguages.tsx index 32d6d9f15..b9b3f477d 100644 --- a/src/extensions/components/registration/agencySelection/AgencyLanguages.tsx +++ b/src/extensions/components/registration/agencySelection/AgencyLanguages.tsx @@ -1,6 +1,6 @@ import { Typography } from '@mui/material'; import * as React from 'react'; -import { useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { apiAgencyLanguages } from '../../../../api/apiAgencyLanguages'; import { LanguagesContext } from '../../../../globalState/provider/LanguagesProvider'; @@ -11,32 +11,36 @@ interface AgencyLanguagesProps { export const AgencyLanguages = ({ agencyId }: AgencyLanguagesProps) => { const { t } = useTranslation(); - const [languages, setLanguages] = useState(['de']); - const { fixed: fixedLanguages } = React.useContext(LanguagesContext); + const [languagesString, setLanguagesString] = useState(''); + const { fixed: fixedLanguages } = useContext(LanguagesContext); useEffect(() => { - if (agencyId !== undefined) { - apiAgencyLanguages(agencyId, false).then((res) => { - const allLanguages = [...fixedLanguages, ...res.languages]; - setLanguages( - allLanguages - .filter((element, index) => { - return allLanguages.indexOf(element) === index; - }) - .sort() + (async () => { + let languages = ['de']; + if (agencyId !== undefined) { + languages = await apiAgencyLanguages(agencyId, false).then( + (res) => (languages = [...fixedLanguages, ...res.languages]) ); - }); - } - }, [agencyId, fixedLanguages]); + } + + setLanguagesString( + languages + .filter( + (element, index) => languages.indexOf(element) === index + ) + .map( + (lang) => + `${t(`languages.${lang}`)} (${lang.toUpperCase()})` + ) + .sort((a, b) => a.localeCompare(b)) + .join(' | ') + ); + })(); + }, [agencyId, fixedLanguages, t]); return ( - {languages.map( - (lang, index) => - `${index !== 0 ? ' |' : ''} ${t( - `languages.${lang}` - )} (${lang.toUpperCase()})` - )} + {languagesString} ); }; diff --git a/src/extensions/components/registration/agencySelection/AgencySelection.tsx b/src/extensions/components/registration/agencySelection/AgencySelection.tsx index ca9e6b66f..48a4954c4 100644 --- a/src/extensions/components/registration/agencySelection/AgencySelection.tsx +++ b/src/extensions/components/registration/agencySelection/AgencySelection.tsx @@ -1,88 +1,56 @@ import * as React from 'react'; -import { useState, useEffect, VFC, useContext } from 'react'; +import { + useState, + useEffect, + VFC, + useContext, + Dispatch, + SetStateAction +} from 'react'; import { AgencySelectionResults } from './AgencySelectionResults'; -import { apiAgencySelection } from '../../../../api'; -import { RegistrationContext } from '../../../../globalState'; -import { AgencyDataInterface } from '../../../../globalState/interfaces'; +import { RegistrationContext, RegistrationData } from '../../../../globalState'; import { Typography } from '@mui/material'; import { useTranslation } from 'react-i18next'; +import { UrlParamsContext } from '../../../../globalState/provider/UrlParamsProvider'; +import { useAgenciesForRegistration } from '../../../../containers/registration/hooks/useAgenciesForRegistration'; export const AgencySelection: VFC<{ + onChange: Dispatch>>; nextStepUrl: string; onNextClick(): void; -}> = ({ nextStepUrl, onNextClick }) => { - const { - sessionStorageRegistrationData, - isConsultantLink, - consultant, - setDataForSessionStorage - } = useContext(RegistrationContext); - +}> = ({ nextStepUrl, onNextClick, onChange }) => { const { t } = useTranslation(); + const { registrationData } = useContext(RegistrationContext); + const { consultant: preselectedConsultant } = useContext(UrlParamsContext); + + const { isLoading, agencies } = useAgenciesForRegistration({ + topic: registrationData.mainTopic, + postcode: registrationData?.zipcode + }); + const [headlineZipcode, setHeadlineZipcode] = useState(''); - const [isLoading, setIsLoading] = useState(false); - const [results, setResults] = useState( - undefined - ); - useEffect(() => { - if (isConsultantLink) { - setResults( - consultant?.agencies?.filter((agency) => - agency?.topicIds?.includes( - sessionStorageRegistrationData?.mainTopicId - ) - ) - ); - } else if (sessionStorageRegistrationData?.zipcode?.length === 5) { - setHeadlineZipcode(sessionStorageRegistrationData.zipcode); - setResults(undefined); - (async () => { - setIsLoading(true); - try { - const agencyResponse = await apiAgencySelection({ - postcode: sessionStorageRegistrationData.zipcode, - // We will keep consultingTypeId identical to mainTopicId - consultingType: - sessionStorageRegistrationData.mainTopicId || - undefined, - topicId: - sessionStorageRegistrationData.mainTopicId || - undefined - }); - setResults(agencyResponse); - if ( - agencyResponse.every( - (agency) => - agency.id !== - sessionStorageRegistrationData.agencyId - ) - ) { - setDataForSessionStorage({ agencyId: undefined }); - } - } catch { - setResults([]); - } - setIsLoading(false); - })(); + useEffect(() => { + if (!preselectedConsultant && registrationData?.zipcode?.length === 5) { + setHeadlineZipcode(registrationData.zipcode); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [sessionStorageRegistrationData, consultant]); + }, [registrationData, preselectedConsultant]); return ( <> - {(isConsultantLink && consultant?.agencies?.length === 1) || - results?.length === 1 + {preselectedConsultant?.agencies?.length === 1 || + agencies?.length === 1 ? t('registration.agency.consultantheadline') : t('registration.agency.headline')} ); diff --git a/src/extensions/components/registration/agencySelection/AgencySelectionResults.tsx b/src/extensions/components/registration/agencySelection/AgencySelectionResults.tsx index 2ae005206..ff975953d 100644 --- a/src/extensions/components/registration/agencySelection/AgencySelectionResults.tsx +++ b/src/extensions/components/registration/agencySelection/AgencySelectionResults.tsx @@ -1,3 +1,4 @@ +import * as React from 'react'; import { Typography, FormControlLabel, @@ -8,15 +9,20 @@ import { Button, Link } from '@mui/material'; -import * as React from 'react'; -import { useContext, useEffect, useState } from 'react'; +import { + Dispatch, + SetStateAction, + useContext, + useEffect, + useState +} from 'react'; import TaskAltIcon from '@mui/icons-material/TaskAlt'; import NoResultsIllustration from '../../../../resources/img/illustrations/no-results.svg'; import ConsultantIllustration from '../../../../resources/img/illustrations/consultant-found.svg'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import { Loading } from '../../../../components/app/Loading'; import { useTranslation } from 'react-i18next'; -import { RegistrationContext } from '../../../../globalState'; +import { RegistrationContext, RegistrationData } from '../../../../globalState'; import { AgencyDataInterface } from '../../../../globalState/interfaces'; import { AgencyLanguages } from './AgencyLanguages'; import { parsePlaceholderString } from '../../../../utils/parsePlaceholderString'; @@ -24,8 +30,10 @@ import { useAppConfig } from '../../../../hooks/useAppConfig'; import { VFC } from 'react'; import { MetaInfo } from '../metaInfo/MetaInfo'; import { REGISTRATION_DATA_VALIDATION } from '../registrationDataValidation'; +import { UrlParamsContext } from '../../../../globalState/provider/UrlParamsProvider'; interface AgencySelectionResultsProps { + onChange: Dispatch>>; isLoading?: boolean; zipcode?: string; results?: AgencyDataInterface[]; @@ -34,6 +42,7 @@ interface AgencySelectionResultsProps { } export const AgencySelectionResults: VFC = ({ + onChange, isLoading, zipcode, results, @@ -42,15 +51,12 @@ export const AgencySelectionResults: VFC = ({ }) => { const { t } = useTranslation(); const settings = useAppConfig(); - const { - setDisabledNextButton, - setDataForSessionStorage, - sessionStorageRegistrationData, - isConsultantLink - } = useContext(RegistrationContext); + const { setDisabledNextButton, registrationData } = + useContext(RegistrationContext); + const { consultant: preselectedConsultant } = useContext(UrlParamsContext); - const [agencyId, setAgencyId] = useState( - sessionStorageRegistrationData?.agencyId + const [selectedAgency, setSelectedAgency] = useState( + registrationData?.agency ); useEffect(() => { @@ -59,10 +65,10 @@ export const AgencySelectionResults: VFC = ({ results?.length > 0 && results?.every((agency) => agency.external) ) { - setAgencyId(undefined); + setSelectedAgency(undefined); setDisabledNextButton(true); - setDataForSessionStorage({ - agencyId: undefined + onChange({ + agency: undefined }); return; } @@ -71,43 +77,36 @@ export const AgencySelectionResults: VFC = ({ results?.length === 1 && results?.every((agency) => !agency.external) ) { - setAgencyId(results[0].id); + setSelectedAgency(results[0]); setDisabledNextButton(false); - setDataForSessionStorage({ - agencyId: results[0].id + onChange({ + agency: results[0] }); return; } + if ( // invalid agencyId, needs to be removed - agencyId && + selectedAgency && results?.length === 0 ) { setDisabledNextButton(true); - setDataForSessionStorage({ - agencyId: undefined + onChange({ + agency: undefined }); return; } + if ( // valid agencyId REGISTRATION_DATA_VALIDATION.agencyId.validation( - agencyId?.toString() + selectedAgency?.id?.toString() ) ) { setDisabledNextButton(false); - setDataForSessionStorage({ - agencyId: agencyId - }); - return; + onChange({ agency: selectedAgency }); } - }, [ - agencyId, - results, - setDataForSessionStorage, - setDisabledNextButton, - zipcode - ]); + }, [selectedAgency, results, onChange, setDisabledNextButton, zipcode]); return ( <> @@ -123,7 +122,7 @@ export const AgencySelectionResults: VFC = ({ )} - {!!results && !isLoading && !isConsultantLink && ( + {!!results && !isLoading && !preselectedConsultant && ( {t('registration.agency.result.headline') + ' ' + zipcode}: @@ -244,9 +243,7 @@ export const AgencySelectionResults: VFC = ({ )} {/* one Result */} {results?.length === 1 && - !results?.every((agency) => { - return agency.external; - }) && ( + !results?.every((agency) => agency.external) && ( = ({ headline={results?.[0].name} description={results?.[0].description} onOverlayClose={() => - setAgencyId(undefined) + setSelectedAgency(undefined) } backButtonLabel={t( 'registration.agency.infoOverlay.backButtonLabel' @@ -312,10 +309,10 @@ export const AgencySelectionResults: VFC = ({ nextStepUrl={nextStepUrl} onNextClick={onNextClick} onOverlayOpen={() => { - setDataForSessionStorage({ - agencyId: results?.[0].id + onChange({ + agency: results?.[0] }); - setAgencyId(results?.[0].id); + setSelectedAgency(results?.[0]); }} /> )} @@ -333,6 +330,7 @@ export const AgencySelectionResults: VFC = ({ > {results?.map((agency, index) => ( = ({ { setDisabledNextButton(false); - setAgencyId(agency.id); - setDataForSessionStorage({ - agencyId: agency.id - }); + setSelectedAgency(agency); + onChange({ agency }); }} sx={{ alignItems: 'flex-start' @@ -354,7 +350,10 @@ export const AgencySelectionResults: VFC = ({ value={agency.id} control={ } label={ @@ -390,7 +389,7 @@ export const AgencySelectionResults: VFC = ({ headline={agency.name} description={agency.description} onOverlayClose={() => - setAgencyId(undefined) + setSelectedAgency(undefined) } backButtonLabel={t( 'registration.agency.infoOverlay.backButtonLabel' @@ -401,10 +400,8 @@ export const AgencySelectionResults: VFC = ({ nextStepUrl={nextStepUrl} onNextClick={onNextClick} onOverlayOpen={() => { - setDataForSessionStorage({ - agencyId: agency.id - }); - setAgencyId(agency.id); + onChange({ agency }); + setSelectedAgency(agency); }} /> )} diff --git a/src/extensions/components/registration/agencySelection/agencySelection.cy.tsx b/src/extensions/components/registration/agencySelection/agencySelection.cy.tsx index d1c65e786..986a2863a 100644 --- a/src/extensions/components/registration/agencySelection/agencySelection.cy.tsx +++ b/src/extensions/components/registration/agencySelection/agencySelection.cy.tsx @@ -15,10 +15,10 @@ it('Get results for zipcode', () => { {}, - setDataForSessionStorage: () => {}, - isConsultantLink: false, - consultant: null, - sessionStorageRegistrationData: { + updateRegistrationData: () => {}, + registrationData: { + agency: null, + mainTopic: null, zipcode: '12345', username: null, agencyId: null, @@ -27,7 +27,11 @@ it('Get results for zipcode', () => { } }} > - {}} /> + {}} + onChange={() => {}} + /> ); diff --git a/src/extensions/components/registration/infoDrawer/InfoDrawer.tsx b/src/extensions/components/registration/infoDrawer/InfoDrawer.tsx index c10a35e00..dc16ee96c 100644 --- a/src/extensions/components/registration/infoDrawer/InfoDrawer.tsx +++ b/src/extensions/components/registration/infoDrawer/InfoDrawer.tsx @@ -1,31 +1,28 @@ import * as React from 'react'; -import { useState, useContext, useEffect } from 'react'; +import { useState, useContext } from 'react'; import { useTranslation } from 'react-i18next'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; import { Box, SwipeableDrawer, Typography } from '@mui/material'; import { RegistrationContext } from '../../../../globalState'; import { PreselectionError } from '../preselectionError/PreselectionError'; +import { UrlParamsContext } from '../../../../globalState/provider/UrlParamsProvider'; interface InfoDrawerProps { trigger?: boolean; } export const InfoDrawer = ({ trigger }: InfoDrawerProps) => { + const { hasAgencyError, hasConsultantError, hasTopicError } = + useContext(RegistrationContext); const { - preselectedAgency, - preselectedTopicName, - hasAgencyError, - hasConsultantError, - hasTopicError, - isConsultantLink, - preselectedData - } = useContext(RegistrationContext); + topic: preselectedTopic, + agency: preselectedAgency, + consultant: preselectedConsultant + } = useContext(UrlParamsContext); const { t } = useTranslation(); const drawerBleeding = 92; const [isDrawerOpen, setIsDrawerOpen] = useState(false); - const [topicName, setTopicName] = useState('-'); - const [agencyName, setAgencyName] = useState('-'); const toggleDrawer = (event: React.KeyboardEvent | React.MouseEvent) => { if ( @@ -39,157 +36,142 @@ export const InfoDrawer = ({ trigger }: InfoDrawerProps) => { setIsDrawerOpen(!isDrawerOpen); }; - useEffect(() => { - if (preselectedTopicName) { - setTopicName(preselectedTopicName); - } - if (preselectedAgency) { - setAgencyName(preselectedAgency.name); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [preselectedAgency, preselectedTopicName]); - - if ( - !(preselectedData.includes('tid') || preselectedData.includes('aid')) && - !isConsultantLink - ) { + if (!preselectedTopic && !preselectedAgency && !preselectedConsultant) { return null; } return ( - <> - .MuiPaper-root': { - top: -drawerBleeding, - overflow: 'visible' - }, - 'top': 0 + .MuiPaper-root': { + top: -drawerBleeding, + overflow: 'visible' + }, + 'top': 0 + }} + anchor="top" + onClose={() => setIsDrawerOpen(false)} + onOpen={() => setIsDrawerOpen(true)} + open={isDrawerOpen} + ModalProps={{ + keepMounted: true + }} + > + { + if (e.key === 'Enter') { + toggleDrawer(e); + } }} - anchor="top" - onClose={() => setIsDrawerOpen(false)} - onOpen={() => setIsDrawerOpen(true)} - open={isDrawerOpen} - ModalProps={{ - keepMounted: true + tabIndex={0} + sx={{ + 'px': '16px', + 'pt': '16px', + 'mt': trigger ? 0 : '48px', + 'position': 'relative', + 'borderBottomLeftRadius': 8, + 'borderBottomRightRadius': 8, + 'visibility': 'visible', + 'right': 0, + 'left': 0, + 'width': '100vw', + 'backgroundColor': 'primary.main', + 'color': 'white', + 'animationName': 'slideIn', + 'animationDuration': '0.8s', + 'animationDelay': '0.3s', + 'animationFillMode': 'forwards', + '@keyframes slideIn': { + '0%': { + top: 0 + }, + '100%': { + top: drawerBleeding + } + } }} > { - if (e.key === 'Enter') { - toggleDrawer(e); - } - }} - tabIndex={0} sx={{ - 'px': '16px', - 'pt': '16px', - 'mt': trigger ? 0 : '48px', - 'position': 'relative', - 'borderBottomLeftRadius': 8, - 'borderBottomRightRadius': 8, - 'visibility': 'visible', - 'right': 0, - 'left': 0, - 'width': '100vw', - 'backgroundColor': 'primary.main', - 'color': 'white', - 'animationName': 'slideIn', - 'animationDuration': '0.8s', - 'animationDelay': '0.3s', - 'animationFillMode': 'forwards', - '@keyframes slideIn': { - '0%': { - top: 0 - }, - '100%': { - top: drawerBleeding - } - } + opacity: isDrawerOpen ? 1 : 0, + overflow: 'scroll', + maxHeight: isDrawerOpen ? '75vh' : 0 }} > - - {hasConsultantError ? ( - - ) : ( - <> - - {t('registration.topic.summary')} - - {hasTopicError && topicName === '-' ? ( - - ) : ( + {hasConsultantError ? ( + + ) : ( + <> + + {t('registration.topic.summary')} + + {hasTopicError && !preselectedTopic ? ( + + ) : ( + preselectedTopic && ( - {topicName} + {preselectedTopic.name} - )} - - {t('registration.agency.summary')} - - {hasAgencyError && agencyName === '-' ? ( - - ) : ( + ) + )} + + {t('registration.agency.summary')} + + {hasAgencyError && !preselectedAgency ? ( + + ) : ( + preselectedAgency && ( - {agencyName} + {preselectedAgency.name} - )} - - )} - - - {isDrawerOpen ? ( - - ) : ( - - )} - + ) + )} + + )} + + + {isDrawerOpen ? ( + + ) : ( + + )} - - + + ); }; diff --git a/src/extensions/components/registration/preselectionBox/PreselectionBox.tsx b/src/extensions/components/registration/preselectionBox/PreselectionBox.tsx index c74a7b7fd..85c96320a 100644 --- a/src/extensions/components/registration/preselectionBox/PreselectionBox.tsx +++ b/src/extensions/components/registration/preselectionBox/PreselectionBox.tsx @@ -1,47 +1,29 @@ import * as React from 'react'; -import { useContext, useState, useEffect, VFC } from 'react'; +import { useContext } from 'react'; import { useTranslation } from 'react-i18next'; import { Box, Typography } from '@mui/material'; import { RegistrationContext } from '../../../../globalState'; import { PreselectionDrawer } from '../preselectionDrawer/preselectionDrawer'; import ReportProblemIcon from '@mui/icons-material/ReportProblem'; import { useResponsive } from '../../../../hooks/useResponsive'; +import { UrlParamsContext } from '../../../../globalState/provider/UrlParamsProvider'; -export const PreselectionBox: VFC<{ +export const PreselectionBox = ({ + hasDrawer = false +}: { hasDrawer?: boolean; -}> = ({ hasDrawer = false }) => { +}) => { + const { hasConsultantError, hasTopicError, hasAgencyError } = + useContext(RegistrationContext); const { - preselectedAgency, - preselectedTopicName, - isConsultantLink, - hasConsultantError, - hasTopicError, - hasAgencyError, - preselectedData - } = useContext(RegistrationContext); + agency: preselectedAgency, + topic: preselectedTopic, + consultant: preselectedConsultant + } = useContext(UrlParamsContext); const { t } = useTranslation(); - const [topicName, setTopicName] = useState('-'); - const [agencyName, setAgencyName] = useState('-'); const { fromM } = useResponsive(); - useEffect(() => { - if (preselectedTopicName) { - setTopicName(preselectedTopicName); - } else { - setTopicName('-'); - } - if (preselectedAgency) { - setAgencyName(preselectedAgency?.name); - } else { - setAgencyName('-'); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [preselectedAgency, preselectedTopicName]); - - if ( - !(preselectedData.includes('tid') || preselectedData.includes('aid')) && - (!isConsultantLink || !hasDrawer) - ) { + if (!preselectedTopic && !preselectedAgency && !preselectedConsultant) { return null; } @@ -59,8 +41,8 @@ export const PreselectionBox: VFC<{ border: '1px solid #c6c5c4' }} > - {isConsultantLink && - (hasConsultantError ? ( + {preselectedConsultant ? ( + hasConsultantError ? ( - {!loading && ( + {!isloading && ( - {isConsultantLink ? ( - hasConsultantError ? ( - - ) : ( - - {t('registration.consultantlink')} - - ) + {preselectedConsultant ? ( + ) : ( <> {t('registration.topic.summary')} - {hasTopicError && topicName === '-' ? ( + {hasTopicError ? ( + /> ) : ( - {topicName} + {preselectedTopic.name} )} {t('registration.agency.summary')} - {hasAgencyError && agencyName === '-' ? ( + {hasAgencyError ? ( + /> ) : ( - {agencyName} + {preselectedAgency.name} )} diff --git a/src/extensions/components/registration/topicSelection/TopicSelection.tsx b/src/extensions/components/registration/topicSelection/TopicSelection.tsx index 94865011f..528aa51a9 100644 --- a/src/extensions/components/registration/topicSelection/TopicSelection.tsx +++ b/src/extensions/components/registration/topicSelection/TopicSelection.tsx @@ -1,3 +1,4 @@ +import * as React from 'react'; import { Typography, Accordion, @@ -9,11 +10,18 @@ import { RadioGroup, FormControl } from '@mui/material'; -import * as React from 'react'; -import { VFC, useContext, useState, useEffect } from 'react'; +import { + VFC, + useContext, + useState, + useEffect, + SetStateAction, + Dispatch, + useCallback +} from 'react'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { useTranslation } from 'react-i18next'; -import { RegistrationContext } from '../../../../globalState'; +import { RegistrationContext, RegistrationData } from '../../../../globalState'; import { apiGetTopicGroups } from '../../../../api/apiGetTopicGroups'; import { apiGetTopicsData } from '../../../../api/apiGetTopicsData'; import { @@ -24,117 +32,117 @@ import { MetaInfo } from '../metaInfo/MetaInfo'; import { Loading } from '../../../../components/app/Loading'; import TaskAltIcon from '@mui/icons-material/TaskAlt'; import { REGISTRATION_DATA_VALIDATION } from '../registrationDataValidation'; +import { UrlParamsContext } from '../../../../globalState/provider/UrlParamsProvider'; export const TopicSelection: VFC<{ + onChange: Dispatch>>; nextStepUrl: string; onNextClick(): void; -}> = ({ nextStepUrl, onNextClick }) => { +}> = ({ onChange, nextStepUrl, onNextClick }) => { + const { setDisabledNextButton, registrationData } = + useContext(RegistrationContext); const { - setDisabledNextButton, - setDataForSessionStorage, - sessionStorageRegistrationData, - preselectedData, - preselectedAgency, - isConsultantLink, - consultant, - hasAgencyError - } = useContext(RegistrationContext); + topic: preselectedTopic, + agency: preselectedAgency, + consultant: preselectedConsultant + } = useContext(UrlParamsContext); const { t } = useTranslation(); const [value, setValue] = useState( - sessionStorageRegistrationData.mainTopicId || undefined + registrationData.mainTopicId || undefined ); - const [topicGroups, setTopicGroups] = useState([]); - const [topics, setTopics] = useState([]); - const [isLoading, setIsLoading] = useState(false); + const [topicGroups, setTopicGroups] = useState(); + const [topics, setTopics] = useState(); const [listView, setListView] = useState(false); const [topicGroupId, setTopicGroupId] = useState( - sessionStorageRegistrationData.topicGroupId || undefined + registrationData.topicGroupId || undefined ); - const getTopic = (mainTopicId: number) => { - return topics?.find((topic) => topic?.id === mainTopicId); - }; + const getTopic = useCallback( + (mainTopicId: number) => + topics?.find((topic) => topic?.id === mainTopicId), + [topics] + ); useEffect(() => { if ( REGISTRATION_DATA_VALIDATION.mainTopicId.validation( value?.toString() ) && - (topicGroups.some((topicGroup) => + (topicGroups?.some((topicGroup) => topicGroup.topicIds.includes(value) ) || - (listView && topics.some((topic) => topic.id === value))) + (listView && topics?.some((topic) => topic.id === value))) ) { setDisabledNextButton(false); } }, [setDisabledNextButton, value, topicGroups, listView, topics]); useEffect(() => { - if ( - (preselectedData.includes('aid') && !hasAgencyError) || - (isConsultantLink && consultant) - ) { - setListView(true); - } else { - setListView(false); - } - }, [consultant, hasAgencyError, isConsultantLink, preselectedData]); + setListView(!!(preselectedAgency || preselectedConsultant)); + }, [preselectedConsultant, preselectedAgency]); useEffect(() => { - if (topics.length === 1) { + if (topics?.length === 1) { setValue(topics[0].id); - setDataForSessionStorage({ - mainTopicId: topics[0].id + onChange({ + mainTopic: topics[0] }); } - }, [setDataForSessionStorage, topics]); + }, [topics, onChange]); useEffect(() => { - const getFilteredTopics = (topics: TopicsDataInterface[]) => { - if (preselectedData.includes('aid') && !hasAgencyError) { - const topicIds = preselectedAgency?.topicIds; - return topics?.filter((topic) => topicIds.includes(topic.id)); - } - if (isConsultantLink && consultant) { - const topicIds = consultant?.agencies - .map((agency) => agency.topicIds) - .flat(); - return topics?.filter((topic) => topicIds.includes(topic.id)); - } - return topics; - }; + const filterConsultantTopics = (t) => + !preselectedConsultant || + preselectedConsultant.agencies.some((a) => + a.topicIds?.includes(t.id) + ); + + const filterAgencyTopics = (t) => + !preselectedAgency || preselectedAgency.topicIds?.includes(t.id); + + const getFilteredTopics = (topics: TopicsDataInterface[]) => + topics + // Filter topic by preselected topic + .filter( + (t) => !preselectedTopic || t.id === preselectedTopic?.id + ) + // Filter topics by consultant topics + .filter(filterConsultantTopics) + // Filter topics by preselected agency + .filter(filterAgencyTopics); (async () => { + setTopics(undefined); + setTopicGroups(undefined); + + const topicsResponse = await apiGetTopicsData(); + const topics = getFilteredTopics(topicsResponse); try { - setIsLoading(true); + const topicIds = topics.map((t) => t.id); const topicGroupsResponse = await apiGetTopicGroups(); - const topicsResponse = await apiGetTopicsData(); - - setTopics(getFilteredTopics(topicsResponse)); + //const filer setTopicGroups( topicGroupsResponse.data.items .filter((topicGroup) => topicGroup.topicIds.length > 0) + .filter((topicGroup) => + topicGroup.topicIds.some(topicIds.includes) + ) .sort((a, b) => { if (a.name === b.name) return 0; return a.name < b.name ? -1 : 1; }) ); - setIsLoading(false); } catch { - setTopics([]); setTopicGroups([]); + setListView(true); } + + setTopics(topics); })(); - }, [ - consultant, - hasAgencyError, - isConsultantLink, - preselectedAgency, - preselectedData - ]); + }, [preselectedConsultant, preselectedAgency, preselectedTopic]); return ( <> - {topics.length === 1 ? ( + {topics?.length === 1 ? ( {t('registration.topic.oneResult')} @@ -148,7 +156,7 @@ export const TopicSelection: VFC<{ )} - {isLoading ? ( + {topics === undefined || topicGroups === undefined ? ( - {topicGroups && topics && listView - ? topics + {listView + ? (topics || []) .sort((a, b) => { if (a.name === b.name) return 0; return a.name < b.name ? -1 : 1; }) .map((topic, index) => ( - + setValue(undefined) + } + onOverlayOpen={() => + setValue(topic.id) + } + onChange={() => { + setValue(topic.id); + onChange({ + mainTopic: topic + }); }} - > - { - setValue(topic.id); - setDataForSessionStorage( - { - mainTopicId: - topic?.id - } - ); - }} - checked={ - value === topic?.id - } - checkedIcon={ - topics.length === - 1 ? ( - - ) : undefined - } - icon={ - topics.length === - 1 ? ( - - ) : undefined - } - /> - } - label={ - - - {topic?.name} - - - } - /> - {topic?.description && ( - - setValue(undefined) - } - backButtonLabel={t( - 'registration.topic.infoOverlay.backButtonLabel' - )} - nextButtonLabel={t( - 'registration.topic.infoOverlay.nextButtonLabel' - )} - nextStepUrl={nextStepUrl} - onNextClick={onNextClick} - onOverlayOpen={() => { - setDataForSessionStorage( - { - mainTopicId: - topic.id - } - ); - setValue(topic.id); - }} - /> - )} - + /> )) - : topicGroups.map((topicGroup) => ( + : (topicGroups || []).map((topicGroup) => ( {topicGroup.topicIds .map((t) => getTopic(t)) + .filter(Boolean) .sort((a, b) => { if (a.name === b.name) return 0; @@ -324,109 +271,48 @@ export const TopicSelection: VFC<{ : 1; }) .map((topic, index) => ( - { + setValue(undefined); + setTopicGroupId( + undefined + ); + }} + onOverlayOpen={() => { + setValue(topic.id); + setTopicGroupId( + topicGroup.id + ); + }} + checked={ + value === + topic.id && + topicGroup.id === + topicGroupId + } + onChange={() => { + setValue(topic.id); + setTopicGroupId( + topicGroup.id + ); + onChange({ + mainTopic: + topic, + topicGroupId: + topicGroup?.id + }); }} - > - { - setValue( - topic.id - ); - setTopicGroupId( - topicGroup.id - ); - setDataForSessionStorage( - { - mainTopicId: - topic?.id, - topicGroupId: - topicGroup?.id - } - ); - }} - checked={ - value === - topic?.id && - topicGroup.id === - topicGroupId - } - /> - } - label={ - - - { - topic?.name - } - - - } - /> - {topic.description && ( - - setValue( - undefined - ) - } - backButtonLabel={t( - 'registration.topic.infoOverlay.backButtonLabel' - )} - nextButtonLabel={t( - 'registration.topic.infoOverlay.nextButtonLabel' - )} - nextStepUrl={ - nextStepUrl - } - onNextClick={ - onNextClick - } - onOverlayOpen={() => { - setDataForSessionStorage( - { - mainTopicId: - topic.id, - topicGroupId: - topicGroup.id - } - ); - setValue( - topic.id - ); - setTopicGroupId( - topicGroup.id - ); - }} - /> - )} - + /> ))} @@ -437,3 +323,79 @@ export const TopicSelection: VFC<{ ); }; + +const TopicSelect = ({ + topics, + topic, + index, + nextStepUrl, + onNextClick, + onChange, + onOverlayClose, + onOverlayOpen, + checked +}) => { + const { t } = useTranslation(); + + return ( + + + ) : undefined + } + icon={ + topics.length === 1 ? ( + + ) : undefined + } + /> + } + label={ + + {topic?.name} + + } + /> + {topic?.description && ( + onOverlayClose(topic)} + onOverlayOpen={() => onOverlayOpen(topic)} + /> + )} + + ); +}; diff --git a/src/extensions/components/registration/topicSelection/topicSelection.cy.tsx b/src/extensions/components/registration/topicSelection/topicSelection.cy.tsx index eb6eaded4..87de83801 100644 --- a/src/extensions/components/registration/topicSelection/topicSelection.cy.tsx +++ b/src/extensions/components/registration/topicSelection/topicSelection.cy.tsx @@ -16,7 +16,11 @@ it('Get accordion content', () => { cy.mount( - {}} /> + {}} + onChange={() => {}} + /> ); diff --git a/src/extensions/components/registration/welcomeScreen/WelcomeScreen.tsx b/src/extensions/components/registration/welcomeScreen/WelcomeScreen.tsx index 146382005..61ce5fa81 100644 --- a/src/extensions/components/registration/welcomeScreen/WelcomeScreen.tsx +++ b/src/extensions/components/registration/welcomeScreen/WelcomeScreen.tsx @@ -5,17 +5,16 @@ import CreateIcon from '@mui/icons-material/Create'; import ChatIcon from '@mui/icons-material/Chat'; import MailIcon from '@mui/icons-material/Mail'; import LockIcon from '@mui/icons-material/Lock'; -import { Link as RouterLink, useLocation } from 'react-router-dom'; -import { useMemo, VFC } from 'react'; +import { Link as RouterLink } from 'react-router-dom'; +import { useMemo } from 'react'; import { PreselectionBox } from '../preselectionBox/PreselectionBox'; interface WelcomeScreenProps { nextStepUrl: string; } -export const WelcomeScreen: VFC = ({ nextStepUrl }) => { +export const WelcomeScreen = ({ nextStepUrl }: WelcomeScreenProps) => { const { t } = useTranslation(); - const { search } = useLocation(); const infoDefinitions = useMemo( () => [ @@ -73,7 +72,10 @@ export const WelcomeScreen: VFC = ({ nextStepUrl }) => { {t('registration.welcomeScreen.subline')} {infoDefinitions.map((info) => ( - + {info.icon} {info.headline} @@ -112,7 +114,7 @@ export const WelcomeScreen: VFC = ({ nextStepUrl }) => { sx={{ mt: { xs: '8px', md: '16px' } }} variant="contained" component={RouterLink} - to={`${nextStepUrl}${search}`} + to={nextStepUrl} > {t('registration.welcomeScreen.register.buttonLabel')} diff --git a/src/extensions/components/registration/zipcodeInput/ZipcodeInput.tsx b/src/extensions/components/registration/zipcodeInput/ZipcodeInput.tsx index 5d547e10d..95e2cea7e 100644 --- a/src/extensions/components/registration/zipcodeInput/ZipcodeInput.tsx +++ b/src/extensions/components/registration/zipcodeInput/ZipcodeInput.tsx @@ -1,33 +1,35 @@ import { InputAdornment, Typography } from '@mui/material'; import * as React from 'react'; import FmdGoodIcon from '@mui/icons-material/FmdGood'; -import { useState, VFC, useContext, useEffect } from 'react'; +import { + useState, + VFC, + useContext, + useEffect, + Dispatch, + SetStateAction +} from 'react'; import { useTranslation } from 'react-i18next'; import { Input } from '../../../../components/input/input'; -import { RegistrationContext } from '../../../../globalState'; +import { RegistrationContext, RegistrationData } from '../../../../globalState'; import { REGISTRATION_DATA_VALIDATION } from '../registrationDataValidation'; -export const ZipcodeInput: VFC = () => { +export const ZipcodeInput: VFC<{ + onChange: Dispatch>>; +}> = ({ onChange }) => { const { t } = useTranslation(); - const { - setDisabledNextButton, - setDataForSessionStorage, - sessionStorageRegistrationData - } = useContext(RegistrationContext); - const [value, setValue] = useState( - sessionStorageRegistrationData.zipcode || '' - ); + const { setDisabledNextButton, registrationData } = + useContext(RegistrationContext); + const [value, setValue] = useState(registrationData.zipcode || ''); useEffect(() => { if (REGISTRATION_DATA_VALIDATION.zipcode.validation(value)) { setDisabledNextButton(false); - setDataForSessionStorage({ zipcode: value }); + onChange({ zipcode: value }); } else { setDisabledNextButton(true); } - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [value]); + }, [setDisabledNextButton, onChange, value]); return ( <> diff --git a/src/extensions/components/registration/zipcodeInput/zipCodeInput.cy.tsx b/src/extensions/components/registration/zipcodeInput/zipCodeInput.cy.tsx index f201f8692..4cf85d8a4 100644 --- a/src/extensions/components/registration/zipcodeInput/zipCodeInput.cy.tsx +++ b/src/extensions/components/registration/zipcodeInput/zipCodeInput.cy.tsx @@ -7,7 +7,7 @@ it('shows correct label', () => { cy.mount( - + {}} /> ); @@ -18,7 +18,7 @@ it('shows correct value', () => { cy.mount( - + {}} /> ); @@ -30,7 +30,7 @@ it('show invalid input onBlur', () => { cy.mount( - + {}} /> ); @@ -43,7 +43,7 @@ it('does not show wrong characters', () => { cy.mount( - + {}} /> ); diff --git a/src/extensions/initApp.tsx b/src/extensions/initApp.tsx index 9a56184f1..54d3e427a 100644 --- a/src/extensions/initApp.tsx +++ b/src/extensions/initApp.tsx @@ -1,36 +1,72 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; +import { ThemeProvider } from '@mui/material'; import { App } from '../components/app/app'; import { Stage } from './components/stage/stage'; -import { ConsultingTypes } from './components/consultingTypes/ConsultingTypes'; import { Imprint } from './components/legalInformationLinks/Imprint'; import { DataProtection } from './components/legalInformationLinks/DataProtection'; import { config, legalLinks } from './resources/scripts/config'; +import { UrlParamsProvider } from '../globalState/provider/UrlParamsProvider'; +import { RegistrationProvider } from '../globalState'; +import { lazy } from 'react'; +import '../resources/styles/mui-variables-mapping.scss'; +import theme from './theme'; +import { Redirect } from 'react-router-dom'; + +const Registration = lazy(() => + import('./components/registration/Registration').then((m) => ({ + default: m.Registration + })) +); + +const NewRegistration = () => ( + + + + + +); ReactDOM.render( - + ( + + ) }, - component: Imprint - }, - { - route: { - path: legalLinks.privacy + { + route: { + path: legalLinks.imprint + }, + component: Imprint }, - component: DataProtection - } - ]} - stageComponent={Stage} - />, + { + route: { + path: legalLinks.privacy + }, + component: DataProtection + } + ]} + stageComponent={Stage} + /> + , document.getElementById('appRoot') ); diff --git a/src/extensions/pages/app.html b/src/extensions/pages/app.html index 4ff1af7e3..7d37f67a4 100644 --- a/src/extensions/pages/app.html +++ b/src/extensions/pages/app.html @@ -1,4 +1,4 @@ - + @@ -28,7 +28,6 @@ -