Develop - Palmistry in Aura and preload payment methods in palmistry
This commit is contained in:
parent
5be54aea7c
commit
a9cab32e20
BIN
public/trial-choice-preview.png
Normal file
BIN
public/trial-choice-preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
BIN
public/trial-choice.MOV
Normal file
BIN
public/trial-choice.MOV
Normal file
Binary file not shown.
@ -20,6 +20,6 @@
|
|||||||
linear-gradient(-45deg, #3a617120 9%, #21212120 72%, #21895120 96%);
|
linear-gradient(-45deg, #3a617120 9%, #21212120 72%, #21895120 96%);
|
||||||
background-blend-mode: color;
|
background-blend-mode: color;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
// transform: scale(1.02);
|
// transform: scale(1.03);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,6 +54,11 @@ function Email() {
|
|||||||
|
|
||||||
const authorize = async () => {
|
const authorize = async () => {
|
||||||
metricService.reachGoal(EGoals.LEAD, [EMetrics.FACEBOOK]);
|
metricService.reachGoal(EGoals.LEAD, [EMetrics.FACEBOOK]);
|
||||||
|
metricService.reachGoal(EGoals.ENTERED_EMAIL, [
|
||||||
|
EMetrics.KLAVIYO,
|
||||||
|
EMetrics.YANDEX,
|
||||||
|
EMetrics.FACEBOOK,
|
||||||
|
]);
|
||||||
await authorization(email, ESourceAuthorization["aura.palmistry.new"]);
|
await authorization(email, ESourceAuthorization["aura.palmistry.new"]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button-container {
|
.button-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0dvh;
|
bottom: 0dvh;
|
||||||
|
|||||||
@ -5,13 +5,22 @@ import { selectors } from "@/store";
|
|||||||
import { getFormattedPrice } from "@/utils/price.utils";
|
import { getFormattedPrice } from "@/utils/price.utils";
|
||||||
import Guarantees from "../../components/Guarantees";
|
import Guarantees from "../../components/Guarantees";
|
||||||
import Button from "../../components/Button";
|
import Button from "../../components/Button";
|
||||||
import PaymentModal from "../../components/PaymentModal";
|
import { useEffect } from "react";
|
||||||
import { useEffect, useState } from "react";
|
import {
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
useNavigate,
|
||||||
|
useOutletContext,
|
||||||
|
useSearchParams,
|
||||||
|
} from "react-router-dom";
|
||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
import { addCurrency, ELocalesPlacement } from "@/locales";
|
import { addCurrency, ELocalesPlacement } from "@/locales";
|
||||||
import { useTranslations } from "@/hooks/translations";
|
import { useTranslations } from "@/hooks/translations";
|
||||||
import Stars from "../../components/Stars";
|
import Stars from "../../components/Stars";
|
||||||
|
import metricService, { EGoals } from "@/services/metric/metricService";
|
||||||
|
|
||||||
|
interface IPaymentContext {
|
||||||
|
isShowPaymentModal: boolean;
|
||||||
|
setIsShowPaymentModal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
function Payment() {
|
function Payment() {
|
||||||
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||||
@ -20,7 +29,9 @@ function Payment() {
|
|||||||
const currency = useSelector(selectors.selectCurrency);
|
const currency = useSelector(selectors.selectCurrency);
|
||||||
const trialPrice = activeProductFromStore?.trialPrice || 0;
|
const trialPrice = activeProductFromStore?.trialPrice || 0;
|
||||||
const fullPrice = activeProductFromStore?.price || 0;
|
const fullPrice = activeProductFromStore?.price || 0;
|
||||||
const [isShowPaymentModal, setIsShowPaymentModal] = useState(false);
|
const { isShowPaymentModal, setIsShowPaymentModal } =
|
||||||
|
useOutletContext<IPaymentContext>();
|
||||||
|
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const subscriptionStatus =
|
const subscriptionStatus =
|
||||||
searchParams.get("redirect_status") === "succeeded" ? "subscribed" : "lead";
|
searchParams.get("redirect_status") === "succeeded" ? "subscribed" : "lead";
|
||||||
@ -31,6 +42,7 @@ function Payment() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (subscriptionStatus !== "subscribed") return;
|
if (subscriptionStatus !== "subscribed") return;
|
||||||
|
metricService.reachGoal(EGoals.PAYMENT_SUCCESS);
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
navigate(routes.client.skipTrial());
|
navigate(routes.client.skipTrial());
|
||||||
}, 1500);
|
}, 1500);
|
||||||
@ -80,13 +92,6 @@ function Payment() {
|
|||||||
{translate("/payment.get_personal_prediction")}
|
{translate("/payment.get_personal_prediction")}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<PaymentModal
|
|
||||||
className={
|
|
||||||
isShowPaymentModal || subscriptionStatus === "subscribed"
|
|
||||||
? styles["payment-modal-active"]
|
|
||||||
: styles["payment-modal-hide"]
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
83
src/components/pages/ABDesign/v1/pages/Camera/index.tsx
Normal file
83
src/components/pages/ABDesign/v1/pages/Camera/index.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import PalmCameraModal from "@/components/palmistry/palm-camera-modal/palm-camera-modal";
|
||||||
|
import styles from "./styles.module.scss";
|
||||||
|
import { DataURIToBlob } from "@/services/data";
|
||||||
|
import { useApi } from "@/api";
|
||||||
|
import { IPalmistryFinger } from "@/api/resources/Palmistry";
|
||||||
|
import { IPalmistryFingerLocal } from "@/store/palmistry";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { actions } from "@/store";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import routes from "@/routes";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Loader, { LoaderColor } from "@/components/Loader";
|
||||||
|
|
||||||
|
const fingersNames = {
|
||||||
|
thumb: "Thumb finger",
|
||||||
|
index_finger: "Index finger",
|
||||||
|
middle_finger: "Middle finger",
|
||||||
|
ring_finger: "Ring finger",
|
||||||
|
pinky: "Little finger",
|
||||||
|
};
|
||||||
|
|
||||||
|
const setFingersNames = (
|
||||||
|
fingers: IPalmistryFinger[]
|
||||||
|
): IPalmistryFingerLocal[] => {
|
||||||
|
if (!fingers) return [];
|
||||||
|
return fingers.map((finger) => {
|
||||||
|
return {
|
||||||
|
...finger,
|
||||||
|
fingerName: fingersNames[finger.name as keyof typeof fingersNames],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function Camera() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const api = useApi();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const getLines = async (file: File | Blob) => {
|
||||||
|
setIsLoading(true);
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
const result = await api.getPalmistryLines({ formData });
|
||||||
|
const fingers = setFingersNames(result?.fingers);
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
actions.palmistry.update({
|
||||||
|
lines: result?.lines,
|
||||||
|
fingers,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
const onTakePhoto = async (photo: string) => {
|
||||||
|
// setIsUpladProcessing(true);
|
||||||
|
const file = DataURIToBlob(photo);
|
||||||
|
await getLines(file);
|
||||||
|
// setPalmPhoto(photo as string);
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
actions.palmistry.update({
|
||||||
|
photo,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
navigate(routes.client.scannedPhotoV1());
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!isLoading && (
|
||||||
|
<PalmCameraModal
|
||||||
|
onClose={() => console.log("close")}
|
||||||
|
onTakePhoto={onTakePhoto}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isLoading && (
|
||||||
|
<Loader className={styles.loader} color={LoaderColor.Black} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Camera;
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
.loader {
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
import routes, { palmistryV1Prefix } from "@/routes";
|
||||||
|
import styles from "./styles.module.scss";
|
||||||
|
import Title from "@/components/Title";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { useTranslations } from "@/hooks/translations";
|
||||||
|
import { ELocalesPlacement } from "@/locales";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { actions } from "@/store";
|
||||||
|
// import StarSVG from "../../images/SVG/Star";
|
||||||
|
import StarSVG from "@/components/PalmistryV1/images/SVG/Star";
|
||||||
|
import Header from "../../components/Header";
|
||||||
|
import QuestionnaireGreenButton from "../../ui/GreenButton";
|
||||||
|
|
||||||
|
function FindHappiness() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const location = useLocation();
|
||||||
|
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const feature = location.pathname.replace(
|
||||||
|
routes.client.palmistryV1Welcome(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
dispatch(
|
||||||
|
actions.userConfig.setFeature(
|
||||||
|
feature.includes("/v1/gender") ? "" : feature
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, [dispatch, location.pathname]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={`${styles.page} page`}>
|
||||||
|
<Header className={styles.header} />
|
||||||
|
<div className={styles["blocks-container"]}>
|
||||||
|
<div className={styles.block}>
|
||||||
|
<img src={`${palmistryV1Prefix}/darts.png`} alt="darts" />
|
||||||
|
<ol>
|
||||||
|
<li>{translate("/find-your-happiness.point1")}</li>
|
||||||
|
<li>
|
||||||
|
<b>{translate("/find-your-happiness.point2")}</b>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
<div className={styles.block}>
|
||||||
|
<StarSVG />
|
||||||
|
<ol>
|
||||||
|
<li>{translate("/find-your-happiness.point3")}</li>
|
||||||
|
<li>{translate("/find-your-happiness.point4")}</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
className={styles.image}
|
||||||
|
src={`${palmistryV1Prefix}/hand-with-lines.png`}
|
||||||
|
alt="Hand with lines"
|
||||||
|
/>
|
||||||
|
<Title variant="h2" className={styles.title}>
|
||||||
|
{translate("/find-your-happiness.title")}
|
||||||
|
</Title>
|
||||||
|
<div className={styles["button-container"]}>
|
||||||
|
<QuestionnaireGreenButton
|
||||||
|
onClick={() => navigate(routes.client.scanInstructionV1())}
|
||||||
|
>
|
||||||
|
{translate("next")}
|
||||||
|
</QuestionnaireGreenButton>
|
||||||
|
</div>
|
||||||
|
<p className={styles.description}>
|
||||||
|
{translate("/find-your-happiness.text")}
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FindHappiness;
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
.blocks-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 24px;
|
||||||
|
|
||||||
|
& > .block {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
height: 52px;
|
||||||
|
padding: 12px 9px;
|
||||||
|
border: solid 2px #3871c1;
|
||||||
|
border-radius: 10px;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
& > ol {
|
||||||
|
list-style-type: disc;
|
||||||
|
padding-left: 15px;
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 250px;
|
||||||
|
margin-top: -21px;
|
||||||
|
min-height: 341px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
width: 100%;
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0dvh;
|
||||||
|
padding: 16px 0;
|
||||||
|
-webkit-backdrop-filter: blur(2px);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
}
|
||||||
@ -20,6 +20,7 @@ import LoadingProfileModalChild from "../../components/LoadingProfileModalChild"
|
|||||||
import ProgressBarSubstrate from "./ProgressBarSubstrate";
|
import ProgressBarSubstrate from "./ProgressBarSubstrate";
|
||||||
import { useTranslations } from "@/hooks/translations";
|
import { useTranslations } from "@/hooks/translations";
|
||||||
import { ELocalesPlacement } from "@/locales";
|
import { ELocalesPlacement } from "@/locales";
|
||||||
|
import { useMetricABFlags } from "@/services/metric/metricService";
|
||||||
|
|
||||||
function LoadingProfilePage() {
|
function LoadingProfilePage() {
|
||||||
// const userDeviceType = useSelector(selectors.selectUserDeviceType);
|
// const userDeviceType = useSelector(selectors.selectUserDeviceType);
|
||||||
@ -32,13 +33,18 @@ function LoadingProfilePage() {
|
|||||||
const [isPause, setIsPause] = useState(false);
|
const [isPause, setIsPause] = useState(false);
|
||||||
const interval = useRef<NodeJS.Timeout>();
|
const interval = useRef<NodeJS.Timeout>();
|
||||||
const pointsRef = useRef<HTMLDivElement[]>([]);
|
const pointsRef = useRef<HTMLDivElement[]>([]);
|
||||||
|
const { flags } = useMetricABFlags();
|
||||||
|
|
||||||
const onEndLoading = useCallback(() => {
|
const onEndLoading = useCallback(() => {
|
||||||
// if (isShowTryApp && userDeviceType === EUserDeviceType.ios) {
|
// if (isShowTryApp && userDeviceType === EUserDeviceType.ios) {
|
||||||
// return navigate(routes.client.tryApp());
|
// return navigate(routes.client.tryApp());
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
if (flags?.auraPalmistry?.[0] === "on") {
|
||||||
|
return navigate(routes.client.findHappinessV1());
|
||||||
|
}
|
||||||
return navigate(routes.client.emailEnterV1());
|
return navigate(routes.client.emailEnterV1());
|
||||||
}, [navigate]);
|
}, [flags?.auraPalmistry, navigate]);
|
||||||
|
|
||||||
const getProgressValue = useCallback(
|
const getProgressValue = useCallback(
|
||||||
(index: number) => {
|
(index: number) => {
|
||||||
|
|||||||
@ -10,7 +10,10 @@ import { EPlacementKeys } from "@/api/resources/Paywall";
|
|||||||
import { usePersonalVideo } from "@/hooks/personalVideo/usePersonalVideo";
|
import { usePersonalVideo } from "@/hooks/personalVideo/usePersonalVideo";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { selectors } from "@/store";
|
import { selectors } from "@/store";
|
||||||
import metricService, { EGoals } from "@/services/metric/metricService";
|
import metricService, {
|
||||||
|
EGoals,
|
||||||
|
useMetricABFlags,
|
||||||
|
} from "@/services/metric/metricService";
|
||||||
import { useTranslations } from "@/hooks/translations";
|
import { useTranslations } from "@/hooks/translations";
|
||||||
import { ELocalesPlacement } from "@/locales";
|
import { ELocalesPlacement } from "@/locales";
|
||||||
|
|
||||||
@ -32,13 +35,18 @@ function OnboardingPage() {
|
|||||||
selectors.selectPersonalVideo
|
selectors.selectPersonalVideo
|
||||||
);
|
);
|
||||||
const authCode = useSelector(selectors.selectAuthCode);
|
const authCode = useSelector(selectors.selectAuthCode);
|
||||||
|
const { flags } = useMetricABFlags();
|
||||||
|
const auraVideoTrial = flags?.auraVideoTrial?.[0];
|
||||||
|
|
||||||
const handleNext = useCallback(() => {
|
const handleNext = useCallback(() => {
|
||||||
|
if (auraVideoTrial === "on") {
|
||||||
|
return navigate(routes.client.trialChoiceVideoV1());
|
||||||
|
}
|
||||||
if (authCode?.length) {
|
if (authCode?.length) {
|
||||||
return navigate(routes.client.tryAppV1());
|
return navigate(routes.client.tryAppV1());
|
||||||
}
|
}
|
||||||
return navigate(routes.client.trialChoiceV1());
|
return navigate(routes.client.trialChoiceV1());
|
||||||
}, [authCode, navigate]);
|
}, [auraVideoTrial, authCode?.length, navigate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isVideoReady && progress >= 100) {
|
if (isVideoReady && progress >= 100) {
|
||||||
|
|||||||
@ -60,10 +60,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.buttons-container {
|
.buttons-container {
|
||||||
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,4 +90,10 @@
|
|||||||
.lottie-animation {
|
.lottie-animation {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 401 / 242;
|
aspect-ratio: 401 / 242;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 393px) {
|
||||||
|
.button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
import Title from "@/components/Title";
|
||||||
|
import styles from "./styles.module.scss";
|
||||||
|
import routes from "@/routes";
|
||||||
|
import BiometricData from "@/components/palmistry/biometric-data/biometric-data";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useTranslations } from "@/hooks/translations";
|
||||||
|
import { ELocalesPlacement } from "@/locales";
|
||||||
|
import ScanInstructionSVG from "@/components/PalmistryV1/images/SVG/ScanInstruction";
|
||||||
|
import QuestionnaireGreenButton from "../../ui/GreenButton";
|
||||||
|
import Header from "../../components/Header";
|
||||||
|
|
||||||
|
function ScanInstruction() {
|
||||||
|
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
navigate(routes.client.cameraV1());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={`${styles.page} page`}>
|
||||||
|
<Header className={styles.header} />
|
||||||
|
<Title variant="h2" className={styles.title}>
|
||||||
|
{translate("/scan-instruction.title")}
|
||||||
|
</Title>
|
||||||
|
<ScanInstructionSVG />
|
||||||
|
<QuestionnaireGreenButton className={styles.button} onClick={handleClick}>
|
||||||
|
{translate("/scan-instruction.button")}
|
||||||
|
</QuestionnaireGreenButton>
|
||||||
|
<BiometricData className={styles.biometric} />
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ScanInstruction;
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
.page {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-style: 14px;
|
||||||
|
line-height: 125%;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.biometric {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 125%;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
& > svg {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
126
src/components/pages/ABDesign/v1/pages/ScannedPhoto/index.tsx
Normal file
126
src/components/pages/ABDesign/v1/pages/ScannedPhoto/index.tsx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import styles from "./styles.module.scss";
|
||||||
|
import { selectors } from "@/store";
|
||||||
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import { IPalmistryLine } from "@/api/resources/Palmistry";
|
||||||
|
import Title from "@/components/Title";
|
||||||
|
import { IPalmistryFingerLocal } from "@/store/palmistry";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import routes from "@/routes";
|
||||||
|
import ScannedPhotoElement from "@/components/palmistry/scanned-photo/scanned-photo";
|
||||||
|
import Header from "../../components/Header";
|
||||||
|
|
||||||
|
const drawElementChangeDelay = 1500;
|
||||||
|
const startDelay = 500;
|
||||||
|
|
||||||
|
function ScannedPhoto() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const photo = useSelector(selectors.selectPalmistryPhoto);
|
||||||
|
const fingers = useSelector(selectors.selectPalmistryFingers);
|
||||||
|
const lines = useSelector(selectors.selectPalmistryLines);
|
||||||
|
|
||||||
|
const changeTitleTimeOut = useRef<NodeJS.Timeout>();
|
||||||
|
|
||||||
|
const [currentElementIndex, setCurrentElementIndex] = useState(0);
|
||||||
|
const [title, setTitle] = useState("");
|
||||||
|
const [shouldDisplayPalmLines, setShouldDisplayPalmLines] = useState(false);
|
||||||
|
const [smallPhotoState, setSmallPhotoState] = useState(false);
|
||||||
|
|
||||||
|
const drawElements = useMemo(() => [...fingers, ...lines], [fingers, lines]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!drawElements[currentElementIndex]) return;
|
||||||
|
changeTitleTimeOut.current = setTimeout(() => {
|
||||||
|
const title =
|
||||||
|
(drawElements[currentElementIndex] as IPalmistryFingerLocal)
|
||||||
|
.fingerName || drawElements[currentElementIndex].name;
|
||||||
|
setTitle(title);
|
||||||
|
if (currentElementIndex < drawElements.length - 1) {
|
||||||
|
setCurrentElementIndex((prevState) => prevState + 1);
|
||||||
|
}
|
||||||
|
}, drawElementChangeDelay);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (changeTitleTimeOut.current) {
|
||||||
|
clearTimeout(changeTitleTimeOut.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [currentElementIndex, drawElements]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setShouldDisplayPalmLines(
|
||||||
|
lines.includes(drawElements[currentElementIndex] as IPalmistryLine)
|
||||||
|
);
|
||||||
|
}, [currentElementIndex, drawElements, lines]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentElementIndex < drawElements.length - 1) return;
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setSmallPhotoState(true);
|
||||||
|
}, drawElementChangeDelay * 2);
|
||||||
|
const goNextTimer = setTimeout(
|
||||||
|
() => navigate(routes.client.emailEnterV1()),
|
||||||
|
drawElementChangeDelay * drawElements.length + 8000
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
if (goNextTimer) {
|
||||||
|
clearTimeout(goNextTimer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [currentElementIndex, drawElements.length, navigate]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentElementIndex < drawElements.length) return;
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
// navigate(routes.client.palmistryV1Email());
|
||||||
|
}, drawElementChangeDelay + 1000);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [currentElementIndex, drawElements.length, navigate]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={`${styles.page} palmistry-container_type_scan-photo`}>
|
||||||
|
<Header className={styles.header} />
|
||||||
|
<Title variant="h2" className={styles.title}>
|
||||||
|
{title}
|
||||||
|
</Title>
|
||||||
|
<ScannedPhotoElement
|
||||||
|
photo={photo}
|
||||||
|
small={smallPhotoState}
|
||||||
|
drawElementChangeDelay={drawElementChangeDelay}
|
||||||
|
startDelay={startDelay}
|
||||||
|
displayLines={shouldDisplayPalmLines}
|
||||||
|
lines={lines}
|
||||||
|
fingers={fingers}
|
||||||
|
drawElements={drawElements}
|
||||||
|
/>
|
||||||
|
<h2
|
||||||
|
className="palmistry-container__waiting-title"
|
||||||
|
style={{
|
||||||
|
animationDelay: `${
|
||||||
|
drawElementChangeDelay * drawElements.length + 2500
|
||||||
|
}ms`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
We are putting together a comprehensive Palmistry Reading just for you!
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<h3
|
||||||
|
className="palmistry-container__waiting-description"
|
||||||
|
style={{
|
||||||
|
animationDelay: `${
|
||||||
|
drawElementChangeDelay * drawElements.length + 3000
|
||||||
|
}ms`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Wow, looks like there is a lot we can tell about your ambitious and
|
||||||
|
strong self-confident future.
|
||||||
|
</h3>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ScannedPhoto;
|
||||||
@ -0,0 +1,162 @@
|
|||||||
|
.page {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 16px 74px;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 560px;
|
||||||
|
height: fit-content;
|
||||||
|
min-height: 100dvh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 40px;
|
||||||
|
|
||||||
|
--font-family-main: "SF Pro Text", sans-serif;
|
||||||
|
--stone-grey: #95959d;
|
||||||
|
--button-color: #121620;
|
||||||
|
--svg-blue: var(--strong-blue);
|
||||||
|
--pale-lavender: #dee5f9;
|
||||||
|
--pale-lavender-20: #dee5f9;
|
||||||
|
--orange: #ff9649;
|
||||||
|
--coral: #ff5c5d;
|
||||||
|
--rich-blue: #2b7cf6;
|
||||||
|
--bright-white: #fbfbfb;
|
||||||
|
--bright-red: #ff5758;
|
||||||
|
--light-gray: #d9d9d9;
|
||||||
|
--vivid-yellow: #ffc700;
|
||||||
|
--pale-gray: #c2cad8;
|
||||||
|
--pale-green: #75db9c;
|
||||||
|
--pale-cerulean: #82b7ef;
|
||||||
|
--greyish: #afafaf;
|
||||||
|
--vivid-green: #00ff38;
|
||||||
|
--dark-charcoal: #191f2d;
|
||||||
|
--blueish-gray: #6b76aa;
|
||||||
|
--violet: #9949ff;
|
||||||
|
--light-lavender: #c5c5d1;
|
||||||
|
--pale-pink: #fcd3df;
|
||||||
|
--cream-yellow: #fffbcd;
|
||||||
|
--pale-aqua: #c9fae6;
|
||||||
|
--pale-seafoam: #d3f1e1;
|
||||||
|
--pale-lilac: #dec6fe;
|
||||||
|
--pale-peach: #fdddc8;
|
||||||
|
--deep-charcoal: #1e1e1e;
|
||||||
|
--black: #000;
|
||||||
|
--bright-sea-green: #04a777;
|
||||||
|
--deep-cornflower-blue: #4663b7;
|
||||||
|
--charcoal-grey: #505051;
|
||||||
|
--pale-light-cerulean: #acd1ff;
|
||||||
|
--main-gradient: #fff;
|
||||||
|
--strong-blue: #066fde;
|
||||||
|
--strong-blue-text: #066fde;
|
||||||
|
--strong-blue-80: rgba(6, 111, 222, 0.8);
|
||||||
|
--midnight-black: #121620;
|
||||||
|
--footer-small-text: #121620;
|
||||||
|
--button-active: #fff;
|
||||||
|
--button-background: var(--pale-blue);
|
||||||
|
--button-active-bg: var(--strong-blue);
|
||||||
|
--slate-blue: #6b7baa;
|
||||||
|
--slate-blue-placeholder: #6b7baa;
|
||||||
|
--pale-blue: #eff2fd;
|
||||||
|
--pale-blue-input: #eff2fd;
|
||||||
|
--midnight-black-input: #121620;
|
||||||
|
--greyish-blue: #8e8e93;
|
||||||
|
--soft-blue: #4a567a;
|
||||||
|
--soft-blue-gray: #4a567a;
|
||||||
|
--soft-blue-periwinkle: #4a567a;
|
||||||
|
--gentle-blue: #9babd9;
|
||||||
|
--gentle-blue-svg: #9babd9;
|
||||||
|
--light-silver: #c7c7c7;
|
||||||
|
--light-silver-to-white: #c7c7c7;
|
||||||
|
--light-silver-to-lilac-blue: #c7c7c7;
|
||||||
|
--light-cornflower-blue: #c2ceee;
|
||||||
|
--white: #fff;
|
||||||
|
--dark-blue: #202b47;
|
||||||
|
--progress-line: #00a3ff;
|
||||||
|
--footer-shield: #b5c4ff;
|
||||||
|
--blue-color-text: #0066fd;
|
||||||
|
--black-color-text: #0066fd;
|
||||||
|
--transparent-to-gold: transparent;
|
||||||
|
--transparent-to-white: transparent;
|
||||||
|
--transparent-to-periwinkle: transparent;
|
||||||
|
--white-to-transparent: #fff;
|
||||||
|
--loader-background: rgba(16, 32, 77, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
min-height: 36px;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 24px;
|
||||||
|
&::first-letter {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.photo-container {
|
||||||
|
width: 100%;
|
||||||
|
height: fit-content;
|
||||||
|
position: relative;
|
||||||
|
// background-color: #cbcbcb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scanned-photo {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.svg-objects {
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.finger-point {
|
||||||
|
animation: finger-show 1s linear;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
transform: scale(0);
|
||||||
|
transform-origin: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 2px;
|
||||||
|
fill-rule: evenodd;
|
||||||
|
clip-rule: evenodd;
|
||||||
|
stroke-miterlimit: 1.5;
|
||||||
|
stroke-dasharray: 500;
|
||||||
|
stroke: #fff;
|
||||||
|
fill: none;
|
||||||
|
animation: line-show 1.5s linear;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
|
||||||
|
&.heart {
|
||||||
|
stroke: #f8d90f;
|
||||||
|
/* animation-delay: 4.5s; */
|
||||||
|
}
|
||||||
|
|
||||||
|
&.life {
|
||||||
|
stroke: #e51c39;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.head {
|
||||||
|
stroke: #00d114;
|
||||||
|
/* animation-delay: 1.5s; */
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fate {
|
||||||
|
stroke: #05ced8;
|
||||||
|
/* animation-delay: 3s; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes finger-show {
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes line-show {
|
||||||
|
100% {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,251 @@
|
|||||||
|
import styles from "./styles.module.scss";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { actions, selectors } from "@/store";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import routes from "@/routes";
|
||||||
|
import EmailsList from "@/components/EmailsList";
|
||||||
|
import Header from "../../components/Header";
|
||||||
|
import BackgroundTopBlob from "../../ui/BackgroundTopBlob";
|
||||||
|
import { useDynamicSize } from "@/hooks/useDynamicSize";
|
||||||
|
import PriceList from "../../components/PriceList";
|
||||||
|
import QuestionnaireGreenButton from "../../ui/GreenButton";
|
||||||
|
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||||
|
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||||
|
import { getRandomArbitrary } from "@/services/random-value";
|
||||||
|
import Loader from "@/components/Loader";
|
||||||
|
import metricService, {
|
||||||
|
EGoals,
|
||||||
|
EMetrics,
|
||||||
|
useMetricABFlags,
|
||||||
|
} from "@/services/metric/metricService";
|
||||||
|
import PersonalVideo from "../TrialPayment/components/PersonalVideo";
|
||||||
|
import Toast from "../../components/Toast";
|
||||||
|
import BlurComponent from "@/components/BlurComponent";
|
||||||
|
import { useTranslations } from "@/hooks/translations";
|
||||||
|
import { ELocalesPlacement } from "@/locales";
|
||||||
|
import DiscountExpires from "../TrialPayment/components/DiscountExpires";
|
||||||
|
|
||||||
|
enum EDisplayOptionButton {
|
||||||
|
"alwaysVisible" = "alwaysVisible",
|
||||||
|
"visibleIfChosen" = "visibleIfChosen",
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayOptionButton: EDisplayOptionButton =
|
||||||
|
EDisplayOptionButton.alwaysVisible; //
|
||||||
|
|
||||||
|
function TrialChoiceVideoPage() {
|
||||||
|
const { translate } = useTranslations(ELocalesPlacement.V1);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const selectedPrice = useSelector(selectors.selectSelectedPrice);
|
||||||
|
const homeConfig = useSelector(selectors.selectHome);
|
||||||
|
const email = useSelector(selectors.selectEmail);
|
||||||
|
const [isDisabled, setIsDisabled] = useState(true);
|
||||||
|
const [visibleToast, setVisibleToast] = useState(false);
|
||||||
|
const [countUsers, setCountUsers] = useState(752);
|
||||||
|
const [isVisibleElements, setIsVisibleElements] = useState(false);
|
||||||
|
const { width: pageWidth, elementRef: pageRef } = useDynamicSize({});
|
||||||
|
const { gender } = useSelector(selectors.selectQuestionnaire);
|
||||||
|
const { products, isLoading, currency, getText } = usePaywall({
|
||||||
|
placementKey: EPlacementKeys["aura.placement.redesign.main"],
|
||||||
|
localesPlacement: ELocalesPlacement.V1,
|
||||||
|
});
|
||||||
|
const arrowLeft = useSelector(selectors.selectTrialChoiceArrowOptions)?.left;
|
||||||
|
const showElementsTimer = useRef<NodeJS.Timeout>();
|
||||||
|
|
||||||
|
const { flags } = useMetricABFlags();
|
||||||
|
const isShowTimer = flags?.showTimerTrial?.[0] === "show";
|
||||||
|
|
||||||
|
const { videoUrl } = useSelector(selectors.selectPersonalVideo);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
metricService.reachGoal(EGoals.AURA_TRIAL_CHOICE_PAGE_VISIT, [
|
||||||
|
EMetrics.KLAVIYO,
|
||||||
|
]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (showElementsTimer.current) clearTimeout(showElementsTimer.current);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const showElements = () => {
|
||||||
|
showElementsTimer.current = setTimeout(() => {
|
||||||
|
setIsVisibleElements(true);
|
||||||
|
}, 33_000);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const randomDelay = getRandomArbitrary(3000, 5000);
|
||||||
|
const countUsersTimeOut = setTimeout(() => {
|
||||||
|
setCountUsers((prevState) => prevState + 1);
|
||||||
|
}, randomDelay);
|
||||||
|
return () => clearTimeout(countUsersTimeOut);
|
||||||
|
}, [countUsers]);
|
||||||
|
|
||||||
|
const handlePriceItem = () => {
|
||||||
|
metricService.reachGoal(EGoals.AURA_SELECT_TRIAL);
|
||||||
|
setIsDisabled(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
if (isDisabled) {
|
||||||
|
setVisibleToast(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(
|
||||||
|
actions.siteConfig.update({
|
||||||
|
home: { pathFromHome: homeConfig.pathFromHome, isShowNavbar: false },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
navigate(routes.client.trialPaymentV1());
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!visibleToast) return;
|
||||||
|
const timeOut = setTimeout(() => {
|
||||||
|
setVisibleToast(false);
|
||||||
|
}, 6000);
|
||||||
|
return () => clearTimeout(timeOut);
|
||||||
|
}, [visibleToast]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className={`${styles.page} page`}
|
||||||
|
ref={pageRef}
|
||||||
|
style={{
|
||||||
|
backgroundColor: gender === "male" ? "#C1E5FF" : "#f7ebff",
|
||||||
|
paddingTop: !videoUrl.length ? "15px" : "0px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<BackgroundTopBlob
|
||||||
|
width={pageWidth}
|
||||||
|
className={styles["background-top-blob"]}
|
||||||
|
height={180}
|
||||||
|
/>
|
||||||
|
<Header className={styles.header} />
|
||||||
|
<PersonalVideo
|
||||||
|
gender={gender}
|
||||||
|
url={"/trial-choice.MOV"}
|
||||||
|
classNameContainer={styles["personal-video"]}
|
||||||
|
isVisibleControllers={isVisibleElements}
|
||||||
|
onVideoStart={showElements}
|
||||||
|
/>
|
||||||
|
{!isLoading && isVisibleElements && (
|
||||||
|
<>
|
||||||
|
{isShowTimer && (
|
||||||
|
<DiscountExpires
|
||||||
|
className={styles["discount-expires"]}
|
||||||
|
style={{
|
||||||
|
marginTop: !videoUrl.length
|
||||||
|
? "60px"
|
||||||
|
: "calc((100% + 84px) / 16* 9 + 16px)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className={styles["price-container"]}>
|
||||||
|
<PriceList
|
||||||
|
products={products}
|
||||||
|
activeItem={selectedPrice}
|
||||||
|
classNameItem={styles["price-item"]}
|
||||||
|
classNameItemActive={`${styles["price-item-active"]} ${styles[gender]}`}
|
||||||
|
currency={currency}
|
||||||
|
click={handlePriceItem}
|
||||||
|
/>
|
||||||
|
<p
|
||||||
|
className={styles["auxiliary-text"]}
|
||||||
|
style={{
|
||||||
|
maxWidth: arrowLeft
|
||||||
|
? `${Number(arrowLeft.slice(0, -2)) - 8}px`
|
||||||
|
: "75%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getText("text.3", {
|
||||||
|
color: "#1C38EA",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
<img
|
||||||
|
className={styles["arrow-image"]}
|
||||||
|
src="/arrow.svg"
|
||||||
|
alt={`Arrow to $${products.at(-1)?.trialPrice}`}
|
||||||
|
style={
|
||||||
|
arrowLeft
|
||||||
|
? {
|
||||||
|
left: arrowLeft,
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles["emails-list-container"]}>
|
||||||
|
<EmailsList
|
||||||
|
title={getText("text.5", {
|
||||||
|
replacementSelector: "strong",
|
||||||
|
replacement: [
|
||||||
|
{
|
||||||
|
target: "${quantity}",
|
||||||
|
replacement: countUsers.toString(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})}
|
||||||
|
products={products}
|
||||||
|
classNameContainer={`${styles["emails-container"]} ${styles[gender]}`}
|
||||||
|
classNameTitle={styles["emails-title"]}
|
||||||
|
classNameEmailItem={styles["email-item"]}
|
||||||
|
direction="right-left"
|
||||||
|
currency={currency}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className={styles.email}>{email}</p>
|
||||||
|
{!isDisabled &&
|
||||||
|
displayOptionButton === EDisplayOptionButton.visibleIfChosen && (
|
||||||
|
<QuestionnaireGreenButton
|
||||||
|
className={styles.button}
|
||||||
|
disabled={isDisabled}
|
||||||
|
onClick={handleNext}
|
||||||
|
>
|
||||||
|
{getText("text.button.1", {
|
||||||
|
color: "#1C38EA",
|
||||||
|
})}
|
||||||
|
</QuestionnaireGreenButton>
|
||||||
|
)}
|
||||||
|
{displayOptionButton === EDisplayOptionButton.alwaysVisible && (
|
||||||
|
<BlurComponent
|
||||||
|
className={styles.blur}
|
||||||
|
gradientClassName={styles["gradient-blur"]}
|
||||||
|
isActiveBlur={true}
|
||||||
|
>
|
||||||
|
<QuestionnaireGreenButton
|
||||||
|
className={`${styles.button} ${
|
||||||
|
isDisabled ? styles.disabled : ""
|
||||||
|
}`}
|
||||||
|
onClick={handleNext}
|
||||||
|
>
|
||||||
|
{getText("text.button.1", {
|
||||||
|
color: "#1C38EA",
|
||||||
|
})}
|
||||||
|
</QuestionnaireGreenButton>
|
||||||
|
</BlurComponent>
|
||||||
|
)}
|
||||||
|
<p className={styles["auxiliary-text"]}>
|
||||||
|
{getText("text.4", {
|
||||||
|
color: "#1C38EA",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{visibleToast && isDisabled && (
|
||||||
|
<Toast classNameContainer={styles["toast-container"]} variant="error">
|
||||||
|
{translate("/trial-choice.button")}
|
||||||
|
{/* Choose an amount that you think is reasonable. */}
|
||||||
|
</Toast>
|
||||||
|
)}
|
||||||
|
{isLoading && <Loader className={styles.loader} />}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TrialChoiceVideoPage;
|
||||||
@ -0,0 +1,229 @@
|
|||||||
|
.page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
min-height: 100dvh;
|
||||||
|
height: fit-content;
|
||||||
|
background-color: #fff0f0;
|
||||||
|
padding: 0 42px 126px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background-top-blob {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
scale: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
z-index: 1;
|
||||||
|
width: calc(100% + 36px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 125%;
|
||||||
|
font-weight: 300;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text.bold {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blue {
|
||||||
|
color: #1c38ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auxiliary-text {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
color: rgb(52, 52, 52);
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
position: sticky;
|
||||||
|
bottom: 8px;
|
||||||
|
filter: opacity(0);
|
||||||
|
will-change: opacity;
|
||||||
|
animation: appearance 1s forwards 1.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
filter: opacity(0);
|
||||||
|
will-change: opacity;
|
||||||
|
animation: appearance 1s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-item {
|
||||||
|
background: #fff;
|
||||||
|
color: rgb(51, 51, 51);
|
||||||
|
box-shadow: rgba(84, 60, 151, 0.25) 2px 2px 6px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-box-pack: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: 600;
|
||||||
|
width: calc((100% - 30px) / 4);
|
||||||
|
max-width: 72px;
|
||||||
|
max-height: 72px;
|
||||||
|
height: auto;
|
||||||
|
aspect-ratio: 1 / 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-item-active {
|
||||||
|
color: rgb(251, 251, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-item-active.male {
|
||||||
|
background-color: #85b6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-item-active.female {
|
||||||
|
background-color: #d1acf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-image {
|
||||||
|
position: absolute;
|
||||||
|
width: 26px;
|
||||||
|
height: 33px;
|
||||||
|
top: 76px;
|
||||||
|
right: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emails-list-container {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
filter: opacity(0);
|
||||||
|
will-change: opacity;
|
||||||
|
animation: appearance 1s forwards 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emails-container.female {
|
||||||
|
background-color: #d6bbee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emails-container.male {
|
||||||
|
background-color: #85b6ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emails-title {
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 125%;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-item {
|
||||||
|
background: rgb(251, 251, 255);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 5px 7px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 130%;
|
||||||
|
display: flex;
|
||||||
|
width: max-content;
|
||||||
|
color: rgb(79, 79, 79);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
font-size: 18px;
|
||||||
|
min-height: 0;
|
||||||
|
height: 50px;
|
||||||
|
position: fixed;
|
||||||
|
bottom: calc(0dvh + 16px);
|
||||||
|
width: calc(100% - 84px);
|
||||||
|
z-index: 10;
|
||||||
|
filter: opacity(0);
|
||||||
|
will-change: opacity;
|
||||||
|
animation: appearance 1s forwards 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blur {
|
||||||
|
position: fixed !important;
|
||||||
|
height: unset !important;
|
||||||
|
bottom: calc(0dvh + 16px);
|
||||||
|
width: calc(100% - 84px) !important;
|
||||||
|
max-width: 396px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-blur {
|
||||||
|
top: -74px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-container {
|
||||||
|
position: fixed;
|
||||||
|
bottom: calc(0dvh + 82px);
|
||||||
|
width: calc(100% - 84px);
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email {
|
||||||
|
font-weight: 500;
|
||||||
|
word-break: break-all;
|
||||||
|
white-space: normal;
|
||||||
|
line-height: 1.3;
|
||||||
|
text-align: center;
|
||||||
|
filter: opacity(0);
|
||||||
|
will-change: opacity;
|
||||||
|
animation: appearance 1s forwards 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-video {
|
||||||
|
// position: fixed !important;
|
||||||
|
// top: 0dvh;
|
||||||
|
// z-index: 30;
|
||||||
|
margin-top: 32px !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
background-image: url("/trial-choice-preview.png") !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discount-expires {
|
||||||
|
flex-direction: row !important;
|
||||||
|
gap: 12px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
|
||||||
|
& > h6 {
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div > p,
|
||||||
|
& > div > div > span:first-child {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
& > div > div > span:last-child {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes appearance {
|
||||||
|
0% {
|
||||||
|
filter: opacity(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
filter: opacity(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,10 +10,18 @@ interface IPersonalVideoProps {
|
|||||||
gender: string;
|
gender: string;
|
||||||
url: string;
|
url: string;
|
||||||
classNameContainer?: string;
|
classNameContainer?: string;
|
||||||
|
isVisibleControllers?: boolean;
|
||||||
|
onVideoStart?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PersonalVideo = React.memo<IPersonalVideoProps>(
|
const PersonalVideo = React.memo<IPersonalVideoProps>(
|
||||||
({ url, gender, classNameContainer = "" }) => {
|
({
|
||||||
|
url,
|
||||||
|
gender,
|
||||||
|
classNameContainer = "",
|
||||||
|
isVisibleControllers = true,
|
||||||
|
onVideoStart,
|
||||||
|
}) => {
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
const [isStarted, setIsStarted] = useState(false);
|
const [isStarted, setIsStarted] = useState(false);
|
||||||
const [isError, setIsError] = useState(false);
|
const [isError, setIsError] = useState(false);
|
||||||
@ -26,6 +34,7 @@ const PersonalVideo = React.memo<IPersonalVideoProps>(
|
|||||||
|
|
||||||
const onStart = () => {
|
const onStart = () => {
|
||||||
setIsStarted(true);
|
setIsStarted(true);
|
||||||
|
if (onVideoStart) onVideoStart();
|
||||||
metricService.reachGoal(EGoals.ROSE_VIDEO_PLAY_START);
|
metricService.reachGoal(EGoals.ROSE_VIDEO_PLAY_START);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -72,7 +81,7 @@ const PersonalVideo = React.memo<IPersonalVideoProps>(
|
|||||||
aspectRatio: "16 / 9",
|
aspectRatio: "16 / 9",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{!isError && isStarted && (
|
{!isError && isStarted && isVisibleControllers && (
|
||||||
<PlayPauseButton
|
<PlayPauseButton
|
||||||
state={isPlaying ? "pause" : "play"}
|
state={isPlaying ? "pause" : "play"}
|
||||||
onClick={handlePlayPause}
|
onClick={handlePlayPause}
|
||||||
|
|||||||
@ -14,6 +14,10 @@ import PaymentModalNew from "@/components/PaymentModalNew";
|
|||||||
import { addCurrency } from "@/locales";
|
import { addCurrency } from "@/locales";
|
||||||
import { getPriceCentsToDollars } from "@/services/price";
|
import { getPriceCentsToDollars } from "@/services/price";
|
||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
|
import metricService, {
|
||||||
|
EGoals,
|
||||||
|
EMetrics,
|
||||||
|
} from "@/services/metric/metricService";
|
||||||
|
|
||||||
export default function PaymentScreen() {
|
export default function PaymentScreen() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -30,13 +34,19 @@ export default function PaymentScreen() {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (subscriptionStatus === "subscribed") {
|
if (subscriptionStatus === "subscribed") {
|
||||||
|
metricService.reachGoal(EGoals.PAYMENT_SUCCESS);
|
||||||
|
if (activeProductFromStore) {
|
||||||
|
metricService.reachGoal(EGoals.PURCHASE, [EMetrics.FACEBOOK], {
|
||||||
|
currency: "USD",
|
||||||
|
value: ((activeProductFromStore.trialPrice || 100) / 100).toFixed(2),
|
||||||
|
});
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// steps.goNext();
|
// steps.goNext();
|
||||||
navigate(routes.client.skipTrial());
|
navigate(routes.client.skipTrial());
|
||||||
}, 1500);
|
}, 1500);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
}, [activeProductFromStore, navigate, subscriptionStatus]);
|
||||||
}, [subscriptionStatus]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!activeProductFromStore) {
|
if (!activeProductFromStore) {
|
||||||
|
|||||||
@ -44,7 +44,14 @@ export const useTranslations = (
|
|||||||
if (_placement === ELocalesPlacement.PalmistryV1) {
|
if (_placement === ELocalesPlacement.PalmistryV1) {
|
||||||
_key = prefixGenderKey(prefixPlacementKey(key));
|
_key = prefixGenderKey(prefixPlacementKey(key));
|
||||||
}
|
}
|
||||||
return t(_key, options);
|
|
||||||
|
const translation = t(_key, options);
|
||||||
|
|
||||||
|
if (translation === key) {
|
||||||
|
return t(`fallback.${_key}`, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
return translation;
|
||||||
},
|
},
|
||||||
[placement, prefixGenderKey, prefixPlacementKey, t]
|
[placement, prefixGenderKey, prefixPlacementKey, t]
|
||||||
);
|
);
|
||||||
|
|||||||
@ -61,6 +61,7 @@ interface ITranslationJSON {
|
|||||||
male: { [key: string]: string }
|
male: { [key: string]: string }
|
||||||
female: { [key: string]: string }
|
female: { [key: string]: string }
|
||||||
default: { [key: string]: string }
|
default: { [key: string]: string }
|
||||||
|
fallback: { male: { [key: string]: string }; female: { [key: string]: string }, default: { [key: string]: string } }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTranslationJSON = async (placement: ELocalesPlacement | undefined, language: string): Promise<ITranslationJSON> => {
|
export const getTranslationJSON = async (placement: ELocalesPlacement | undefined, language: string): Promise<ITranslationJSON> => {
|
||||||
@ -70,12 +71,23 @@ export const getTranslationJSON = async (placement: ELocalesPlacement | undefine
|
|||||||
const localePlacement = placement || ELocalesPlacement.V1
|
const localePlacement = placement || ELocalesPlacement.V1
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
const responseMale = await fetch(`${protocol}//${host}/locales/${localePlacement}/${defaultLanguage}/male_${defaultLanguage}.json`)
|
const [
|
||||||
const resultMale = await responseMale.json()
|
resultMale,
|
||||||
|
resultFemale,
|
||||||
|
resultMaleFallback,
|
||||||
|
resultFemaleFallback,
|
||||||
|
] = await Promise.all([
|
||||||
|
(await fetch(`${protocol}//${host}/locales/${localePlacement}/${defaultLanguage}/male_${defaultLanguage}.json`)).json(),
|
||||||
|
(await fetch(`${protocol}//${host}/locales/${localePlacement}/${defaultLanguage}/female_${defaultLanguage}.json`)).json(),
|
||||||
|
(await fetch(`${protocol}//${host}/locales/${localePlacement}/en/male_en.json`)).json(),
|
||||||
|
(await fetch(`${protocol}//${host}/locales/${localePlacement}/en/female_en.json`)).json()
|
||||||
|
]);
|
||||||
|
|
||||||
const responseFemale = await fetch(`${protocol}//${host}/locales/${localePlacement}/${defaultLanguage}/female_${defaultLanguage}.json`)
|
result = {
|
||||||
const resultFemale = await responseFemale.json()
|
male: resultMale, female: resultFemale, default: resultMale, fallback: {
|
||||||
result = { male: resultMale, female: resultFemale, default: resultMale }
|
male: resultMaleFallback, female: resultFemaleFallback, default: resultMaleFallback
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
result = await getTranslationJSON(localePlacement, fallbackLng)
|
result = await getTranslationJSON(localePlacement, fallbackLng)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,11 @@ import MentionedInPage from "@/components/pages/ABDesign/v1/pages/MentionedIn";
|
|||||||
import TryAppPage from "@/components/pages/ABDesign/v1/pages/TryApp";
|
import TryAppPage from "@/components/pages/ABDesign/v1/pages/TryApp";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { ELocalesPlacement } from "@/locales";
|
import { ELocalesPlacement } from "@/locales";
|
||||||
|
import TrialChoiceVideoPage from "@/components/pages/ABDesign/v1/pages/TrialChoiceVideo";
|
||||||
|
import FindHappiness from "@/components/pages/ABDesign/v1/pages/FindHappiness";
|
||||||
|
import ScanInstruction from "@/components/pages/ABDesign/v1/pages/ScanInstruction";
|
||||||
|
import Camera from "@/components/pages/ABDesign/v1/pages/Camera";
|
||||||
|
import ScannedPhoto from "@/components/pages/ABDesign/v1/pages/ScannedPhoto";
|
||||||
|
|
||||||
function ABDesignV1Routes() {
|
function ABDesignV1Routes() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -46,7 +51,7 @@ function ABDesignV1Routes() {
|
|||||||
<Routes>
|
<Routes>
|
||||||
<Route element={<LayoutABDesignV1 />}>
|
<Route element={<LayoutABDesignV1 />}>
|
||||||
<Route path={routes.client.genderV1()} element={<GenderPage />}>
|
<Route path={routes.client.genderV1()} element={<GenderPage />}>
|
||||||
<Route path=":targetId*" element={<GenderPage />} />
|
<Route path=":targetId/*" element={<GenderPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
<Route
|
||||||
path={routes.client.questionnaireV1()}
|
path={routes.client.questionnaireV1()}
|
||||||
@ -138,6 +143,10 @@ function ABDesignV1Routes() {
|
|||||||
path={routes.client.trialChoiceV1()}
|
path={routes.client.trialChoiceV1()}
|
||||||
element={<TrialChoicePage />}
|
element={<TrialChoicePage />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path={routes.client.trialChoiceVideoV1()}
|
||||||
|
element={<TrialChoiceVideoPage />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={routes.client.trialPaymentV1()}
|
path={routes.client.trialPaymentV1()}
|
||||||
element={<TrialPaymentPage />}
|
element={<TrialPaymentPage />}
|
||||||
@ -157,6 +166,19 @@ function ABDesignV1Routes() {
|
|||||||
path={routes.client.mentionedInV1()}
|
path={routes.client.mentionedInV1()}
|
||||||
element={<MentionedInPage />}
|
element={<MentionedInPage />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path={routes.client.findHappinessV1()}
|
||||||
|
element={<FindHappiness />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path={routes.client.scanInstructionV1()}
|
||||||
|
element={<ScanInstruction />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path={routes.client.scannedPhotoV1()}
|
||||||
|
element={<ScannedPhoto />}
|
||||||
|
/>
|
||||||
|
<Route path={routes.client.cameraV1()} element={<Camera />} />
|
||||||
</Route>
|
</Route>
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
import Header from "@/components/pages/ABDesign/v1/components/Header";
|
import Header from "@/components/pages/ABDesign/v1/components/Header";
|
||||||
import styles from "./styles.module.css";
|
import styles from "./styles.module.css";
|
||||||
import { useSchemeColorByElement } from "@/hooks/useSchemeColorByElement";
|
import { useSchemeColorByElement } from "@/hooks/useSchemeColorByElement";
|
||||||
import { useRef } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { Outlet, useLocation } from "react-router-dom";
|
import { Outlet, useLocation, useSearchParams } from "react-router-dom";
|
||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
|
import PaymentModal from "@/components/PalmistryV1/components/PaymentModal";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { selectors } from "@/store";
|
||||||
|
|
||||||
const isBackButtonVisibleRoutes = [
|
const isBackButtonVisibleRoutes = [
|
||||||
routes.client.palmistryV1Birthdate(),
|
routes.client.palmistryV1Birthdate(),
|
||||||
@ -24,11 +27,17 @@ const isBackButtonVisibleRoutes = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
function LayoutPalmistryV1() {
|
function LayoutPalmistryV1() {
|
||||||
|
const token = useSelector(selectors.selectToken);
|
||||||
|
const activeProductFromStore = useSelector(selectors.selectActiveProduct);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const mainRef = useRef<HTMLDivElement>(null);
|
const mainRef = useRef<HTMLDivElement>(null);
|
||||||
useSchemeColorByElement(mainRef.current, "section.page, .page, section", [
|
useSchemeColorByElement(mainRef.current, "section.page, .page, section", [
|
||||||
location,
|
location,
|
||||||
]);
|
]);
|
||||||
|
const [isShowPaymentModal, setIsShowPaymentModal] = useState(false);
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const subscriptionStatus =
|
||||||
|
searchParams.get("redirect_status") === "succeeded" ? "subscribed" : "lead";
|
||||||
|
|
||||||
const getIsBackButtonVisible = () => {
|
const getIsBackButtonVisible = () => {
|
||||||
for (const route of isBackButtonVisibleRoutes) {
|
for (const route of isBackButtonVisibleRoutes) {
|
||||||
@ -47,7 +56,16 @@ function LayoutPalmistryV1() {
|
|||||||
/>
|
/>
|
||||||
{/* <Suspense fallback={<LoadingPage />}> */}
|
{/* <Suspense fallback={<LoadingPage />}> */}
|
||||||
<section className={styles.page}>
|
<section className={styles.page}>
|
||||||
<Outlet />
|
<Outlet context={{ isShowPaymentModal, setIsShowPaymentModal }} />
|
||||||
|
{!!token.length && !!activeProductFromStore && (
|
||||||
|
<PaymentModal
|
||||||
|
className={
|
||||||
|
isShowPaymentModal || subscriptionStatus === "subscribed"
|
||||||
|
? styles["payment-modal-active"]
|
||||||
|
: styles["payment-modal-hide"]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
{/* </Suspense> */}
|
{/* </Suspense> */}
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@ -22,4 +22,12 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-modal-hide {
|
||||||
|
transform: translateY(150%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-modal-active {
|
||||||
|
animation: appearance 1s;
|
||||||
}
|
}
|
||||||
@ -221,12 +221,17 @@ const routes = {
|
|||||||
emailConfirmV1: () => [host, "v1", "email-confirm"].join("/"),
|
emailConfirmV1: () => [host, "v1", "email-confirm"].join("/"),
|
||||||
onboardingV1: () => [host, "v1", "onboarding"].join("/"),
|
onboardingV1: () => [host, "v1", "onboarding"].join("/"),
|
||||||
trialChoiceV1: () => [host, "v1", "trial-choice"].join("/"),
|
trialChoiceV1: () => [host, "v1", "trial-choice"].join("/"),
|
||||||
|
trialChoiceVideoV1: () => [host, "v1", "trial-choice-video"].join("/"),
|
||||||
trialPaymentV1: () => [host, "v1", "trial-payment"].join("/"),
|
trialPaymentV1: () => [host, "v1", "trial-payment"].join("/"),
|
||||||
tryAppV1: () => [host, "v1", "try-app"].join("/"),
|
tryAppV1: () => [host, "v1", "try-app"].join("/"),
|
||||||
trialPaymentWithDiscountV1: () =>
|
trialPaymentWithDiscountV1: () =>
|
||||||
[host, "v1", "trial-payment-with-discount"].join("/"),
|
[host, "v1", "trial-payment-with-discount"].join("/"),
|
||||||
additionalDiscountV1: () => [host, "v1", "additional-discount"].join("/"),
|
additionalDiscountV1: () => [host, "v1", "additional-discount"].join("/"),
|
||||||
mentionedInV1: () => [host, "v1", "mentionedIn"].join("/"),
|
mentionedInV1: () => [host, "v1", "mentionedIn"].join("/"),
|
||||||
|
findHappinessV1: () => [host, "v1", "find-happiness"].join("/"),
|
||||||
|
scanInstructionV1: () => [host, "v1", "scan-instruction"].join("/"),
|
||||||
|
cameraV1: () => [host, "v1", "camera"].join("/"),
|
||||||
|
scannedPhotoV1: () => [host, "v1", "scanned-photo"].join("/"),
|
||||||
|
|
||||||
loadingPage: () => [host, "loading-page"].join("/"),
|
loadingPage: () => [host, "loading-page"].join("/"),
|
||||||
notFound: () => [host, "404"].join("/"),
|
notFound: () => [host, "404"].join("/"),
|
||||||
|
|||||||
@ -131,7 +131,9 @@ const initMetricAB = () => {
|
|||||||
|
|
||||||
type TABFlags = {
|
type TABFlags = {
|
||||||
showTimerTrial: "show" | "hide";
|
showTimerTrial: "show" | "hide";
|
||||||
text: "1" | "2" | "3"
|
text: "1" | "2" | "3";
|
||||||
|
auraVideoTrial: "on";
|
||||||
|
auraPalmistry: "on";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useMetricABFlags = () => {
|
export const useMetricABFlags = () => {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { language } from "@/locales";
|
||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ fbq('track', 'PageView');`;
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Helmet>
|
<Helmet htmlAttributes={{ lang: language }}>
|
||||||
<script>{FBScript}</script>
|
<script>{FBScript}</script>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user