Merge branch 'develop' into 'main'

hint-palm

See merge request witapp/aura-webapp!681
This commit is contained in:
Daniil Chemerkin 2025-03-16 01:41:12 +00:00
commit 1049acf1a5
41 changed files with 452 additions and 58 deletions

View File

@ -7,7 +7,6 @@
"day": "Day",
"year": "Year",
"month": "Month",
"aura_paywall_redesign_main": {
"text_0": "We've helped millions of people to have happier lives and better relationships, and we want to help you too.",
"text_0_color": "millions",
@ -120,11 +119,14 @@
},
"payment_modal": {
"title": "Choose payment method",
"title1": "Payment method",
"credit_card": "Credit Card",
"price_for_days": "<trialPrice> for your <trialDuration>-day trial",
"email_reminder": "We`ll email you a reminder",
"description": "You will be charged only <priceForDays>. \n<emailReminder> before your trial period ends. \nCancel anytime. The charge will appear on your bill as witapps.",
"address": "2108 N ST STE 5446 SACRAMENTO, CA 95816"
"address": "2108 N ST STE 5446 SACRAMENTO, CA 95816",
"form_error": "Проверьте правильность заполнения формы",
"price_information": "A <price> charge"
},
"add_report": "Add Report",
"unlimited_readings": "Unlimited Readings",
@ -499,7 +501,6 @@
"title7": "Analyzing relationship needs...",
"title8": "Charting best guidance plan...",
"title9": "Predicting future results...",
"popup": {
"title1": "Do you enjoy time spent alone?",
"title2": "Are you adventurous person?",
@ -510,7 +511,6 @@
"personality_traits": "Personality traits",
"relationship_pattern": "Relationship Pattern"
},
"description": "Sit tight! We`re building your perfect guidance plane based on your unique astrological blueprint and data of millions users."
},
"/email": {
@ -716,4 +716,4 @@
"unlock_profound": "Unlock profound insights into your personality, relationships, career trajectory, and life's pivotal moments through astrology, empowering you to make informed decisions and achieve greater fulfillment.",
"choose_from": "Choose from 80+ experts astrologers."
}
}
}

View File

