From 2fc26aceb065bd56ab9e5b530b497a1c594be078 Mon Sep 17 00:00:00 2001 From: gofnnp Date: Wed, 4 Oct 2023 01:51:12 +0400 Subject: [PATCH] feat: onboarding home and compatibility pages --- public/finger.png | Bin 0 -> 1058 bytes src/components/Compatibility/index.tsx | 143 ++++++++++++++++-- src/components/Compatibility/nameInput.tsx | 45 +++--- .../Compatibility/styles.module.css | 25 ++- src/components/HomePage/index.tsx | 78 +++++++--- src/components/Onboarding/index.tsx | 116 ++++++++++++++ src/components/Onboarding/styles.module.css | 52 +++++++ src/components/TextWithFinger/index.tsx | 36 +++++ .../TextWithFinger/styles.module.css | 83 ++++++++++ src/store/index.ts | 101 +++++++++---- src/store/onboarding.ts | 57 +++++++ 11 files changed, 657 insertions(+), 79 deletions(-) create mode 100644 public/finger.png create mode 100644 src/components/Onboarding/index.tsx create mode 100644 src/components/Onboarding/styles.module.css create mode 100644 src/components/TextWithFinger/index.tsx create mode 100644 src/components/TextWithFinger/styles.module.css create mode 100644 src/store/onboarding.ts diff --git a/public/finger.png b/public/finger.png new file mode 100644 index 0000000000000000000000000000000000000000..1fc988a2e03273b897d3df2c34aad3a5f9b26f24 GIT binary patch literal 1058 zcmV+-1l{|IP)$&JLr;y(b~JmMcxa27)LruWe9?xrN0D>IRRVy^LgYD`c&vz-iGmEm^D8!pJWwl}fZyt%4JHz64*F zzsHf+{M;%`fs1g(Vgh-^<}b!o*#;C%Cz0Fe^?DpUB)7Kip7hg~ZzjP9>NqIKDUqw= zVf_p2$B>>|B9*^6m$WG;u!W+k@F|_1uoc#f?qrBo z?CEon)T#!S~|jE1%u#@7hzx^nD#U|@hQm&;=%QKZ||RTTw=F7%#n zd!w5ijc@}?`ss_=F}zVj+tnQ$9GpVw^l}*dWu%o*>~11l*kP$=l|)gp<~(5(i^T}s zhQKfIZqSHb6k~}*c$ed58cx;`?;?@Mc9B#~^KHf^w-IiTc=#~9w6rvmN~JEIC}dgI zw^awnaUBV_B4mBszIX4=Ua>fJv;fadQRd6nSw&Hhn;3?HTw`W#E|Ohco!BfCChk9e zeDEsr8ym5|+qktlSWKsrJYPH!K~2+bBTP}0qX>7zgGWzB1VKvR{V{TDYb%K-BL@#% zSXhXxt#43xHwXU-14Kz)o|#X@?1X>h^WF=$V|+47Oh%)Gb&j2E(_{vBBorhVL9i8m z_wl3iPUj{pYd_M9ytA2r>%ufm+m=@xW*ygsSN=EQ%~jgA{N|18_BXV_{U*U~A}?`W zSYbsGCxe#d6vLpvfrsCgX)U$5bP+kg^$1(wk*{SLMLCPGH^QdbvW#T|C~VqBm_knQ zM%X6848x+pQLQ%LbGD|X$RVCYIG@jdYxaGySeiu+@kCeb%7 literal 0 HcmV?d00001 diff --git a/src/components/Compatibility/index.tsx b/src/components/Compatibility/index.tsx index c129916..e2b1225 100644 --- a/src/components/Compatibility/index.tsx +++ b/src/components/Compatibility/index.tsx @@ -2,10 +2,10 @@ import { useTranslation } from "react-i18next"; import MainButton from "../MainButton"; import Title from "../Title"; import styles from "./styles.module.css"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import NameInput from "./nameInput"; import DatePicker from "./DatePicker"; -import { IDate } from "@/services/date"; +import { IDate, getDateAsString } from "@/services/date"; import { AICompatCategories, useApi, useApiCall } from "@/api"; import { useNavigate } from "react-router-dom"; import routes from "@/routes"; @@ -18,6 +18,8 @@ import { } from "@/services/zodiac-sign"; import { Asset } from "@/api/resources/Assets"; import { getRandomArbitrary } from "@/services/random-value"; +import Onboarding, { EDirectionOnboarding } from "../Onboarding"; +import TextWithFinger from "../TextWithFinger"; function CompatibilityPage(): JSX.Element { const { t, i18n } = useTranslation(); @@ -26,15 +28,24 @@ function CompatibilityPage(): JSX.Element { const [isDisabled, setIsDisabled] = useState(true); const [isDisabledName, setIsDisabledName] = useState(true); const [isDisabledDate, setIsDisabledDate] = useState(true); + const [isChangeDate, setIsChangeDate] = useState(false); + const [currentOnboarding, setCurrentOnboarding] = useState(0); const [name, setName] = useState(""); const [selectedDate, setSelectedDate] = useState(""); const [compatCategory, setCompatCategory] = useState(1); const homeConfig = useSelector(selectors.selectHome); const showNavbarFooter = homeConfig.isShowNavbar; const birthdate = useSelector(selectors.selectBirthdate); + const onboardingCompatibility = useSelector( + selectors.selectOnboardingCompatibility + ); const zodiacSign = getZodiacSignByDate(birthdate); const [asset, setAsset] = useState(); const api = useApi(); + const inputRef = useRef(null); + const dateRef = useRef(null); + const categoriesRef = useRef(null); + const mainButtonRef = useRef(null); const locale = i18n.language; const assetsData = useCallback(async () => { @@ -59,6 +70,13 @@ function CompatibilityPage(): JSX.Element { const handleNext = () => { if (isDisabled) return; + dispatch( + actions.onboardingConfig.update({ + compatibility: { + isShown: true, + }, + }) + ); dispatch( actions.compatibility.update({ rightUser: { @@ -112,6 +130,7 @@ function CompatibilityPage(): JSX.Element { const handleValidDate = (date: string | IDate) => { setIsDisabledDate(date === ""); + setIsChangeDate(date !== getDateAsString(new Date())); setSelectedDate(date); checkAllDisabled(); }; @@ -147,23 +166,115 @@ function CompatibilityPage(): JSX.Element { +
-
+ {!onboardingCompatibility.isShown && <> + {currentOnboarding === 0 && ( + + setCurrentOnboarding(1)} + /> + + )} + {currentOnboarding === 1 && ( + + <> + + + + + )} + {currentOnboarding === 3 && ( + + + + )} + } +
{ + if (!name.length) return; + if (e.key === "Enter") { + setCurrentOnboarding(1); + (e.target as HTMLInputElement).blur(); + dateRef.current?.scrollIntoView({ behavior: "smooth" }); + } + }} onValid={handleValidName} onInvalid={() => setIsDisabledName(true)} />
-
+
+ {currentOnboarding === 2 && !onboardingCompatibility.isShown && ( + + setCurrentOnboarding(3)} + /> + + )} {data && data.length && ( -
+
{data.map((item, index) => ( -
+
setCurrentOnboarding(3)} + > )} - - {t("check")} - + + {t("check")} + +
); diff --git a/src/components/Compatibility/nameInput.tsx b/src/components/Compatibility/nameInput.tsx index df9d20c..a7f417a 100644 --- a/src/components/Compatibility/nameInput.tsx +++ b/src/components/Compatibility/nameInput.tsx @@ -1,37 +1,48 @@ -import { useEffect, useState } from 'react' -import { FormField } from '@/types' -import styles from './styles.module.css' +import { useEffect, useState } from "react"; +import styles from "./styles.module.css"; -const isValidName = (name: string) => { - return name.length > 0 && name.length < 30 +interface INameInput { + name: string; + value: T; + label?: string | null; + placeholder?: string | null; + inputClassName?: string; + onKeyDown: (event: React.KeyboardEvent) => void; + onValid: (value: string) => void; + onInvalid: () => void; } -function NameInput(props: FormField): JSX.Element { - const { name, value, placeholder, onValid, onInvalid } = props - const [userName, setUserName] = useState(value) +const isValidName = (name: string) => { + return name.length > 0 && name.length < 30; +}; + +function NameInput(props: INameInput): JSX.Element { + const { name, value, placeholder, onValid, onInvalid, onKeyDown } = props; + const [userName, setUserName] = useState(value); const handleChange = (event: React.ChangeEvent) => { - setUserName(event.target.value) - } + setUserName(event.target.value); + }; useEffect(() => { if (isValidName(userName)) { - onValid(userName) + onValid(userName); } else { - onInvalid() + onInvalid(); } - }, [userName, onInvalid, onValid]) + }, [userName, onInvalid, onValid]); return ( -
+
- ) + ); } -export default NameInput +export default NameInput; diff --git a/src/components/Compatibility/styles.module.css b/src/components/Compatibility/styles.module.css index 904791e..5254944 100644 --- a/src/components/Compatibility/styles.module.css +++ b/src/components/Compatibility/styles.module.css @@ -9,7 +9,6 @@ background-repeat: no-repeat; } - .cross { position: absolute; top: 24px; @@ -46,10 +45,15 @@ .content { width: 100%; - position: relative; + /* position: relative; */ z-index: 2; } +.input-container__name-container, +.input-container__date-container { + position: relative; +} + .blurring { position: absolute; top: 0; @@ -143,6 +147,7 @@ } .compatibility-categories { + position: relative; display: flex; flex-direction: column; gap: 16px; @@ -203,3 +208,19 @@ background-color: #ea445a; border-color: #ea445a; } + +.compatibility-onboarding__button { + font-size: 15; + font-weight: 400; + border: none; + border-radius: 25px; + width: 92px; + height: 40px; + background-color: #18D136; + margin-top: 14px; + color: #fff; +} + +.compatibility-onboarding__button:disabled { + background-color: gray; +} \ No newline at end of file diff --git a/src/components/HomePage/index.tsx b/src/components/HomePage/index.tsx index 7195c02..8dbf216 100644 --- a/src/components/HomePage/index.tsx +++ b/src/components/HomePage/index.tsx @@ -4,7 +4,7 @@ import routes from "@/routes"; import styles from "./styles.module.css"; import { useApi, useApiCall } from "@/api"; import { Asset } from "@/api/resources/Assets"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import BlurringSubstrate from "../BlurringSubstrate"; import EnergyValues from "../EnergyValues"; import { UserAura } from "@/api/resources/Auras"; @@ -19,7 +19,8 @@ import Title from "../Title"; import { UserDailyForecast } from "@/api/resources/UserDailyForecasts"; import { EPathsFromHome } from "@/store/siteConfig"; import { buildFilename, saveFile } from "../WallpaperPage/utils"; - +import Onboarding from "../Onboarding"; +import TextWithFinger from "../TextWithFinger"; const buttonTextFormatter = (text: string): JSX.Element => { const sentences = text.split("."); @@ -38,8 +39,26 @@ function HomePage(): JSX.Element { const { t } = useTranslation(); const navigate = useNavigate(); const dispatch = useDispatch(); + const buttonsRef = useRef(null); + const homeConfig = useSelector(selectors.selectHome); const isShowNavbar = homeConfig.isShowNavbar; + const onboardingConfigHome = useSelector(selectors.selectOnboardingHome); + + const [isShowOnboardingHome, setIsShowOnboardingHome] = useState( + !onboardingConfigHome.isShown + ); + + useEffect(() => { + dispatch( + actions.onboardingConfig.update({ + home: { + isShown: true, + }, + }) + ); + }, [dispatch]); + const handleCompatibility = () => { dispatch( actions.siteConfig.update({ @@ -57,7 +76,6 @@ function HomePage(): JSX.Element { navigate(routes.client.breath()); }; - const { i18n } = useTranslation(); const locale = i18n.language; const birthdate = useSelector(selectors.selectBirthdate); @@ -107,7 +125,7 @@ function HomePage(): JSX.Element { const downloadImg = () => { if (!asset) return; - saveFile(asset.url.replace("http://", "https://"), buildFilename('1')); + saveFile(asset.url.replace("http://", "https://"), buildFilename("1")); }; return ( @@ -133,23 +151,43 @@ function HomePage(): JSX.Element { {/* */}
-
- {
- + { +
- {buttonTextFormatter(t("aura-money_compatibility-button"))} - - - {buttonTextFormatter(t("aura-10_breath-button"))} - -
} + + setIsShowOnboardingHome(false)} + /> + + + {buttonTextFormatter(t("aura-money_compatibility-button"))} + + + {buttonTextFormatter(t("aura-10_breath-button"))} + +
+ }
{dailyForecast && dailyForecast.forecasts.map((forecast, index) => ( diff --git a/src/components/Onboarding/index.tsx b/src/components/Onboarding/index.tsx new file mode 100644 index 0000000..0c0b3cd --- /dev/null +++ b/src/components/Onboarding/index.tsx @@ -0,0 +1,116 @@ +import { useEffect, useRef, useState } from "react"; +import styles from "./styles.module.css"; + +export enum EDirectionOnboarding { + LEFT = "left", + RIGHT = "right", + TOP = "top", + BOTTOM = "bottom", +} + +interface OnboardingProps { + targetRef: React.RefObject; + isShow: boolean; + direction?: EDirectionOnboarding; + showBackground?: boolean; + children: JSX.Element; +} +interface IBoardingCoordinates { + top: number; + left: number; +} + +const getCoordinates = ( + targetRef: React.RefObject, + direction: EDirectionOnboarding, + onboardingRef: React.RefObject +): IBoardingCoordinates => { + if (targetRef.current && onboardingRef.current) { + switch (direction) { + case EDirectionOnboarding.LEFT: + return { + top: + targetRef.current.offsetTop + + targetRef.current.offsetHeight / 2 - + onboardingRef.current.offsetHeight / 2, + left: + targetRef.current.offsetLeft - onboardingRef.current.offsetWidth, + }; + case EDirectionOnboarding.RIGHT: + return { + top: + targetRef.current.offsetTop + + targetRef.current.offsetHeight / 2 - + onboardingRef.current.offsetHeight / 2, + left: targetRef.current.offsetLeft + targetRef.current.offsetWidth, + }; + case EDirectionOnboarding.TOP: + return { + top: targetRef.current.offsetTop - onboardingRef.current.offsetHeight, + left: + targetRef.current.offsetLeft + + targetRef.current.offsetWidth / 2 - + onboardingRef.current.offsetWidth / 2, + }; + case EDirectionOnboarding.BOTTOM: + return { + top: targetRef.current.offsetTop + targetRef.current.offsetHeight, + left: + targetRef.current.offsetLeft + + targetRef.current.offsetWidth / 2 - + onboardingRef.current.offsetWidth / 2, + }; + } + } + return { + top: 0, + left: 0, + }; +}; + +const getClassNameContainer = ( + direction: EDirectionOnboarding, + showBackground: boolean +) => { + return `${styles["onboarding-container"]} ${ + styles[`direction-${direction}`] + } ${showBackground ? styles["background"] : ""}`; +}; + +function Onboarding({ + targetRef, + isShow, + direction = EDirectionOnboarding.TOP, + showBackground = false, + children, +}: OnboardingProps): JSX.Element { + const onboardingRef = useRef(null); + const [coordinates, setCoordinates] = useState({ + top: 0, + left: 0, + }); + useEffect(() => { + setCoordinates(getCoordinates(targetRef, direction, onboardingRef)); + }, [direction, targetRef, children]); + const [top, left] = [coordinates.top, coordinates.left]; + + if (!isShow) { + return <>; + } + + return ( +
+
+ {children} +
+
+ ); +} + +export default Onboarding; diff --git a/src/components/Onboarding/styles.module.css b/src/components/Onboarding/styles.module.css new file mode 100644 index 0000000..d3b5c1b --- /dev/null +++ b/src/components/Onboarding/styles.module.css @@ -0,0 +1,52 @@ +.onboarding-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 99; +} + +/* .onboarding-container.background { + pointer-events: all; + background-color: #000000db; +} */ +.background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: all; + background-color: #000000db; + /* z-index: 1; */ +} + +.onboarding { + position: absolute; + width: 200px; + height: fit-content; + display: flex; + align-items: center; +} + +.direction-top > .onboarding { + flex-direction: column; +} + +.direction-bottom > .onboarding { + flex-direction: column-reverse; +} + +.direction-left > .onboarding { + flex-direction: row; +} + +.direction-right > .onboarding { + flex-direction: row-reverse; +} + +.hide { + opacity: 0; +} diff --git a/src/components/TextWithFinger/index.tsx b/src/components/TextWithFinger/index.tsx new file mode 100644 index 0000000..c69bc35 --- /dev/null +++ b/src/components/TextWithFinger/index.tsx @@ -0,0 +1,36 @@ +import { EDirectionOnboarding } from "../Onboarding"; +import styles from "./styles.module.css"; + +interface OnboardingProps { + text: string; + showCross?: boolean; + direction?: EDirectionOnboarding; + crossClickHandler?: () => void; +} + +function Onboarding({ + text, + showCross = true, + direction = EDirectionOnboarding.TOP, + crossClickHandler, +}: OnboardingProps): JSX.Element { + return ( + <> + {text.length && ( +
+ {showCross && ( +
+ )} + {text} +
+ )} + finger + + ); +} + +export default Onboarding; diff --git a/src/components/TextWithFinger/styles.module.css b/src/components/TextWithFinger/styles.module.css new file mode 100644 index 0000000..27ab885 --- /dev/null +++ b/src/components/TextWithFinger/styles.module.css @@ -0,0 +1,83 @@ +.onboarding-text { + position: relative; + width: 100%; + background-color: #fff; + border-radius: 10px; + padding: 24px 10px 10px; + height: fit-content; + font-size: 15px; + font-weight: 500; + pointer-events: all; +} + +.cross { + width: 12px; + height: 12px; + position: absolute; + top: 8px; + right: 8px; + rotate: 45deg; +} + +.cross::before { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 14px; + height: 2px; + background-color: #bdbdbd; +} + +.cross::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 2px; + height: 14px; + background-color: #bdbdbd; +} + +.finger { + margin: 8px 0; + animation: jump 3s ease infinite; +} + +.direction-bottom.finger { + rotate: 180deg; +} + +.direction-left.finger { + rotate: -90deg; +} + +.direction-right.finger { + rotate: 90deg; +} + +@keyframes jump { + 0% { + transform: translateY(0); + } + 15% { + transform: translateY(-8px); + } + 30% { + transform: translateY(7px); + } + 45% { + transform: translateY(-6px); + } + 60% { + transform: translateY(5px); + } + 75% { + transform: translateY(-4px); + } + 100% { + transform: translateY(0); + } +} diff --git a/src/store/index.ts b/src/store/index.ts index 56f127e..ba0546d 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,24 +1,64 @@ -import { combineReducers, configureStore, createAction } from '@reduxjs/toolkit' -import token, { actions as tokenActions, selectToken } from './token' -import user, { actions as userActions, selectUser } from './user' -import form, { actions as formActions, selectors as formSelectors } from './form' -import aura, { actions as auraActions } from './aura' -import siteConfig, { selectHome, actions as siteConfigActions } from './siteConfig' -import payment, { actions as paymentActions, selectIsDiscount } from './payment' -import subscriptionPlans, { actions as subscriptionPlasActions, selectPlanById } from './subscriptionPlan' -import status, { actions as userStatusActions, selectStatus } from './status' -import compatibility, { actions as compatibilityActions } from './compatibility' -import userCallbacks, { actions as userCallbacksActions } from './userCallbacks' -import { loadStore, backupStore } from './storageHelper' -import { selectAuraCoordinates } from './aura' -import { selectSelectedPrice } from './payment' -import { selectRightUser, selectCategoryId } from './compatibility' -import { selectUserCallbacksDescription, selectUserCallbacksPrevStat } from './userCallbacks' +import { + combineReducers, + configureStore, + createAction, +} from "@reduxjs/toolkit"; +import token, { actions as tokenActions, selectToken } from "./token"; +import user, { actions as userActions, selectUser } from "./user"; +import form, { + actions as formActions, + selectors as formSelectors, +} from "./form"; +import aura, { actions as auraActions } from "./aura"; +import siteConfig, { + selectHome, + actions as siteConfigActions, +} from "./siteConfig"; +import onboardingConfig, { + selectOnboarding, + selectOnboardingBreath, + selectOnboardingCompatibility, + selectOnboardingHome, + actions as onboardingConfigActions, +} from "./onboarding"; +import payment, { + actions as paymentActions, + selectIsDiscount, +} from "./payment"; +import subscriptionPlans, { + actions as subscriptionPlasActions, + selectPlanById, +} from "./subscriptionPlan"; +import status, { actions as userStatusActions, selectStatus } from "./status"; +import compatibility, { + actions as compatibilityActions, +} from "./compatibility"; +import userCallbacks, { + actions as userCallbacksActions, +} from "./userCallbacks"; +import { loadStore, backupStore } from "./storageHelper"; +import { selectAuraCoordinates } from "./aura"; +import { selectSelectedPrice } from "./payment"; +import { selectRightUser, selectCategoryId } from "./compatibility"; +import { + selectUserCallbacksDescription, + selectUserCallbacksPrevStat, +} from "./userCallbacks"; - - -const preloadedState = loadStore() -export const reducer = combineReducers({ token, user, form, status, subscriptionPlans, aura, payment, compatibility, userCallbacks, siteConfig }) +const preloadedState = loadStore(); +export const reducer = combineReducers({ + token, + user, + form, + status, + subscriptionPlans, + aura, + payment, + compatibility, + userCallbacks, + siteConfig, + onboardingConfig, +}); export const actions = { token: tokenActions, user: userActions, @@ -30,8 +70,9 @@ export const actions = { compatibility: compatibilityActions, payment: paymentActions, userCallbacks: userCallbacksActions, - reset: createAction('reset'), -} + onboardingConfig: onboardingConfigActions, + reset: createAction("reset"), +}; export const selectors = { selectToken, selectUser, @@ -45,14 +86,18 @@ export const selectors = { selectUserCallbacksPrevStat, selectHome, selectIsDiscount, + selectOnboarding, + selectOnboardingHome, + selectOnboardingCompatibility, + selectOnboardingBreath, ...formSelectors, -} -export type RootState = ReturnType +}; +export type RootState = ReturnType; export const store = configureStore({ reducer, preloadedState, devTools: import.meta.env.DEV, -}) -export type AppDispatch = typeof store.dispatch -export type StoreType = typeof store -export const unsubscribe = backupStore(store) +}); +export type AppDispatch = typeof store.dispatch; +export type StoreType = typeof store; +export const unsubscribe = backupStore(store); diff --git a/src/store/onboarding.ts b/src/store/onboarding.ts new file mode 100644 index 0000000..0ead691 --- /dev/null +++ b/src/store/onboarding.ts @@ -0,0 +1,57 @@ +import { createSlice, createSelector } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; + +interface IOnboardingConfig { + home: { + isShown: boolean; + }; + compatibility: { + isShown: boolean; + }; + breath: { + isShown: boolean; + }; +} + +const initialState: IOnboardingConfig = { + home: { + isShown: false, + }, + compatibility: { + isShown: false, + }, + breath: { + isShown: false, + }, +}; + +const onboardingConfigSlice = createSlice({ + name: "onboardingConfig", + initialState, + reducers: { + update(state, action: PayloadAction>) { + return { ...state, ...action.payload }; + }, + }, + extraReducers: (builder) => builder.addCase("reset", () => initialState), +}); + +export const { actions } = onboardingConfigSlice; +export const selectOnboarding = createSelector( + (state: { onboardingConfig: IOnboardingConfig }) => state.onboardingConfig, + (onboardingConfig) => onboardingConfig +); +export const selectOnboardingHome = createSelector( + (state: { onboardingConfig: IOnboardingConfig }) => state.onboardingConfig.home, + (onboardingConfig) => onboardingConfig +); +export const selectOnboardingCompatibility = createSelector( + (state: { onboardingConfig: IOnboardingConfig }) => + state.onboardingConfig.compatibility, + (onboardingConfig) => onboardingConfig +); +export const selectOnboardingBreath = createSelector( + (state: { onboardingConfig: IOnboardingConfig }) => state.onboardingConfig.breath, + (onboardingConfig) => onboardingConfig +); +export default onboardingConfigSlice.reducer;