Merge branch 'preview/advisor-short-path' into 'develop'
add short path for advisor chat See merge request witapp/aura-webapp!77
This commit is contained in:
commit
10c567e169
@ -27,6 +27,7 @@ import {
|
||||
Assistants,
|
||||
OpenAI,
|
||||
SinglePayment,
|
||||
Products,
|
||||
} from './resources'
|
||||
|
||||
const api = {
|
||||
@ -69,7 +70,8 @@ const api = {
|
||||
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),
|
||||
createSinglePayment: createMethod<SinglePayment.PayloadPost, SinglePayment.ResponsePost>(SinglePayment.createRequestPost),
|
||||
checkProductPurchased: createMethod<Products.PayloadGet, Products.ResponseGet>(Products.createRequest),
|
||||
}
|
||||
|
||||
export type ApiContextValue = typeof api
|
||||
|
||||
29
src/api/resources/Products.ts
Normal file
29
src/api/resources/Products.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import routes from "@/routes";
|
||||
import { getAuthHeaders } from "../utils";
|
||||
|
||||
interface Payload {
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface PayloadGet extends Payload {
|
||||
productKey: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
interface ResponseGetSuccess {
|
||||
status: string;
|
||||
type: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
interface ResponseGetError {
|
||||
status: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type ResponseGet = ResponseGetSuccess | ResponseGetError;
|
||||
|
||||
export const createRequest = ({ token, productKey, email }: PayloadGet): Request => {
|
||||
const url = new URL(routes.server.dApiCheckProductPurchased(productKey, email));
|
||||
return new Request(url, { method: "GET", headers: getAuthHeaders(token) });
|
||||
};
|
||||
@ -38,7 +38,7 @@ export interface ResponseGet {
|
||||
currency: string;
|
||||
}
|
||||
|
||||
export interface ResponsePost {
|
||||
interface ResponsePostNewPaymentData {
|
||||
paymentIntent: {
|
||||
status: string;
|
||||
data: {
|
||||
@ -60,13 +60,23 @@ export interface ResponsePost {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ResponsePostExistPaymentData {
|
||||
interface ResponsePostExistPaymentData {
|
||||
payment: {
|
||||
status: string;
|
||||
invoiceId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ResponsePostError {
|
||||
status: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export type ResponsePost =
|
||||
| ResponsePostNewPaymentData
|
||||
| ResponsePostExistPaymentData
|
||||
| ResponsePostError;
|
||||
|
||||
export const createRequestPost = ({ data, token }: PayloadPost): Request => {
|
||||
const url = new URL(routes.server.dApiPaymentCheckout());
|
||||
const body = JSON.stringify(data);
|
||||
|
||||
@ -25,3 +25,4 @@ export * as AIRequestsV2 from "./AIRequestsV2";
|
||||
export * as Assistants from "./Assistants";
|
||||
export * as OpenAI from "./OpenAI";
|
||||
export * as SinglePayment from "./SinglePayment";
|
||||
export * as Products from "./Products";
|
||||
|
||||
@ -111,6 +111,8 @@ import SuccessPaymentPage from "../pages/PaymentWithEmailPage/ResultPayment/Succ
|
||||
import FailPaymentPage from "../pages/PaymentWithEmailPage/ResultPayment/FailPaymentPage";
|
||||
import { useSchemeColorByElement } from "@/hooks/useSchemeColorByElement";
|
||||
import GetInformationPartnerPage from "../pages/GetInformationPartner";
|
||||
import BirthPlacePage from "../pages/BirthPlacePage";
|
||||
import LoadingPage from "../pages/LoadingPage";
|
||||
|
||||
const isProduction = import.meta.env.MODE === "production";
|
||||
|
||||
@ -252,13 +254,14 @@ function App(): JSX.Element {
|
||||
return (
|
||||
<Routes>
|
||||
<Route element={<Layout setIsSpecialOfferOpen={setIsSpecialOfferOpen} />}>
|
||||
<Route path={routes.client.loadingPage()} element={<LoadingPage />} />
|
||||
{/* Email - Pay - Email */}
|
||||
<Route path={routes.client.epeGender()} element={<GenderPage />} />
|
||||
<Route path={routes.client.epeBirthdate()} element={<BirthdayPage />} />
|
||||
<Route
|
||||
{/* <Route
|
||||
path={routes.client.epePayment()}
|
||||
element={<PaymentWithEmailPage />}
|
||||
/>
|
||||
/> */}
|
||||
<Route
|
||||
path={routes.client.epeSuccessPayment()}
|
||||
element={<SuccessPaymentPage />}
|
||||
@ -269,6 +272,68 @@ function App(): JSX.Element {
|
||||
/>
|
||||
{/* Email - Pay - Email */}
|
||||
|
||||
{/* Advisor short path */}
|
||||
<Route
|
||||
element={
|
||||
<CheckPurchasedSingleProductOutlet
|
||||
productKey="chat.aura"
|
||||
isProductPage={false}
|
||||
failedUrl={routes.client.advisorChatPrivate(
|
||||
"asst_WWkAlT4Ovs6gKRy6VEn9LqNS"
|
||||
)}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Route
|
||||
path={routes.client.advisorChatGender()}
|
||||
element={<GenderPage />}
|
||||
/>
|
||||
<Route
|
||||
path={routes.client.advisorChatBirthdate()}
|
||||
element={<BirthdayPage />}
|
||||
/>
|
||||
<Route
|
||||
path={routes.client.advisorChatBirthtime()}
|
||||
element={<BirthtimePage />}
|
||||
/>
|
||||
<Route
|
||||
path={routes.client.advisorChatBirthPlace()}
|
||||
element={<BirthPlacePage />}
|
||||
/>
|
||||
<Route
|
||||
path={routes.client.advisorChatSuccessPayment()}
|
||||
element={<SuccessPaymentPage />}
|
||||
/>
|
||||
<Route
|
||||
path={routes.client.advisorChatFailPayment()}
|
||||
element={<FailPaymentPage />}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route
|
||||
element={
|
||||
<CheckPurchasedSingleProductOutlet
|
||||
isProductPage={true}
|
||||
failedUrl={routes.client.advisorChatGender()}
|
||||
productKey="chat.aura"
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Route path={`${routes.client.advisorChatPrivate()}`}>
|
||||
<Route path=":id" element={<AdvisorChatPage />} />
|
||||
</Route>
|
||||
</Route>
|
||||
{/* Advisor short path */}
|
||||
|
||||
{/* Single Payment Page Short Path */}
|
||||
<Route
|
||||
path={routes.client.singlePaymentShortPath()}
|
||||
element={<PaymentWithEmailPage />}
|
||||
>
|
||||
<Route path=":productId" element={<PaymentWithEmailPage />} />
|
||||
</Route>
|
||||
{/* Single Payment Page Short Path */}
|
||||
|
||||
{/* Test Routes Start */}
|
||||
<Route path={routes.client.notFound()} element={<NotFoundPage />} />
|
||||
<Route path={routes.client.gender()} element={<GenderPage />}>
|
||||
@ -692,6 +757,63 @@ function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
interface ICheckPurchasedSingleProductOutletProps {
|
||||
productKey: string;
|
||||
isProductPage: boolean;
|
||||
failedUrl: string;
|
||||
}
|
||||
|
||||
function CheckPurchasedSingleProductOutlet({
|
||||
productKey,
|
||||
isProductPage,
|
||||
failedUrl,
|
||||
}: ICheckPurchasedSingleProductOutletProps): JSX.Element {
|
||||
const { user, token } = useAuth();
|
||||
const api = useApi();
|
||||
|
||||
const loadData = useCallback(async () => {
|
||||
if (!token?.length || !user?.email || !productKey?.length)
|
||||
return {
|
||||
status: "error",
|
||||
error: "Missing params",
|
||||
};
|
||||
try {
|
||||
const purchased = await api.checkProductPurchased({
|
||||
email: user?.email || "",
|
||||
productKey,
|
||||
token,
|
||||
});
|
||||
return purchased;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return {
|
||||
status: "error",
|
||||
error: "Something went wrong",
|
||||
};
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const { data, isPending } = useApiCall(loadData);
|
||||
|
||||
if (!data || isPending) {
|
||||
return <LoadingPage />;
|
||||
}
|
||||
|
||||
if (
|
||||
isProductPage &&
|
||||
(!("active" in data) || !data.active || !token.length || !user?.email)
|
||||
) {
|
||||
return <Navigate to={failedUrl} replace={true} />;
|
||||
}
|
||||
|
||||
if (!isProductPage && data && "active" in data && data.active) {
|
||||
return <Navigate to={failedUrl} replace={true} />;
|
||||
}
|
||||
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
function AuthorizedUserOutlet(): JSX.Element {
|
||||
const status = useSelector(selectors.selectStatus);
|
||||
const { user } = useAuth();
|
||||
|
||||
@ -17,9 +17,13 @@ function BirthdayPage(): JSX.Element {
|
||||
const navigate = useNavigate();
|
||||
const birthdate = useSelector(selectors.selectBirthdate);
|
||||
const [isDisabled, setIsDisabled] = useState(true);
|
||||
const nextRoute = window.location.href.includes("/epe/")
|
||||
? routes.client.epePayment()
|
||||
: routes.client.didYouKnow();
|
||||
let nextRoute = routes.client.didYouKnow();
|
||||
if (window.location.href.includes("/epe/")) {
|
||||
nextRoute = routes.client.singlePaymentShortPath("moons.pdf.aura");
|
||||
}
|
||||
if (window.location.href.includes("/advisor-chat/")) {
|
||||
nextRoute = routes.client.advisorChatBirthtime();
|
||||
}
|
||||
const handleNext = () => navigate(nextRoute);
|
||||
const handleValid = (birthdate: string) => {
|
||||
dispatch(actions.form.addDate(birthdate));
|
||||
|
||||
@ -1,28 +1,34 @@
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { actions, selectors } from '@/store'
|
||||
import { TimePicker } from "../DateTimePicker"
|
||||
import Title from "../Title"
|
||||
import MainButton from "../MainButton"
|
||||
import routes from "@/routes"
|
||||
import './styles.css'
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { actions, selectors } from "@/store";
|
||||
import { TimePicker } from "../DateTimePicker";
|
||||
import Title from "../Title";
|
||||
import MainButton from "../MainButton";
|
||||
import routes from "@/routes";
|
||||
import "./styles.css";
|
||||
|
||||
function BirthtimePage(): JSX.Element {
|
||||
const { t } = useTranslation()
|
||||
const dispatch = useDispatch()
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const birthtime = useSelector(selectors.selectBirthtime)
|
||||
const handleNext = () => navigate(routes.client.createProfile())
|
||||
const handleChange = (value: string) => dispatch(actions.form.addTime(value))
|
||||
const birthtime = useSelector(selectors.selectBirthtime);
|
||||
let nextRoute = routes.client.createProfile();
|
||||
if (window.location.href.includes("/advisor-chat/")) {
|
||||
nextRoute = routes.client.advisorChatBirthPlace();
|
||||
}
|
||||
const handleNext = () => navigate(nextRoute);
|
||||
const handleChange = (value: string) => dispatch(actions.form.addTime(value));
|
||||
return (
|
||||
<section className='page'>
|
||||
<Title variant="h2" className="mt-24">{t('born_time_question')}</Title>
|
||||
<p className="description">{t('nasa_data_using')}</p>
|
||||
<TimePicker value={birthtime} onChange={handleChange}/>
|
||||
<MainButton onClick={handleNext}>{t('next')}</MainButton>
|
||||
<section className="page">
|
||||
<Title variant="h2" className="mt-24">
|
||||
{t("born_time_question")}
|
||||
</Title>
|
||||
<p className="description">{t("nasa_data_using")}</p>
|
||||
<TimePicker value={birthtime} onChange={handleChange} />
|
||||
<MainButton onClick={handleNext}>{t("next")}</MainButton>
|
||||
</section>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default BirthtimePage
|
||||
export default BirthtimePage;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import { useDispatch } from "react-redux";
|
||||
@ -8,6 +7,7 @@ import { actions } from "@/store";
|
||||
// import { useAuth } from "@/auth";
|
||||
import styles from "./styles.module.css";
|
||||
import Loader from "@/components/Loader";
|
||||
import { paymentResultPathsOfProducts } from "@/data/products";
|
||||
|
||||
function PaymentResultPage(): JSX.Element {
|
||||
// const api = useApi();
|
||||
@ -16,7 +16,7 @@ function PaymentResultPage(): JSX.Element {
|
||||
const dispatch = useDispatch();
|
||||
const [searchParams] = useSearchParams();
|
||||
const status = searchParams.get("redirect_status");
|
||||
const type = searchParams.get("type");
|
||||
const redirect_type = searchParams.get("redirect_type");
|
||||
// const { id } = useParams();
|
||||
// const requestTimeOutRef = useRef<NodeJS.Timeout>();
|
||||
const [isLoading] = useState(true);
|
||||
@ -90,18 +90,19 @@ function PaymentResultPage(): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (status === "succeeded") {
|
||||
dispatch(actions.status.update("subscribed"));
|
||||
let successPaymentRoute = routes.client.paymentSuccess();
|
||||
if (type === "epe") {
|
||||
successPaymentRoute = routes.client.epeSuccessPayment();
|
||||
if (
|
||||
!paymentResultPathsOfProducts[redirect_type || ""] ||
|
||||
!redirect_type
|
||||
) {
|
||||
return navigate(paymentResultPathsOfProducts.default.success);
|
||||
}
|
||||
return navigate(successPaymentRoute);
|
||||
return navigate(paymentResultPathsOfProducts[redirect_type].success);
|
||||
}
|
||||
let failPaymentRoute = routes.client.paymentFail();
|
||||
if (type === "epe") {
|
||||
failPaymentRoute = routes.client.epeFailPayment();
|
||||
if (!paymentResultPathsOfProducts[redirect_type || ""] || !redirect_type) {
|
||||
return navigate(paymentResultPathsOfProducts.default.fail);
|
||||
}
|
||||
return navigate(failPaymentRoute);
|
||||
}, [navigate, status, dispatch]);
|
||||
return navigate(paymentResultPathsOfProducts[redirect_type].fail);
|
||||
}, [navigate, status, dispatch, redirect_type]);
|
||||
|
||||
return <div className={styles.page}>{isLoading && <Loader />}</div>;
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ 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";
|
||||
@ -27,7 +26,7 @@ function AddConsultationPage() {
|
||||
const tokenFromStore = useSelector(selectors.selectToken);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [paymentIntent, setPaymentIntent] = useState<
|
||||
ResponsePost | ResponsePostExistPaymentData | null
|
||||
ResponsePost | null
|
||||
>(null);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const returnUrl = `${window.location.protocol}//${
|
||||
|
||||
@ -11,7 +11,6 @@ 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";
|
||||
@ -28,7 +27,7 @@ function AddReportPage() {
|
||||
const api = useApi();
|
||||
const tokenFromStore = useSelector(selectors.selectToken);
|
||||
const [paymentIntent, setPaymentIntent] = useState<
|
||||
ResponsePost | ResponsePostExistPaymentData | null
|
||||
ResponsePost | null
|
||||
>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isError, setIsError] = useState(false);
|
||||
|
||||
@ -18,7 +18,6 @@ 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";
|
||||
@ -42,7 +41,7 @@ function UnlimitedReadingsPage() {
|
||||
const tokenFromStore = useSelector(selectors.selectToken);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [paymentIntent, setPaymentIntent] = useState<
|
||||
ResponsePost | ResponsePostExistPaymentData | null
|
||||
ResponsePost | null
|
||||
>(null);
|
||||
const [isError, setIsError] = useState(false);
|
||||
const returnUrl = `${window.location.protocol}//${
|
||||
|
||||
@ -4,6 +4,7 @@ interface IChatHeaderProps {
|
||||
name: string;
|
||||
avatar: string;
|
||||
classNameContainer?: string;
|
||||
hasBackButton?: boolean;
|
||||
clickBackButton: () => void;
|
||||
}
|
||||
|
||||
@ -11,13 +12,16 @@ function ChatHeader({
|
||||
name,
|
||||
avatar,
|
||||
classNameContainer = "",
|
||||
hasBackButton = true,
|
||||
clickBackButton,
|
||||
}: IChatHeaderProps) {
|
||||
return (
|
||||
<div className={`${styles.container} ${classNameContainer}`}>
|
||||
<div className={styles["back-button"]} onClick={clickBackButton}>
|
||||
<div className={styles["arrow"]} /> Advisors
|
||||
</div>
|
||||
{hasBackButton && (
|
||||
<div className={styles["back-button"]} onClick={clickBackButton}>
|
||||
<div className={styles["arrow"]} /> Advisors
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.name}>
|
||||
{name}
|
||||
<span className={styles["online-status"]} />
|
||||
|
||||
@ -16,6 +16,7 @@ import useDetectScroll from "@smakss/react-scroll-direction";
|
||||
import { getZodiacSignByDate } from "@/services/zodiac-sign";
|
||||
|
||||
function AdvisorChatPage() {
|
||||
const isPrivateChat = window.location.href.includes("/advisor-chat-private/");
|
||||
const { id } = useParams();
|
||||
const api = useApi();
|
||||
const navigate = useNavigate();
|
||||
@ -25,7 +26,9 @@ function AdvisorChatPage() {
|
||||
const birthdate = useSelector(selectors.selectBirthdate);
|
||||
const zodiacSign = getZodiacSignByDate(birthdate);
|
||||
const { username } = useSelector(selectors.selectUser);
|
||||
const { gender } = useSelector(selectors.selectQuestionnaire);
|
||||
const { gender, birthtime, birthPlace } = useSelector(
|
||||
selectors.selectQuestionnaire
|
||||
);
|
||||
const [assistant, setAssistant] = useState<IAssistant>();
|
||||
const [messageText, setMessageText] = useState("");
|
||||
const [textareaRows, setTextareaRows] = useState(1);
|
||||
@ -88,7 +91,7 @@ function AdvisorChatPage() {
|
||||
idAssistant: string | number
|
||||
) => {
|
||||
const currentAssistant = aiAssistants.find(
|
||||
(a) => a.id === Number(idAssistant)
|
||||
(a) => a.external_id === idAssistant
|
||||
);
|
||||
return currentAssistant;
|
||||
};
|
||||
@ -196,10 +199,17 @@ function AdvisorChatPage() {
|
||||
return run;
|
||||
};
|
||||
|
||||
const createMessage = async (messageText: string, threadId: string) => {
|
||||
const getContentMessage = (messageText: string) => {
|
||||
const content = `#USER INFO: zodiac sign - ${zodiacSign}; gender - ${gender}; birthdate - ${birthdate}; name - ${
|
||||
username || "unknown"
|
||||
};# ${messageText}`;
|
||||
}; birthtime - ${birthtime || "unknown"}; birthPlace - ${
|
||||
birthPlace || "unknown"
|
||||
}# ${messageText}`;
|
||||
return content;
|
||||
};
|
||||
|
||||
const createMessage = async (messageText: string, threadId: string) => {
|
||||
const content = getContentMessage(messageText);
|
||||
const message = await api.createMessage({
|
||||
token: openAiToken,
|
||||
method: "POST",
|
||||
@ -260,7 +270,8 @@ function AdvisorChatPage() {
|
||||
threadId = assistant.external_chat_id;
|
||||
assistantId = assistant.external_id || "";
|
||||
} else {
|
||||
const thread = await createThread(messageText);
|
||||
const content = getContentMessage(messageText);
|
||||
const thread = await createThread(content);
|
||||
threadId = thread.id;
|
||||
assistantId = assistant?.external_id || "";
|
||||
|
||||
@ -321,6 +332,7 @@ function AdvisorChatPage() {
|
||||
avatar={assistant?.photo?.th2x || ""}
|
||||
classNameContainer={styles["header-container"]}
|
||||
clickBackButton={() => navigate(-1)}
|
||||
hasBackButton={!isPrivateChat}
|
||||
/>
|
||||
)}
|
||||
{!!messages.length && (
|
||||
|
||||
@ -31,7 +31,7 @@ function Advisors() {
|
||||
useApiCall<Assistants.Response>(loadData);
|
||||
|
||||
const handleAdvisorClick = (assistant: IAssistant) => {
|
||||
navigate(routes.client.advisorChat(assistant.id));
|
||||
navigate(routes.client.advisorChat(assistant.external_id));
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
48
src/components/pages/BirthPlacePage/index.tsx
Normal file
48
src/components/pages/BirthPlacePage/index.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import Title from "@/components/Title";
|
||||
import styles from "./styles.module.css";
|
||||
import PlacePicker from "@/components/PlacePicker";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { actions, selectors } from "@/store";
|
||||
import MainButton from "@/components/MainButton";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
|
||||
function BirthPlacePage() {
|
||||
const dispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { birthPlace } = useSelector(selectors.selectQuestionnaire);
|
||||
|
||||
const handleChange = (birthPlace: string) => {
|
||||
return dispatch(actions.questionnaire.update({ birthPlace }));
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
navigate(routes.client.singlePaymentShortPath("chat.aura"));
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
<Title variant="h1" className={styles.title}>
|
||||
Where were you born?
|
||||
</Title>
|
||||
<p className={styles.description}>
|
||||
Please select the city where you were born.
|
||||
</p>
|
||||
<PlacePicker
|
||||
value={birthPlace}
|
||||
name="birthPlace"
|
||||
maxLength={1000}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{!!birthPlace.length && (
|
||||
<MainButton className={styles.button} onClick={handleNext}>
|
||||
{t("next")}
|
||||
</MainButton>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default BirthPlacePage;
|
||||
28
src/components/pages/BirthPlacePage/styles.module.css
Normal file
28
src/components/pages/BirthPlacePage/styles.module.css
Normal file
@ -0,0 +1,28 @@
|
||||
.page {
|
||||
height: fit-content;
|
||||
min-height: calc(100dvh - 50px);
|
||||
background-image: url(/bunch_of_cards.webp);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-top: 40px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* .button {
|
||||
background: linear-gradient(
|
||||
165.54deg,
|
||||
rgb(20, 19, 51) -33.39%,
|
||||
rgb(32, 34, 97) 15.89%,
|
||||
rgb(84, 60, 151) 55.84%,
|
||||
rgb(105, 57, 162) 74.96%
|
||||
);
|
||||
min-height: 0;
|
||||
height: 49px;
|
||||
border-radius: 12px;
|
||||
margin-top: 26px;
|
||||
} */
|
||||
@ -23,6 +23,9 @@ function GenderPage(): JSX.Element {
|
||||
if (pathName.includes("/epe/gender")) {
|
||||
return navigate(routes.client.epeBirthdate());
|
||||
}
|
||||
if (pathName.includes("/advisor-chat/gender")) {
|
||||
return navigate(routes.client.advisorChatBirthdate());
|
||||
}
|
||||
navigate(`/questionnaire/profile/flowChoice`);
|
||||
};
|
||||
|
||||
|
||||
12
src/components/pages/LoadingPage/index.tsx
Normal file
12
src/components/pages/LoadingPage/index.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import Loader, { LoaderColor } from "@/components/Loader";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
function LoadingPage() {
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
<Loader color={LoaderColor.Black} />
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default LoadingPage;
|
||||
8
src/components/pages/LoadingPage/styles.module.css
Normal file
8
src/components/pages/LoadingPage/styles.module.css
Normal file
@ -0,0 +1,8 @@
|
||||
.page {
|
||||
height: fit-content;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@ -8,7 +8,12 @@ import MainButton from "@/components/MainButton";
|
||||
function FailPaymentPage(): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const handleNext = () => navigate(routes.client.epePayment());
|
||||
const isAdvisorChat = window.location.href.includes("/advisor-chat/");
|
||||
let nextRoute = routes.client.epePayment();
|
||||
if (isAdvisorChat) {
|
||||
nextRoute = routes.client.advisorChatGender();
|
||||
}
|
||||
const handleNext = () => navigate(nextRoute);
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
|
||||
@ -1,9 +1,17 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import styles from "./styles.module.css";
|
||||
import Title from "@/components/Title";
|
||||
import MainButton from "@/components/MainButton";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
|
||||
function SuccessPaymentPage(): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const isAdvisorChat = window.location.href.includes("/advisor-chat/");
|
||||
const titleText = isAdvisorChat
|
||||
? "The payment was successful"
|
||||
: "The information has been sent to your email";
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
@ -13,9 +21,21 @@ function SuccessPaymentPage(): JSX.Element {
|
||||
style={{ minHeight: "98px" }}
|
||||
/>
|
||||
<div className={styles.text}>
|
||||
<Title variant="h1">The information has been sent to your email</Title>
|
||||
<Title variant="h1">{titleText}</Title>
|
||||
<p>{t("auweb.pay_good.text1")}</p>
|
||||
</div>
|
||||
{isAdvisorChat && (
|
||||
<MainButton
|
||||
className={styles.button}
|
||||
onClick={() =>
|
||||
navigate(
|
||||
routes.client.advisorChatPrivate("asst_WWkAlT4Ovs6gKRy6VEn9LqNS")
|
||||
)
|
||||
}
|
||||
>
|
||||
{t("auweb.pay_good.button")}
|
||||
</MainButton>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@ -12,51 +12,63 @@ import { getClientTimezone } from "@/locales";
|
||||
import ErrorText from "@/components/ErrorText";
|
||||
import Title from "@/components/Title";
|
||||
import NameInput from "@/components/EmailEnterPage/NameInput";
|
||||
import {
|
||||
ResponseGet,
|
||||
ResponsePost,
|
||||
ResponsePostExistPaymentData,
|
||||
} from "@/api/resources/SinglePayment";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useParams } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import PaymentForm from "./PaymentForm";
|
||||
import { getPriceCentsToDollars } from "@/services/price";
|
||||
import { createSinglePayment } from "@/services/singlePayment";
|
||||
import { useSinglePayment } from "@/hooks/payment/useSinglePayment";
|
||||
|
||||
function PaymentWithEmailPage() {
|
||||
const { productId } = useParams();
|
||||
const { t, i18n } = useTranslation();
|
||||
const tokenFromStore = useSelector(selectors.selectToken);
|
||||
const { signUp, user: userFromStore } = useAuth();
|
||||
const api = useApi();
|
||||
const navigate = useNavigate();
|
||||
const timezone = getClientTimezone();
|
||||
const dispatch = useDispatch();
|
||||
const birthday = useSelector(selectors.selectBirthday);
|
||||
const { gender } = useSelector(selectors.selectQuestionnaire);
|
||||
const locale = i18n.language;
|
||||
const [email, setEmail] = useState("");
|
||||
const [name, setName] = useState("");
|
||||
const [isValidEmail, setIsValidEmail] = useState(false);
|
||||
const [isValidName, setIsValidName] = useState(true);
|
||||
const [isValidName, setIsValidName] = useState(productId !== "chat.aura");
|
||||
const [isDisabled, setIsDisabled] = useState(true);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isLoadingPage, setIsLoadingPage] = useState(false);
|
||||
const [isAuth, setIsAuth] = useState(false);
|
||||
const [apiError, setApiError] = useState<ApiError | null>(null);
|
||||
const [error, setError] = useState<boolean>(false);
|
||||
const [paymentIntent, setPaymentIntent] = useState<
|
||||
ResponsePost | ResponsePostExistPaymentData | null
|
||||
>(null);
|
||||
const [currentProduct, setCurrentProduct] = useState<ResponseGet>();
|
||||
const returnUrl = `${window.location.protocol}//${window.location.host}/payment/result/?type=epe`;
|
||||
const returnUrl = `${window.location.protocol}//${
|
||||
window.location.host
|
||||
}${routes.client.paymentResult()}`;
|
||||
|
||||
const [isLoadingAuth, setIsLoadingAuth] = useState<boolean>(false);
|
||||
|
||||
const {
|
||||
product,
|
||||
paymentIntent,
|
||||
createSinglePayment,
|
||||
isLoading: isLoadingSinglePayment,
|
||||
error: errorSinglePayment,
|
||||
} = useSinglePayment();
|
||||
|
||||
useEffect(() => {
|
||||
if (isValidName && isValidEmail) {
|
||||
if (
|
||||
isValidName &&
|
||||
isValidEmail &&
|
||||
!(error || apiError || errorSinglePayment?.error)
|
||||
) {
|
||||
setIsDisabled(false);
|
||||
} else {
|
||||
setIsDisabled(true);
|
||||
}
|
||||
}, [isValidEmail, email, isValidName, name]);
|
||||
}, [
|
||||
isValidEmail,
|
||||
email,
|
||||
isValidName,
|
||||
name,
|
||||
error,
|
||||
apiError,
|
||||
errorSinglePayment?.error,
|
||||
]);
|
||||
|
||||
const handleValidEmail = (email: string) => {
|
||||
dispatch(actions.form.addEmail(email));
|
||||
@ -71,7 +83,7 @@ function PaymentWithEmailPage() {
|
||||
|
||||
const authorization = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
setIsLoadingAuth(true);
|
||||
const auth = await api.auth({ email, timezone, locale });
|
||||
const {
|
||||
auth: { token, user },
|
||||
@ -103,6 +115,7 @@ function PaymentWithEmailPage() {
|
||||
dispatch(actions.status.update("registred"));
|
||||
setIsAuth(true);
|
||||
const userUpdated = await api.getUser({ token });
|
||||
setIsLoadingAuth(false);
|
||||
return { user: userUpdated?.user, token };
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@ -111,19 +124,10 @@ function PaymentWithEmailPage() {
|
||||
} else {
|
||||
setError(true);
|
||||
}
|
||||
setIsLoadingAuth(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getCurrentProduct = async (token: string) => {
|
||||
const productsSinglePayment = await api.getSinglePaymentProducts({
|
||||
token,
|
||||
});
|
||||
const currentProduct = productsSinglePayment.find(
|
||||
(product) => product.key === "moons.pdf.aura"
|
||||
);
|
||||
return currentProduct;
|
||||
};
|
||||
|
||||
const handleClick = async () => {
|
||||
const authData = await authorization();
|
||||
if (!authData) {
|
||||
@ -131,76 +135,32 @@ function PaymentWithEmailPage() {
|
||||
}
|
||||
const { user, token } = authData;
|
||||
|
||||
const currentProduct = await getCurrentProduct(token);
|
||||
if (!currentProduct) {
|
||||
setError(true);
|
||||
return;
|
||||
}
|
||||
setCurrentProduct(currentProduct);
|
||||
|
||||
const { productId, key } = currentProduct;
|
||||
const paymentInfo = {
|
||||
productId,
|
||||
key,
|
||||
};
|
||||
const paymentIntent = await createSinglePayment(
|
||||
await createSinglePayment({
|
||||
user,
|
||||
paymentInfo,
|
||||
token,
|
||||
email,
|
||||
name,
|
||||
birthday,
|
||||
targetProductKey: productId || "",
|
||||
returnUrl,
|
||||
api,
|
||||
gender
|
||||
);
|
||||
setPaymentIntent(paymentIntent);
|
||||
setIsLoading(false);
|
||||
if ("payment" in paymentIntent) {
|
||||
if (paymentIntent.payment.status === "paid")
|
||||
return navigate(routes.client.epeSuccessPayment());
|
||||
return navigate(routes.client.epeFailPayment());
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleAuthUser = useCallback(async () => {
|
||||
if (!tokenFromStore.length || !userFromStore) {
|
||||
return;
|
||||
}
|
||||
setIsLoadingPage(true);
|
||||
const currentProduct = await getCurrentProduct(tokenFromStore);
|
||||
if (!currentProduct) {
|
||||
setError(true);
|
||||
return;
|
||||
}
|
||||
setCurrentProduct(currentProduct);
|
||||
|
||||
const { productId, key } = currentProduct;
|
||||
const paymentInfo = {
|
||||
productId,
|
||||
key,
|
||||
};
|
||||
const paymentIntent = await createSinglePayment(
|
||||
userFromStore,
|
||||
paymentInfo,
|
||||
tokenFromStore,
|
||||
userFromStore.email,
|
||||
userFromStore.profile.full_name,
|
||||
userFromStore.profile.birthday,
|
||||
await createSinglePayment({
|
||||
user: userFromStore,
|
||||
token: tokenFromStore,
|
||||
targetProductKey: productId || "",
|
||||
returnUrl,
|
||||
api,
|
||||
gender
|
||||
);
|
||||
setPaymentIntent(paymentIntent);
|
||||
setIsLoadingPage(false);
|
||||
setIsLoading(false);
|
||||
if ("payment" in paymentIntent) {
|
||||
if (paymentIntent.payment.status === "paid")
|
||||
return navigate(routes.client.epeSuccessPayment());
|
||||
return navigate(routes.client.epeFailPayment());
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
});
|
||||
}, [
|
||||
createSinglePayment,
|
||||
productId,
|
||||
returnUrl,
|
||||
tokenFromStore,
|
||||
userFromStore,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
handleAuthUser();
|
||||
@ -209,60 +169,68 @@ function PaymentWithEmailPage() {
|
||||
|
||||
return (
|
||||
<div className={`${styles.page} page`}>
|
||||
{isLoadingPage && <Loader color={LoaderColor.Black} />}
|
||||
{!isLoadingPage &&
|
||||
{(isLoadingSinglePayment || isLoadingSinglePayment) && (
|
||||
<Loader color={LoaderColor.Black} />
|
||||
)}
|
||||
{!isLoadingSinglePayment &&
|
||||
!isLoadingAuth &&
|
||||
paymentIntent &&
|
||||
"paymentIntent" in paymentIntent &&
|
||||
!!tokenFromStore.length && (
|
||||
<>
|
||||
<Title variant="h1" className={styles.title}>
|
||||
{getPriceCentsToDollars(currentProduct?.amount || 0)}$
|
||||
{getPriceCentsToDollars(product?.amount || 0)}$
|
||||
</Title>
|
||||
<PaymentForm
|
||||
stripePublicKey={paymentIntent.paymentIntent.data.public_key}
|
||||
clientSecret={paymentIntent.paymentIntent.data.client_secret}
|
||||
returnUrl={returnUrl}
|
||||
returnUrl={`${returnUrl}?redirect_type=${product?.key}`}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{(!tokenFromStore || !paymentIntent) && !isLoadingPage && (
|
||||
<>
|
||||
<NameInput
|
||||
value={name}
|
||||
placeholder="Your name"
|
||||
onValid={handleValidName}
|
||||
onInvalid={() => setIsValidName(true)}
|
||||
/>
|
||||
<EmailInput
|
||||
name="email"
|
||||
value={email}
|
||||
placeholder={t("your_email")}
|
||||
onValid={handleValidEmail}
|
||||
onInvalid={() => setIsValidEmail(false)}
|
||||
/>
|
||||
{(!tokenFromStore ||
|
||||
!paymentIntent ||
|
||||
(productId !== "chat.aura" && !name.length)) &&
|
||||
!isLoadingSinglePayment &&
|
||||
!isLoadingAuth && (
|
||||
<>
|
||||
<NameInput
|
||||
value={name}
|
||||
placeholder="Your name"
|
||||
onValid={handleValidName}
|
||||
onInvalid={() => setIsValidName(productId !== "chat.aura")}
|
||||
/>
|
||||
<EmailInput
|
||||
name="email"
|
||||
value={email}
|
||||
placeholder={t("your_email")}
|
||||
onValid={handleValidEmail}
|
||||
onInvalid={() => setIsValidEmail(false)}
|
||||
/>
|
||||
|
||||
<MainButton
|
||||
className={styles.button}
|
||||
onClick={handleClick}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
{isLoading && <Loader color={LoaderColor.White} />}
|
||||
{!isLoading &&
|
||||
!(!apiError && !error && !isLoading && isAuth) &&
|
||||
t("_continue")}
|
||||
{!apiError && !error && !isLoading && isAuth && (
|
||||
<img
|
||||
className={styles["success-icon"]}
|
||||
src="/SuccessIcon.png"
|
||||
alt="Success Icon"
|
||||
/>
|
||||
)}
|
||||
</MainButton>
|
||||
</>
|
||||
)}
|
||||
{(error || apiError) && (
|
||||
<MainButton
|
||||
className={styles.button}
|
||||
onClick={handleClick}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
{isLoadingSinglePayment && <Loader color={LoaderColor.White} />}
|
||||
{!isLoadingSinglePayment &&
|
||||
!(!apiError && !error && !isLoadingSinglePayment && isAuth) &&
|
||||
t("_continue")}
|
||||
{!apiError && !error && !isLoadingSinglePayment && isAuth && (
|
||||
<img
|
||||
className={styles["success-icon"]}
|
||||
src="/SuccessIcon.png"
|
||||
alt="Success Icon"
|
||||
/>
|
||||
)}
|
||||
</MainButton>
|
||||
</>
|
||||
)}
|
||||
{(error || apiError || errorSinglePayment?.error) && (
|
||||
<Title variant="h3" style={{ color: "red", margin: 0 }}>
|
||||
Something went wrong
|
||||
Something went wrong:{" "}
|
||||
{errorSinglePayment?.error?.length && errorSinglePayment?.error}
|
||||
</Title>
|
||||
)}
|
||||
{apiError && (
|
||||
|
||||
35
src/data/products.ts
Normal file
35
src/data/products.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import routes from "@/routes";
|
||||
|
||||
interface IProductUrls {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export const productUrls: IProductUrls = {
|
||||
"chat.aura": routes.client.advisorChatPrivate(
|
||||
"asst_WWkAlT4Ovs6gKRy6VEn9LqNS"
|
||||
),
|
||||
};
|
||||
|
||||
interface IPaymentResultPathsOfProducts {
|
||||
[key: string]: IPaymentResultPathsOfProduct;
|
||||
}
|
||||
|
||||
interface IPaymentResultPathsOfProduct {
|
||||
success: string;
|
||||
fail: string;
|
||||
}
|
||||
|
||||
export const paymentResultPathsOfProducts: IPaymentResultPathsOfProducts = {
|
||||
"moons.pdf.aura": {
|
||||
success: routes.client.epeSuccessPayment(),
|
||||
fail: routes.client.epeFailPayment(),
|
||||
},
|
||||
"chat.aura": {
|
||||
success: routes.client.advisorChatSuccessPayment(),
|
||||
fail: routes.client.advisorChatFailPayment(),
|
||||
},
|
||||
default: {
|
||||
success: routes.client.paymentSuccess(),
|
||||
fail: routes.client.paymentFail(),
|
||||
},
|
||||
};
|
||||
156
src/hooks/payment/useSinglePayment.ts
Normal file
156
src/hooks/payment/useSinglePayment.ts
Normal file
@ -0,0 +1,156 @@
|
||||
import { SinglePayment, useApi } from "@/api";
|
||||
import { User } from "@/api/resources/User";
|
||||
import { AuthToken } from "@/api/types";
|
||||
import { productUrls } from "@/data/products";
|
||||
import routes from "@/routes";
|
||||
import { getZodiacSignByDate } from "@/services/zodiac-sign";
|
||||
import { selectors } from "@/store";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
interface ICreateSinglePaymentProps {
|
||||
user: User;
|
||||
token: AuthToken;
|
||||
targetProductKey: string;
|
||||
returnUrl: string;
|
||||
}
|
||||
|
||||
interface IErrorSinglePayment {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const useSinglePayment = () => {
|
||||
const api = useApi();
|
||||
const navigate = useNavigate();
|
||||
const [paymentIntent, setPaymentIntent] =
|
||||
useState<SinglePayment.ResponsePost>();
|
||||
const [product, setProduct] = useState<SinglePayment.ResponseGet>();
|
||||
const [error, setError] = useState<IErrorSinglePayment>(
|
||||
{} as IErrorSinglePayment
|
||||
);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { gender } = useSelector(selectors.selectQuestionnaire);
|
||||
const birthday = useSelector(selectors.selectBirthday);
|
||||
|
||||
const getCurrentProduct = useCallback(
|
||||
async (token: AuthToken, targetProductKey: string) => {
|
||||
const productsSinglePayment = await api.getSinglePaymentProducts({
|
||||
token,
|
||||
});
|
||||
const currentProduct = productsSinglePayment.find(
|
||||
(product) => product.key === targetProductKey
|
||||
);
|
||||
return currentProduct;
|
||||
},
|
||||
[api]
|
||||
);
|
||||
|
||||
const handlerPaymentIntentResult = useCallback(
|
||||
(paymentIntent: SinglePayment.ResponsePost, type: string) => {
|
||||
if (!("payment" in paymentIntent)) return;
|
||||
let status = "failed";
|
||||
if (paymentIntent.payment.status === "paid") {
|
||||
status = "succeeded";
|
||||
}
|
||||
return navigate(
|
||||
`${routes.client.paymentResult()}?redirect_status=${status}&redirect_type=${type}`
|
||||
);
|
||||
},
|
||||
[navigate]
|
||||
);
|
||||
|
||||
const checkProductPurchased = useCallback(
|
||||
async (email: string, productKey: string, token: AuthToken) => {
|
||||
try {
|
||||
const purchased = await api.checkProductPurchased({
|
||||
email,
|
||||
productKey,
|
||||
token,
|
||||
});
|
||||
|
||||
if (
|
||||
"active" in purchased &&
|
||||
purchased.active &&
|
||||
productUrls[productKey].length
|
||||
) {
|
||||
return navigate(productUrls[productKey]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
[api, navigate]
|
||||
);
|
||||
|
||||
const createSinglePayment = useCallback(
|
||||
async ({
|
||||
user,
|
||||
token,
|
||||
targetProductKey,
|
||||
returnUrl,
|
||||
}: ICreateSinglePaymentProps) => {
|
||||
setIsLoading(true);
|
||||
const product = await getCurrentProduct(token, targetProductKey);
|
||||
if (!product) {
|
||||
setError({ error: "Product not found" });
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
setProduct(product);
|
||||
await checkProductPurchased(user?.email || "", targetProductKey, token);
|
||||
const paymentIntent = await api.createSinglePayment({
|
||||
token,
|
||||
data: {
|
||||
user: {
|
||||
id: `${user?.id}`,
|
||||
email: user?.email,
|
||||
name: user.username || "",
|
||||
sign:
|
||||
user?.profile?.sign?.sign ||
|
||||
getZodiacSignByDate(user.profile.birthday || birthday || ""),
|
||||
age: user?.profile?.age?.years || 1,
|
||||
gender: user.profile.gender || gender || "",
|
||||
},
|
||||
partner: {
|
||||
sign: null,
|
||||
age: null,
|
||||
},
|
||||
paymentInfo: {
|
||||
productId: product?.productId || "",
|
||||
key: product?.key || "",
|
||||
},
|
||||
return_url: returnUrl,
|
||||
},
|
||||
});
|
||||
if ("message" in paymentIntent) {
|
||||
setError({ error: paymentIntent.message });
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
handlerPaymentIntentResult(paymentIntent, targetProductKey);
|
||||
setPaymentIntent(paymentIntent);
|
||||
setIsLoading(false);
|
||||
return paymentIntent;
|
||||
},
|
||||
[
|
||||
api,
|
||||
birthday,
|
||||
checkProductPurchased,
|
||||
gender,
|
||||
getCurrentProduct,
|
||||
handlerPaymentIntentResult,
|
||||
]
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
product,
|
||||
paymentIntent,
|
||||
createSinglePayment,
|
||||
isLoading,
|
||||
error,
|
||||
}),
|
||||
[product, paymentIntent, createSinglePayment, isLoading, error]
|
||||
);
|
||||
};
|
||||
@ -8,7 +8,6 @@ export const useSchemeColorByElement = (
|
||||
) => {
|
||||
useEffect(() => {
|
||||
const pageElement = element?.querySelectorAll(searchSelectors)[0];
|
||||
console.log("pageElement", pageElement);
|
||||
|
||||
const scheme = document.querySelector('meta[name="theme-color"]');
|
||||
if (scheme && !pageElement) {
|
||||
@ -20,7 +19,6 @@ export const useSchemeColorByElement = (
|
||||
if (colorScheme?.a === 0) {
|
||||
backgroundColor = "#ffffff";
|
||||
}
|
||||
console.log("backgroundColor", backgroundColor);
|
||||
if (scheme && backgroundColor.length) {
|
||||
return scheme.setAttribute("content", backgroundColor);
|
||||
}
|
||||
|
||||
@ -124,7 +124,7 @@ const routes = {
|
||||
|
||||
// Advisors
|
||||
advisors: () => [host, "advisors"].join("/"),
|
||||
advisorChat: (id: number) => [host, "advisors", id].join("/"),
|
||||
advisorChat: (id: string) => [host, "advisors", id].join("/"),
|
||||
// Email - Pay - Email
|
||||
epeGender: () => [host, "epe", "gender"].join("/"),
|
||||
epeBirthdate: () => [host, "epe", "birthdate"].join("/"),
|
||||
@ -132,8 +132,26 @@ const routes = {
|
||||
epeSuccessPayment: () => [host, "epe", "success-payment"].join("/"),
|
||||
epeFailPayment: () => [host, "epe", "fail-payment"].join("/"),
|
||||
|
||||
// Advisor short path
|
||||
advisorChatGender: () => [host, "advisor-chat", "gender"].join("/"),
|
||||
advisorChatBirthdate: () => [host, "advisor-chat", "birthdate"].join("/"),
|
||||
advisorChatBirthtime: () => [host, "advisor-chat", "birthtime"].join("/"),
|
||||
advisorChatBirthPlace: () =>
|
||||
[host, "advisor-chat", "birth-place"].join("/"),
|
||||
advisorChatPayment: () => [host, "advisor-chat", "payment"].join("/"),
|
||||
advisorChatSuccessPayment: () =>
|
||||
[host, "advisor-chat", "success-payment"].join("/"),
|
||||
advisorChatFailPayment: () =>
|
||||
[host, "advisor-chat", "fail-payment"].join("/"),
|
||||
advisorChatPrivate: (id?: string) =>
|
||||
[host, "advisor-chat-private", id].join("/"),
|
||||
|
||||
singlePaymentShortPath: (productId?: string) =>
|
||||
[host, "single-payment", productId].join("/"),
|
||||
|
||||
getInformationPartner: () => [host, "get-information-partner"].join("/"),
|
||||
|
||||
loadingPage: () => [host, "loading-page"].join("/"),
|
||||
notFound: () => [host, "404"].join("/"),
|
||||
},
|
||||
server: {
|
||||
@ -194,6 +212,10 @@ const routes = {
|
||||
dApiTestPaymentProducts: () =>
|
||||
[dApiHost, "payment", "test", "products"].join("/"),
|
||||
dApiPaymentCheckout: () => [dApiHost, "payment", "checkout"].join("/"),
|
||||
dApiCheckProductPurchased: (productKey: string, email: string) =>
|
||||
[dApiHost, "payment", "products", `${productKey}?email=${email}`].join(
|
||||
"/"
|
||||
),
|
||||
|
||||
assistants: () => [apiHost, prefix, "ai", "assistants.json"].join("/"),
|
||||
setExternalChatIdAssistants: (chatId: string) =>
|
||||
@ -230,6 +252,9 @@ export const entrypoints = [
|
||||
routes.client.trialChoice(),
|
||||
routes.client.palmistry(),
|
||||
routes.client.advisors(),
|
||||
routes.client.advisorChatGender(),
|
||||
routes.client.advisorChatSuccessPayment(),
|
||||
routes.client.advisorChatFailPayment(),
|
||||
];
|
||||
export const isEntrypoint = (path: string) => entrypoints.includes(path);
|
||||
export const isNotEntrypoint = (path: string) => !isEntrypoint(path);
|
||||
@ -318,11 +343,13 @@ export const withoutFooterRoutes = [
|
||||
routes.client.advisors(),
|
||||
routes.client.epeSuccessPayment(),
|
||||
routes.client.getInformationPartner(),
|
||||
routes.client.advisorChatBirthPlace(),
|
||||
];
|
||||
|
||||
export const withoutFooterPartOfRoutes = [
|
||||
routes.client.questionnaire(),
|
||||
routes.client.advisors(),
|
||||
routes.client.advisorChatPrivate(),
|
||||
];
|
||||
|
||||
export const hasNoFooter = (path: string) => {
|
||||
@ -393,6 +420,7 @@ export const withoutHeaderRoutes = [
|
||||
routes.client.advisors(),
|
||||
routes.client.epeSuccessPayment(),
|
||||
routes.client.getInformationPartner(),
|
||||
routes.client.advisorChatPrivate(),
|
||||
];
|
||||
export const hasNoHeader = (path: string) => {
|
||||
let result = true;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user