@ -17,6 +17,7 @@ import CameraModal from "../../components/CameraModal";
import metricService, { EGoals, EMetrics } from "@/services/metric/metricService";
import Modal from "@/components/Modal";
import Title from "@/components/Title";
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
const isProduction = import.meta.env.MODE === "production";
@ -47,10 +48,17 @@ function Camera() {
const [isLoading, setIsLoading] = useState(false);
const [uploadMenuModalIsOpen, setUploadMenuModalIsOpen] = useState(false);
const [toastVisible, setToastVisible] = useState<EToastVisible | null>(null);
const [isRequestCameraModalOpen, setIsRequestCameraModalOpen] = useState(isIphoneSafari ? false : true);
const [cameraKey, setCameraKey] = useState(0);
const [isCameraModalOpen, setIsCameraModalOpen] = useState(false);
const { isReady, variant: cameraRequestModalCompatibilityV2 } = useUnleash({
flag: EUnleashFlags.cameraRequestModalCompatibilityV2
});
const isCameraRequestModal = cameraRequestModalCompatibilityV2 !== "hide";
const [isRequestCameraModalOpen, setIsRequestCameraModalOpen] = useState((isIphoneSafari || !isCameraRequestModal) ? false : true);
const handleNext = () => {
metricService.reachGoal(EGoals.CAMERA_SUCCESS, [EMetrics.YANDEX, EMetrics.KLAVIYO]);
navigate(routes.client.compatibilityV2ScannedPhoto());
@ -185,7 +193,7 @@ function Camera() {
const cameraError = (error: string | DOMException) => {
console.error("Camera error", error)
if (!isIphoneSafari) return;
if (!isIphoneSafari || !isCameraRequestModal) return;
if (error === "Video is not ready") {
return setToastVisible(EToastVisible.no_access_camera)
}
@ -210,6 +218,8 @@ function Camera() {
}
};
if (!isReady) return <Loader color={LoaderColor.Black} />;
return (
<>
<Modal
@ -269,7 +279,7 @@ function Camera() {
onClose={() => console.log("close")}
onTakePhoto={onTakePhoto}
onError={cameraError}
isCameraVisible={isIphoneSafari ? true : isCameraModalOpen}
isCameraVisible={(isIphoneSafari || isCameraRequestModal) ? true : isCameraModalOpen}
reinitializeKey={cameraKey}
/>
)}

View File

@ -14,6 +14,7 @@ import ProgressBarLine from "@/components/ui/ProgressBarLine";
import Modal from "@/components/Modal";
import { useAuthentication } from "@/hooks/authentication/use-authentication";
import { ESourceAuthorization } from "@/api/resources/User";
import { getRandomArbitrary } from "@/services/random-value";
const drawElementChangeDelay = 1500;
const startDelay = 500;
@ -175,6 +176,15 @@ function ScannedPhoto() {
const [isPause, setIsPause] = useState(false);
const interval = useRef<NodeJS.Timeout>();
const loadingPointSettings = useMemo(() => {
const currentPointIndex = Math.floor(progress / 100);
const endFastLoading = getRandomArbitrary(15, 30);
const startFastLoading = getRandomArbitrary(70, 85);
const pauseLoading = getRandomArbitrary(40, 60);
return { endFastLoading, pauseLoading, startFastLoading, currentPointIndex };
}, [Math.floor(progress / 100)])
const getProgressValue = useCallback(
(index: number) => {
const integerDivision = Math.floor(progress / 100);
@ -206,7 +216,24 @@ function ScannedPhoto() {
}
}, [progress]);
const getLoadingTime = useCallback((progress: number) => {
const progressOfCurrentPoint = progress % 100;
if (progressOfCurrentPoint < loadingPointSettings.endFastLoading) {
return 40;
}
if (progressOfCurrentPoint > loadingPointSettings.startFastLoading) {
return 40;
}
if (progressOfCurrentPoint === loadingPointSettings.pauseLoading) {
return 3000;
}
return 100
}, [Math.floor(progress / 100)]);
useEffect(() => {
const loadingTime = getLoadingTime(progress);
if (progress >= loadingProfilePoints.length * 100) {
return onEndLoading();
}
@ -215,7 +242,7 @@ function ScannedPhoto() {
if (!isPause && !isDecorationShown) return prevProgress + 1;
return prevProgress;
});
}, 100);
}, loadingTime);
return () => {
clearTimeout(interval.current);
};

View File

@ -3,12 +3,13 @@
width: 100dvw;
max-width: 560px;
display: flex;
justify-content: space-between;
justify-content: center;
align-items: center;
gap: 0px;
margin-top: 8px;
&.with-partner {
justify-content: space-between;
// &>img:first-child {
// margin-right: -30%;
// }

View File

@ -44,11 +44,18 @@ function TrialPayment() {
"/v1/palmistry/ticket.svg",
])
const { variant: pageAfterTrialPaymentCompatibilityV2 } = useUnleash({
flag: EUnleashFlags.pageAfterTrialPaymentCompatibilityV2
});
const { isReady, variant: zodiacImages } = useUnleash({
flag: EUnleashFlags.zodiacImages
});
const handleNext = () => {
if (pageAfterTrialPaymentCompatibilityV2 === "paymentForm") {
return navigate(routes.client.compatibilityV2PaymentModal());
}
navigate(routes.client.compatibilityV2Payment());
};

View File

@ -17,6 +17,7 @@ import { ESourceAuthorization } from "@/api/resources/User";
import { ELottieKeys, useLottie } from "@/hooks/lottie/useLottie";
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
import { useMetricABFlags } from "@/services/metric/metricService";
import { getRandomArbitrary } from "@/services/random-value";
// const drawElementChangeDelay = 1500;
// const startDelay = 500;
@ -177,6 +178,15 @@ function ScannedPhoto() {
const [isPause, setIsPause] = useState(false);
const interval = useRef<NodeJS.Timeout>();
const loadingPointSettings = useMemo(() => {
const currentPointIndex = Math.floor(progress / 100);
const endFastLoading = getRandomArbitrary(15, 30);
const startFastLoading = getRandomArbitrary(70, 85);
const pauseLoading = getRandomArbitrary(40, 60);
return { endFastLoading, pauseLoading, startFastLoading, currentPointIndex };
}, [Math.floor(progress / 100)])
const getProgressValue = useCallback(
(index: number) => {
const integerDivision = Math.floor(progress / 100);
@ -211,23 +221,33 @@ function ScannedPhoto() {
}
}, [progress]);
const getLoadingTime = useCallback((progress: number) => {
const progressOfCurrentPoint = progress % 100;
if (progressOfCurrentPoint < loadingPointSettings.endFastLoading) {
return 40;
}
if (progressOfCurrentPoint > loadingPointSettings.startFastLoading) {
return 40;
}
if (progressOfCurrentPoint === loadingPointSettings.pauseLoading) {
return 3000;
}
return 100
}, [Math.floor(progress / 100)]);
useEffect(() => {
const loadingTime = getLoadingTime(progress);
if (progress >= loadingProfilePoints.length * 100) {
return onEndLoading();
}
const getDelay = () => {
if (progress < 15) return 50;
if (progress === 15) return 2500;
return 100;
}
interval.current = setTimeout(() => {
setProgress((prevProgress) => {
if (!isPause && !isDecorationShown) return prevProgress + 1;
return prevProgress;
});
}, getDelay());
}, loadingTime);
return () => {
clearTimeout(interval.current);

View File

@ -24,6 +24,8 @@ import { formatDateToLocale } from "@/locales/localFormats";
import { useEffect } from "react";
import metricService, { EGoals, EMetrics } from "@/services/metric/metricService";
import MoneyBackGuarantee from "../../components/MoneyBackGuarantee";
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
import Loader, { LoaderColor } from "@/components/Loader";
function TrialPayment() {
const { height, elementRef } = useDynamicSize<HTMLDivElement>({});
@ -41,7 +43,14 @@ function TrialPayment() {
"/v1/palmistry/ticket.svg",
])
const { isReady, variant: pageAfterTrialPaymentCompatibilityV3 } = useUnleash({
flag: EUnleashFlags.pageAfterTrialPaymentCompatibilityV3
});
const handleNext = () => {
if (pageAfterTrialPaymentCompatibilityV3 === "paymentForm") {
return navigate(routes.client.compatibilityV3PaymentModal());
}
navigate(routes.client.compatibilityV3Payment());
};
@ -50,6 +59,10 @@ function TrialPayment() {
metricService.reachGoal(EGoals.AURA_TRIAL_PAYMENT_PAGE_VISIT, [EMetrics.KLAVIYO]);
}, []);
if (!isReady) {
return <Loader color={LoaderColor.Black} />;
}
return (
<>
<Title className={styles["information-title"]}>

View File

@ -2,12 +2,13 @@
position: relative;
width: 100dvw;
display: flex;
justify-content: space-between;
justify-content: center;
align-items: center;
gap: 0px;
margin-top: 8px;
&.with-partner {
justify-content: space-between;
// &>img:first-child {
// margin-right: -30%;
// }

View File

@ -35,6 +35,12 @@
padding-top: 5px;
}
.dark-theme .date-picker__field > select {
background: #343639;
border: 2px solid #54689F;
color: #F7F7F7;
}
.date-picker__field-label {
color: #6b7baa;
font-size: 12px;
@ -42,6 +48,10 @@
margin: 0 0 6px 6px;
}
.dark-theme .date-picker__field-label {
color: #8DA4EA;
}
.date-picker__input {
display: block;
font-size: 16px;

View File

@ -24,3 +24,8 @@
// transform: scale(1.03);
}
}
:global(.dark-theme) .container {
background-color: #343639;
color: #F7F7F7;
}

View File

@ -1,6 +1,9 @@
import styles from "./styles.module.scss";
function ScanInstructionSVG() {
return (
<svg
className={styles.svg}
width="355"
height="339"
viewBox="0 0 355 339"

View File

@ -0,0 +1,11 @@
:global(.dark-theme) .svg {
&>rect {
stroke: #DCDCDC;
}
&>rect:first-child {
stroke: none;
fill: #2D2D34;
fill-opacity: 1;
}
}

View File

@ -14,9 +14,10 @@ import Toast from "@/components/pages/ABDesign/v1/components/Toast";
import { useTranslations } from "@/hooks/translations";
import { ELocalesPlacement } from "@/locales";
import CameraModal from "../../components/CameraModal";
import metricService, { EGoals, EMetrics, useMetricABFlags } from "@/services/metric/metricService";
import metricService, { EGoals, EMetrics } from "@/services/metric/metricService";
import Modal from "@/components/Modal";
import Title from "@/components/Title";
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
const isProduction = import.meta.env.MODE === "production";
@ -50,8 +51,16 @@ function Camera() {
const [cameraKey, setCameraKey] = useState(0);
const [isCameraModalOpen, setIsCameraModalOpen] = useState(false);
const { flags, ready } = useMetricABFlags();
const isCameraRequestModal = flags?.cameraRequestModal?.[0] !== "without";
// const { flags, ready } = useMetricABFlags();
const { isReady, variant: cameraRequestModalPalmistryV1 } = useUnleash({
flag: EUnleashFlags.cameraRequestModalPalmistryV1
});
// const isCameraRequestModal = flags?.cameraRequestModal?.[0] !== "without";
const isCameraRequestModal = cameraRequestModalPalmistryV1 !== "hide";
const [isRequestCameraModalOpen, setIsRequestCameraModalOpen] = useState((isIphoneSafari || !isCameraRequestModal) ? false : true);
const handleNext = () => {
@ -213,7 +222,7 @@ function Camera() {
}
};
if (!ready) return null;
if (!isReady) return <Loader color={LoaderColor.Black} />;
return (
<>

View File

@ -56,6 +56,7 @@
.modal-container {
max-width: 290px;
padding: 24px 0px 0px;
overflow: hidden;
&>.modal-title {
@ -85,4 +86,8 @@
}
}
}
}
}
:global(.dark-theme) .modal-container {
background-color: #343639;
}

View File

@ -119,6 +119,7 @@ function Email() {
name="email"
value={email}
placeholder={translate("/email.placeholder_email")}
placeholderClassName={styles["input-placeholder"]}
inputClassName={styles.input}
onValid={handleValidEmail}
onInvalid={() => setIsValidEmail(false)}
@ -127,6 +128,7 @@ function Email() {
value={name}
placeholder={translate("/email.placeholder_name")}
inputClassName={styles.input}
placeholderClassName={styles["input-placeholder"]}
onValid={handleValidName}
onInvalid={() => setIsValidName(true)}
/>

View File

@ -9,6 +9,15 @@
min-height: 60px;
}
:global(.dark-theme) .input {
background-color: #343639;
color: #F7F7F7;
}
:global(.dark-theme) .input-placeholder {
color: #B3B3B3;
}
.not-share {
padding-top: 5px;
font-size: 20px;

View File

@ -10,7 +10,8 @@ import { useEffect } from "react";
import { actions } from "@/store";
// import StarSVG from "../../images/SVG/Star";
import { usePreloadImages } from "@/hooks/preload/images";
import { useMetricABFlags } from "@/services/metric/metricService";
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
import Loader, { LoaderColor } from "@/components/Loader";
function FindHappiness() {
const navigate = useNavigate();
@ -18,8 +19,14 @@ function FindHappiness() {
const location = useLocation();
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
usePreloadImages(["/male-gender.webp", "/female-gender.webp"]);
const { flags, ready } = useMetricABFlags();
const imageType = flags?.welcomePageImage?.[0];
// const { flags, ready } = useMetricABFlags();
// const imageType = flags?.welcomePageImage?.[0];
const { isReady, variant: welcomePageImagePalmistryV1 } = useUnleash({
flag: EUnleashFlags.welcomePageImagePalmistryV1
})
const imageType = welcomePageImagePalmistryV1;
const image = imageType === "v1" ? "/hand-with-lines-v1.png" : "/hand-with-lines.png";
@ -33,7 +40,7 @@ function FindHappiness() {
}
}, [dispatch, location.pathname]);
if (!ready) return null;
if (!isReady) return <Loader color={LoaderColor.Black} />;
return (
<>

View File

@ -56,10 +56,10 @@ function HeadOrHeartResult() {
{headOrHeart === "both" &&
translate("/both.title", {
wonderful: (
<b style={{ color: "#224e90" }}>{translate("/both.wonderful")}</b>
<b className={styles.highlite}>{translate("/both.wonderful")}</b>
),
zodiacSign: (
<b style={{ color: "#224e90" }}>
<b className={styles.highlite}>
{translate(`zodiac_signs.${zodiacSign?.toLowerCase()}`)}
</b>
),
@ -68,7 +68,7 @@ function HeadOrHeartResult() {
{headOrHeart === "head" &&
translate("/with-head.title", {
zodiacSign: (
<b style={{ color: "#224e90" }}>
<b className={styles.highlite}>
{translate(`zodiac_signs.${zodiacSign?.toLowerCase()}`)}
</b>
),
@ -77,7 +77,7 @@ function HeadOrHeartResult() {
{headOrHeart === "heart" &&
translate("/with-heart.title", {
zodiacSign: (
<b style={{ color: "#224e90" }}>
<b className={styles.highlite}>
{translate(`zodiac_signs.${zodiacSign?.toLowerCase()}`)}
</b>
),
@ -86,7 +86,7 @@ function HeadOrHeartResult() {
{headOrHeart === "depends" &&
translate("/depends.title", {
zodiacSign: (
<b style={{ color: "#224e90" }}>
<b className={styles.highlite}>
{translate(`zodiac_signs.${zodiacSign?.toLowerCase()}`)}
</b>
),

View File

@ -9,7 +9,7 @@
justify-content: space-between;
padding-top: 24px;
& > .button {
&>.button {
width: 48%;
min-width: 0px;
font-size: 23px;
@ -27,3 +27,18 @@
.lottie-animation {
aspect-ratio: 128 / 82;
}
:global(.dark-theme) .buttons-container {
&>.back-button {
border-color: #F7F7F7;
color: #F7F7F7;
}
}
.highlite {
color: #224e90;
}
:global(.dark-theme) .highlite {
color: #4F8DE5;
}

View File

@ -144,7 +144,7 @@ function ScannedPhoto() {
drawElements={drawElements}
/>
<h2
className="palmistry-container__waiting-title"
className={`palmistry-container__waiting-title ${styles.waitingTitle}`}
style={{
animationDelay: `${drawElementChangeDelay * drawElements.length + 2500
}ms`,
@ -154,7 +154,7 @@ function ScannedPhoto() {
</h2>
<h3
className="palmistry-container__waiting-description"
className={`palmistry-container__waiting-description ${styles.waitingDescription}`}
style={{
animationDelay: `${drawElementChangeDelay * drawElements.length + 3000
}ms`,

View File

@ -92,6 +92,10 @@
}
}
:global(.dark-theme) .title {
color: #F7F7F7;
}
.photo-container {
width: 100%;
height: fit-content;
@ -161,4 +165,12 @@
100% {
stroke-dashoffset: 0;
}
}
:global(.dark-theme) .waitingTitle {
color: #F7F7F7;
}
:global(.dark-theme) .waitingDescription {
color: #F7F7F7;
}

View File

@ -17,6 +17,8 @@ import { usePreloadImages } from "@/hooks/preload/images";
import useTimer from "@/hooks/palmistry/use-timer";
import { useEffect } from "react";
import metricService, { EGoals, EMetrics } from "@/services/metric/metricService";
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
import Loader, { LoaderColor } from "@/components/Loader";
function TrialPayment() {
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
@ -26,7 +28,14 @@ function TrialPayment() {
"/v1/palmistry/ticket.svg",
])
const { isReady, variant: pageAfterTrialPaymentPalmistryV1 } = useUnleash({
flag: EUnleashFlags.pageAfterTrialPaymentPalmistryV1
});
const handleNext = () => {
if (pageAfterTrialPaymentPalmistryV1 === "paymentForm") {
return navigate(routes.client.palmistryV1PaymentModal());
}
navigate(routes.client.palmistryV1Payment());
};
@ -35,6 +44,10 @@ function TrialPayment() {
metricService.reachGoal(EGoals.AURA_TRIAL_PAYMENT_PAGE_VISIT, [EMetrics.KLAVIYO]);
}, []);
if (!isReady) {
return <Loader color={LoaderColor.Black} />;
}
return (
<>
<AppNumberOne />

View File

@ -1,9 +1,13 @@
import MainButton from "@/components/MainButton";
import { useEffect } from "react";
import { useEffect, useState } from "react";
import styles from "./styles.module.scss";
import { usePayment } from "@/hooks/payment/nmi/usePayment";
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
import SecurityPayments from "../../SecurityPayments";
import { useTranslations } from "@/hooks/translations";
import { ELocalesPlacement } from "@/locales";
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
import Loader, { LoaderColor } from "@/components/Loader";
export type TConfirmType = "payment" | "setup";
@ -31,6 +35,12 @@ export default function CheckoutForm({
onModalClosed,
isHide = false,
}: ICheckoutFormProps) {
const { translate } = useTranslations(ELocalesPlacement.V1);
const [payButtonClicked, setPayButtonClicked] = useState(false);
const { variant, isReady } = useUnleash({
flag: EUnleashFlags.paymentButtonLogic
});
const isNewPaymentButton = variant === "new";
const {
isLoading,
@ -70,12 +80,20 @@ export default function CheckoutForm({
const handleSubmit = (e: React.FormEvent<HTMLFormElement> | React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
setPayButtonClicked(true);
if (!isFormValid) {
return;
}
submitInlineForm();
};
if (!isReady) {
return <div className={styles.loaderContainer}>
<Loader color={LoaderColor.Black} />
</div>;
}
return (
<form
className={`${styles.form} ${isHide ? styles.hide : ""}`}
@ -128,12 +146,18 @@ export default function CheckoutForm({
</div>
</div>
</div>
{isNewPaymentButton && payButtonClicked && !isFormValid &&
<p className={styles.errorMessage} style={{ marginBottom: "16px" }}>
{translate("payment_modal.form_error")}
</p>
}
<SecurityPayments />
<MainButton
color="blue"
disabled={isLoading || !isFormValid}
// disabled={isLoading || !isFormValid}
disabled={isNewPaymentButton ? isLoading : (isLoading || !isFormValid)}
id="submit"
className={styles.button}
onClick={handleSubmit}

View File

@ -231,4 +231,11 @@
// .formContainer {
// margin-left: -15px;
// }
// }
// }
.loaderContainer {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}

View File

@ -14,6 +14,7 @@ import metricService, { EGoals, EMetrics } from "@/services/metric/metricService
import Toast from "@/components/pages/ABDesign/v1/components/Toast";
import Header from "@/components/PalmistryV1/components/Header";
import { useNavigate } from "react-router-dom";
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
interface IPaymentPageProps {
isSinglePayment?: boolean;
@ -54,7 +55,12 @@ function PaymentPage({
const [isPaymentSuccess, setIsPaymentSuccess] = useState(false);
const [isPaymentError, setIsPaymentError] = useState(false);
const isLoading = false;
const { isReady, variant: _paymentModalPageVarinat } = useUnleash({
flag: EUnleashFlags.paymentModalPage
})
const paymentModalPageVarinat = _paymentModalPageVarinat || "v0";
const isLoading = !isReady;
const paymentMethodsButtons = [
{
@ -129,15 +135,22 @@ function PaymentPage({
<div
className={`${styles["payment-modal"]} ${isLoading ? styles.hide : ""} ${className}`}
>
{!isSinglePayment && <Title variant="h3" className={styles.title}>
{translate("payment_modal.title")}
</Title>}
<PaymentMethodsChoice
{!isSinglePayment && paymentModalPageVarinat !== "v2" &&
<Title variant="h3" className={styles.title}>
{translate("payment_modal.title")}
</Title>
}
{!isSinglePayment && paymentModalPageVarinat === "v2" &&
<Title variant="h3" className={styles.title}>
{translate("payment_modal.title1")}
</Title>
}
{paymentModalPageVarinat !== "v2" && <PaymentMethodsChoice
paymentMethods={paymentMethodsButtons}
selectedPaymentMethod={paymentMethodsButtons[0].id}
onSelectPaymentMethod={() => { }}
/>
{!isSinglePayment && activeProduct && (
/>}
{paymentModalPageVarinat === "v0" && !isSinglePayment && activeProduct && (
<div>
<p className={styles["sub-plan-description"]}>
{translate("payment_modal.description", {
@ -159,7 +172,27 @@ function PaymentPage({
</p>
</div>
)}
<div className={styles["payment-method-container"]}>
{
paymentModalPageVarinat === "v1" && (
<p className={styles["price-information"]}>
{translate("payment_modal.price_information", {
price: addCurrency(
getPrice(activeProduct),
currency
)
})}
</p>
)
}
<div
className={styles["payment-method-container"]}
style={{
marginTop: (
paymentModalPageVarinat === "v0" ||
paymentModalPageVarinat === "v2"
) ? "0px" : "18px"
}}
>
{!!activeProduct && <CheckoutForm
placementKey={placementKey}
activeProduct={activeProduct}

View File

@ -94,4 +94,9 @@
position: sticky;
bottom: calc(0dvh + 16px);
z-index: 1000;
}
.price-information {
margin-top: 16px;
font-weight: 500;
}

View File

@ -34,3 +34,7 @@
font-size: 16px;
}
.dark-theme .policy p,
.dark-theme .policy a {
color: #F7F7F7;
}

View File

@ -13,4 +13,8 @@
.text > a {
text-decoration: underline;
}
:global(.dark-theme) .text {
color: #B3B3B3;
}

View File

@ -5,6 +5,7 @@ import { FormField } from "@/types";
interface INameInputProps {
value: string;
placeholder: string;
placeholderClassName?: string;
onValid: (value: string) => void;
onInvalid: () => void;
}
@ -17,6 +18,7 @@ function NameInput({
inputClassName,
value,
placeholder,
placeholderClassName,
onValid,
onInvalid,
}: INameInputProps & Partial<FormField<string>>) {
@ -43,7 +45,7 @@ function NameInput({
onChange={handleChangeName}
placeholder=" "
/>
<span className={styles["input__placeholder"]}>{placeholder}</span>
<span className={`${styles["input__placeholder"]} ${placeholderClassName}`}>{placeholder}</span>
</div>
);
}

View File

@ -35,18 +35,22 @@ function GetInformationPartnerPage() {
const handleNext = () => {
if (path === "palmistry" || path === "palmistry-v1") {
return navigate(
`${routes.client.palmistryOnboardingV1()}?path=palmistry`
);
// return navigate(
// `${routes.client.palmistryOnboardingV1()}?path=palmistry`
// );
return navigate(routes.client.home());
}
if (path === "compatibility") {
return navigate(`${routes.client.palmistryOnboardingV1()}?path=compatibility`);
// return navigate(`${routes.client.palmistryOnboardingV1()}?path=compatibility`);
return navigate(routes.client.home());
}
if (path === "email-compatibility") {
return navigate(routes.client.emailMarketingV1Onboarding());
// return navigate(routes.client.emailMarketingV1Onboarding());
return navigate(routes.client.home());
}
if (path === "email-palmistry") {
return navigate(routes.client.palmistryV2Onboarding());
// return navigate(routes.client.palmistryV2Onboarding());
return navigate(routes.client.home());
}
navigate(routes.client.home());
};

View File

@ -150,6 +150,21 @@
opacity: 0;
}
body.dark-theme .scanned-photo__decoration__corners {
background: linear-gradient(to right,
#BAC2EE 2px,
transparent 2px) 0 0,
linear-gradient(to right, #BAC2EE 2px, transparent 2px) 0 100%,
linear-gradient(to left, #BAC2EE 2px, transparent 2px) 100% 0,
linear-gradient(to left, #BAC2EE 2px, transparent 2px) 100% 100%,
linear-gradient(to bottom, #BAC2EE 2px, transparent 2px) 0 0,
linear-gradient(to bottom, #BAC2EE 2px, transparent 2px) 100% 0,
linear-gradient(to top, #BAC2EE 2px, transparent 2px) 0 100%,
linear-gradient(to top, #BAC2EE 2px, transparent 2px) 100% 100%;
background-repeat: no-repeat;
background-size: 15px 15px;
}
.scanned-photo__decoration__corners>svg {
position: absolute;
}
@ -289,4 +304,16 @@
100% {
stroke-dashoffset: 0;
}
}
body.dark-theme .scanned-photo__decoration_svg>circle:first-child {
stroke: #4C526C;
}
body.dark-theme .scanned-photo__decoration_svg>circle:last-child {
fill: #BAC2EE;
}
body.dark-theme .scanned-photo__decoration__light-blue-circle {
background: #4C526C;
}

View File

@ -255,6 +255,7 @@ export default function StepScanPhoto(props: Props) {
y="0px"
viewBox="0 0 220 220"
enableBackground="new 0 0 0 0"
className="scanned-photo__decoration_svg"
>
<circle
fill="none"

View File

@ -7,3 +7,7 @@
line-height: 140%;
color: rgb(79, 79, 79);
}
:global(.dark-theme) .have-account {
color: #B3B3B3;
}

View File

@ -7,6 +7,15 @@ export enum EUnleashFlags {
"zodiacImages" = "zodiacImages",
"dynamicHandsCompV2" = "dynamicHandsCompV2",
"dynamicHandsPalmistryV1" = "dynamicHandsPalmistryV1",
"welcomePageImagePalmistryV1" = "welcomePageImagePalmistryV1",
"cameraRequestModalPalmistryV1" = "cameraRequestModalPalmistryV1",
"cameraRequestModalCompatibilityV2" = "cameraRequestModalCompatibilityV2",
"darkThemePalmistryV1" = "darkThemePalmistryV1",
"paymentButtonLogic" = "paymentButtonLogic",
"pageAfterTrialPaymentCompatibilityV2" = "pageAfterTrialPaymentCompatibilityV2",
"pageAfterTrialPaymentCompatibilityV3" = "pageAfterTrialPaymentCompatibilityV3",
"pageAfterTrialPaymentPalmistryV1" = "pageAfterTrialPaymentPalmistryV1",
"paymentModalPage" = "paymentModalPage",
}
interface IUseUnleashProps<T extends EUnleashFlags> {
@ -18,6 +27,15 @@ interface IVariants {
[EUnleashFlags.zodiacImages]: "new" | "old";
[EUnleashFlags.dynamicHandsCompV2]: "show" | "hide";
[EUnleashFlags.dynamicHandsPalmistryV1]: "show" | "hide";
[EUnleashFlags.welcomePageImagePalmistryV1]: "v0" | "v1";
[EUnleashFlags.cameraRequestModalPalmistryV1]: "show" | "hide";
[EUnleashFlags.cameraRequestModalCompatibilityV2]: "show" | "hide";
[EUnleashFlags.darkThemePalmistryV1]: "enabled" | "disabled";
[EUnleashFlags.paymentButtonLogic]: "old" | "new";
[EUnleashFlags.pageAfterTrialPaymentCompatibilityV2]: "paymentInformation" | "paymentForm";
[EUnleashFlags.pageAfterTrialPaymentCompatibilityV3]: "paymentInformation" | "paymentForm";
[EUnleashFlags.pageAfterTrialPaymentPalmistryV1]: "paymentInformation" | "paymentForm";
[EUnleashFlags.paymentModalPage]: "v0" | "v1" | "v2";
}
/**

View File

@ -260,4 +260,9 @@ input, textarea {
html, body {
touch-action: pan-y;
}
body.dark-theme {
background-color: #222225;
color: #F7F7F7;
}

View File

@ -73,7 +73,10 @@ function CompatibilityV2Routes() {
}
function onPopState(): void {
if (document.location.toString() === `${window.location.origin}${routes.client.compatibilityV2Payment()}`) {
if (
document.location.toString() === `${window.location.origin}${routes.client.compatibilityV2Payment()}` ||
document.location.toString() === `${window.location.origin}${routes.client.compatibilityV2TrialPayment()}`
) {
navigate(routes.client.compatibilityV2SaveOff());
}
}

View File

@ -75,7 +75,10 @@ function CompatibilityV3Routes() {
}
function onPopState(): void {
if (document.location.toString() === `${window.location.origin}${routes.client.compatibilityV3Payment()}`) {
if (
document.location.toString() === `${window.location.origin}${routes.client.compatibilityV3Payment()}` ||
document.location.toString() === `${window.location.origin}${routes.client.compatibilityV3TrialPayment()}`
) {
navigate(routes.client.compatibilityV3SaveOff());
}
}

View File

@ -11,10 +11,18 @@
padding: 8px 0 30px;
}
:global(.dark-theme) .header>button>svg>path {
fill: #BAC2EE;
}
.header-title {
color: #275CA7;
}
:global(.dark-theme) .header>svg>path {
fill: #4F8DE5;
}
.page {
width: 100%;
height: fit-content;

View File

@ -41,7 +41,7 @@ function StepperLayoutPalmistryV1() {
<StepperBar
length={stepperRoutes.length}
currentStep={getCurrentStep()}
color="#353E75"
color="#B2BCFF"
className={styles["stepper-bar"]}
/>
<Outlet />

View File

@ -39,13 +39,38 @@ import AddGuides from "@/components/palmistry/AdditionalPurchases/pages/AddGuide
import PaymentPage from "@/components/Payment/nmi/PaymentPage";
import { EPlacementKeys } from "@/api/resources/Paywall";
import TryApp from "@/components/PalmistryV1/pages/TryApp";
import { EUnleashFlags, useUnleash } from "@/hooks/ab/unleash/useUnleash";
import Loader, { LoaderColor } from "@/components/Loader";
const removePrefix = (path: string) => path.replace(palmistryV1Prefix, "");
const availableUrlsDarkTheme = [
routes.client.palmistryV1Welcome(),
routes.client.palmistryV1Gender(),
routes.client.palmistryV1Birthdate(),
routes.client.palmistryV1PalmsInformation(),
routes.client.palmistryV1WhatAspects(),
routes.client.palmistryV1RelationshipStatus(),
routes.client.palmistryV1ElementResonates(),
routes.client.palmistryV1FavoriteColor(),
routes.client.palmistryV1HeadOrHeart(),
routes.client.palmistryV1HeadOrHeartResult(),
routes.client.palmistryV1RelateFollowing(),
routes.client.palmistryV1LetScan(),
routes.client.palmistryV1ScanInstruction(),
routes.client.palmistryV1Camera(),
routes.client.palmistryV1ScannedPhoto(),
routes.client.palmistryV1Email(),
]
function PalmistryV1Routes() {
const navigate = useNavigate();
const dispatch = useDispatch();
const { isReady, variant: darkThemePalmistryV1Variant } = useUnleash({
flag: EUnleashFlags.darkThemePalmistryV1
})
useEffect(() => {
dispatch(actions.palmistry.update({ fromRedesign: true }));
}, [dispatch]);
@ -67,7 +92,10 @@ function PalmistryV1Routes() {
}
function onPopState(): void {
if (document.location.toString() === `${window.location.origin}${routes.client.palmistryV1Payment()}`) {
if (
document.location.toString() === `${window.location.origin}${routes.client.palmistryV1Payment()}` ||
document.location.toString() === `${window.location.origin}${routes.client.palmistryV1TrialPayment()}`
) {
navigate(routes.client.palmistryV1SaveOff());
}
}
@ -84,6 +112,34 @@ function PalmistryV1Routes() {
}, 1500);
}
useEffect(() => {
const isAvailableUrl = availableUrlsDarkTheme.reduce((acc, url) => {
if (window.location.pathname.startsWith(url)) {
return true;
}
return acc;
}, false);
if (isAvailableUrl && darkThemePalmistryV1Variant === "enabled") {
document.body.classList.add("dark-theme");
} else {
document.body.classList.remove("dark-theme");
}
}, [window.location.pathname])
if (!isReady) {
return <div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100dvh"
}}
>
<Loader color={LoaderColor.Black} />
</div>
}
return (
<Routes>
<Route element={<PrivateOutlet />}>

View File

@ -1,3 +1,4 @@
import { getSourceByPathname } from "@/utils/source.utils";
import Clarity from "@microsoft/clarity";
import { useExperiments } from "yandex-metrica-ab-react";
@ -180,6 +181,8 @@ const userParams = (parameters: Partial<IUserParams>) => {
const reachGoal = (goal: EGoals, usingMetrics: EMetrics[], options?: unknown) => {
console.log("goal: ", goal);
const isProduction = environments.MODE === "production";
const source = getSourceByPathname();
// if (!isProduction) return console.log("ANALYTIC IS NOT WORKING: Not production");
if (usingMetrics.includes(EMetrics.YANDEX)) {
@ -194,7 +197,10 @@ const reachGoal = (goal: EGoals, usingMetrics: EMetrics[], options?: unknown) =>
console.error("Google Analytics not found")
} else {
const eventName = goal === EGoals.PAYMENT_SUCCESS ? "purchase" : goal;
window.gtag('event', eventName, options);
window.gtag('event', eventName, {
source,
...(options as Record<string, unknown>)
});
console.log("goalGA: ", goal);
}
}