Preview/additional purchases
This commit is contained in:
parent
a033e4c542
commit
f088690aa5
BIN
public/videos/background-video-1.mp4
Normal file
BIN
public/videos/background-video-1.mp4
Normal file
Binary file not shown.
@ -67,6 +67,7 @@ const api = {
|
||||
runThread: createMethod<OpenAI.PayloadRunThread, OpenAI.ResponseGetStatusRunThread>(OpenAI.createRequest),
|
||||
getStatusRunThread: createMethod<OpenAI.PayloadRunThread, OpenAI.ResponseGetStatusRunThread>(OpenAI.createRequest),
|
||||
getListRuns: createMethod<OpenAI.PayloadGetListRuns, OpenAI.ResponseGetListRuns>(OpenAI.createRequest),
|
||||
// Single payment
|
||||
getSinglePaymentProducts: createMethod<SinglePayment.PayloadGet, SinglePayment.ResponseGet[]>(SinglePayment.createRequestGet),
|
||||
createSinglePayment: createMethod<SinglePayment.PayloadPost, SinglePayment.ResponsePost | SinglePayment.ResponsePostExistPaymentData>(SinglePayment.createRequestPost),
|
||||
}
|
||||
|
||||
@ -109,6 +109,7 @@ import AdvisorChatPage from "../pages/AdvisorChat";
|
||||
import PaymentWithEmailPage from "../pages/PaymentWithEmailPage";
|
||||
import SuccessPaymentPage from "../pages/PaymentWithEmailPage/ResultPayment/SuccessPaymentPage";
|
||||
import FailPaymentPage from "../pages/PaymentWithEmailPage/ResultPayment/FailPaymentPage";
|
||||
import GetInformationPartnerPage from "../pages/GetInformationPartner";
|
||||
|
||||
const isProduction = import.meta.env.MODE === "production";
|
||||
|
||||
@ -233,9 +234,18 @@ function App(): JSX.Element {
|
||||
{/* Email - Pay - Email */}
|
||||
<Route path={routes.client.epeGender()} element={<GenderPage />} />
|
||||
<Route path={routes.client.epeBirthdate()} element={<BirthdayPage />} />
|
||||
<Route path={routes.client.epePayment()} element={<PaymentWithEmailPage />} />
|
||||
<Route path={routes.client.epeSuccessPayment()} element={<SuccessPaymentPage />} />
|
||||
<Route path={routes.client.epeFailPayment()} element={<FailPaymentPage />} />
|
||||
<Route
|
||||
path={routes.client.epePayment()}
|
||||
element={<PaymentWithEmailPage />}
|
||||
/>
|
||||
<Route
|
||||
path={routes.client.epeSuccessPayment()}
|
||||
element={<SuccessPaymentPage />}
|
||||
/>
|
||||
<Route
|
||||
path={routes.client.epeFailPayment()}
|
||||
element={<FailPaymentPage />}
|
||||
/>
|
||||
{/* Email - Pay - Email */}
|
||||
|
||||
{/* Test Routes Start */}
|
||||
@ -351,19 +361,29 @@ function App(): JSX.Element {
|
||||
{/* Email Letters End */}
|
||||
|
||||
{/* Additional Purchases */}
|
||||
<Route element={<AdditionalPurchases />}>
|
||||
<Route path={routes.client.addReport()} element={<AddReportPage />} />
|
||||
<Route
|
||||
path={routes.client.unlimitedReadings()}
|
||||
element={<UnlimitedReadingsPage />}
|
||||
/>
|
||||
<Route
|
||||
path={routes.client.addConsultation()}
|
||||
element={<AddConsultationPage />}
|
||||
/>
|
||||
<Route element={<PrivateOutlet />}>
|
||||
<Route element={<AdditionalPurchases />}>
|
||||
<Route
|
||||
path={routes.client.addReport()}
|
||||
element={<AddReportPage />}
|
||||
/>
|
||||
<Route
|
||||
path={routes.client.unlimitedReadings()}
|
||||
element={<UnlimitedReadingsPage />}
|
||||
/>
|
||||
<Route
|
||||
path={routes.client.addConsultation()}
|
||||
element={<AddConsultationPage />}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
{/* Additional Purchases End */}
|
||||
|
||||
<Route
|
||||
path={routes.client.getInformationPartner()}
|
||||
element={<GetInformationPartnerPage />}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={routes.client.paymentResult()}
|
||||
element={<PaymentResultPage />}
|
||||
|
||||
@ -27,7 +27,11 @@ function PaymentSuccessPage(): JSX.Element {
|
||||
<Title variant="h1">{t("auweb.pay_good.title")}</Title>
|
||||
<p>{t("auweb.pay_good.text1")}</p>
|
||||
</div>
|
||||
<MainButton className={styles.button} onClick={handleNext}>
|
||||
<MainButton
|
||||
className={styles.button}
|
||||
onClick={handleNext}
|
||||
id="success-payment"
|
||||
>
|
||||
{t("auweb.pay_good.button")}
|
||||
</MainButton>
|
||||
</section>
|
||||
|
||||
@ -6,6 +6,7 @@ interface IFooterButtonProps {
|
||||
classNameButton?: string;
|
||||
classNameSkip?: string;
|
||||
children?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
onClick: () => void;
|
||||
onClickSkip: () => void;
|
||||
}
|
||||
@ -17,11 +18,13 @@ function FooterButton({
|
||||
classNameContainer = "",
|
||||
classNameButton = "",
|
||||
classNameSkip = "",
|
||||
disabled = false,
|
||||
}: IFooterButtonProps) {
|
||||
return (
|
||||
<div className={`${styles.container} ${classNameContainer}`}>
|
||||
<MainButton
|
||||
className={`${styles.button} ${classNameButton}`}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { ISignUpOffer } from "@/data/additionalPurchases";
|
||||
import styles from "./styles.module.css";
|
||||
import CheckMark from "./check-mark-1.svg";
|
||||
import { getPriceCentsToDollars } from "@/services/price";
|
||||
|
||||
interface ISignUpOfferProps extends ISignUpOffer {
|
||||
isActive: boolean;
|
||||
@ -31,9 +32,14 @@ function SignUpOffer(props: ISignUpOfferProps) {
|
||||
)}
|
||||
<div className={styles["price-container"]}>
|
||||
<span className={styles.price}>
|
||||
<span className={styles["current-price"]}>${current}</span> ({" "}
|
||||
{id === "ultra-pack" ? "regular price" : "was"}{" "}
|
||||
<span className={styles["old-price"]}>${old}</span> )
|
||||
<span className={styles["current-price"]}>
|
||||
${getPriceCentsToDollars(current || 0)}
|
||||
</span>{" "}
|
||||
( {id === "ultra-pack" ? "regular price" : "was"}{" "}
|
||||
<span className={styles["old-price"]}>
|
||||
${getPriceCentsToDollars(old || 0)}
|
||||
</span>{" "}
|
||||
)
|
||||
</span>
|
||||
{id !== "ultra-pack" && (
|
||||
<div className={styles["discount-container"]}>
|
||||
|
||||
@ -5,14 +5,93 @@ import PaymentAddress from "../../components/PaymentAddress";
|
||||
import FooterButton from "../../components/FooterButton";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import { useAuth } from "@/auth";
|
||||
import { SinglePayment, useApi, useApiCall } from "@/api";
|
||||
import { useSelector } from "react-redux";
|
||||
import { selectors } from "@/store";
|
||||
import { useCallback, useState } from "react";
|
||||
import {
|
||||
ResponsePost,
|
||||
ResponsePostExistPaymentData,
|
||||
} from "@/api/resources/SinglePayment";
|
||||
import { createSinglePayment } from "@/services/singlePayment";
|
||||
import Modal from "@/components/Modal";
|
||||
import PaymentForm from "@/components/pages/PaymentWithEmailPage/PaymentForm";
|
||||
import { getPriceCentsToDollars } from "@/services/price";
|
||||
import Loader, { LoaderColor } from "@/components/Loader";
|
||||
|
||||
function AddConsultationPage() {
|
||||
const navigate = useNavigate();
|
||||
const handleNext = () => {
|
||||
navigate(routes.client.home());
|
||||
const { user: userFromStore } = useAuth();
|
||||
const api = useApi();
|
||||
const tokenFromStore = useSelector(selectors.selectToken);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [paymentIntent, setPaymentIntent] = useState<
|
||||
ResponsePost | ResponsePostExistPaymentData | null
|
||||
>(null);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const returnUrl = `${window.location.protocol}//${
|
||||
window.location.host
|
||||
}${routes.client.getInformationPartner()}`;
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
return await api.getSinglePaymentProducts({ token: tokenFromStore });
|
||||
}, [api, tokenFromStore]);
|
||||
|
||||
const { data: products, isPending: isPendingProducts } =
|
||||
useApiCall<SinglePayment.ResponseGet[]>(loadData);
|
||||
|
||||
const currentProduct = products?.find(
|
||||
(product) => product.key === "main.unique.individual.consultation"
|
||||
);
|
||||
|
||||
const handleClick = async () => {
|
||||
if (!userFromStore || !currentProduct) return;
|
||||
setIsLoading(true);
|
||||
const paymentIntent = await createSinglePayment(
|
||||
userFromStore,
|
||||
currentProduct.productId,
|
||||
tokenFromStore,
|
||||
userFromStore.email,
|
||||
userFromStore.profile.full_name,
|
||||
userFromStore.profile.birthday,
|
||||
returnUrl,
|
||||
api
|
||||
);
|
||||
setPaymentIntent(paymentIntent);
|
||||
setIsLoading(false);
|
||||
if ("payment" in paymentIntent) {
|
||||
if (paymentIntent.payment.status === "paid")
|
||||
return navigate(routes.client.getInformationPartner());
|
||||
return setIsError(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClickSkip = () => {
|
||||
navigate(routes.client.getInformationPartner());
|
||||
};
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{!isLoading &&
|
||||
paymentIntent &&
|
||||
"paymentIntent" in paymentIntent &&
|
||||
!!tokenFromStore.length && (
|
||||
<>
|
||||
<Modal
|
||||
open={!!paymentIntent}
|
||||
onClose={() => setPaymentIntent(null)}
|
||||
>
|
||||
<Title variant="h1" className={styles["modal-title"]}>
|
||||
{getPriceCentsToDollars(currentProduct?.amount || 0)}$
|
||||
</Title>
|
||||
<PaymentForm
|
||||
stripePublicKey={paymentIntent.paymentIntent.data.public_key}
|
||||
clientSecret={paymentIntent.paymentIntent.data.client_secret}
|
||||
returnUrl={returnUrl}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
<Title variant="h2" className={styles.title}>
|
||||
More for you
|
||||
</Title>
|
||||
@ -27,11 +106,22 @@ function AddConsultationPage() {
|
||||
This is a non-recuring payment.
|
||||
</p>
|
||||
<PaymentAddress />
|
||||
{isError && (
|
||||
<p className={styles.error}>
|
||||
Something went wrong. Please try again later.
|
||||
</p>
|
||||
)}
|
||||
<FooterButton
|
||||
onClick={handleNext}
|
||||
onClickSkip={handleNext}
|
||||
children="Get my consultation"
|
||||
/>
|
||||
onClick={handleClick}
|
||||
onClickSkip={handleClickSkip}
|
||||
disabled={isPendingProducts || isLoading}
|
||||
>
|
||||
{isPendingProducts || isLoading ? (
|
||||
<Loader color={LoaderColor.White} />
|
||||
) : (
|
||||
"Get my consultation"
|
||||
)}
|
||||
</FooterButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -36,3 +36,12 @@
|
||||
line-height: 140%;
|
||||
color: rgb(79, 79, 79);
|
||||
}
|
||||
|
||||
.error {
|
||||
max-width: 526px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: red;
|
||||
}
|
||||
|
||||
@ -3,22 +3,102 @@ import ThankYouBanner from "../../components/ThankYouBanner";
|
||||
import styles from "./styles.module.css";
|
||||
import { signUpOffers } from "@/data/additionalPurchases";
|
||||
import SignUpOffer from "../../components/SignUpOffer";
|
||||
import { useState } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import FooterButton from "../../components/FooterButton";
|
||||
import routes from "@/routes";
|
||||
import PaymentAddress from "../../components/PaymentAddress";
|
||||
import { createSinglePayment } from "@/services/singlePayment";
|
||||
import {
|
||||
ResponsePost,
|
||||
ResponsePostExistPaymentData,
|
||||
} from "@/api/resources/SinglePayment";
|
||||
import { useAuth } from "@/auth";
|
||||
import { useSelector } from "react-redux";
|
||||
import { selectors } from "@/store";
|
||||
import { SinglePayment, useApi, useApiCall } from "@/api";
|
||||
import Loader, { LoaderColor } from "@/components/Loader";
|
||||
import { getPriceCentsToDollars } from "@/services/price";
|
||||
import Modal from "@/components/Modal";
|
||||
import PaymentForm from "@/components/pages/PaymentWithEmailPage/PaymentForm";
|
||||
|
||||
function AddReportPage() {
|
||||
const navigate = useNavigate();
|
||||
const [activeOffer, setActiveOffer] = useState(signUpOffers[0].id);
|
||||
const { user: userFromStore } = useAuth();
|
||||
const api = useApi();
|
||||
const tokenFromStore = useSelector(selectors.selectToken);
|
||||
const [paymentIntent, setPaymentIntent] = useState<
|
||||
ResponsePost | ResponsePostExistPaymentData | null
|
||||
>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const [activeOffer, setActiveOffer] = useState(signUpOffers[0]);
|
||||
const returnUrl = `${window.location.protocol}//${
|
||||
window.location.host
|
||||
}${routes.client.unlimitedReadings()}`;
|
||||
|
||||
const handleClick = () => {
|
||||
const loadData = useCallback(async () => {
|
||||
return await api.getSinglePaymentProducts({ token: tokenFromStore });
|
||||
}, [api, tokenFromStore]);
|
||||
|
||||
const { data: products, isPending: isPendingProducts } =
|
||||
useApiCall<SinglePayment.ResponseGet[]>(loadData);
|
||||
|
||||
const getCurrentProduct = (id: string) => {
|
||||
return products?.find((product) => product.key === id) || null;
|
||||
};
|
||||
|
||||
const handleClick = async () => {
|
||||
if (!userFromStore || !activeOffer) return;
|
||||
const currentProduct = getCurrentProduct(activeOffer?.productKey);
|
||||
if (!currentProduct) return;
|
||||
setIsLoading(true);
|
||||
const paymentIntent = await createSinglePayment(
|
||||
userFromStore,
|
||||
currentProduct.productId,
|
||||
tokenFromStore,
|
||||
userFromStore.email,
|
||||
userFromStore.profile.full_name,
|
||||
userFromStore.profile.birthday,
|
||||
returnUrl,
|
||||
api
|
||||
);
|
||||
setPaymentIntent(paymentIntent);
|
||||
setIsLoading(false);
|
||||
if ("payment" in paymentIntent) {
|
||||
if (paymentIntent.payment.status === "paid")
|
||||
return navigate(routes.client.unlimitedReadings());
|
||||
return setIsError(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClickSkip = () => {
|
||||
navigate(routes.client.unlimitedReadings());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{!isLoading &&
|
||||
paymentIntent &&
|
||||
"paymentIntent" in paymentIntent &&
|
||||
!!tokenFromStore.length && (
|
||||
<>
|
||||
<Modal
|
||||
open={!!paymentIntent}
|
||||
onClose={() => setPaymentIntent(null)}
|
||||
>
|
||||
<Title variant="h1" className={styles["modal-title"]}>
|
||||
{getPriceCentsToDollars(activeOffer.price.current || 0)}$
|
||||
</Title>
|
||||
<PaymentForm
|
||||
stripePublicKey={paymentIntent.paymentIntent.data.public_key}
|
||||
clientSecret={paymentIntent.paymentIntent.data.client_secret}
|
||||
returnUrl={returnUrl}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
|
||||
<ThankYouBanner />
|
||||
<Title variant="h2" className={styles.title}>
|
||||
Choose your sign-up offer 🔥
|
||||
@ -27,26 +107,33 @@ function AddReportPage() {
|
||||
Available only now
|
||||
</Title>
|
||||
<div className={styles["offers-container"]}>
|
||||
{signUpOffers.map((offer, index) => (
|
||||
<SignUpOffer
|
||||
key={index}
|
||||
id={offer.id}
|
||||
title={offer.title}
|
||||
subtitle={offer.subtitle}
|
||||
price={offer.price}
|
||||
emoji={offer.emoji}
|
||||
isActive={offer.id === activeOffer}
|
||||
onClick={() => setActiveOffer(offer.id)}
|
||||
/>
|
||||
))}
|
||||
{!isPendingProducts &&
|
||||
signUpOffers.map((offer, index) => (
|
||||
<SignUpOffer
|
||||
key={index}
|
||||
isActive={offer.id === activeOffer.id}
|
||||
onClick={() => setActiveOffer(offer)}
|
||||
{...offer}
|
||||
/>
|
||||
))}
|
||||
{isPendingProducts && <Loader color={LoaderColor.Black} />}
|
||||
</div>
|
||||
<p className={styles.description}>
|
||||
*You will be charged for the add-on services or offers selected at the
|
||||
time of purchase. This is a non-recuring payment.
|
||||
</p>
|
||||
{isError && (
|
||||
<p className={`${styles.description} ${styles.error}`}>
|
||||
Something went wrong. Please try again later.
|
||||
</p>
|
||||
)}
|
||||
<PaymentAddress />
|
||||
<FooterButton onClick={handleClick} onClickSkip={handleClick}>
|
||||
Get my copy
|
||||
<FooterButton
|
||||
onClick={handleClick}
|
||||
onClickSkip={handleClickSkip}
|
||||
disabled={isPendingProducts || isLoading}
|
||||
>
|
||||
{isLoading ? <Loader color={LoaderColor.White} /> : "Get my copy"}
|
||||
</FooterButton>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -34,6 +34,10 @@
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
@ -56,3 +60,7 @@
|
||||
margin-bottom: 20px;
|
||||
color: rgb(79, 79, 79);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
@ -9,26 +9,105 @@ import SliderNextArrow from "../../components/SliderNextArrow";
|
||||
import SliderPrevArrow from "../../components/SliderPrevArrow";
|
||||
import routes from "@/routes";
|
||||
import PaymentAddress from "../../components/PaymentAddress";
|
||||
import { useAuth } from "@/auth";
|
||||
import { SinglePayment, useApi, useApiCall } from "@/api";
|
||||
import { useSelector } from "react-redux";
|
||||
import { selectors } from "@/store";
|
||||
import { useCallback, useState } from "react";
|
||||
import { createSinglePayment } from "@/services/singlePayment";
|
||||
import Loader, { LoaderColor } from "@/components/Loader";
|
||||
import {
|
||||
ResponsePost,
|
||||
ResponsePostExistPaymentData,
|
||||
} from "@/api/resources/SinglePayment";
|
||||
import Modal from "@/components/Modal";
|
||||
import { getPriceCentsToDollars } from "@/services/price";
|
||||
import PaymentForm from "@/components/pages/PaymentWithEmailPage/PaymentForm";
|
||||
|
||||
const sliderSettings = {
|
||||
dots: false,
|
||||
infinite: false,
|
||||
speed: 500,
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
className: styles["slider-container"],
|
||||
nextArrow: <SliderNextArrow />,
|
||||
prevArrow: <SliderPrevArrow />,
|
||||
};
|
||||
|
||||
function UnlimitedReadingsPage() {
|
||||
const navigate = useNavigate();
|
||||
const handleClick = () => {
|
||||
navigate(routes.client.addConsultation());
|
||||
const { user: userFromStore } = useAuth();
|
||||
const api = useApi();
|
||||
const tokenFromStore = useSelector(selectors.selectToken);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [paymentIntent, setPaymentIntent] = useState<
|
||||
ResponsePost | ResponsePostExistPaymentData | null
|
||||
>(null);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const returnUrl = `${window.location.protocol}//${
|
||||
window.location.host
|
||||
}${routes.client.addConsultation()}`;
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
return await api.getSinglePaymentProducts({ token: tokenFromStore });
|
||||
}, [api, tokenFromStore]);
|
||||
|
||||
const { data: products, isPending: isPendingProducts } =
|
||||
useApiCall<SinglePayment.ResponseGet[]>(loadData);
|
||||
|
||||
const currentProduct = products?.find(
|
||||
(product) => product.key === "main.unlimited.readings"
|
||||
);
|
||||
|
||||
const handleClick = async () => {
|
||||
if (!userFromStore || !currentProduct) return;
|
||||
setIsLoading(true);
|
||||
const paymentIntent = await createSinglePayment(
|
||||
userFromStore,
|
||||
currentProduct.productId,
|
||||
tokenFromStore,
|
||||
userFromStore.email,
|
||||
userFromStore.profile.full_name,
|
||||
userFromStore.profile.birthday,
|
||||
returnUrl,
|
||||
api
|
||||
);
|
||||
setPaymentIntent(paymentIntent);
|
||||
setIsLoading(false);
|
||||
if ("payment" in paymentIntent) {
|
||||
if (paymentIntent.payment.status === "paid")
|
||||
return navigate(routes.client.addConsultation());
|
||||
return setIsError(true);
|
||||
}
|
||||
};
|
||||
|
||||
const sliderSettings = {
|
||||
dots: false,
|
||||
infinite: false,
|
||||
speed: 500,
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
className: styles["slider-container"],
|
||||
nextArrow: <SliderNextArrow />,
|
||||
prevArrow: <SliderPrevArrow />,
|
||||
const handleClickSkip = () => {
|
||||
navigate(routes.client.addConsultation());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{!isLoading &&
|
||||
paymentIntent &&
|
||||
"paymentIntent" in paymentIntent &&
|
||||
!!tokenFromStore.length && (
|
||||
<>
|
||||
<Modal
|
||||
open={!!paymentIntent}
|
||||
onClose={() => setPaymentIntent(null)}
|
||||
>
|
||||
<Title variant="h1" className={styles["modal-title"]}>
|
||||
{getPriceCentsToDollars(currentProduct?.amount || 0)}$
|
||||
</Title>
|
||||
<PaymentForm
|
||||
stripePublicKey={paymentIntent.paymentIntent.data.public_key}
|
||||
clientSecret={paymentIntent.paymentIntent.data.client_secret}
|
||||
returnUrl={returnUrl}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
<Slider {...sliderSettings}>
|
||||
<FirstSlide />
|
||||
<div className={styles.slider}>
|
||||
@ -54,14 +133,24 @@ function UnlimitedReadingsPage() {
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{isError && (
|
||||
<p className={styles.error}>
|
||||
Something went wrong. Please try again later.
|
||||
</p>
|
||||
)}
|
||||
<FooterButton
|
||||
onClick={handleClick}
|
||||
onClickSkip={handleClick}
|
||||
onClickSkip={handleClickSkip}
|
||||
classNameContainer={styles["buttons-container"]}
|
||||
classNameButton={styles.button}
|
||||
classNameSkip={styles.skip}
|
||||
disabled={isPendingProducts || isLoading}
|
||||
>
|
||||
Add unlimited readings
|
||||
{isPendingProducts || isLoading ? (
|
||||
<Loader color={LoaderColor.White} />
|
||||
) : (
|
||||
"Add unlimited readings"
|
||||
)}
|
||||
</FooterButton>
|
||||
<p className={styles.policy}>
|
||||
Please note: In addition to your subscription, your account will be
|
||||
|
||||
@ -82,3 +82,12 @@
|
||||
height: 189px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.error {
|
||||
max-width: 526px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: red;
|
||||
}
|
||||
|
||||
56
src/components/pages/GetInformationPartner/index.tsx
Normal file
56
src/components/pages/GetInformationPartner/index.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import Title from "@/components/Title";
|
||||
import styles from "./styles.module.css";
|
||||
import MainButton from "@/components/MainButton";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
|
||||
function GetInformationPartnerPage() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleBack = () => {
|
||||
navigate(routes.client.addConsultation());
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
navigate(routes.client.home());
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
<video className={styles["background-video"]} loop autoPlay muted>
|
||||
<source src="/videos/background-video-1.mp4" type="video/mp4" />
|
||||
<source src="/videos/background-video-1.mp4" type="video/ogg" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<Title variant="h2" className={styles.title}>
|
||||
AURA
|
||||
</Title>
|
||||
<div className={styles["bottom-container"]}>
|
||||
<Title variant="h3" className={styles.subtitle}>
|
||||
Wonderful! Let's find out what's working (and what isn't) and go from
|
||||
there.
|
||||
</Title>
|
||||
<p className={styles.description}>
|
||||
Now we need some information about Your Partner's Profile to create
|
||||
the astrological synastry blueprint between you and your partner.
|
||||
</p>
|
||||
<div className={styles["buttons-container"]}>
|
||||
<MainButton
|
||||
onClick={handleBack}
|
||||
className={`${styles.button} ${styles["back-button"]}`}
|
||||
>
|
||||
Back
|
||||
</MainButton>
|
||||
<MainButton
|
||||
onClick={handleNext}
|
||||
className={`${styles.button} ${styles["next-button"]}`}
|
||||
>
|
||||
Next
|
||||
</MainButton>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default GetInformationPartnerPage;
|
||||
64
src/components/pages/GetInformationPartner/styles.module.css
Normal file
64
src/components/pages/GetInformationPartner/styles.module.css
Normal file
@ -0,0 +1,64 @@
|
||||
.page {
|
||||
background-color: #171717;
|
||||
height: fit-content;
|
||||
min-height: 100dvh;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 58px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.buttons-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
.button {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
width: 160px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
background-color: transparent;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
.next-button {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.bottom-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
margin-bottom: 170px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.description {
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.background-video {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
float: left;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
@ -12,7 +12,6 @@ import { getClientTimezone } from "@/locales";
|
||||
import ErrorText from "@/components/ErrorText";
|
||||
import Title from "@/components/Title";
|
||||
import NameInput from "@/components/EmailEnterPage/NameInput";
|
||||
import { getZodiacSignByDate } from "@/services/zodiac-sign";
|
||||
import {
|
||||
ResponseGet,
|
||||
ResponsePost,
|
||||
@ -22,7 +21,7 @@ import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import PaymentForm from "./PaymentForm";
|
||||
import { getPriceCentsToDollars } from "@/services/price";
|
||||
import { User } from "@/api/resources/User";
|
||||
import { createSinglePayment } from "@/services/singlePayment";
|
||||
|
||||
function PaymentWithEmailPage() {
|
||||
const { t, i18n } = useTranslation();
|
||||
@ -125,36 +124,6 @@ function PaymentWithEmailPage() {
|
||||
return currentProduct;
|
||||
};
|
||||
|
||||
const createSinglePayment = async (
|
||||
user: User,
|
||||
productId: string,
|
||||
token: string,
|
||||
email: string,
|
||||
name: string | null
|
||||
) => {
|
||||
return await api.createSinglePayment({
|
||||
token,
|
||||
data: {
|
||||
user: {
|
||||
id: `${user?.id}`,
|
||||
email,
|
||||
name: name || "",
|
||||
sign: user?.profile?.sign?.sign || getZodiacSignByDate(birthday),
|
||||
age: user?.profile?.age?.years || 1,
|
||||
gender: gender,
|
||||
},
|
||||
partner: {
|
||||
sign: "partner_cancer",
|
||||
age: 26,
|
||||
},
|
||||
paymentInfo: {
|
||||
productId,
|
||||
},
|
||||
return_url: returnUrl,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleClick = async () => {
|
||||
const authData = await authorization();
|
||||
if (!authData) {
|
||||
@ -175,7 +144,11 @@ function PaymentWithEmailPage() {
|
||||
productId,
|
||||
token,
|
||||
email,
|
||||
name
|
||||
name,
|
||||
birthday,
|
||||
returnUrl,
|
||||
api,
|
||||
gender
|
||||
);
|
||||
setPaymentIntent(paymentIntent);
|
||||
setIsLoading(false);
|
||||
@ -204,7 +177,11 @@ function PaymentWithEmailPage() {
|
||||
productId,
|
||||
tokenFromStore,
|
||||
userFromStore.email,
|
||||
userFromStore.profile.full_name
|
||||
userFromStore.profile.full_name,
|
||||
userFromStore.profile.birthday,
|
||||
returnUrl,
|
||||
api,
|
||||
gender
|
||||
);
|
||||
setPaymentIntent(paymentIntent);
|
||||
setIsLoadingPage(false);
|
||||
|
||||
@ -37,6 +37,7 @@ export interface ISignUpOffer {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
productKey: string;
|
||||
price: ISignUpOfferPrice;
|
||||
emoji: string;
|
||||
}
|
||||
@ -47,40 +48,44 @@ export const signUpOffers: ISignUpOffer[] = [
|
||||
title: "ULTRA PACK",
|
||||
subtitle: "(3 in 1 + 2 secret bonus reading)",
|
||||
price: {
|
||||
current: 49.99,
|
||||
old: 99.99,
|
||||
current: 4999,
|
||||
old: 9999,
|
||||
discount: 50,
|
||||
},
|
||||
productKey: "main.ultra.pack",
|
||||
emoji: "star_struck.png",
|
||||
},
|
||||
{
|
||||
id: "numerology-analyses",
|
||||
title: "NUMEROLOGY ANALYSIS",
|
||||
price: {
|
||||
current: 14.99,
|
||||
old: 29.99,
|
||||
current: 1499,
|
||||
old: 2999,
|
||||
discount: 50,
|
||||
},
|
||||
productKey: "main.numerology.analysis",
|
||||
emoji: "input_numbers.png",
|
||||
},
|
||||
{
|
||||
id: "tarot-reading",
|
||||
title: "TAROT READING",
|
||||
price: {
|
||||
current: 19.99,
|
||||
old: 34.99,
|
||||
current: 1999,
|
||||
old: 3499,
|
||||
discount: 45,
|
||||
},
|
||||
productKey: "main.tarot.reading",
|
||||
emoji: "sunset.png",
|
||||
},
|
||||
{
|
||||
id: "palmistry-guide",
|
||||
title: "PALMISTRY GUIDE",
|
||||
price: {
|
||||
current: 19.99,
|
||||
old: 29.99,
|
||||
current: 1999,
|
||||
old: 2999,
|
||||
discount: 30,
|
||||
},
|
||||
productKey: "main.palmistry.guide",
|
||||
emoji: "rised_hand.png",
|
||||
},
|
||||
];
|
||||
|
||||
@ -4,7 +4,10 @@ const isProduction = import.meta.env.MODE === "production";
|
||||
|
||||
const host = "";
|
||||
export const apiHost = "https://api-web.aura.wit.life";
|
||||
const dApiHost = isProduction ? "https://d.api.witapps.us" : "https://dev.api.witapps.us"
|
||||
const dApiHost = isProduction
|
||||
? "https://d.api.witapps.us"
|
||||
: "https://dev.api.witapps.us";
|
||||
// const dApiHost = "https://d.api.witapps.us";
|
||||
const siteHost = "https://aura.wit.life";
|
||||
const prefix = "api/v1";
|
||||
const openAIHost = " https://api.openai.com";
|
||||
@ -38,7 +41,8 @@ const routes = {
|
||||
palmistryPaywall: () => [host, "palmistry", "paywall"].join("/"),
|
||||
palmistryPayment: () => [host, "palmistry", "payment"].join("/"),
|
||||
palmistryDiscount: () => [host, "palmistry", "discount"].join("/"),
|
||||
palmistryPremiumBundle: () => [host, "palmistry", "premium-bundle"].join("/"),
|
||||
palmistryPremiumBundle: () =>
|
||||
[host, "palmistry", "premium-bundle"].join("/"),
|
||||
birthday: () => [host, "birthday"].join("/"),
|
||||
didYouKnow: () => [host, "did-you-know"].join("/"),
|
||||
freePeriodInfo: () => [host, "free-period"].join("/"),
|
||||
@ -128,6 +132,8 @@ const routes = {
|
||||
epeSuccessPayment: () => [host, "epe", "success-payment"].join("/"),
|
||||
epeFailPayment: () => [host, "epe", "fail-payment"].join("/"),
|
||||
|
||||
getInformationPartner: () => [host, "get-information-partner"].join("/"),
|
||||
|
||||
notFound: () => [host, "404"].join("/"),
|
||||
},
|
||||
server: {
|
||||
@ -311,6 +317,7 @@ export const withoutFooterRoutes = [
|
||||
routes.client.addConsultation(),
|
||||
routes.client.advisors(),
|
||||
routes.client.epeSuccessPayment(),
|
||||
routes.client.getInformationPartner(),
|
||||
];
|
||||
|
||||
export const withoutFooterPartOfRoutes = [
|
||||
@ -385,6 +392,7 @@ export const withoutHeaderRoutes = [
|
||||
routes.client.tryApp(),
|
||||
routes.client.advisors(),
|
||||
routes.client.epeSuccessPayment(),
|
||||
routes.client.getInformationPartner(),
|
||||
];
|
||||
export const hasNoHeader = (path: string) => {
|
||||
let result = true;
|
||||
|
||||
37
src/services/singlePayment/index.ts
Normal file
37
src/services/singlePayment/index.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { User } from "@/api/resources/User";
|
||||
import { getZodiacSignByDate } from "../zodiac-sign";
|
||||
import { ApiContextValue } from "@/api";
|
||||
|
||||
export const createSinglePayment = async (
|
||||
user: User,
|
||||
productId: string,
|
||||
token: string,
|
||||
email: string,
|
||||
name: string | null,
|
||||
birthday: string | null,
|
||||
returnUrl: string,
|
||||
api: ApiContextValue,
|
||||
gender?: string
|
||||
) => {
|
||||
return await api.createSinglePayment({
|
||||
token,
|
||||
data: {
|
||||
user: {
|
||||
id: `${user?.id}`,
|
||||
email,
|
||||
name: name || "",
|
||||
sign: user?.profile?.sign?.sign || getZodiacSignByDate(birthday || ""),
|
||||
age: user?.profile?.age?.years || 1,
|
||||
gender: gender || "",
|
||||
},
|
||||
partner: {
|
||||
sign: "partner_cancer",
|
||||
age: 26,
|
||||
},
|
||||
paymentInfo: {
|
||||
productId,
|
||||
},
|
||||
return_url: returnUrl,
|
||||
},
|
||||
});
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user