develop
This commit is contained in:
parent
5c13b4727c
commit
c9eacda411
BIN
public/v1/email-marketing/circular-text.png
Normal file
BIN
public/v1/email-marketing/circular-text.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
@ -41,6 +41,7 @@ const api = {
|
||||
// getElement: createMethod<Element.Payload, Element.Response>(Element.createRequest),
|
||||
getElements: createMethod<Elements.Payload, Elements.Response>(Elements.createRequest),
|
||||
getUser: createMethod<User.GetPayload, User.Response>(User.createGetRequest),
|
||||
getMe: createMethod<User.GetPayload, User.IMeResponse>(User.createMeRequest),
|
||||
updateUser: createMethod<User.PatchPayload, User.Response>(User.createPatchRequest),
|
||||
getAssets: createMethod<Assets.Payload, Assets.Response>(Assets.createRequest),
|
||||
getAssetCategories: createMethod<AssetCategories.Payload, AssetCategories.Response>(AssetCategories.createRequest),
|
||||
@ -83,6 +84,7 @@ const api = {
|
||||
// Payment
|
||||
makePayment: createMethod<Payment.PayloadPost, Payment.ResponsePost>(Payment.createRequestPost),
|
||||
getPaymentConfig: createMethod<null, Payment.IPaymentConfigResponse>(Payment.getConfigRequest),
|
||||
getPaymentMethods: createMethod<Payment.Payload, Payment.IPaymentMethodsResponse>(Payment.getMethodsRequest),
|
||||
// User videos
|
||||
getUserVideos: createMethod<UserVideos.PayloadGet, UserVideos.ResponseGet>(UserVideos.createRequest),
|
||||
// User PDF
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import routes from "@/routes";
|
||||
import { getAuthHeaders, getBaseHeaders } from "../utils";
|
||||
|
||||
interface Payload {
|
||||
export interface Payload {
|
||||
token: string;
|
||||
}
|
||||
|
||||
@ -33,12 +33,19 @@ interface ResponsePostSuccess {
|
||||
}
|
||||
}
|
||||
|
||||
interface ResponsePostSinglePaymentSuccess {
|
||||
payment: {
|
||||
status: string;
|
||||
invoiceId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ResponsePostError {
|
||||
status: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type ResponsePost = ResponsePostSuccess | ResponsePostError;
|
||||
export type ResponsePost = ResponsePostSuccess | ResponsePostSinglePaymentSuccess | ResponsePostError;
|
||||
|
||||
export const createRequestPost = ({ token, productId, placementId, paywallId, paymentToken }: PayloadPost): Request => {
|
||||
const url = new URL(routes.server.makePayment());
|
||||
@ -64,3 +71,13 @@ export const getConfigRequest = (): Request => {
|
||||
const url = new URL(routes.server.getPaymentConfig());
|
||||
return new Request(url, { method: "GET", headers: getBaseHeaders() });
|
||||
};
|
||||
|
||||
export interface IPaymentMethodsResponse {
|
||||
status: "success" | "error",
|
||||
message: string,
|
||||
}
|
||||
|
||||
export const getMethodsRequest = ({ token }: Payload): Request => {
|
||||
const url = new URL(routes.server.getPaymentMethods());
|
||||
return new Request(url, { method: "GET", headers: getAuthHeaders(token) });
|
||||
};
|
||||
|
||||
@ -194,3 +194,57 @@ export const createAuthorizeRequest = (data: ICreateAuthorizePayload): Request =
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
export interface IMeResponse {
|
||||
user: IUser;
|
||||
}
|
||||
|
||||
export interface IUser {
|
||||
ipLookup?: {
|
||||
country: string;
|
||||
region: string;
|
||||
eu: string;
|
||||
timezone: string;
|
||||
city: string;
|
||||
},
|
||||
profile: {
|
||||
birthplace: {
|
||||
address: string;
|
||||
},
|
||||
name: string;
|
||||
birthdate: string;
|
||||
gender: string;
|
||||
age: number;
|
||||
sign: string;
|
||||
},
|
||||
partner?: {
|
||||
birthplace: {
|
||||
address: string;
|
||||
},
|
||||
birthdate: string;
|
||||
gender: string;
|
||||
age: number;
|
||||
sign: string;
|
||||
},
|
||||
_id: string;
|
||||
initialIp?: string;
|
||||
sessionId?: string;
|
||||
email: string;
|
||||
locale: string;
|
||||
timezone: string;
|
||||
source: string;
|
||||
sign: boolean;
|
||||
signDate: string;
|
||||
password: string;
|
||||
externalId: string;
|
||||
klaviyoId: string;
|
||||
stripeId: string | null;
|
||||
assistants: string[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export const createMeRequest = ({ token }: GetPayload): Request => {
|
||||
const url = new URL(routes.server.me());
|
||||
return new Request(url, { method: "GET", headers: getAuthHeaders(token) });
|
||||
}
|
||||
|
||||
@ -103,7 +103,6 @@ import AdditionalDiscount from "../pages/AdditionalDiscount";
|
||||
import TrialPaymentWithDiscount from "../pages/TrialPaymentWithDiscount";
|
||||
import MarketingLanding from "../pages/EmailLetters/MarketingLanding";
|
||||
import MarketingTrialPayment from "../pages/EmailLetters/MarketingTrialPayment";
|
||||
import { ScrollToTop } from "@/hooks/scrollToTop";
|
||||
import { EUserDeviceType } from "@/store/userConfig";
|
||||
import TryAppPage from "../pages/TryApp";
|
||||
import AdditionalPurchases from "../pages/AdditionalPurchases";
|
||||
@ -135,6 +134,7 @@ import ChatsRoutes from "@/routerComponents/Chats";
|
||||
import CookieYesController from "@/routerComponents/CookieYesController";
|
||||
import PalmistryV2Routes from "@/routerComponents/Palmistry/v2";
|
||||
import MarketingLandingV1Routes from "@/routerComponents/MarketingLanding/v1";
|
||||
import { useScrollToTop } from "@/hooks/useScrollToTop";
|
||||
|
||||
const isProduction = import.meta.env.MODE === "production";
|
||||
|
||||
@ -145,6 +145,7 @@ if (isProduction) {
|
||||
function App(): JSX.Element {
|
||||
const location = useLocation();
|
||||
const [leoApng, setLeoApng] = useState<Error | APNG>(Error);
|
||||
useScrollToTop({ scrollBehavior: "auto" });
|
||||
// const [
|
||||
// padLockApng,
|
||||
// setPadLockApng,
|
||||
@ -240,7 +241,22 @@ function App(): JSX.Element {
|
||||
try {
|
||||
const { token } = await api.getRealToken({ token: jwtToken });
|
||||
const { user } = await api.getUser({ token });
|
||||
const { user: userMe } = await api.getMe({ token });
|
||||
signUp(token, user);
|
||||
|
||||
dispatch(actions.questionnaire.update({
|
||||
gender: userMe.profile.gender ?? undefined,
|
||||
birthPlace: userMe.profile.birthplace?.address ?? undefined,
|
||||
birthdate: userMe.profile.birthdate ?? undefined,
|
||||
partnerBirthPlace: userMe.partner?.birthplace?.address ?? undefined,
|
||||
partnerBirthdate: userMe.partner?.birthdate ?? undefined,
|
||||
partnerGender: userMe.partner?.gender ?? undefined,
|
||||
}))
|
||||
|
||||
dispatch(actions.user.update({
|
||||
username: userMe.profile.name ?? undefined,
|
||||
}));
|
||||
|
||||
} catch (error) {
|
||||
console.log("Error of get real token or get user: ");
|
||||
console.error(error);
|
||||
@ -1053,14 +1069,13 @@ function Layout(): JSX.Element {
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<ScrollToTop />
|
||||
{showHeader ? (
|
||||
<Header
|
||||
openMenu={() => setIsMenuOpen(true)}
|
||||
/>
|
||||
) : null}
|
||||
{isRouteFullDataModal && (
|
||||
<Modal open={isShowFullDataModal} isCloseButtonVisible={false}>
|
||||
<Modal open={isShowFullDataModal} isCloseButtonVisible={false} onClose={() => { }}>
|
||||
<FullDataModal onClose={onCloseFullDataModal} />
|
||||
</Modal>
|
||||
)}
|
||||
@ -1250,7 +1265,7 @@ export function PrivateOutlet(): JSX.Element {
|
||||
function PrivateSubscriptionOutlet(): JSX.Element {
|
||||
const isProduction = import.meta.env.MODE === "production";
|
||||
const status = useSelector(selectors.selectStatus);
|
||||
return status === "subscribed" || !isProduction || true ? (
|
||||
return status === "subscribed" || !isProduction ? (
|
||||
<Outlet />
|
||||
) : (
|
||||
<Navigate to={getRouteBy(status)} replace={true} />
|
||||
|
||||
@ -19,19 +19,20 @@ import { Products, useApi, useApiCall } from "@/api";
|
||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||
import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall";
|
||||
import { useAuth } from "@/auth";
|
||||
import { ResponsePost } from "@/api/resources/SinglePayment";
|
||||
import { createSinglePayment } from "@/services/singlePayment";
|
||||
import Modal from "@/components/Modal";
|
||||
import Title from "@/components/Title";
|
||||
import PaymentForm from "@/components/pages/SinglePaymentPage/PaymentForm";
|
||||
// import PaymentForm from "@/components/pages/SinglePaymentPage/PaymentForm";
|
||||
import { getPriceCentsToDollars } from "@/services/price";
|
||||
import { IMessage } from "@/api/resources/ChatMessages";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import PaymentForm from "@/components/Payment/nmi/PaymentForm";
|
||||
|
||||
const returnUrl = `${window.location.protocol}//${
|
||||
window.location.host
|
||||
}${routes.client.chatsExpert()}`;
|
||||
const returnUrl = `${window.location.protocol}//${window.location.host
|
||||
}${routes.client.chatsExpert()}`;
|
||||
|
||||
const placementKey = EPlacementKeys["aura.placement.chat"];
|
||||
|
||||
function ExpertChat() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.Chats);
|
||||
@ -66,12 +67,16 @@ function ExpertChat() {
|
||||
// Payment
|
||||
const { user: userFromStore } = useAuth();
|
||||
const tokenFromStore = useSelector(selectors.selectToken);
|
||||
const [paymentIntent, setPaymentIntent] = useState<ResponsePost | null>(null);
|
||||
const [isLoadingPayment, setIsLoadingPayment] = useState(false);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const [currentProduct, setCurrentProduct] = useState<IPaywallProduct | null>(
|
||||
null
|
||||
);
|
||||
// const [currentProduct, setCurrentProduct] = useState<IPaywallProduct | null>(
|
||||
// null
|
||||
// );
|
||||
const currentProduct = useSelector(selectors.selectActiveProduct);
|
||||
const setCurrentProduct = (product: IPaywallProduct) => {
|
||||
dispatch(actions.payment.update({ activeProduct: product }));
|
||||
};
|
||||
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
|
||||
|
||||
const isPayedFirstPurchase = useSelector(
|
||||
selectors.selectIsPayedFirstPurchase
|
||||
@ -101,7 +106,7 @@ function ExpertChat() {
|
||||
>(checkIsPayedFirstPurchase);
|
||||
|
||||
const { products } = usePaywall({
|
||||
placementKey: EPlacementKeys["aura.placement.chat"],
|
||||
placementKey,
|
||||
});
|
||||
|
||||
const scrollToBottom = () => {
|
||||
@ -182,6 +187,13 @@ function ExpertChat() {
|
||||
if (!currentProduct) return;
|
||||
setCurrentProduct(currentProduct);
|
||||
setIsLoadingPayment(true);
|
||||
const isPaymentMethodExist = await api.getPaymentMethods({ token: tokenFromStore });
|
||||
if (isPaymentMethodExist.status === "error") {
|
||||
return setIsPaymentModalOpen(true);
|
||||
}
|
||||
// if (!isPayedFirstPurchase) {
|
||||
// return setIsPaymentModalOpen(true);
|
||||
// }
|
||||
const { _id, key } = currentProduct;
|
||||
const paymentInfo = {
|
||||
productId: _id,
|
||||
@ -197,7 +209,7 @@ function ExpertChat() {
|
||||
returnUrl,
|
||||
api
|
||||
);
|
||||
setPaymentIntent(paymentIntent);
|
||||
// setPaymentIntent(paymentIntent);
|
||||
if ("payment" in paymentIntent) {
|
||||
if (paymentIntent.payment.status === "paid") return closeModals();
|
||||
return setIsError(true);
|
||||
@ -249,27 +261,41 @@ function ExpertChat() {
|
||||
);
|
||||
};
|
||||
|
||||
const onPaymentError = () => {
|
||||
setIsPaymentModalOpen(false);
|
||||
return setIsError(true);
|
||||
}
|
||||
|
||||
const onPaymentSuccess = () => {
|
||||
setIsPaymentModalOpen(false);
|
||||
setIsLoadingPayment(false);
|
||||
return closeModals();
|
||||
}
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
{!isLoading &&
|
||||
paymentIntent &&
|
||||
"paymentIntent" in paymentIntent &&
|
||||
!!tokenFromStore.length &&
|
||||
currentProduct && (
|
||||
<>
|
||||
<Modal
|
||||
open={!!paymentIntent}
|
||||
onClose={() => setPaymentIntent(null)}
|
||||
open={isPaymentModalOpen}
|
||||
onClose={() => setIsPaymentModalOpen(false)}
|
||||
containerClassName={styles.modal}
|
||||
>
|
||||
<Title variant="h1" className={styles["modal-title"]}>
|
||||
{getPriceCentsToDollars(currentProduct.price || 0)}$
|
||||
</Title>
|
||||
<PaymentForm
|
||||
{/* <PaymentForm
|
||||
isLoadingPayment={isLoadingPayment}
|
||||
stripePublicKey={paymentIntent.paymentIntent.data.public_key}
|
||||
clientSecret={paymentIntent.paymentIntent.data.client_secret}
|
||||
returnUrl={returnUrl}
|
||||
/> */}
|
||||
<PaymentForm
|
||||
placementKey={placementKey}
|
||||
onPaymentError={onPaymentError}
|
||||
onPaymentSuccess={onPaymentSuccess}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { images } from '../../data';
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface CustomerCounterProps {
|
||||
@ -7,7 +8,7 @@ interface CustomerCounterProps {
|
||||
function CustomerCounter({ count }: CustomerCounterProps) {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.circularText}>
|
||||
{/* <div className={styles.circularText}>
|
||||
<svg viewBox="0 0 100 100">
|
||||
<path
|
||||
id="curve"
|
||||
@ -25,7 +26,8 @@ function CustomerCounter({ count }: CustomerCounterProps) {
|
||||
</textPath>
|
||||
</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div> */}
|
||||
<img className={styles.circularText} src={images("circular-text.png")} alt="" />
|
||||
<div className={styles.count}>{count}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
position: relative;
|
||||
width: 263px;
|
||||
height: 263px;
|
||||
background: #7A6BE2;
|
||||
// background: #7A6BE2;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -16,8 +16,8 @@
|
||||
|
||||
.circularText {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: calc(100% + 24px);
|
||||
height: calc(100% + 24px);
|
||||
animation: rotate 20s linear infinite;
|
||||
-webkit-transform: translateZ(0);
|
||||
-webkit-perspective: 1000;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ReactNode, useEffect } from "react";
|
||||
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
interface ModalProps {
|
||||
@ -8,7 +8,7 @@ interface ModalProps {
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
type?: "hidden" | "normal";
|
||||
onClose?: () => void;
|
||||
onClose: () => void;
|
||||
removeNoScroll?: boolean;
|
||||
}
|
||||
|
||||
@ -22,6 +22,8 @@ function Modal({
|
||||
onClose,
|
||||
removeNoScroll = true
|
||||
}: ModalProps): JSX.Element {
|
||||
const modalContentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleClose = (event: React.MouseEvent) => {
|
||||
if (event.target !== event.currentTarget) return;
|
||||
document.body.classList.remove("no-scroll");
|
||||
@ -42,18 +44,53 @@ function Modal({
|
||||
};
|
||||
}, [open, removeNoScroll]);
|
||||
|
||||
const [position, setPosition] = useState({ top: 0, left: 0 });
|
||||
|
||||
const getModalContentPosition = useCallback(() => {
|
||||
const modalContent = modalContentRef.current;
|
||||
if (!modalContent) return {
|
||||
top: 0,
|
||||
left: 0
|
||||
};
|
||||
const { top, left } = modalContent.getBoundingClientRect();
|
||||
return { top, left };
|
||||
}, [modalContentRef]);
|
||||
|
||||
useEffect(() => {
|
||||
const updatePosition = () => {
|
||||
requestAnimationFrame(() => {
|
||||
setPosition(getModalContentPosition());
|
||||
});
|
||||
};
|
||||
|
||||
if (open) {
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
window.addEventListener('resize', updatePosition);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', updatePosition);
|
||||
};
|
||||
}, [getModalContentPosition, open]);
|
||||
|
||||
if (!open && type === "normal") return <></>;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${styles.modal} ${className} ${
|
||||
type === "hidden" && !open ? styles.hidden : ""
|
||||
}`}
|
||||
className={`${styles.modal} ${className} ${type === "hidden" && !open ? styles.hidden : ""
|
||||
}`}
|
||||
onClick={handleClose}
|
||||
>
|
||||
<div className={`${styles["modal-content"]} ${containerClassName}`}>
|
||||
{isCloseButtonVisible && (
|
||||
<button className={styles["modal-close-btn"]} onClick={handleClose} />
|
||||
)}
|
||||
{isCloseButtonVisible && (
|
||||
<button className={styles["modal-close-btn"]} onClick={handleClose}
|
||||
style={{
|
||||
top: `${position.top + 16}px`,
|
||||
left: `${position.left + 15}px`
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className={`${styles["modal-content"]} ${containerClassName}`} ref={modalContentRef}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -53,6 +53,7 @@
|
||||
background-position: center;
|
||||
background-color: transparent;
|
||||
background-image: url(./close.svg);
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.modal .main-btn {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
.footer {
|
||||
width: 100%;
|
||||
margin-top: 30px;
|
||||
margin: 30px 0 62px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
@ -100,7 +100,7 @@ function PaymentForm({
|
||||
)}
|
||||
>
|
||||
|
||||
<Modal containerClassName={styles["modal-content"]} open={isPaymentModalOpen}>
|
||||
<Modal containerClassName={styles["modal-content"]} open={isPaymentModalOpen} onClose={() => setIsPaymentModalOpen(false)}>
|
||||
<NMIPaymentForm onPaymentError={onPaymentError} onPaymentSuccess={onPaymentSuccess} placementKey={EPlacementKeys['aura.placement.palmistry.redesign']} />
|
||||
</Modal>
|
||||
|
||||
|
||||
@ -14,9 +14,11 @@ import { useNavigate } from "react-router-dom";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { usePreloadImages } from "@/hooks/preload/images";
|
||||
import useTimer from "@/hooks/palmistry/use-timer";
|
||||
|
||||
function TrialPayment() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||
const time = useTimer();
|
||||
const navigate = useNavigate();
|
||||
usePreloadImages([
|
||||
"/v1/palmistry/ticket.svg",
|
||||
@ -66,6 +68,24 @@ function TrialPayment() {
|
||||
</Title>
|
||||
<img className={styles.partners} src={`${palmistryV1Prefix}/partners.png`} alt="Partners" />
|
||||
<Footer />
|
||||
|
||||
|
||||
<div className={styles["paywall__get-prediction"]}>
|
||||
<div>
|
||||
{translate("/paywall.offer_reserved.title", undefined, ELocalesPlacement.PalmistryV0)}
|
||||
<span className={styles["paywall__get-prediction-timer"]}>
|
||||
<span>{time}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
className={styles["paywall__get-prediction-button"]}
|
||||
onClick={handleNext}
|
||||
>
|
||||
{translate("/paywall.offer_reserved.button", undefined, ELocalesPlacement.PalmistryV0)}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
margin: 40px 18px 20px;
|
||||
font-weight: 700;
|
||||
|
||||
& > span {
|
||||
&>span {
|
||||
color: #224e90;
|
||||
}
|
||||
}
|
||||
@ -32,7 +32,7 @@
|
||||
font-size: 32px;
|
||||
margin-top: 50px;
|
||||
|
||||
& > span {
|
||||
&>span {
|
||||
color: #224e90;
|
||||
}
|
||||
}
|
||||
@ -40,3 +40,49 @@
|
||||
.partners {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.paywall__get-prediction {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
align-items: center;
|
||||
background: #eff2fd;
|
||||
box-shadow: 0 -3px 11px rgba(0, 0, 0, .15);
|
||||
color: #4a567a;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
padding: 12px 24px;
|
||||
transition: all 0.5s;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.paywall__get-prediction-timer {
|
||||
font-family: "SF Mono Bold, sans-serif";
|
||||
border-radius: 4px;
|
||||
background: initial;
|
||||
display: inline;
|
||||
margin: 5px;
|
||||
min-width: 62px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.paywall__get-prediction-timer>span {
|
||||
color: #066fde;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.paywall__get-prediction-button {
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
line-height: 26px;
|
||||
min-height: auto;
|
||||
min-width: auto;
|
||||
padding: 6px 8px;
|
||||
white-space: nowrap;
|
||||
width: auto;
|
||||
}
|
||||
@ -14,7 +14,7 @@ import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||
import MoneyBackGuarantee from "../../components/MoneyBackGuarantee";
|
||||
import PalmsSayAbout from "../../components/PalmsSayAbout";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { getZodiacSignByDate } from "@/services/zodiac-sign";
|
||||
import WithPartnerInformation from "../../components/WithPartnerInformation";
|
||||
import PersonalInformation from "../../components/PersonalInformation";
|
||||
@ -22,10 +22,13 @@ import Reviews from "../../components/Reviews";
|
||||
import Address from "../../components/Address";
|
||||
import Modal from "@/components/Modal";
|
||||
import PaymentForm from "@/components/Payment/nmi/PaymentForm";
|
||||
import { useApi, useApiCall, User } from "@/api";
|
||||
|
||||
const placementKey = EPlacementKeys["aura.placement.email.palmistry"];
|
||||
|
||||
function TrialPayment() {
|
||||
const api = useApi();
|
||||
const token = useSelector(selectors.selectToken);
|
||||
const dispatch = useDispatch();
|
||||
// const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||
const { products } = usePaywall({
|
||||
@ -36,16 +39,15 @@ function TrialPayment() {
|
||||
|
||||
const trialDuration = activeProduct?.trialDuration || 7;
|
||||
|
||||
const birthdate = useSelector(selectors.selectBirthdate);
|
||||
const zodiacSign = getZodiacSignByDate(birthdate);
|
||||
const {
|
||||
gender,
|
||||
birthPlace,
|
||||
partnerBirthPlace,
|
||||
partnerBirthdate,
|
||||
partnerGender,
|
||||
flowChoice,
|
||||
birthdate,
|
||||
} = useSelector(selectors.selectQuestionnaire)
|
||||
const zodiacSign = getZodiacSignByDate(birthdate);
|
||||
const partnerZodiacSign = getZodiacSignByDate(partnerBirthdate);
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -72,12 +74,16 @@ function TrialPayment() {
|
||||
setIsPaymentModalOpen(true);
|
||||
};
|
||||
|
||||
const userData = useCallback(async () => {
|
||||
const { user } = await api.getMe({ token: token });
|
||||
return user;
|
||||
}, [api]);
|
||||
|
||||
const { data: user } = useApiCall<User.IUser>(userData);
|
||||
|
||||
const singleOrWithPartner = useMemo(() => {
|
||||
if (["relationship", "married"].includes(flowChoice)) {
|
||||
return "partner";
|
||||
}
|
||||
return "single";
|
||||
}, [flowChoice]);
|
||||
return user?.partner ? "partner" : "single";
|
||||
}, [user]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeProduct) return;
|
||||
|
||||
@ -127,8 +127,13 @@ export const usePayment = ({
|
||||
paymentToken: response.token
|
||||
});
|
||||
setPaymentResponse(res);
|
||||
setIsPaymentSuccess(res.status === "paid");
|
||||
if (res.status !== "paid") {
|
||||
if ("payment" in res) {
|
||||
setIsPaymentSuccess(res.payment.status === "paid");
|
||||
} else {
|
||||
setIsPaymentSuccess(res.status === "paid");
|
||||
}
|
||||
const status = "payment" in res ? res.payment.status : res.status;
|
||||
if (status !== "paid") {
|
||||
setError("message" in res ? res.message : "Something went wrong")
|
||||
}
|
||||
} catch (error: any) {
|
||||
|
||||
@ -39,7 +39,11 @@ export const useMakePayment = ({
|
||||
paywallId
|
||||
});
|
||||
|
||||
if (res.status === "paid") {
|
||||
if ("payment" in res && res.payment.status === "paid") {
|
||||
return window.location.href = `${returnPaidUrl}?redirect_status=succeeded`;
|
||||
}
|
||||
|
||||
if ("status" in res && res.status === "paid") {
|
||||
return window.location.href = `${returnPaidUrl}?redirect_status=succeeded`;
|
||||
}
|
||||
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
import { useEffect } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
export const ScrollToTop = () => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}, [pathname]);
|
||||
|
||||
return null;
|
||||
};
|
||||
24
src/hooks/useScrollToTop.tsx
Normal file
24
src/hooks/useScrollToTop.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { useLayoutEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
interface ScrollToTopProps {
|
||||
scrollBehavior?: ScrollBehavior;
|
||||
element?: HTMLElement | null | Window;
|
||||
}
|
||||
|
||||
export const useScrollToTop = ({
|
||||
scrollBehavior = 'auto',
|
||||
element = window
|
||||
}: ScrollToTopProps = {}) => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!element) return;
|
||||
|
||||
element.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
behavior: scrollBehavior
|
||||
});
|
||||
}, [pathname]);
|
||||
};
|
||||
@ -190,7 +190,7 @@ div[class^="divider"] {
|
||||
#root {
|
||||
height: 100%;
|
||||
min-width: 100vw;
|
||||
overflow: auto;
|
||||
/* overflow: auto; */
|
||||
}
|
||||
|
||||
a,
|
||||
|
||||
@ -303,6 +303,8 @@ const routes = {
|
||||
server: {
|
||||
userLocale: () => ["https://ipapi.co", "json"].join("/"),
|
||||
user: () => [apiHost, prefix, "user.json"].join("/"),
|
||||
// new method for getting user data
|
||||
me: () => [dApiHost, "users", "me"].join("/"),
|
||||
// token: () => [apiHost, prefix, "auth", "token.json"].join("/"),
|
||||
elements: () => [oldBackendPrefix, "elements.json"].join("/"),
|
||||
zodiacs: (zodiac: string) =>
|
||||
@ -382,6 +384,8 @@ const routes = {
|
||||
// Payment
|
||||
makePayment: () => [dApiHost, dApiPrefix, "payment", "checkout"].join("/"),
|
||||
getPaymentConfig: () => [dApiHost, dApiPrefix, "payment", "config"].join("/"),
|
||||
// check payment method exist
|
||||
getPaymentMethods: () => [dApiHost, dApiPrefix, "payment", "method"].join("/"),
|
||||
|
||||
// User videos
|
||||
getUserVideos: () => [dApiHost, "users", "videos", "combined"].join("/"),
|
||||
|
||||
@ -144,6 +144,18 @@ t.src=v;s=b.getElementsByTagName(e)[0];
|
||||
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
||||
'https://connect.facebook.net/en_US/fbevents.js');
|
||||
fbq('init', '1218510985903341');
|
||||
fbq('track', 'PageView');`;
|
||||
|
||||
const FBScriptCompatibilityFR = `
|
||||
!function(f,b,e,v,n,t,s)
|
||||
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
||||
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
|
||||
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
|
||||
n.queue=[];t=b.createElement(e);t.async=!0;
|
||||
t.src=v;s=b.getElementsByTagName(e)[0];
|
||||
s.parentNode.insertBefore(t,s)}(window, document,'script',
|
||||
'https://connect.facebook.net/en_US/fbevents.js');
|
||||
fbq('init', '923313529582091');
|
||||
fbq('track', 'PageView');`;
|
||||
|
||||
// Chats
|
||||
@ -187,6 +199,9 @@ fbq('track', 'PageView');`;
|
||||
{!isPalmistry && !isChats && locale === "en" && (
|
||||
<script>{FBScriptCompatibilityEN}</script>
|
||||
)}
|
||||
{!isPalmistry && !isChats && locale === "fr" && (
|
||||
<script>{FBScriptCompatibilityFR}</script>
|
||||
)}
|
||||
{/* Chats */}
|
||||
{isChats && locale === "en" && <script>{FBScriptChatsEN}</script>}
|
||||
</Helmet>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user