Merge branch 'develop' into 'main'
AW-483-484-485-fix-bugs See merge request witapp/aura-webapp!814
This commit is contained in:
commit
6b22791f90
@ -93,6 +93,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/compatibility-test": {
|
||||
"title": "Тест на Совместимость",
|
||||
"subtitle": "Всё начинается с вас! Выберите ваш пол.",
|
||||
"points": {
|
||||
"point1": "Тест займмет не более 1 мин.",
|
||||
"point2": "Ты получишь разбор совместимости.",
|
||||
"point3": "Решишь проблемы в отношениях за месяц.",
|
||||
"point4": "Сэкономишь сотни долларов на ненадёжных прогнозах.",
|
||||
"point5": "Получишь персональный анализ."
|
||||
},
|
||||
"already_have_account": "Уже есть аккаунт? Войти",
|
||||
"button": "Начать"
|
||||
},
|
||||
"/birthdate": {
|
||||
"title": "When Were You Born?",
|
||||
"text": "Your birth date can reveal strengths and values that may help you move forward"
|
||||
@ -159,7 +172,71 @@
|
||||
"answer1": "Single",
|
||||
"answer2": "In a Relationship",
|
||||
"answer3": "Married",
|
||||
"answer4": "Divorced"
|
||||
"answer4": "Divorced",
|
||||
"v1": {
|
||||
"title": "Чтобы яснее разобрать вашу суть, укажите какие у вас отношения сейчас?",
|
||||
"answer1": "Single",
|
||||
"answer2": "Начало",
|
||||
"answer3": "Долгие отношения",
|
||||
"answer4": "Развиваются непонятно куда",
|
||||
"answer5": "Разрыв/кризис"
|
||||
}
|
||||
},
|
||||
"/relationship-status-result": {
|
||||
"start": {
|
||||
"title": "How can you really tell if your partner is the right one—and avoid making a mistake?",
|
||||
"text": "Understanding your partner’s emotions is the key to a harmonious relationship. Our system predicts compatibility with up to 98% accuracy, giving you clear, detailed reports."
|
||||
},
|
||||
"in_relationship": {
|
||||
"title": "Love is important, but why isn’t love alone enough for a happy, lasting relationship?",
|
||||
"text": "Daily life can either strengthen your relationship or create tension if your ideas of comfort and care don’t match. Our system predicts compatibility with up to 98% accuracy, helping you understand how long-lasting and harmonious your partnership can be."
|
||||
},
|
||||
"developing": {
|
||||
"title": "Feel like your partner just doesn’t get you? It’s not by chance—it’s about differences in your natal patterns.",
|
||||
"text": "Closeness isn’t just about attraction—it’s about understanding each other’s wants and emotions. Our data from 2 million people backs this up, and we know how to help."
|
||||
},
|
||||
"crisis": {
|
||||
"title": "Why do some couples split up quickly, while others stay together for years?",
|
||||
"text": "A shared future isn’t just about dreams—it’s about common goals, values, and everyday effort. Our system predicts compatibility with up to 98% accuracy. We’ll show you, step by step, how to build a strong relationship."
|
||||
},
|
||||
"single": {
|
||||
"title": "Does it feel like there are no worthy partners out there, or you keep ending up with the wrong people? It all comes down to incompatible choices.",
|
||||
"text": "Our system predicts compatibility with up to 98% accuracy, helping you find the right partner and opening up new possibilities in your personal life."
|
||||
}
|
||||
},
|
||||
"/fear-in-relationship": {
|
||||
"title": "Чего вы боитесь больше всего в отношениях?",
|
||||
"answer1": "Потратить жизнь не на того",
|
||||
"answer2": "Остаться одиноким",
|
||||
"answer3": "Пропустить свою судьбу",
|
||||
"answer4": "Любовь без уважения и поддержки",
|
||||
"answer5": "Не узнать правду о совместимости"
|
||||
},
|
||||
"/important-step": {
|
||||
"title": "Ты сделал важный шаг!",
|
||||
"text": "Обозначил свой главный страх в отношениях. Это шаг к тому, чтобы больше не терять время. Мы поможем избежать все ошибки."
|
||||
},
|
||||
"/who-matter": {
|
||||
"title": "О ком вы думаете, строя отношения?",
|
||||
"answer1": "О себе",
|
||||
"answer2": "О детях",
|
||||
"answer3": "О семье"
|
||||
},
|
||||
"/your-priority": {
|
||||
"title": "Что сейчас для вас в приоритете?",
|
||||
"answer1": "Внутренняя гармония",
|
||||
"answer2": "Надёжные отношения",
|
||||
"answer3": "Новые впечатления"
|
||||
},
|
||||
"/personalized-relationship-analysis": {
|
||||
"single": {
|
||||
"title": "Что такое персонализированный анализ отношений?",
|
||||
"text": "У вас свои сильные стороны, ожидания и трудности. Мы создаём персональный портрет, чтобы помочь вам лучше понять себя и свои истинные потребности в отношениях.<br><br>Мы анализируем ваши ответы и показываем, что мешало вам раньше, а что наоборот поможет встретить именно «своего» человека и построить счастливую любовь."
|
||||
},
|
||||
"relationship": {
|
||||
"title": "Что такое персонализированный анализ отношений?",
|
||||
"text": "Каждая пара уникальна. У вас свои сильные стороны и свои трудности. Мы создаём персональный портрет отношений, чтобы помочь укрепить доверие, гармонию и близость именно в вашей паре.<br><br>Мы анализируем ваши ответы и показываем, что мешает вашей любви, а что наоборот поможет сделать её глубже и счастливее."
|
||||
}
|
||||
},
|
||||
"/element-resonates": {
|
||||
"title": "Which Element Empowers You the Most?",
|
||||
|
||||
@ -56,6 +56,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/compatibility-test": {
|
||||
"title": "Тест на Совместимость",
|
||||
"subtitle": "Всё начинается с вас! Выберите ваш пол.",
|
||||
"points": {
|
||||
"point1": "Тест займмет не более 1 мин.",
|
||||
"point2": "Ты получишь разбор совместимости по хиромантическому анализу линий на твоей ладони.",
|
||||
"point3": "Решишь проблемы в отношениях за месяц.",
|
||||
"point4": "Сэкономишь сотни долларов на ненадёжных прогнозах.",
|
||||
"point5": "Получишь персональный анализ."
|
||||
},
|
||||
"already_have_account": "Уже есть аккаунт? Войти"
|
||||
},
|
||||
"/birthdate": {
|
||||
"title": "Когда вы родились?",
|
||||
"text": "Ваша дата рождения может раскрыть сильные стороны и ценности, которые помогут вам двигаться вперёд."
|
||||
|
||||
BIN
public/v2/compatibility/review/like_1.png
Normal file
BIN
public/v2/compatibility/review/like_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
public/v2/compatibility/review/like_2.png
Normal file
BIN
public/v2/compatibility/review/like_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 30 KiB |
@ -44,7 +44,7 @@ export interface IAnswersSessionPalmistry {
|
||||
|
||||
export interface IAnswersSessionCompatibilityV2 {
|
||||
what_aspects: 'love_relationships' | 'health_vitality' | 'career_destiny' | 'life_transitions', // Type: string, optional - 'love_relationships' | 'health_vitality' | 'career_destiny';
|
||||
relationship_status: 'single' | 'in_relationship' | 'engaged' | 'divorced' | 'complicated', // Type: string, optional - 'single' | 'in_relationship';
|
||||
relationship_status: 'single' | 'in_relationship' | 'engaged' | 'divorced' | 'complicated' | 'start' | 'developing' | 'crisis', // Type: string, optional - 'single' | 'in_relationship';
|
||||
element_resonates: 'water' | 'fire' | 'air' | 'earth' | 'light' | 'darkness', // Type: string, optional - 'water' | 'fire' | 'air' | 'earth';
|
||||
favorite_color: 'blue' | 'green' | 'orange' | 'violet' | 'red' | 'yellow' | 'turquoise', // Type: string, optional - 'blue' | 'green' | 'orange' | 'violet' | 'red' | 'yellow';
|
||||
head_or_heart: 'head' | 'heart' | 'both' | 'depends', // Type: string, optional - 'head' | 'heart' | 'both';
|
||||
@ -57,6 +57,9 @@ export interface IAnswersSessionCompatibilityV2 {
|
||||
partner_expectations: 1 | 2 | 3 | 4 | 5; // Type: number, optional - 1 | 2 | 3 | 4 | 5;
|
||||
romantic_gestures: "love" | "neutral" | "dislike"; // Type: string, optional - "love" | "neutral" | "dislike";
|
||||
checking_phone: "against" | "allow" | "normally"; // Type: string, optional - "against" | "allow" | "normally";
|
||||
fear_in_relationship: "wasting_life_for_wrong_person" | "stay_single" | "skip_destiny" | "love_without_respect_and_support" | "not_find_truth_about_compatibility";
|
||||
who_matter: "me" | "kids" | "family";
|
||||
your_priority: "inner_harmony" | "reliable_relationship" | "new_impressions";
|
||||
}
|
||||
|
||||
export interface IAnswersSessionCompatibilityV3 {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 71px;
|
||||
// height: 71px;
|
||||
padding: 25px 16px;
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
& > .likesImages {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-left: 4px;
|
||||
margin-left: 8px;
|
||||
|
||||
& > .likesImage {
|
||||
width: 28px;
|
||||
@ -102,8 +102,8 @@
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
border: 2px solid rgba(255, 255, 255, 1);
|
||||
margin-left: -4px;
|
||||
// border: 2px solid rgba(255, 255, 255, 1);
|
||||
margin-left: -8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import styles from "./styles.module.scss";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
import Title from "@/components/Title";
|
||||
import PrivacyPolicy from "@/components/pages/ABDesign/v1/components/PrivacyPolicy";
|
||||
import Button from "../../components/Button";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
|
||||
function CompatibilityTestPage() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleNext = () => {
|
||||
navigate(routes.client.compatibilityV2RelationshipStatus());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Title variant="h2" className={styles.title}>
|
||||
{translate("/compatibility-test.title", {
|
||||
br: <br />,
|
||||
})}
|
||||
</Title>
|
||||
<ul className={styles.points}>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<li key={index}>
|
||||
{translate(`/compatibility-test.points.point${index + 1}`)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{/* <ChooseGender onSelectGender={selectGender} /> */}
|
||||
{/* <div className={styles["genders-container"]}>
|
||||
{localGenders.map((_gender, index) => (
|
||||
<Answer
|
||||
key={index}
|
||||
answer={_gender}
|
||||
isSelected={gender === _gender.id}
|
||||
onClick={() =>
|
||||
selectGender(genders.find((g) => g.id === _gender.id) ?? null)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</div> */}
|
||||
<Button className={styles.button} onClick={handleNext}>
|
||||
{translate("/compatibility-test.button")}
|
||||
</Button>
|
||||
<PrivacyPolicy
|
||||
containerClassName={styles["privacy-policy"]}
|
||||
haveCheckbox={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CompatibilityTestPage;
|
||||
@ -0,0 +1,53 @@
|
||||
.container {
|
||||
position: relative;
|
||||
min-height: calc(100dvh - 110px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 18px;
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
line-height: 125%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.privacy-policy {
|
||||
max-width: 316px;
|
||||
text-align: center;
|
||||
position: sticky;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.points {
|
||||
margin-top: 46px;
|
||||
color: #2c2c2c;
|
||||
list-style-type: disc;
|
||||
|
||||
li {
|
||||
font-size: 20px;
|
||||
line-height: 125%;
|
||||
font-weight: 300;
|
||||
margin-left: 28px;
|
||||
|
||||
&::marker {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button.button {
|
||||
background: rgba(37, 99, 235, 1);
|
||||
box-shadow: 2px 5px 2.5px -1px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 20px;
|
||||
margin-top: 64px;
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
|
||||
:global(body.dark-theme) {
|
||||
.points {
|
||||
color: #f7f7f7;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
import Title from "@/components/Title";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { IAnswersSessionCompatibilityV2 } from "@/api/resources/Session";
|
||||
import { useMemo } from "react";
|
||||
import Answer from "../../components/Answer";
|
||||
import { useSelector } from "react-redux";
|
||||
import { actions, selectors } from "@/store";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useSession } from "@/hooks/session/useSession";
|
||||
import { ESourceAuthorization } from "@/api/resources/User";
|
||||
import routes from "@/routes";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { sleep } from "@/services/date";
|
||||
import { answerTimeOut } from "../../data";
|
||||
|
||||
function FearInRelationship() {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const { updateSession } = useSession();
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
const { fearInRelationship } = useSelector(
|
||||
selectors.selectCompatibilityV2Answers
|
||||
);
|
||||
|
||||
const answers: {
|
||||
id: IAnswersSessionCompatibilityV2["fear_in_relationship"];
|
||||
title: string;
|
||||
}[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "wasting_life_for_wrong_person",
|
||||
title: translate("/fear-in-relationship.answer1"),
|
||||
},
|
||||
{
|
||||
id: "stay_single",
|
||||
title: translate("/fear-in-relationship.answer2"),
|
||||
},
|
||||
{
|
||||
id: "skip_destiny",
|
||||
title: translate("/fear-in-relationship.answer3"),
|
||||
},
|
||||
{
|
||||
id: "love_without_respect_and_support",
|
||||
title: translate("/fear-in-relationship.answer4"),
|
||||
},
|
||||
{
|
||||
id: "not_find_truth_about_compatibility",
|
||||
title: translate("/fear-in-relationship.answer5"),
|
||||
},
|
||||
],
|
||||
[translate]
|
||||
);
|
||||
|
||||
const handleClick = async (
|
||||
id: IAnswersSessionCompatibilityV2["fear_in_relationship"]
|
||||
) => {
|
||||
dispatch(actions.compatibilityV2Answers.update({ fearInRelationship: id }));
|
||||
updateSession(
|
||||
{
|
||||
answers: {
|
||||
fear_in_relationship: id,
|
||||
},
|
||||
},
|
||||
ESourceAuthorization["aura.compatibility.v2"]
|
||||
);
|
||||
if (id !== fearInRelationship) await sleep(answerTimeOut);
|
||||
navigate(routes.client.compatibilityV2ImportantStep());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Title variant="h2" className={styles.title}>
|
||||
{translate("/fear-in-relationship.title")}
|
||||
</Title>
|
||||
{answers.map((answers, index) => (
|
||||
<Answer
|
||||
key={index}
|
||||
answer={answers}
|
||||
isSelected={fearInRelationship === answers.id}
|
||||
onClick={() => handleClick(answers.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FearInRelationship;
|
||||
@ -54,6 +54,10 @@ function GenderPage() {
|
||||
flag: EUnleashFlags.v2CompatibilityReviewPage,
|
||||
});
|
||||
|
||||
const { variant: pathToEnteringBirthdate = "hide" } = useUnleash({
|
||||
flag: EUnleashFlags.v2CompatibilityPathToEnteringBirthdate,
|
||||
});
|
||||
|
||||
const pageType = flags?.genderPageType?.[0] || genderPageType || "v2";
|
||||
const genderButtonIcon = flags?.genderButtonIcon?.[0] || "hide";
|
||||
|
||||
@ -115,12 +119,24 @@ function GenderPage() {
|
||||
if (relationshipStatusPagePlacement === "v2") {
|
||||
return navigate(routes.client.compatibilityV2RelationshipStatus());
|
||||
}
|
||||
console.log(reviewPage);
|
||||
|
||||
if (reviewPage === "show") {
|
||||
return navigate(routes.client.compatibilityV2Review());
|
||||
}
|
||||
if (pathToEnteringBirthdate === "show") {
|
||||
return navigate(routes.client.compatibilityV2CompatibilityTest());
|
||||
}
|
||||
return navigate(routes.client.compatibilityV2Birthdate());
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [gender, navigate]);
|
||||
}, [
|
||||
gender,
|
||||
navigate,
|
||||
reviewPage,
|
||||
pathToEnteringBirthdate,
|
||||
relationshipStatusPagePlacement,
|
||||
isReady,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (privacyPolicyChecked && gender && isSelected) {
|
||||
@ -129,6 +145,20 @@ function GenderPage() {
|
||||
}
|
||||
}, [gender, handleNext, isSelected, privacyPolicyChecked]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (pathToEnteringBirthdate === "show" && !pathToEnteringBirthdateShown) {
|
||||
// dispatch(
|
||||
// actions.compatibilityV2.update({ pathToEnteringBirthdateShown: true })
|
||||
// );
|
||||
// return navigate(routes.client.compatibilityV2CompatibilityTest());
|
||||
// }
|
||||
// }, [
|
||||
// dispatch,
|
||||
// navigate,
|
||||
// pathToEnteringBirthdate,
|
||||
// pathToEnteringBirthdateShown,
|
||||
// ]);
|
||||
|
||||
if (!ready || !isReady) return <Loader color={LoaderColor.Black} />;
|
||||
|
||||
if (relationshipStatusPagePlacement === "v1" && !noRedirectAB) {
|
||||
|
||||
26
src/components/CompatibilityV2/pages/ImportantStep/index.tsx
Normal file
26
src/components/CompatibilityV2/pages/ImportantStep/index.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import AnswerExplanation from "../../templates/AnswerExplanation";
|
||||
import routes from "@/routes";
|
||||
import styles from "./styles.module.scss";
|
||||
|
||||
function ImportantStep() {
|
||||
const navigate = useNavigate();
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
|
||||
return (
|
||||
<AnswerExplanation
|
||||
title={translate(`/important-step.title`)}
|
||||
text={translate(`/important-step.text`)}
|
||||
buttonType="next"
|
||||
classNameTitle={styles.title}
|
||||
classNameText={styles.text}
|
||||
handleNext={() => {
|
||||
navigate(routes.client.compatibilityV2WhoMatter());
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ImportantStep;
|
||||
@ -0,0 +1,8 @@
|
||||
.title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 62px;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-bottom: 62px;
|
||||
}
|
||||
@ -14,7 +14,10 @@ import { getZodiacSignByDate } from "@/services/zodiac-sign";
|
||||
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
|
||||
import Loader, { LoaderColor } from "@/components/Loader";
|
||||
import { useEffect } from "react";
|
||||
import metricService, { EGoals, EMetrics } from "@/services/metric/metricService";
|
||||
import metricService, {
|
||||
EGoals,
|
||||
EMetrics,
|
||||
} from "@/services/metric/metricService";
|
||||
|
||||
function PalmsInformation() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
@ -26,33 +29,47 @@ function PalmsInformation() {
|
||||
});
|
||||
|
||||
const { isReady, variant: zodiacImages } = useUnleash({
|
||||
flag: EUnleashFlags.zodiacImages
|
||||
flag: EUnleashFlags.zodiacImages,
|
||||
});
|
||||
|
||||
const { variant: relationshipStatusPagePlacement = "v0" } = useUnleash({
|
||||
flag: EUnleashFlags.v2CompatibilityRelationshipStatusPagePlacement
|
||||
flag: EUnleashFlags.v2CompatibilityRelationshipStatusPagePlacement,
|
||||
});
|
||||
|
||||
const { variant: pathToEnteringBirthdate = "hide" } = useUnleash({
|
||||
flag: EUnleashFlags.v2CompatibilityPathToEnteringBirthdate,
|
||||
});
|
||||
|
||||
const handleNext = () => {
|
||||
if (relationshipStatusPagePlacement === "v1" || relationshipStatusPagePlacement === "v2") {
|
||||
if (
|
||||
relationshipStatusPagePlacement === "v1" ||
|
||||
relationshipStatusPagePlacement === "v2" ||
|
||||
pathToEnteringBirthdate === "show"
|
||||
) {
|
||||
return navigate(`${routes.client.compatibilityV2RelateFollowing()}/1`);
|
||||
}
|
||||
navigate(routes.client.compatibilityV2RelationshipStatus());
|
||||
};
|
||||
|
||||
if (!isReady) {
|
||||
return <Loader color={LoaderColor.Black} />;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const ua = window.navigator.userAgent;
|
||||
if (ua.includes("FBAN") || ua.includes("FBAV") || ua.includes("FBIOS")) {
|
||||
metricService.reachGoal(EGoals.STAYED_IN_FB, [EMetrics.YANDEX, EMetrics.KLAVIYO])
|
||||
metricService.reachGoal(EGoals.STAYED_IN_FB, [
|
||||
EMetrics.YANDEX,
|
||||
EMetrics.KLAVIYO,
|
||||
]);
|
||||
}
|
||||
if (ua.includes("Instagram")) {
|
||||
metricService.reachGoal(EGoals.STAYED_IN_INSTAGRAM, [EMetrics.YANDEX, EMetrics.KLAVIYO])
|
||||
metricService.reachGoal(EGoals.STAYED_IN_INSTAGRAM, [
|
||||
EMetrics.YANDEX,
|
||||
EMetrics.KLAVIYO,
|
||||
]);
|
||||
}
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
if (!isReady) {
|
||||
return <Loader color={LoaderColor.Black} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles["page-container"]}>
|
||||
@ -78,11 +95,11 @@ function PalmsInformation() {
|
||||
{translate(`/palms-information.${zodiacSign?.toLowerCase()}.title`)}
|
||||
</Title>
|
||||
<p className={styles.description}>
|
||||
{translate(`/palms-information.${zodiacSign?.toLowerCase()}.description`)}
|
||||
{translate(
|
||||
`/palms-information.${zodiacSign?.toLowerCase()}.description`
|
||||
)}
|
||||
</p>
|
||||
<Button onClick={handleNext}>
|
||||
{translate("next")}
|
||||
</Button>
|
||||
<Button onClick={handleNext}>{translate("next")}</Button>
|
||||
<Policy>
|
||||
{translate("privacy_policy", {
|
||||
eulaLink: (
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
import styles from "./styles.module.scss";
|
||||
import AnswerExplanation from "../../templates/AnswerExplanation";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import { useSelector } from "react-redux";
|
||||
import { selectors } from "@/store";
|
||||
import { useMemo } from "react";
|
||||
|
||||
function PersonalizedRelationshipAnalysis() {
|
||||
const navigate = useNavigate();
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
const { relationshipStatus: relationshipStatusAnswer } = useSelector(
|
||||
selectors.selectCompatibilityV2Answers
|
||||
);
|
||||
|
||||
const relationshipStatus = useMemo(() => {
|
||||
return relationshipStatusAnswer === "single" ? "single" : "relationship";
|
||||
}, [relationshipStatusAnswer]);
|
||||
|
||||
return (
|
||||
<AnswerExplanation
|
||||
title={translate(
|
||||
`/personalized-relationship-analysis.${relationshipStatus}.title`
|
||||
)}
|
||||
text={translate(
|
||||
`/personalized-relationship-analysis.${relationshipStatus}.text`
|
||||
)}
|
||||
buttonType="next"
|
||||
classNameTitle={styles.title}
|
||||
classNameText={styles.text}
|
||||
handleNext={() => {
|
||||
navigate(routes.client.compatibilityV2Birthdate());
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default PersonalizedRelationshipAnalysis;
|
||||
@ -0,0 +1,7 @@
|
||||
.title {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-bottom: 38px;
|
||||
}
|
||||
@ -14,6 +14,7 @@ import { useSession } from "@/hooks/session/useSession";
|
||||
import { IAnswersSessionCompatibilityV2 } from "@/api/resources/Session";
|
||||
import { ESourceAuthorization } from "@/api/resources/User";
|
||||
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
|
||||
import Loader, { LoaderColor } from "@/components/Loader";
|
||||
|
||||
function RelationshipStatus() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
@ -25,14 +26,47 @@ function RelationshipStatus() {
|
||||
);
|
||||
|
||||
const { variant: relationshipStatusPagePlacement = "v0" } = useUnleash({
|
||||
flag: EUnleashFlags.v2CompatibilityRelationshipStatusPagePlacement
|
||||
flag: EUnleashFlags.v2CompatibilityRelationshipStatusPagePlacement,
|
||||
});
|
||||
|
||||
const { variant: pathToEnteringBirthdate = "hide", isReady } = useUnleash({
|
||||
flag: EUnleashFlags.v2CompatibilityPathToEnteringBirthdate,
|
||||
});
|
||||
|
||||
const isPathToEnteringBirthdate = useMemo(
|
||||
() => isReady && pathToEnteringBirthdate === "show",
|
||||
[isReady, pathToEnteringBirthdate]
|
||||
);
|
||||
|
||||
const answers: {
|
||||
id: IAnswersSessionCompatibilityV2["relationship_status"];
|
||||
title: string;
|
||||
}[] = useMemo(
|
||||
() => [
|
||||
}[] = useMemo(() => {
|
||||
if (isPathToEnteringBirthdate) {
|
||||
return [
|
||||
{
|
||||
id: "single",
|
||||
title: translate("/relationship-status.v1.answer1"),
|
||||
},
|
||||
{
|
||||
id: "start",
|
||||
title: translate("/relationship-status.v1.answer2"),
|
||||
},
|
||||
{
|
||||
id: "in_relationship",
|
||||
title: translate("/relationship-status.v1.answer3"),
|
||||
},
|
||||
{
|
||||
id: "developing",
|
||||
title: translate("/relationship-status.v1.answer4"),
|
||||
},
|
||||
{
|
||||
id: "crisis",
|
||||
title: translate("/relationship-status.v1.answer5"),
|
||||
},
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
id: "single",
|
||||
title: translate("/relationship-status.answer1"),
|
||||
@ -53,21 +87,31 @@ function RelationshipStatus() {
|
||||
// id: "complicated",
|
||||
// title: translate("/relationship-status.answer5"),
|
||||
// },
|
||||
],
|
||||
[translate]
|
||||
);
|
||||
];
|
||||
}, [isPathToEnteringBirthdate, translate]);
|
||||
|
||||
const handleClick = async (id: IAnswersSessionCompatibilityV2["relationship_status"]) => {
|
||||
const handleClick = async (
|
||||
id: IAnswersSessionCompatibilityV2["relationship_status"]
|
||||
) => {
|
||||
dispatch(actions.compatibilityV2Answers.update({ relationshipStatus: id }));
|
||||
updateSession({
|
||||
answers: {
|
||||
relationship_status: id,
|
||||
updateSession(
|
||||
{
|
||||
answers: {
|
||||
relationship_status: id,
|
||||
},
|
||||
},
|
||||
}, ESourceAuthorization["aura.compatibility.v2"]);
|
||||
ESourceAuthorization["aura.compatibility.v2"]
|
||||
);
|
||||
if (id !== relationshipStatus) await sleep(answerTimeOut);
|
||||
|
||||
if (isPathToEnteringBirthdate) {
|
||||
return navigate(routes.client.compatibilityV2RelationshipStatusResult());
|
||||
}
|
||||
|
||||
if (relationshipStatusPagePlacement === "v1") {
|
||||
return navigate(`${routes.client.compatibilityV2Gender()}?noRedirectAB=true`);
|
||||
return navigate(
|
||||
`${routes.client.compatibilityV2Gender()}?noRedirectAB=true`
|
||||
);
|
||||
}
|
||||
|
||||
if (relationshipStatusPagePlacement === "v2") {
|
||||
@ -77,10 +121,16 @@ function RelationshipStatus() {
|
||||
navigate(`${routes.client.compatibilityV2RelateFollowing()}/1`);
|
||||
};
|
||||
|
||||
if (!isReady) {
|
||||
return <Loader color={LoaderColor.Black} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Title variant="h2" className={styles.title}>
|
||||
{translate("/relationship-status.title")}
|
||||
{isPathToEnteringBirthdate
|
||||
? translate("/relationship-status.v1.title")
|
||||
: translate("/relationship-status.title")}
|
||||
</Title>
|
||||
{answers.map((answers, index) => (
|
||||
<Answer
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import routes from "@/routes";
|
||||
import { selectors } from "@/store";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import AnswerExplanation from "../../templates/AnswerExplanation";
|
||||
|
||||
function RelationshipStatusResult() {
|
||||
const navigate = useNavigate();
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
const { relationshipStatus } = useSelector(
|
||||
selectors.selectCompatibilityV2Answers
|
||||
);
|
||||
|
||||
return (
|
||||
<AnswerExplanation
|
||||
title={translate(
|
||||
`/relationship-status-result.${relationshipStatus}.title`
|
||||
)}
|
||||
text={translate(`/relationship-status-result.${relationshipStatus}.text`)}
|
||||
handleNext={() => {
|
||||
navigate(routes.client.compatibilityV2FearInRelationship());
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default RelationshipStatusResult;
|
||||
@ -11,15 +11,31 @@ import { images } from "../../data";
|
||||
import Button from "../../components/Button";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
|
||||
import Loader, { LoaderColor } from "@/components/Loader";
|
||||
|
||||
function ReviewPage() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
const navigate = useNavigate();
|
||||
const { variant: pathToEnteringBirthdate = "hide", isReady } = useUnleash({
|
||||
flag: EUnleashFlags.v2CompatibilityPathToEnteringBirthdate,
|
||||
});
|
||||
|
||||
const handleNext = () => {
|
||||
if (pathToEnteringBirthdate === "show") {
|
||||
return navigate(routes.client.compatibilityV2CompatibilityTest());
|
||||
}
|
||||
navigate(routes.client.compatibilityV2Birthdate());
|
||||
};
|
||||
|
||||
if (!isReady) {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Loader color={LoaderColor.Black} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Title variant="h1" className={styles.title}>
|
||||
@ -51,9 +67,9 @@ function ReviewPage() {
|
||||
verifiedText={translate("/review.review.verified_user")}
|
||||
likesText={translate("/review.review.likes")}
|
||||
likesImages={[
|
||||
"review/like_1.jpg",
|
||||
"review/like_2.jpg",
|
||||
"review/like_3.png",
|
||||
images("review/like_1.png"),
|
||||
images("review/like_2.png"),
|
||||
images("review/like_3.png"),
|
||||
]}
|
||||
image={images("review/avatar.jpg")}
|
||||
/>
|
||||
|
||||
@ -6,6 +6,10 @@
|
||||
min-height: 100dvh;
|
||||
margin: 0 auto;
|
||||
padding: 45px 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
& * {
|
||||
font-family: Inter;
|
||||
|
||||
79
src/components/CompatibilityV2/pages/WhoMatter/index.tsx
Normal file
79
src/components/CompatibilityV2/pages/WhoMatter/index.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
import Title from "@/components/Title";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { IAnswersSessionCompatibilityV2 } from "@/api/resources/Session";
|
||||
import { useMemo } from "react";
|
||||
import Answer from "../../components/Answer";
|
||||
import { useSelector } from "react-redux";
|
||||
import { actions, selectors } from "@/store";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useSession } from "@/hooks/session/useSession";
|
||||
import { ESourceAuthorization } from "@/api/resources/User";
|
||||
import routes from "@/routes";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { sleep } from "@/services/date";
|
||||
import { answerTimeOut } from "../../data";
|
||||
|
||||
function WhoMatter() {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const { updateSession } = useSession();
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
const { whoMatter } = useSelector(selectors.selectCompatibilityV2Answers);
|
||||
|
||||
const answers: {
|
||||
id: IAnswersSessionCompatibilityV2["who_matter"];
|
||||
title: string;
|
||||
}[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "me",
|
||||
title: translate("/who-matter.answer1"),
|
||||
},
|
||||
{
|
||||
id: "kids",
|
||||
title: translate("/who-matter.answer2"),
|
||||
},
|
||||
{
|
||||
id: "family",
|
||||
title: translate("/who-matter.answer3"),
|
||||
},
|
||||
],
|
||||
[translate]
|
||||
);
|
||||
|
||||
const handleClick = async (
|
||||
id: IAnswersSessionCompatibilityV2["who_matter"]
|
||||
) => {
|
||||
dispatch(actions.compatibilityV2Answers.update({ whoMatter: id }));
|
||||
updateSession(
|
||||
{
|
||||
answers: {
|
||||
who_matter: id,
|
||||
},
|
||||
},
|
||||
ESourceAuthorization["aura.compatibility.v2"]
|
||||
);
|
||||
if (id !== whoMatter) await sleep(answerTimeOut);
|
||||
navigate(routes.client.compatibilityV2YourPriority());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Title variant="h2" className={styles.title}>
|
||||
{translate("/who-matter.title")}
|
||||
</Title>
|
||||
{answers.map((answers, index) => (
|
||||
<Answer
|
||||
key={index}
|
||||
answer={answers}
|
||||
isSelected={whoMatter === answers.id}
|
||||
onClick={() => handleClick(answers.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WhoMatter;
|
||||
79
src/components/CompatibilityV2/pages/YourPriority/index.tsx
Normal file
79
src/components/CompatibilityV2/pages/YourPriority/index.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
import Title from "@/components/Title";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { IAnswersSessionCompatibilityV2 } from "@/api/resources/Session";
|
||||
import { useMemo } from "react";
|
||||
import Answer from "../../components/Answer";
|
||||
import { useSelector } from "react-redux";
|
||||
import { actions, selectors } from "@/store";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useSession } from "@/hooks/session/useSession";
|
||||
import { ESourceAuthorization } from "@/api/resources/User";
|
||||
import routes from "@/routes";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { sleep } from "@/services/date";
|
||||
import { answerTimeOut } from "../../data";
|
||||
|
||||
function YourPriority() {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const { updateSession } = useSession();
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
const { yourPriority } = useSelector(selectors.selectCompatibilityV2Answers);
|
||||
|
||||
const answers: {
|
||||
id: IAnswersSessionCompatibilityV2["your_priority"];
|
||||
title: string;
|
||||
}[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "inner_harmony",
|
||||
title: translate("/your-priority.answer1"),
|
||||
},
|
||||
{
|
||||
id: "reliable_relationship",
|
||||
title: translate("/your-priority.answer2"),
|
||||
},
|
||||
{
|
||||
id: "new_impressions",
|
||||
title: translate("/your-priority.answer3"),
|
||||
},
|
||||
],
|
||||
[translate]
|
||||
);
|
||||
|
||||
const handleClick = async (
|
||||
id: IAnswersSessionCompatibilityV2["your_priority"]
|
||||
) => {
|
||||
dispatch(actions.compatibilityV2Answers.update({ yourPriority: id }));
|
||||
updateSession(
|
||||
{
|
||||
answers: {
|
||||
your_priority: id,
|
||||
},
|
||||
},
|
||||
ESourceAuthorization["aura.compatibility.v2"]
|
||||
);
|
||||
if (id !== yourPriority) await sleep(answerTimeOut);
|
||||
navigate(routes.client.compatibilityV2PersonalizedRelationshipAnalysis());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Title variant="h2" className={styles.title}>
|
||||
{translate("/your-priority.title")}
|
||||
</Title>
|
||||
{answers.map((answers, index) => (
|
||||
<Answer
|
||||
key={index}
|
||||
answer={answers}
|
||||
isSelected={yourPriority === answers.id}
|
||||
onClick={() => handleClick(answers.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default YourPriority;
|
||||
@ -0,0 +1,73 @@
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import styles from "./styles.module.scss";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import Title from "@/components/Title";
|
||||
import Button from "../../components/Button";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
interface IAnswerExplanation {
|
||||
title?: string;
|
||||
text?: string;
|
||||
buttonType?: "back_next" | "next";
|
||||
classNameTitle?: string;
|
||||
classNameText?: string;
|
||||
handleBack?: () => void;
|
||||
handleNext: () => void;
|
||||
}
|
||||
|
||||
function AnswerExplanation({
|
||||
title,
|
||||
text,
|
||||
buttonType = "back_next",
|
||||
classNameTitle = "",
|
||||
classNameText = "",
|
||||
handleBack,
|
||||
handleNext,
|
||||
}: IAnswerExplanation) {
|
||||
const { translate } = useTranslations(ELocalesPlacement.CompatibilityV2);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleBackClick = () => {
|
||||
return handleBack ? handleBack() : navigate(-1);
|
||||
};
|
||||
|
||||
const handleNextClick = () => {
|
||||
return handleNext();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{!!title?.length && (
|
||||
<Title variant="h2" className={`${styles.title} ${classNameTitle}`}>
|
||||
{title}
|
||||
</Title>
|
||||
)}
|
||||
{!!text?.length && (
|
||||
<p className={`${styles.text} ${classNameText}`}>{text}</p>
|
||||
)}
|
||||
{buttonType === "back_next" && (
|
||||
<div className={styles["buttons-container"]}>
|
||||
<Button
|
||||
className={`${styles.button} ${styles["back-button"]}`}
|
||||
onClick={handleBackClick}
|
||||
>
|
||||
{translate("back")}
|
||||
</Button>
|
||||
<Button
|
||||
className={`${styles.button} ${styles["next-button"]}`}
|
||||
onClick={handleNextClick}
|
||||
>
|
||||
{translate("next")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{buttonType === "next" && (
|
||||
<Button className={styles.button} onClick={handleNextClick}>
|
||||
{translate("next")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AnswerExplanation;
|
||||
@ -0,0 +1,44 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 20px;
|
||||
line-height: 25px;
|
||||
text-align: center;
|
||||
margin-bottom: 74px;
|
||||
}
|
||||
|
||||
.buttons-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding-top: 24px;
|
||||
|
||||
& > .button {
|
||||
width: 48%;
|
||||
min-width: 0px;
|
||||
font-size: 23px;
|
||||
height: 44px;
|
||||
min-height: 0px;
|
||||
|
||||
&.back-button {
|
||||
background: none;
|
||||
color: #2c2c2c;
|
||||
border: solid #2c2c2c 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:global(body.dark-theme) {
|
||||
.buttons-container > .button.back-button {
|
||||
color: #fff;
|
||||
border-color: #fff;
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,7 @@ export enum EUnleashFlags {
|
||||
"v2CompatibilityCameraTemplate" = "v2-compatibility-camera-template",
|
||||
"v2CompatibilityRelationshipStatusPagePlacement" = "v2-compatibility-relationship-status-page-placement",
|
||||
"v2CompatibilityReviewPage" = "v2-compatibility-review-page",
|
||||
"v2CompatibilityPathToEnteringBirthdate" = "v2-compatibility-path-to-entering-birthdate",
|
||||
}
|
||||
|
||||
interface IUseUnleashProps<T extends EUnleashFlags> {
|
||||
@ -78,6 +79,7 @@ interface IVariants {
|
||||
[EUnleashFlags.v2CompatibilityCameraTemplate]: 'v0' | 'v1' | 'v2' | 'v3' | 'v4';
|
||||
[EUnleashFlags.v2CompatibilityRelationshipStatusPagePlacement]: 'v0' | 'v1' | 'v2';
|
||||
[EUnleashFlags.v2CompatibilityReviewPage]: 'show' | 'hide';
|
||||
[EUnleashFlags.v2CompatibilityPathToEnteringBirthdate]: 'hide' | 'show';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -34,17 +34,28 @@ function Layout() {
|
||||
]);
|
||||
|
||||
const { variant: relationshipStatusPagePlacement = "v0" } = useUnleash({
|
||||
flag: EUnleashFlags.v2CompatibilityRelationshipStatusPagePlacement
|
||||
flag: EUnleashFlags.v2CompatibilityRelationshipStatusPagePlacement,
|
||||
});
|
||||
|
||||
const getIsBackButtonVisible = () => {
|
||||
if (
|
||||
relationshipStatusPagePlacement === "v1" && location.pathname.includes(routes.client.compatibilityV2RelationshipStatus())
|
||||
location.pathname.includes(
|
||||
routes.client.compatibilityV2RelationshipStatusResult()
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
relationshipStatusPagePlacement === "v1" && location.pathname.includes(routes.client.compatibilityV2Gender())
|
||||
relationshipStatusPagePlacement === "v1" &&
|
||||
location.pathname.includes(
|
||||
routes.client.compatibilityV2RelationshipStatus()
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
relationshipStatusPagePlacement === "v1" &&
|
||||
location.pathname.includes(routes.client.compatibilityV2Gender())
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,30 +1,13 @@
|
||||
import { useSchemeColorByElement } from "@/hooks/useSchemeColorByElement";
|
||||
import { useRef } from "react";
|
||||
import { useMemo, useRef } from "react";
|
||||
import { Outlet, useLocation } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import StepperBar from "@/components/CompatibilityV2/components/StepperBar";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useSelector } from "react-redux";
|
||||
import { selectors } from "@/store";
|
||||
|
||||
const stepperRoutes = [
|
||||
routes.client.compatibilityV2Birthdate(),
|
||||
routes.client.compatibilityV2PalmsInformation(),
|
||||
// routes.client.compatibilityV2WhatAspects(),
|
||||
routes.client.compatibilityV2RelationshipStatus(),
|
||||
routes.client.compatibilityV2RelateFollowing(),
|
||||
routes.client.compatibilityV2RomanticGestures(),
|
||||
routes.client.compatibilityV2CheckingPhone(),
|
||||
routes.client.compatibilityV2BirthdatePartner(),
|
||||
routes.client.compatibilityV2PalmsInformationPartner(),
|
||||
routes.client.compatibilityV2DateEvent(),
|
||||
|
||||
// routes.client.compatibilityV2ElementResonates(),
|
||||
// routes.client.compatibilityV2FavoriteColor(),
|
||||
routes.client.compatibilityV2HeadOrHeart(),
|
||||
routes.client.compatibilityV2HeadOrHeartResult(),
|
||||
routes.client.compatibilityV2Email(),
|
||||
];
|
||||
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
|
||||
import Loader, { LoaderColor } from "@/components/Loader";
|
||||
|
||||
function StepperLayout() {
|
||||
const darkTheme = useSelector(selectors.selectDarkTheme);
|
||||
@ -33,27 +16,86 @@ function StepperLayout() {
|
||||
useSchemeColorByElement(mainRef.current, "section.page, .page, section", [
|
||||
location,
|
||||
]);
|
||||
const { variant: pathToEnteringBirthdate = "hide", isReady } = useUnleash({
|
||||
flag: EUnleashFlags.v2CompatibilityPathToEnteringBirthdate,
|
||||
});
|
||||
|
||||
const stepperRoutes = useMemo(() => {
|
||||
if (pathToEnteringBirthdate === "show") {
|
||||
return [
|
||||
routes.client.compatibilityV2RelationshipStatus(),
|
||||
routes.client.compatibilityV2FearInRelationship(),
|
||||
routes.client.compatibilityV2ImportantStep(),
|
||||
routes.client.compatibilityV2WhoMatter(),
|
||||
routes.client.compatibilityV2YourPriority(),
|
||||
|
||||
routes.client.compatibilityV2Birthdate(),
|
||||
routes.client.compatibilityV2PalmsInformation(),
|
||||
// routes.client.compatibilityV2WhatAspects(),
|
||||
routes.client.compatibilityV2RelateFollowing(),
|
||||
routes.client.compatibilityV2RomanticGestures(),
|
||||
routes.client.compatibilityV2CheckingPhone(),
|
||||
routes.client.compatibilityV2BirthdatePartner(),
|
||||
routes.client.compatibilityV2PalmsInformationPartner(),
|
||||
routes.client.compatibilityV2DateEvent(),
|
||||
|
||||
// routes.client.compatibilityV2ElementResonates(),
|
||||
// routes.client.compatibilityV2FavoriteColor(),
|
||||
routes.client.compatibilityV2HeadOrHeart(),
|
||||
routes.client.compatibilityV2HeadOrHeartResult(),
|
||||
routes.client.compatibilityV2Email(),
|
||||
];
|
||||
}
|
||||
return [
|
||||
routes.client.compatibilityV2Birthdate(),
|
||||
routes.client.compatibilityV2PalmsInformation(),
|
||||
// routes.client.compatibilityV2WhatAspects(),
|
||||
routes.client.compatibilityV2RelationshipStatus(),
|
||||
routes.client.compatibilityV2RelateFollowing(),
|
||||
routes.client.compatibilityV2RomanticGestures(),
|
||||
routes.client.compatibilityV2CheckingPhone(),
|
||||
routes.client.compatibilityV2BirthdatePartner(),
|
||||
routes.client.compatibilityV2PalmsInformationPartner(),
|
||||
routes.client.compatibilityV2DateEvent(),
|
||||
|
||||
// routes.client.compatibilityV2ElementResonates(),
|
||||
// routes.client.compatibilityV2FavoriteColor(),
|
||||
routes.client.compatibilityV2HeadOrHeart(),
|
||||
routes.client.compatibilityV2HeadOrHeartResult(),
|
||||
routes.client.compatibilityV2Email(),
|
||||
];
|
||||
}, [pathToEnteringBirthdate]);
|
||||
|
||||
const getCurrentStep = () => {
|
||||
let index = 0;
|
||||
for (const route of stepperRoutes) {
|
||||
if (location.pathname.includes(route)) {
|
||||
if (location.pathname.includes(routes.client.compatibilityV2BirthdatePartner())) {
|
||||
if (
|
||||
location.pathname.includes(
|
||||
routes.client.compatibilityV2BirthdatePartner()
|
||||
)
|
||||
) {
|
||||
return 8;
|
||||
}
|
||||
if (location.pathname.includes(routes.client.compatibilityV2PalmsInformationPartner())) {
|
||||
if (
|
||||
location.pathname.includes(
|
||||
routes.client.compatibilityV2PalmsInformationPartner()
|
||||
)
|
||||
) {
|
||||
return 9;
|
||||
}
|
||||
return index + 1;
|
||||
}
|
||||
|
||||
|
||||
index++;
|
||||
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
if (!isReady) {
|
||||
return <Loader color={LoaderColor.Black} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<StepperBar
|
||||
|
||||
@ -44,13 +44,22 @@ import AddConsultant from "@/components/palmistry/AdditionalPurchases/pages/AddC
|
||||
import AddGuides from "@/components/palmistry/AdditionalPurchases/pages/AddGuides";
|
||||
import PaymentPage from "@/components/Payment/nmi/PaymentPage";
|
||||
import TryApp from "@/components/CompatibilityV2/pages/TryApp";
|
||||
import { ESiteTheme, useTheme } from "@/hooks/theme/useTheme";
|
||||
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
|
||||
import Loader, { LoaderColor } from "@/components/Loader";
|
||||
import { useTheme } from "@/hooks/theme/useTheme";
|
||||
import ScanHand from "@/components/CompatibilityV2/pages/ScanHand";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { useFunnel } from "@/hooks/funnel/useFunnel";
|
||||
import Review from "@/components/CompatibilityV2/pages/Review";
|
||||
import {
|
||||
getSystemColorScheme,
|
||||
subscribeToSystemThemeChange,
|
||||
} from "@/utils/Theme";
|
||||
import CompatibilityTestPage from "@/components/CompatibilityV2/pages/CompatibilityTest";
|
||||
import RelationshipStatusResult from "@/components/CompatibilityV2/pages/RelationshipStatusResult";
|
||||
import FearInRelationship from "@/components/CompatibilityV2/pages/FearInRelationship";
|
||||
import ImportantStep from "@/components/CompatibilityV2/pages/ImportantStep";
|
||||
import WhoMatter from "@/components/CompatibilityV2/pages/WhoMatter";
|
||||
import YourPriority from "@/components/CompatibilityV2/pages/YourPriority";
|
||||
import PersonalizedRelationshipAnalysis from "@/components/CompatibilityV2/pages/PersonalizedRelationshipAnalysis";
|
||||
|
||||
const removePrefix = (path: string) => path.replace(compatibilityV2Prefix, "");
|
||||
|
||||
@ -59,10 +68,6 @@ function CompatibilityV2Routes() {
|
||||
const dispatch = useDispatch();
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
const { isReady, variant: darkThemeCompatibilityV2Variant } = useUnleash({
|
||||
flag: EUnleashFlags.darkThemeCompatibilityV2,
|
||||
});
|
||||
|
||||
const { funnelData } = useFunnel({
|
||||
funnel: ELocalesPlacement.CompatibilityV2,
|
||||
paymentPlacement: "",
|
||||
@ -140,28 +145,17 @@ function CompatibilityV2Routes() {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (darkThemeCompatibilityV2Variant === "enabled") {
|
||||
setTheme(ESiteTheme.Dark);
|
||||
}
|
||||
return () => {
|
||||
setTheme(ESiteTheme.Light);
|
||||
};
|
||||
}, [darkThemeCompatibilityV2Variant]);
|
||||
const systemColorScheme = getSystemColorScheme();
|
||||
setTheme(systemColorScheme);
|
||||
|
||||
if (!isReady) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100dvh",
|
||||
}}
|
||||
>
|
||||
<Loader color={LoaderColor.Black} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const unsubscribe = subscribeToSystemThemeChange((theme) => {
|
||||
setTheme(theme);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe?.();
|
||||
};
|
||||
}, [setTheme]);
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
@ -261,6 +255,12 @@ function CompatibilityV2Routes() {
|
||||
>
|
||||
<Route path="*" element={<Gender />} />
|
||||
</Route>
|
||||
<Route
|
||||
path={removePrefix(
|
||||
routes.client.compatibilityV2CompatibilityTest()
|
||||
)}
|
||||
element={<CompatibilityTestPage />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(routes.client.compatibilityV2GenderPartner())}
|
||||
element={<GenderPartner />}
|
||||
@ -271,7 +271,39 @@ function CompatibilityV2Routes() {
|
||||
>
|
||||
<Route path="*" element={<FindHappiness />} />
|
||||
</Route>
|
||||
<Route
|
||||
path={removePrefix(
|
||||
routes.client.compatibilityV2RelationshipStatusResult()
|
||||
)}
|
||||
element={<RelationshipStatusResult />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(
|
||||
routes.client.compatibilityV2PersonalizedRelationshipAnalysis()
|
||||
)}
|
||||
element={<PersonalizedRelationshipAnalysis />}
|
||||
/>
|
||||
<Route element={<StepperLayout />}>
|
||||
<Route
|
||||
path={removePrefix(
|
||||
routes.client.compatibilityV2FearInRelationship()
|
||||
)}
|
||||
element={<FearInRelationship />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(
|
||||
routes.client.compatibilityV2ImportantStep()
|
||||
)}
|
||||
element={<ImportantStep />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(routes.client.compatibilityV2WhoMatter())}
|
||||
element={<WhoMatter />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(routes.client.compatibilityV2YourPriority())}
|
||||
element={<YourPriority />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(routes.client.compatibilityV2Birthdate())}
|
||||
element={<Birthdate />}
|
||||
|
||||
@ -224,6 +224,7 @@ const routes = {
|
||||
palmistryOnboardingV1: () => [palmistryV1Prefix, "onboarding"].join("/"),
|
||||
// CompatibilityV2
|
||||
compatibilityV2Welcome: () => [compatibilityV2Prefix, "welcome"].join("/"),
|
||||
compatibilityV2CompatibilityTest: () => [compatibilityV2Prefix, "compatibility-test"].join("/"),
|
||||
compatibilityV2Gender: () => [compatibilityV2Prefix, "gender"].join("/"),
|
||||
compatibilityV2GenderPartner: () => [compatibilityV2Prefix, "gender-partner"].join("/"),
|
||||
compatibilityV2Birthdate: () => [compatibilityV2Prefix, "birthdate"].join("/"),
|
||||
@ -233,6 +234,11 @@ const routes = {
|
||||
compatibilityV2DateEvent: () => [compatibilityV2Prefix, "date-event"].join("/"),
|
||||
compatibilityV2WhatAspects: () => [compatibilityV2Prefix, "what-aspects"].join("/"),
|
||||
compatibilityV2RelationshipStatus: () => [compatibilityV2Prefix, "relationship-status"].join("/"),
|
||||
compatibilityV2RelationshipStatusResult: () => [compatibilityV2Prefix, "relationship-status-result"].join("/"),
|
||||
compatibilityV2FearInRelationship: () => [compatibilityV2Prefix, "fear-in-relationship"].join("/"),
|
||||
compatibilityV2ImportantStep: () => [compatibilityV2Prefix, "important-step"].join("/"),
|
||||
compatibilityV2WhoMatter: () => [compatibilityV2Prefix, "who-matter"].join("/"),
|
||||
compatibilityV2YourPriority: () => [compatibilityV2Prefix, "your-priority"].join("/"),
|
||||
compatibilityV2ElementResonates: () => [compatibilityV2Prefix, "element-resonates"].join("/"),
|
||||
compatibilityV2FavoriteColor: () => [compatibilityV2Prefix, "favorite-color"].join("/"),
|
||||
compatibilityV2HeadOrHeart: () => [compatibilityV2Prefix, "head-or-heart"].join("/"),
|
||||
@ -240,6 +246,7 @@ const routes = {
|
||||
compatibilityV2RelateFollowing: () => [compatibilityV2Prefix, "relate-following"].join("/"),
|
||||
compatibilityV2RomanticGestures: () => [compatibilityV2Prefix, "romantic-gestures"].join("/"),
|
||||
compatibilityV2CheckingPhone: () => [compatibilityV2Prefix, "checking-phone"].join("/"),
|
||||
compatibilityV2PersonalizedRelationshipAnalysis: () => [compatibilityV2Prefix, "personalized-relationship-analysis"].join("/"),
|
||||
compatibilityV2LetScan: () => [compatibilityV2Prefix, "let-scan"].join("/"),
|
||||
compatibilityV2ScanInstruction: () => [compatibilityV2Prefix, "scan-instruction"].join("/"),
|
||||
compatibilityV2Camera: () => [compatibilityV2Prefix, "camera"].join("/"),
|
||||
|
||||
@ -2,7 +2,9 @@ import { IPalmistryFinger, IPalmistryLine } from "@/api/resources/Palmistry";
|
||||
import { createSlice, createSelector } from "@reduxjs/toolkit";
|
||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
export type ICompatibilityV2FingerLocal = IPalmistryFinger & { fingerName?: string }
|
||||
export type ICompatibilityV2FingerLocal = IPalmistryFinger & {
|
||||
fingerName?: string;
|
||||
};
|
||||
|
||||
interface ICompatibilityV2 {
|
||||
lines: IPalmistryLine[];
|
||||
@ -35,7 +37,8 @@ export const selectCompatibilityV2Lines = createSelector(
|
||||
(compatibilityV2) => compatibilityV2
|
||||
);
|
||||
export const selectCompatibilityV2Fingers = createSelector(
|
||||
(state: { compatibilityV2: ICompatibilityV2 }) => state.compatibilityV2.fingers,
|
||||
(state: { compatibilityV2: ICompatibilityV2 }) =>
|
||||
state.compatibilityV2.fingers,
|
||||
(compatibilityV2) => compatibilityV2
|
||||
);
|
||||
export const selectCompatibilityV2Photo = createSelector(
|
||||
@ -43,7 +46,8 @@ export const selectCompatibilityV2Photo = createSelector(
|
||||
(compatibilityV2) => compatibilityV2
|
||||
);
|
||||
export const selectCompatibilityV2FromRedesign = createSelector(
|
||||
(state: { compatibilityV2: ICompatibilityV2 }) => state.compatibilityV2.fromRedesign,
|
||||
(state: { compatibilityV2: ICompatibilityV2 }) =>
|
||||
state.compatibilityV2.fromRedesign,
|
||||
(compatibilityV2) => compatibilityV2
|
||||
);
|
||||
export default compatibilityV2Slice.reducer;
|
||||
|
||||
@ -2,41 +2,48 @@ import { createSlice, createSelector } from "@reduxjs/toolkit";
|
||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
export interface ICompatibilityV2Answers {
|
||||
whatAspects: string;
|
||||
relationshipStatus: string;
|
||||
elementResonates: string;
|
||||
favoriteColor: string;
|
||||
headOrHeart: string;
|
||||
dateEvent: string;
|
||||
romanticGestures: string;
|
||||
checkingPhone: string;
|
||||
whatAspects: string;
|
||||
relationshipStatus: string;
|
||||
elementResonates: string;
|
||||
favoriteColor: string;
|
||||
headOrHeart: string;
|
||||
dateEvent: string;
|
||||
romanticGestures: string;
|
||||
checkingPhone: string;
|
||||
fearInRelationship: string;
|
||||
whoMatter: string;
|
||||
yourPriority: string;
|
||||
}
|
||||
|
||||
const initialState: ICompatibilityV2Answers = {
|
||||
whatAspects: "",
|
||||
relationshipStatus: "",
|
||||
elementResonates: "",
|
||||
favoriteColor: "",
|
||||
headOrHeart: "",
|
||||
dateEvent: "",
|
||||
romanticGestures: "",
|
||||
checkingPhone: "",
|
||||
whatAspects: "",
|
||||
relationshipStatus: "",
|
||||
elementResonates: "",
|
||||
favoriteColor: "",
|
||||
headOrHeart: "",
|
||||
dateEvent: "",
|
||||
romanticGestures: "",
|
||||
checkingPhone: "",
|
||||
fearInRelationship: "",
|
||||
whoMatter: "",
|
||||
yourPriority: "",
|
||||
};
|
||||
|
||||
const compatibilityV2AnswersSlice = createSlice({
|
||||
name: "compatibilityV2Answers",
|
||||
initialState,
|
||||
reducers: {
|
||||
update(state, action: PayloadAction<Partial<ICompatibilityV2Answers>>) {
|
||||
return { ...state, ...action.payload };
|
||||
},
|
||||
name: "compatibilityV2Answers",
|
||||
initialState,
|
||||
reducers: {
|
||||
update(state, action: PayloadAction<Partial<ICompatibilityV2Answers>>) {
|
||||
return { ...state, ...action.payload };
|
||||
},
|
||||
extraReducers: (builder) => builder.addCase("reset", () => initialState),
|
||||
},
|
||||
extraReducers: (builder) => builder.addCase("reset", () => initialState),
|
||||
});
|
||||
|
||||
export const { actions } = compatibilityV2AnswersSlice;
|
||||
export const selectCompatibilityV2Answers = createSelector(
|
||||
(state: { compatibilityV2Answers: ICompatibilityV2Answers }) => state.compatibilityV2Answers,
|
||||
(compatibilityV2Answers) => compatibilityV2Answers
|
||||
(state: { compatibilityV2Answers: ICompatibilityV2Answers }) =>
|
||||
state.compatibilityV2Answers,
|
||||
(compatibilityV2Answers) => compatibilityV2Answers
|
||||
);
|
||||
export default compatibilityV2AnswersSlice.reducer;
|
||||
|
||||
@ -88,39 +88,56 @@ import palmistry, {
|
||||
selectPalmistryFingers,
|
||||
selectPalmistryFromRedesign,
|
||||
selectPalmistryLines,
|
||||
selectPalmistryPhoto
|
||||
selectPalmistryPhoto,
|
||||
} from "./palmistry";
|
||||
import compatibilityV2, {
|
||||
actions as compatibilityV2Actions,
|
||||
selectCompatibilityV2Fingers,
|
||||
selectCompatibilityV2FromRedesign,
|
||||
selectCompatibilityV2Lines,
|
||||
selectCompatibilityV2Photo
|
||||
selectCompatibilityV2Photo,
|
||||
} from "./compatibilityV2";
|
||||
import retainingFunnel, {
|
||||
actions as retainingFunnelActions,
|
||||
selectRetainingFunnel,
|
||||
selectCancellingSubscriptionId
|
||||
selectCancellingSubscriptionId,
|
||||
} from "./retainingFunnel";
|
||||
import compatibilityV3, {
|
||||
actions as compatibilityV3Actions,
|
||||
selectCompatibilityV3Fingers,
|
||||
selectCompatibilityV3FromRedesign,
|
||||
selectCompatibilityV3Lines,
|
||||
selectCompatibilityV3Photo
|
||||
selectCompatibilityV3Photo,
|
||||
} from "./compatibilityV3";
|
||||
import compatibilityV4, {
|
||||
actions as compatibilityV4Actions,
|
||||
selectCompatibilityV4Fingers,
|
||||
selectCompatibilityV4FromRedesign,
|
||||
selectCompatibilityV4Lines,
|
||||
selectCompatibilityV4Photo
|
||||
selectCompatibilityV4Photo,
|
||||
} from "./compatibilityV4";
|
||||
import { selectPaywallsIsMustUpdate, selectPaywalls } from "./paywalls";
|
||||
import privacyPolicy, { actions as privacyPolicyActions, selectPrivacyPolicy } from "./privacyPolicy";
|
||||
import personalVideo, { actions as personalVideoActions, selectPersonalVideo } from "./personalVideo";
|
||||
import chats, { actions as chatsActions, selectAnswers, selectCurrentStep } from "./chats";
|
||||
import chat, { actions as chatActions, selectCurrentAssistant, selectCurrentChatId, selectDateEndChatting, selectIsAutoRefill, selectIsPayedFirstPurchase } from "./chat";
|
||||
import privacyPolicy, {
|
||||
actions as privacyPolicyActions,
|
||||
selectPrivacyPolicy,
|
||||
} from "./privacyPolicy";
|
||||
import personalVideo, {
|
||||
actions as personalVideoActions,
|
||||
selectPersonalVideo,
|
||||
} from "./personalVideo";
|
||||
import chats, {
|
||||
actions as chatsActions,
|
||||
selectAnswers,
|
||||
selectCurrentStep,
|
||||
} from "./chats";
|
||||
import chat, {
|
||||
actions as chatActions,
|
||||
selectCurrentAssistant,
|
||||
selectCurrentChatId,
|
||||
selectDateEndChatting,
|
||||
selectIsAutoRefill,
|
||||
selectIsPayedFirstPurchase,
|
||||
} from "./chat";
|
||||
|
||||
const preloadedState = loadStore();
|
||||
export const actions = {
|
||||
|
||||
31
src/utils/Theme/index.ts
Normal file
31
src/utils/Theme/index.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { ESiteTheme } from "@/hooks/theme/useTheme";
|
||||
|
||||
export function getSystemColorScheme(): ESiteTheme {
|
||||
if (typeof window !== "undefined" && window.matchMedia) {
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? ESiteTheme.Dark
|
||||
: ESiteTheme.Light;
|
||||
}
|
||||
|
||||
return ESiteTheme.Light;
|
||||
}
|
||||
|
||||
export function subscribeToSystemThemeChange(
|
||||
callback: (theme: ESiteTheme) => void
|
||||
) {
|
||||
if (typeof window === "undefined" || !window.matchMedia) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
const handleChange = (e: MediaQueryListEvent) => {
|
||||
callback(e.matches ? ESiteTheme.Dark : ESiteTheme.Light);
|
||||
};
|
||||
|
||||
mediaQuery.addEventListener("change", handleChange);
|
||||
|
||||
return () => {
|
||||
mediaQuery.removeEventListener("change", handleChange);
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user