feat: add besties horoscope result page
This commit is contained in:
parent
0f6f8f7e72
commit
16d79d3f3a
@ -22,7 +22,8 @@ import {
|
|||||||
Zodiacs,
|
Zodiacs,
|
||||||
GoogleAuth,
|
GoogleAuth,
|
||||||
SubscriptionPlans,
|
SubscriptionPlans,
|
||||||
AppleAuth
|
AppleAuth,
|
||||||
|
AIRequestsV2
|
||||||
} from './resources'
|
} from './resources'
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
@ -52,6 +53,8 @@ const api = {
|
|||||||
getUserCallbacks: createMethod<UserCallbacks.PayloadGet, UserCallbacks.Response>(UserCallbacks.createRequestGet),
|
getUserCallbacks: createMethod<UserCallbacks.PayloadGet, UserCallbacks.Response>(UserCallbacks.createRequestGet),
|
||||||
getTranslations: createMethod<Translations.Payload, Translations.Response>(Translations.createRequest),
|
getTranslations: createMethod<Translations.Payload, Translations.Response>(Translations.createRequest),
|
||||||
getZodiacs: createMethod<Zodiacs.Payload, Zodiacs.Response>(Zodiacs.createRequest),
|
getZodiacs: createMethod<Zodiacs.Payload, Zodiacs.Response>(Zodiacs.createRequest),
|
||||||
|
AIRequestsV2: createMethod<AIRequestsV2.Payload, AIRequestsV2.Response>(AIRequestsV2.createRequest),
|
||||||
|
getAIRequestsV2: createMethod<AIRequestsV2.PayloadGet, AIRequestsV2.IAiResponseGet>(AIRequestsV2.createRequestGet),
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiContextValue = typeof api
|
export type ApiContextValue = typeof api
|
||||||
|
|||||||
102
src/api/resources/AIRequestsV2.ts
Normal file
102
src/api/resources/AIRequestsV2.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import routes from "@/routes";
|
||||||
|
import { getAuthHeaders } from "../utils";
|
||||||
|
|
||||||
|
const dateFormatter = (date: string): string => {
|
||||||
|
return date
|
||||||
|
.split("-")
|
||||||
|
.map((item) => item.padStart(2, "0"))
|
||||||
|
.join("-");
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Payload {
|
||||||
|
promptKey: string;
|
||||||
|
aiRequest: IAIRequestPayload;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PayloadGet {
|
||||||
|
id: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IAIRequestPayload {
|
||||||
|
birthDate: string;
|
||||||
|
sign: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Response {
|
||||||
|
ai_request: IAiResponse;
|
||||||
|
meta: IMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IMeta {
|
||||||
|
links: {
|
||||||
|
self: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAiResponse {
|
||||||
|
id: string;
|
||||||
|
prompt_key: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
state: string;
|
||||||
|
is_reused: boolean;
|
||||||
|
finished_at: string;
|
||||||
|
job_id: null;
|
||||||
|
response: IAIRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAiResponseGet {
|
||||||
|
ai_request: IAIRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IAIRequest {
|
||||||
|
id: string;
|
||||||
|
prompt_key: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
state: string;
|
||||||
|
is_reused: boolean;
|
||||||
|
finished_at: string;
|
||||||
|
job_id: unknown;
|
||||||
|
response: {
|
||||||
|
id: string;
|
||||||
|
inputs: {
|
||||||
|
sign: string;
|
||||||
|
birth_date: string;
|
||||||
|
};
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
body: string;
|
||||||
|
has_vocal: boolean;
|
||||||
|
speech: unknown;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createRequest = ({
|
||||||
|
promptKey,
|
||||||
|
aiRequest,
|
||||||
|
token,
|
||||||
|
}: Payload): Request => {
|
||||||
|
const url = new URL(routes.server.aiRequestsV2(promptKey));
|
||||||
|
const body = JSON.stringify({
|
||||||
|
ai_request: {
|
||||||
|
birth_date: dateFormatter(aiRequest.birthDate),
|
||||||
|
sign: aiRequest.sign,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return new Request(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: getAuthHeaders(token),
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createRequestGet = ({ id, token }: PayloadGet): Request => {
|
||||||
|
const url = new URL(routes.server.getAiRequestsV2(id));
|
||||||
|
return new Request(url, {
|
||||||
|
method: "GET",
|
||||||
|
headers: getAuthHeaders(token),
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -21,3 +21,4 @@ export * as Zodiacs from "./Zodiacs";
|
|||||||
export * as GoogleAuth from "./GoogleAuth";
|
export * as GoogleAuth from "./GoogleAuth";
|
||||||
export * as SubscriptionPlans from "./SubscriptionPlans";
|
export * as SubscriptionPlans from "./SubscriptionPlans";
|
||||||
export * as AppleAuth from "./AppleAuth";
|
export * as AppleAuth from "./AppleAuth";
|
||||||
|
export * as AIRequestsV2 from "./AIRequestsV2";
|
||||||
|
|||||||
@ -52,6 +52,7 @@ import { StripePage } from "../StripePage";
|
|||||||
import AuthPage from "../AuthPage";
|
import AuthPage from "../AuthPage";
|
||||||
import AuthResultPage from "../AuthResultPage";
|
import AuthResultPage from "../AuthResultPage";
|
||||||
import MagicBallPage from "../pages/MagicBall";
|
import MagicBallPage from "../pages/MagicBall";
|
||||||
|
import BestiesHoroscopeResult from "../pages/BestiesHoroscopeResult";
|
||||||
|
|
||||||
function App(): JSX.Element {
|
function App(): JSX.Element {
|
||||||
const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState<boolean>(false);
|
const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState<boolean>(false);
|
||||||
@ -179,10 +180,6 @@ function App(): JSX.Element {
|
|||||||
element={<ProtectWallpaperPage />}
|
element={<ProtectWallpaperPage />}
|
||||||
/> */}
|
/> */}
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
|
||||||
path={routes.client.magicBall()}
|
|
||||||
element={<MagicBallPage />}
|
|
||||||
/>
|
|
||||||
<Route element={<PrivateOutlet />}>
|
<Route element={<PrivateOutlet />}>
|
||||||
<Route element={<AuthorizedUserOutlet />}>
|
<Route element={<AuthorizedUserOutlet />}>
|
||||||
<Route
|
<Route
|
||||||
@ -220,6 +217,14 @@ function App(): JSX.Element {
|
|||||||
path={routes.client.wallpaper()}
|
path={routes.client.wallpaper()}
|
||||||
element={<WallpaperPage />}
|
element={<WallpaperPage />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path={routes.client.magicBall()}
|
||||||
|
element={<MagicBallPage />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path={routes.client.horoscopeBestiesResult()}
|
||||||
|
element={<BestiesHoroscopeResult />}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="*" element={<NotFoundPage />} />
|
<Route path="*" element={<NotFoundPage />} />
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { getRandomArbitrary } from "@/services/random-value";
|
|||||||
|
|
||||||
interface BestiesHoroscopeSliderProps {
|
interface BestiesHoroscopeSliderProps {
|
||||||
data: Horoscope;
|
data: Horoscope;
|
||||||
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Horoscope {
|
export interface Horoscope {
|
||||||
@ -19,6 +20,7 @@ export interface Horoscope {
|
|||||||
|
|
||||||
function BestiesHoroscopeSlider({
|
function BestiesHoroscopeSlider({
|
||||||
data,
|
data,
|
||||||
|
onClick
|
||||||
}: BestiesHoroscopeSliderProps): JSX.Element {
|
}: BestiesHoroscopeSliderProps): JSX.Element {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
const { i18n, t } = useTranslation();
|
const { i18n, t } = useTranslation();
|
||||||
@ -51,9 +53,7 @@ function BestiesHoroscopeSlider({
|
|||||||
style={{
|
style={{
|
||||||
background: `url(${backgroundUrl})`,
|
background: `url(${backgroundUrl})`,
|
||||||
}}
|
}}
|
||||||
onLoad={() => {
|
onClick={onClick}
|
||||||
console.log("start");
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<p className={styles.text}>
|
<p className={styles.text}>
|
||||||
{t("au.besties.result")}
|
{t("au.besties.result")}
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
background-size: cover !important;
|
background-size: cover !important;
|
||||||
background-position: center !important;
|
background-position: center !important;
|
||||||
background-color: #000 !important;
|
background-color: #000 !important;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { buildFilename, saveFile } from "../WallpaperPage/utils";
|
|||||||
import Onboarding from "../Onboarding";
|
import Onboarding from "../Onboarding";
|
||||||
import TextWithFinger from "../TextWithFinger";
|
import TextWithFinger from "../TextWithFinger";
|
||||||
import Slider from "../Slider";
|
import Slider from "../Slider";
|
||||||
import BestiesHoroscopeSlider from "../BestiesHoroscopeSlider";
|
import BestiesHoroscopeSlider, { Horoscope } from "../BestiesHoroscopeSlider";
|
||||||
|
|
||||||
const buttonTextFormatter = (text: string): JSX.Element => {
|
const buttonTextFormatter = (text: string): JSX.Element => {
|
||||||
const sentences = text.split(".");
|
const sentences = text.split(".");
|
||||||
@ -136,6 +136,13 @@ function HomePage(): JSX.Element {
|
|||||||
saveFile(asset.url.replace("http://", "https://"), buildFilename("1"));
|
saveFile(asset.url.replace("http://", "https://"), buildFilename("1"));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleBestiesHoroscope = (item: Horoscope) => {
|
||||||
|
const { name, birthDate } = item;
|
||||||
|
navigate(
|
||||||
|
`${routes.client.horoscopeBestiesResult()}?name=${name}&birthDate=${birthDate}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={`${styles.page} page`}
|
className={`${styles.page} page`}
|
||||||
@ -143,6 +150,12 @@ function HomePage(): JSX.Element {
|
|||||||
backgroundImage: `url(${asset?.url.replace("http://", "https://")})`,
|
backgroundImage: `url(${asset?.url.replace("http://", "https://")})`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{/* <div
|
||||||
|
className={styles.background}
|
||||||
|
style={{
|
||||||
|
backgroundImage: `url(${asset?.url.replace("http://", "https://")})`,
|
||||||
|
}}
|
||||||
|
> */}
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<BlurringSubstrate>
|
<BlurringSubstrate>
|
||||||
<div className={styles["header__energies"]}>
|
<div className={styles["header__energies"]}>
|
||||||
@ -214,7 +227,13 @@ function HomePage(): JSX.Element {
|
|||||||
</Title>
|
</Title>
|
||||||
<Slider>
|
<Slider>
|
||||||
{bestiesHoroscopes.map((item, index) => (
|
{bestiesHoroscopes.map((item, index) => (
|
||||||
<BestiesHoroscopeSlider data={item} key={index} />
|
<BestiesHoroscopeSlider
|
||||||
|
data={item}
|
||||||
|
key={index}
|
||||||
|
onClick={() => {
|
||||||
|
handleBestiesHoroscope(item);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</Slider>
|
</Slider>
|
||||||
</div>
|
</div>
|
||||||
@ -242,6 +261,7 @@ function HomePage(): JSX.Element {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* </div> */}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,17 @@
|
|||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-size: calc(100% + 186px);
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
83
src/components/ProgressBarsModal/index.tsx
Normal file
83
src/components/ProgressBarsModal/index.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Title from "../Title";
|
||||||
|
import ProgressBarLine from "../ui/ProgressBarLine";
|
||||||
|
import styles from "./styles.module.css";
|
||||||
|
import Spinner from "../ui/Spinner";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { selectors } from "@/store";
|
||||||
|
|
||||||
|
interface IProgressBarsModalProps {
|
||||||
|
children: JSX.Element[];
|
||||||
|
progressBars: ProgressBar[];
|
||||||
|
className?: string;
|
||||||
|
onEndLoading: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProgressBar {
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getProgressValue = (index: number, value: number) => {
|
||||||
|
const integerDivision = Math.floor(value / 100);
|
||||||
|
if (integerDivision > index) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
if (integerDivision === index) {
|
||||||
|
return value % 100;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
function ProgressBarsModal({
|
||||||
|
progressBars,
|
||||||
|
className,
|
||||||
|
onEndLoading,
|
||||||
|
children,
|
||||||
|
}: React.PropsWithChildren<IProgressBarsModalProps>): JSX.Element {
|
||||||
|
const [progress, setProgress] = useState(0);
|
||||||
|
const homeConfig = useSelector(selectors.selectHome);
|
||||||
|
const showNavbarFooter = homeConfig.isShowNavbar;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (progress >= progressBars.length * 100) {
|
||||||
|
return onEndLoading();
|
||||||
|
}
|
||||||
|
const interval = setTimeout(() => {
|
||||||
|
setProgress((prevProgress) => {
|
||||||
|
return prevProgress + 1;
|
||||||
|
});
|
||||||
|
}, 50);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(interval);
|
||||||
|
};
|
||||||
|
}, [progress, onEndLoading]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className={`${styles.container} ${className || ""}`}
|
||||||
|
style={{ paddingBottom: showNavbarFooter ? "48px" : "16px" }}
|
||||||
|
>
|
||||||
|
<div className={styles.title}>{children[0]}</div>
|
||||||
|
{progressBars.map((progressBar, index) => (
|
||||||
|
<div className={styles["progress-bar"]} key={index}>
|
||||||
|
<Title variant="h4" className={styles["progress-bar__label"]}>
|
||||||
|
{progressBar.label}
|
||||||
|
</Title>
|
||||||
|
<div className={styles["progress-bar__container"]}>
|
||||||
|
<ProgressBarLine
|
||||||
|
containerClassName={styles["progress-bar__line"]}
|
||||||
|
value={getProgressValue(index, progress)}
|
||||||
|
delay={50}
|
||||||
|
/>
|
||||||
|
<span className={styles["progress-bar__percentage"]}>
|
||||||
|
{getProgressValue(index, progress)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<Spinner width="40px" strokeWidth={3} />
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProgressBarsModal;
|
||||||
39
src/components/ProgressBarsModal/styles.module.css
Normal file
39
src/components/ProgressBarsModal/styles.module.css
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
.container {
|
||||||
|
padding: 16px 32px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #fd3761;
|
||||||
|
font-weight: 600;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar__label {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar__container {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar__line {
|
||||||
|
width: calc(100% - 56px);
|
||||||
|
}
|
||||||
159
src/components/pages/BestiesHoroscopeResult/index.tsx
Normal file
159
src/components/pages/BestiesHoroscopeResult/index.tsx
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import Title from "@/components/Title";
|
||||||
|
import styles from "./styles.module.css";
|
||||||
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { selectors } from "@/store";
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
import { AICompats, AIRequestsV2, useApi, useApiCall } from "@/api";
|
||||||
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
import routes from "@/routes";
|
||||||
|
import FullScreenModal from "@/components/FullScreenModal";
|
||||||
|
import ProgressBarsModal, { ProgressBar } from "@/components/ProgressBarsModal";
|
||||||
|
import {
|
||||||
|
getCategoryIdByZodiacSign,
|
||||||
|
getZodiacSignByDate,
|
||||||
|
} from "@/services/zodiac-sign";
|
||||||
|
import { getRandomArbitrary } from "@/services/random-value";
|
||||||
|
|
||||||
|
function BestiesHoroscopeResult(): JSX.Element {
|
||||||
|
const token = useSelector(selectors.selectToken);
|
||||||
|
const { i18n, t } = useTranslation();
|
||||||
|
const locale = i18n.language;
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const api = useApi();
|
||||||
|
const homeConfig = useSelector(selectors.selectHome);
|
||||||
|
const showNavbarFooter = homeConfig.isShowNavbar;
|
||||||
|
const [text, setText] = useState("Loading...");
|
||||||
|
const [isOpenModal, setIsOpenModal] = useState(true);
|
||||||
|
const [isVisualLoading, setIsVisualLoading] = useState(true);
|
||||||
|
const [isDataLoading, setIsDataLoading] = useState(true);
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const name = searchParams.get("name");
|
||||||
|
const birthDate = searchParams.get("birthDate") || "";
|
||||||
|
const zodiacSign = getZodiacSignByDate(birthDate);
|
||||||
|
const [backgroundUrl, setBackgroundUrl] = useState("");
|
||||||
|
|
||||||
|
const progressBars: ProgressBar[] = [
|
||||||
|
{
|
||||||
|
label: t("au.besties.loading1"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("au.besties.loading2"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("au.besties.loading3"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("au.besties.loading4"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
return navigate(routes.client.home());
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadData = useCallback(async () => {
|
||||||
|
const payload: AIRequestsV2.Payload = {
|
||||||
|
aiRequest: {
|
||||||
|
birthDate,
|
||||||
|
sign: getZodiacSignByDate(birthDate).toLowerCase(),
|
||||||
|
},
|
||||||
|
promptKey: "horoscope_besties",
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
const aIRequest = await api.AIRequestsV2(payload);
|
||||||
|
if (aIRequest.ai_request.state !== "ready") {
|
||||||
|
const getAIRequest = async () => {
|
||||||
|
const aIRequestById = await api.getAIRequestsV2({
|
||||||
|
id: aIRequest.ai_request.id,
|
||||||
|
token,
|
||||||
|
});
|
||||||
|
if (aIRequestById.ai_request.state !== "ready") {
|
||||||
|
setTimeout(getAIRequest, 3000);
|
||||||
|
}
|
||||||
|
setText(aIRequestById?.ai_request?.response?.body || "Loading...");
|
||||||
|
setIsDataLoading(false);
|
||||||
|
checkLoading();
|
||||||
|
return aIRequestById.ai_request;
|
||||||
|
};
|
||||||
|
return await getAIRequest();
|
||||||
|
}
|
||||||
|
setIsDataLoading(false);
|
||||||
|
checkLoading();
|
||||||
|
setText(aIRequest?.ai_request?.response?.response?.body || "Loading...");
|
||||||
|
|
||||||
|
return aIRequest?.ai_request?.response;
|
||||||
|
}, [api, token, birthDate]);
|
||||||
|
|
||||||
|
useApiCall<AIRequestsV2.IAIRequest>(loadData);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const { asset_categories } = await api.getAssetCategories({ locale });
|
||||||
|
const categoryId = getCategoryIdByZodiacSign(
|
||||||
|
zodiacSign,
|
||||||
|
asset_categories
|
||||||
|
);
|
||||||
|
const assets = (
|
||||||
|
await api.getAssets({ category: String(categoryId || "1") })
|
||||||
|
).assets;
|
||||||
|
const randomAsset = assets[getRandomArbitrary(0, assets.length - 1)];
|
||||||
|
setBackgroundUrl(randomAsset.url);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error: ", error);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [api, locale, zodiacSign]);
|
||||||
|
|
||||||
|
const getPaddingBottomPage = () => {
|
||||||
|
if (showNavbarFooter) return "164px";
|
||||||
|
return "108px";
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkLoading() {
|
||||||
|
if (isVisualLoading || isDataLoading) {
|
||||||
|
setIsOpenModal(true);
|
||||||
|
} else {
|
||||||
|
setIsOpenModal(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className={`${styles.page} page`}
|
||||||
|
style={{ paddingBottom: getPaddingBottomPage() }}
|
||||||
|
>
|
||||||
|
<FullScreenModal isOpen={isOpenModal}>
|
||||||
|
<ProgressBarsModal
|
||||||
|
progressBars={progressBars}
|
||||||
|
onEndLoading={() => {
|
||||||
|
setIsVisualLoading(false);
|
||||||
|
checkLoading();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Title variant="h2">
|
||||||
|
{t("au.besties.title")}{" "}
|
||||||
|
<span className={styles["loading-name"]}>{name}</span>
|
||||||
|
</Title>
|
||||||
|
<></>
|
||||||
|
</ProgressBarsModal>
|
||||||
|
</FullScreenModal>
|
||||||
|
<div className={styles["cross-container"]}>
|
||||||
|
<div className={styles.cross} onClick={handleNext}></div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={styles["sign-image"]}
|
||||||
|
style={{ backgroundImage: `url(${backgroundUrl})` }}
|
||||||
|
>
|
||||||
|
<Title variant="h2">
|
||||||
|
{t("au.besties.result")}{" "}
|
||||||
|
<span className={styles["loading-name"]}>{name}</span>
|
||||||
|
</Title>
|
||||||
|
</div>
|
||||||
|
<p className={styles.text}>{text}</p>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BestiesHoroscopeResult;
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
.page {
|
||||||
|
position: relative;
|
||||||
|
height: fit-content;
|
||||||
|
min-height: 100vh;
|
||||||
|
flex: auto;
|
||||||
|
/* max-height: -webkit-fill-available; */
|
||||||
|
background-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding-bottom: 180px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-name {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sign-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 446px;
|
||||||
|
color: #fd433f;
|
||||||
|
border-radius: 17px;
|
||||||
|
background-color: #000;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 0 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border: solid 2px #bdbdbd;
|
||||||
|
border-radius: 100%;
|
||||||
|
rotate: 45deg;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 10px;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #bdbdbd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cross::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 2px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: #bdbdbd;
|
||||||
|
}
|
||||||
@ -34,6 +34,7 @@ const routes = {
|
|||||||
home: () => [host, "home"].join("/"),
|
home: () => [host, "home"].join("/"),
|
||||||
breathResult: () => [host, "breath", "result"].join("/"),
|
breathResult: () => [host, "breath", "result"].join("/"),
|
||||||
magicBall: () => [host, "magic-ball"].join("/"),
|
magicBall: () => [host, "magic-ball"].join("/"),
|
||||||
|
horoscopeBestiesResult: () => [host, "horoscope", "besties"].join("/"),
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
appleAuth: (origin: string) => [apiHost, "auth", "apple", `gate?origin=${origin}`].join("/"),
|
appleAuth: (origin: string) => [apiHost, "auth", "apple", `gate?origin=${origin}`].join("/"),
|
||||||
@ -81,6 +82,8 @@ const routes = {
|
|||||||
getUserCallbacks: (id: string) =>
|
getUserCallbacks: (id: string) =>
|
||||||
[apiHost, prefix, "user", "callbacks", `${id}.json`].join("/"),
|
[apiHost, prefix, "user", "callbacks", `${id}.json`].join("/"),
|
||||||
getTranslations: () => [siteHost, "api/v2", "t.json"].join("/"),
|
getTranslations: () => [siteHost, "api/v2", "t.json"].join("/"),
|
||||||
|
aiRequestsV2: (promptKey: string) => [apiHost, "api/v2", "ai", "prompts", promptKey, "requests.json"].join("/"),
|
||||||
|
getAiRequestsV2: (id: string) => [apiHost, "api/v2", "ai", "requests", `${id}.json`].join("/"),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -133,6 +136,7 @@ export const withoutFooterRoutes = [
|
|||||||
routes.client.paymentFail(),
|
routes.client.paymentFail(),
|
||||||
routes.client.paymentStripe(),
|
routes.client.paymentStripe(),
|
||||||
routes.client.magicBall(),
|
routes.client.magicBall(),
|
||||||
|
routes.client.horoscopeBestiesResult(),
|
||||||
];
|
];
|
||||||
export const hasNoFooter = (path: string) =>
|
export const hasNoFooter = (path: string) =>
|
||||||
!withoutFooterRoutes.includes(path);
|
!withoutFooterRoutes.includes(path);
|
||||||
@ -156,6 +160,7 @@ export const withoutHeaderRoutes = [
|
|||||||
routes.client.paymentSuccess(),
|
routes.client.paymentSuccess(),
|
||||||
routes.client.paymentFail(),
|
routes.client.paymentFail(),
|
||||||
routes.client.magicBall(),
|
routes.client.magicBall(),
|
||||||
|
routes.client.horoscopeBestiesResult(),
|
||||||
];
|
];
|
||||||
export const hasNoHeader = (path: string) =>
|
export const hasNoHeader = (path: string) =>
|
||||||
!withoutHeaderRoutes.includes(path);
|
!withoutHeaderRoutes.includes(path);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user