feat: onboarding home and compatibility pages

This commit is contained in:
gofnnp 2023-10-04 01:51:12 +04:00
parent 73a9da6a4e
commit 2fc26aceb0
11 changed files with 657 additions and 79 deletions

BIN
public/finger.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -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<string>("");
const [selectedDate, setSelectedDate] = useState<string | IDate>("");
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<Asset>();
const api = useApi();
const inputRef = useRef<HTMLInputElement>(null);
const dateRef = useRef<HTMLInputElement>(null);
const categoriesRef = useRef<HTMLInputElement>(null);
const mainButtonRef = useRef<HTMLInputElement>(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 {
+
</Title>
<div className={styles["inputs-container"]}>
<div className={styles["input-container__name-container"]}>
{!onboardingCompatibility.isShown && <>
{currentOnboarding === 0 && (
<Onboarding
targetRef={inputRef}
isShow={currentOnboarding === 0}
direction={EDirectionOnboarding.BOTTOM}
showBackground={true}
>
<TextWithFinger
text={t("au.web_onbording.name")}
direction={EDirectionOnboarding.BOTTOM}
crossClickHandler={() => setCurrentOnboarding(1)}
/>
</Onboarding>
)}
{currentOnboarding === 1 && (
<Onboarding
targetRef={dateRef}
isShow={currentOnboarding === 1}
direction={EDirectionOnboarding.BOTTOM}
showBackground={true}
>
<>
<button
className={styles["compatibility-onboarding__button"]}
disabled={!isChangeDate}
onClick={() => setCurrentOnboarding(2)}
>
Done
</button>
<TextWithFinger
text={t("au.web_onbording.date")}
direction={EDirectionOnboarding.BOTTOM}
showCross={false}
/>
</>
</Onboarding>
)}
{currentOnboarding === 3 && (
<Onboarding
targetRef={mainButtonRef}
isShow={currentOnboarding === 3}
direction={EDirectionOnboarding.TOP}
showBackground={true}
>
<TextWithFinger
text={""}
direction={EDirectionOnboarding.TOP}
showCross={false}
/>
</Onboarding>
)}
</>}
<div
className={styles["input-container__name-container"]}
style={{ zIndex: currentOnboarding === 0 ? 99 : 1 }}
ref={inputRef}
>
<NameInput
name="name"
value={name}
placeholder={t("name")}
onKeyDown={(e) => {
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)}
/>
</div>
<div className={styles["input-container__date-container"]}>
<div
className={styles["input-container__date-container"]}
style={{ zIndex: currentOnboarding === 1 ? 99 : 1 }}
ref={dateRef}
>
<DatePicker onDateChange={handleValidDate} />
</div>
</div>
{currentOnboarding === 2 && !onboardingCompatibility.isShown && (
<Onboarding
targetRef={categoriesRef}
isShow={currentOnboarding === 2}
direction={EDirectionOnboarding.TOP}
showBackground={true}
>
<TextWithFinger
text={t("au.web_onbording.category")}
direction={EDirectionOnboarding.TOP}
showCross={true}
crossClickHandler={() => setCurrentOnboarding(3)}
/>
</Onboarding>
)}
{data && data.length && (
<div className={styles["compatibility-categories"]}>
<div
className={styles["compatibility-categories"]}
style={{ zIndex: currentOnboarding === 2 ? 99 : 1 }}
ref={categoriesRef}
>
{data.map((item, index) => (
<div className="compatibility-categories__item" key={index}>
<div
className="compatibility-categories__item"
key={index}
onClick={() => setCurrentOnboarding(3)}
>
<input
className={`${styles["compatibility-categories__input"]} ${
compatCategory === item.id
@ -182,13 +293,21 @@ function CompatibilityPage(): JSX.Element {
))}
</div>
)}
<MainButton
className={styles["check-btn"]}
onClick={handleNext}
disabled={isDisabled}
<div
style={{
position: "relative",
zIndex: currentOnboarding === 3 ? 99 : 1,
}}
ref={mainButtonRef}
>
{t("check")}
</MainButton>
<MainButton
className={styles["check-btn"]}
onClick={handleNext}
disabled={isDisabled}
>
{t("check")}
</MainButton>
</div>
</div>
</section>
);

View File

@ -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<T> {
name: string;
value: T;
label?: string | null;
placeholder?: string | null;
inputClassName?: string;
onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void;
onValid: (value: string) => void;
onInvalid: () => void;
}
function NameInput(props: FormField<string>): 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<string>): JSX.Element {
const { name, value, placeholder, onValid, onInvalid, onKeyDown } = props;
const [userName, setUserName] = useState(value);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
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 (
<div className={styles['name-input-container']}>
<div className={styles["name-input-container"]}>
<input
name={name}
type="text"
value={userName}
onChange={handleChange}
placeholder={placeholder ?? ' '}
onKeyDown={onKeyDown}
placeholder={placeholder ?? " "}
/>
</div>
)
);
}
export default NameInput
export default NameInput;

View File

@ -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;
}

View File

@ -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<HTMLDivElement>(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 {
{/* <a href={asset?.url.replace('http://', 'https://')} download></a> */}
</div>
</div>
<div className={styles.content} style={{ marginTop: isShowNavbar ? "calc(100vh - 570px)" : "calc(100vh - 500px)"}}>
{<div className={`${styles["content__buttons"]} ${isShowNavbar ? styles["content__buttons--hidden"] : ""}`}>
<BlurringSubstrate
style={{ color: "#fa71ea" }}
className={styles["content__buttons-item"]}
clickHandler={handleCompatibility}
<div
className={styles.content}
style={{
marginTop: isShowNavbar
? "calc(100vh - 570px)"
: "calc(100vh - 500px)",
}}
>
{
<div
ref={buttonsRef}
className={`${styles["content__buttons"]} ${
isShowNavbar ? styles["content__buttons--hidden"] : ""
}`}
>
{buttonTextFormatter(t("aura-money_compatibility-button"))}
</BlurringSubstrate>
<BlurringSubstrate
style={{ color: "#00f0ff" }}
className={styles["content__buttons-item"]}
clickHandler={handleBreath}
>
{buttonTextFormatter(t("aura-10_breath-button"))}
</BlurringSubstrate>
</div>}
<Onboarding targetRef={buttonsRef} isShow={isShowOnboardingHome}>
<TextWithFinger
text={t("au.web_onbording.start")}
crossClickHandler={() => setIsShowOnboardingHome(false)}
/>
</Onboarding>
<BlurringSubstrate
style={{ color: "#fa71ea" }}
className={styles["content__buttons-item"]}
clickHandler={handleCompatibility}
>
{buttonTextFormatter(t("aura-money_compatibility-button"))}
</BlurringSubstrate>
<BlurringSubstrate
style={{ color: "#00f0ff" }}
className={styles["content__buttons-item"]}
clickHandler={handleBreath}
>
{buttonTextFormatter(t("aura-10_breath-button"))}
</BlurringSubstrate>
</div>
}
<div className={styles["content__daily-forecast"]}>
{dailyForecast &&
dailyForecast.forecasts.map((forecast, index) => (

View File

@ -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<HTMLElement>;
isShow: boolean;
direction?: EDirectionOnboarding;
showBackground?: boolean;
children: JSX.Element;
}
interface IBoardingCoordinates {
top: number;
left: number;
}
const getCoordinates = (
targetRef: React.RefObject<HTMLElement>,
direction: EDirectionOnboarding,
onboardingRef: React.RefObject<HTMLDivElement>
): 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<HTMLDivElement>(null);
const [coordinates, setCoordinates] = useState<IBoardingCoordinates>({
top: 0,
left: 0,
});
useEffect(() => {
setCoordinates(getCoordinates(targetRef, direction, onboardingRef));
}, [direction, targetRef, children]);
const [top, left] = [coordinates.top, coordinates.left];
if (!isShow) {
return <></>;
}
return (
<div className={getClassNameContainer(direction, showBackground)}>
<div
className={`${styles["onboarding"]} ${
!targetRef.current || !onboardingRef.current ? styles["hide"] : ""
}`}
style={{ top: `${top}px`, left: `${left}px` }}
ref={onboardingRef}
>
{children}
</div>
</div>
);
}
export default Onboarding;

View File

@ -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;
}

View File

@ -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 && (
<div className={styles["onboarding-text"]}>
{showCross && (
<div className={styles["cross"]} onClick={crossClickHandler}></div>
)}
{text}
</div>
)}
<img
className={`${styles["finger"]} ${styles[`direction-${direction}`]}`}
src="/finger.png"
alt="finger"
/>
</>
);
}
export default Onboarding;

View File

@ -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);
}
}

View File

@ -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<typeof reducer>
};
export type RootState = ReturnType<typeof reducer>;
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);

57
src/store/onboarding.ts Normal file
View File

@ -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<Partial<IOnboardingConfig>>) {
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;