diff --git a/public/ButtonReload.svg b/public/ButtonReload.svg new file mode 100644 index 0000000..d486126 --- /dev/null +++ b/public/ButtonReload.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/ButtonSave.svg b/public/ButtonSave.svg new file mode 100644 index 0000000..0178cc2 --- /dev/null +++ b/public/ButtonSave.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/api/resources/AIRequestsV2.ts b/src/api/resources/AIRequestsV2.ts index d52398c..67e6a74 100644 --- a/src/api/resources/AIRequestsV2.ts +++ b/src/api/resources/AIRequestsV2.ts @@ -8,9 +8,52 @@ const dateFormatter = (date: string): string => { .join("-"); }; +interface IBestiesHoroscopePayload { + birthDate: string; + sign: string; +} + +interface IMyHoroscopePayload { + birthDate: string; + sign: string; + period: string; +} + +interface IPredictionMoonsPayload { + birthDate: string; + sign: string; + period: string; +} + +interface IThermalPayload { + birthDate: string; + sign: string; + period: string; +} + +interface IMoonPhaseTrackerPayload { + birthDate: string; + sign: string; + period: string; +} + +interface IEnergyVampirismPayload { + birthDate: string; + sign: string; + period: string; +} + +export type AIRequestPayload = + | IBestiesHoroscopePayload + | IPredictionMoonsPayload + | IMyHoroscopePayload + | IThermalPayload + | IMoonPhaseTrackerPayload + | IEnergyVampirismPayload; + export interface Payload { promptKey: string; - aiRequest: IAIRequestPayload; + aiRequest: AIRequestPayload; token: string; } @@ -19,11 +62,6 @@ export interface PayloadGet { token: string; } -interface IAIRequestPayload { - birthDate: string; - sign: string; -} - export interface Response { ai_request: IAiResponse; meta: IMeta; @@ -74,18 +112,42 @@ export interface IAIRequest { }; } +const getBody = (aiRequest: AIRequestPayload, promptKey: string): string => { + if (["horoscope_besties"].includes(promptKey)) { + return JSON.stringify({ + ai_request: { + birth_date: dateFormatter(aiRequest.birthDate), + sign: aiRequest.sign, + }, + }); + } + if ( + [ + "horoscope_name", + "prediction_moons", + "compatibility_thermal", + "moonse_phase", + "energy_vampirism", + ].includes(promptKey) + ) { + return JSON.stringify({ + ai_request: { + birth_date: dateFormatter(aiRequest.birthDate), + sign: aiRequest.sign, + period: (aiRequest as IPredictionMoonsPayload).period, + }, + }); + } + return JSON.stringify({ aiRequest }); +}; + 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, - }, - }); + const body = getBody(aiRequest, promptKey); return new Request(url, { method: "POST", headers: getAuthHeaders(token), diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 1bb954c..8afb617 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -53,6 +53,12 @@ import AuthPage from "../AuthPage"; import AuthResultPage from "../AuthResultPage"; import MagicBallPage from "../pages/MagicBall"; import BestiesHoroscopeResult from "../pages/BestiesHoroscopeResult"; +import PredictionMoonResult from "../pages/PredictionMoonResult"; +import MyHoroscopeResult from "../pages/MyHoroscopeResult"; +import ThermalResult from "../pages/ThermalResult"; +import MoonPhaseTrackerResult from "../pages/MoonPhaseTrackerResult"; +import EnergyVampirismResult from "../pages/EnergyVampirismResult"; +import NameHoroscopeResult from "../pages/NameHoroscopeResult"; function App(): JSX.Element { const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState(false); @@ -225,6 +231,30 @@ function App(): JSX.Element { path={routes.client.horoscopeBestiesResult()} element={} /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> } /> @@ -351,7 +381,6 @@ function PrivateOutlet(): JSX.Element { function PrivateSubscriptionOutlet(): JSX.Element { // const isProduction = import.meta.env.MODE === "production"; const isProduction = false; - console.log(isProduction); const status = useSelector(selectors.selectStatus); return status === "subscribed" || !isProduction ? ( diff --git a/src/components/EnergyVampirism/index.tsx b/src/components/EnergyVampirism/index.tsx new file mode 100644 index 0000000..71d5773 --- /dev/null +++ b/src/components/EnergyVampirism/index.tsx @@ -0,0 +1,52 @@ +import { useTranslation } from "react-i18next"; +import styles from "./styles.module.css"; +import { useApi } from "@/api"; +import { getRandomArbitrary } from "@/services/random-value"; +import { useEffect, useState } from "react"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; + +interface IEnergyVampirismProps { + onClick: () => void; +} + +function EnergyVampirism({ onClick }: IEnergyVampirismProps): JSX.Element { + const api = useApi(); + const { t } = useTranslation(); + const [backgroundUrl, setBackgroundUrl] = useState(""); + const name = useSelector(selectors.selectUser).username; + + const getImage = async () => { + try { + const categoryId = "au.energy_vampirism"; + 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); + } + }; + + useEffect(() => { + getImage(); + }, []); + + return ( +
+

Today

+

+ {name?.length ? `${name}, ` : ""} + Discover the {t("Find out if you're an energy vampire today or not?")} +

+ {/*
*/} +
+ ); +} + +export default EnergyVampirism; diff --git a/src/components/EnergyVampirism/styles.module.css b/src/components/EnergyVampirism/styles.module.css new file mode 100644 index 0000000..ea3a30e --- /dev/null +++ b/src/components/EnergyVampirism/styles.module.css @@ -0,0 +1,29 @@ +.container { + width: 100%; + height: 410px; + border-radius: 17px; + background-color: #000; + background-position: center; + background-repeat: no-repeat; + background-size: cover; + padding: 18px; + display: flex; + flex-direction: column; + justify-content: flex-end; + align-items: flex-start; + gap: 16px; +} + +.period { + font-size: 17px; + font-weight: 500; + line-height: 20px; + color: #b1b0b0; +} + +.text { + font-size: 23px; + font-weight: 500; + line-height: 27px; + color: #fff; +} diff --git a/src/components/HomePage/index.tsx b/src/components/HomePage/index.tsx index 93159d6..44a4339 100644 --- a/src/components/HomePage/index.tsx +++ b/src/components/HomePage/index.tsx @@ -16,13 +16,22 @@ import { import { actions, selectors } from "@/store"; import { getRandomArbitrary } from "@/services/random-value"; import Title from "../Title"; -import { UserDailyForecast } from "@/api/resources/UserDailyForecasts"; +// import { UserDailyForecast } from "@/api/resources/UserDailyForecasts"; import { EPathsFromHome } from "@/store/siteConfig"; import { buildFilename, saveFile } from "../WallpaperPage/utils"; import Onboarding from "../Onboarding"; import TextWithFinger from "../TextWithFinger"; import Slider from "../Slider"; import BestiesHoroscopeSlider, { Horoscope } from "../BestiesHoroscopeSlider"; +import PredictionMoonsSlider, { + IPredictionMoon, +} from "../PredictionMoonsSlider"; +import { predictionMoonsPeriods } from "@/data"; +import WallpapersZodiacSign from "../WallpapersZodiacSign"; +import ThermalSlider from "../ThermalSlider"; +import MoonPhaseTracker from "../MoonPhaseTracker"; +import EnergyVampirism from "../EnergyVampirism"; +import NameHoroscopeSlider from "../NameHoroscopeSlider"; const buttonTextFormatter = (text: string): JSX.Element => { const sentences = text.split("."); @@ -37,16 +46,23 @@ const buttonTextFormatter = (text: string): JSX.Element => { function HomePage(): JSX.Element { const token = useSelector(selectors.selectToken); - const { t } = useTranslation(); const navigate = useNavigate(); const dispatch = useDispatch(); const buttonsRef = useRef(null); + const { i18n, t } = useTranslation(); + const locale = i18n.language; + const birthdate = useSelector(selectors.selectBirthdate); + const zodiacSign = getZodiacSignByDate(birthdate); + const [asset, setAsset] = useState(); + const [moonsAssets, setMoonsAssets] = useState([]); + const api = useApi(); + const homeConfig = useSelector(selectors.selectHome); const isShowNavbar = homeConfig.isShowNavbar; const onboardingConfigHome = useSelector(selectors.selectOnboardingHome); - const bestiesHoroscopes = useSelector(selectors.selectCompatibilities); + const compatibilities = useSelector(selectors.selectCompatibilities); const [isShowOnboardingHome, setIsShowOnboardingHome] = useState( !onboardingConfigHome?.isShown @@ -80,17 +96,14 @@ function HomePage(): JSX.Element { navigate(routes.client.breath()); }; + const handleMyHoroscope = () => { + navigate(routes.client.myHoroscopeResult()); + }; + const handleMagicBall = () => { navigate(routes.client.magicBall()); }; - const { i18n } = useTranslation(); - const locale = i18n.language; - const birthdate = useSelector(selectors.selectBirthdate); - const zodiacSign = getZodiacSignByDate(birthdate); - const [asset, setAsset] = useState(); - const api = useApi(); - const assetsData = useCallback(async () => { const { asset_categories } = await api.getAssetCategories({ locale }); const categoryId = getCategoryIdByZodiacSign(zodiacSign, asset_categories); @@ -121,15 +134,32 @@ function HomePage(): JSX.Element { // isPending } = useApiCall(auraData); - const dailyForecastData = useCallback(async () => { - const { user_daily_forecast } = await api.getDailyForecasts({ token }); - return user_daily_forecast; - }, [api, token]); + // const dailyForecastData = useCallback(async () => { + // const { user_daily_forecast } = await api.getDailyForecasts({ token }); + // return user_daily_forecast; + // }, [api, token]); - const { - data: dailyForecast, - // isPending - } = useApiCall(dailyForecastData); + // const { + // data: dailyForecast, + // // isPending + // } = useApiCall(dailyForecastData); + + useEffect(() => { + getMoonsImages(); + }, []); + + const getMoonsImages = async () => { + const assets = ( + await api.getAssets({ category: String("au.prediction_moons" || "1") }) + ).assets; + const randomAssets: Asset[] = []; + for (let i = 0; i < predictionMoonsPeriods.length; i++) { + const index = getRandomArbitrary(0, assets.length - 1); + randomAssets.push(assets[index]); + assets.splice(index, 1); + } + setMoonsAssets(randomAssets); + }; const downloadImg = () => { if (!asset) return; @@ -143,6 +173,30 @@ function HomePage(): JSX.Element { ); }; + const handleThermal = (item: Horoscope) => { + const { name, birthDate } = item; + navigate( + `${routes.client.thermalResult()}?name=${name}&birthDate=${birthDate}` + ); + }; + + const handlePredictionMoon = (item: IPredictionMoon) => { + const { period } = item; + navigate(`${routes.client.predictionMoonResult()}?period=${period}`); + }; + + const handleMoonPhaseTracker = () => { + navigate(`${routes.client.moonPhaseTracker()}?period=today`); + }; + + const handleEnergyVampirism = () => { + navigate(`${routes.client.energyVampirismResult()}?period=today`); + }; + + const handleNameHoroscope = (item: IPredictionMoon) => { + navigate(`${routes.client.nameHoroscopeResult()}?period=${item.period}`); + }; + return (
{buttonTextFormatter(t("aura-10_breath-button"))} + + {buttonTextFormatter( + t("Receive an In-Depth Analysis and Today’s Horoscope") + )} +
-
+
{"Your Besties' Horoscope"} - {bestiesHoroscopes.map((item, index) => ( + {compatibilities.map((item, index) => (
+
+ + {"Prediction Based on Your Moons"} + + + {predictionMoonsPeriods.map((item, index) => ( + { + handlePredictionMoon(item); + }} + /> + ))} + +
{/* END SLIDERS */} -
+ + {"Energy Vampirism"} + + + + {/* SLIDERS */} +
+
+ + {t("Your Name's Horoscope")} + + + {predictionMoonsPeriods.map((item, index) => ( + { + handleNameHoroscope(item); + }} + /> + ))} + +
+
+ {/* END SLIDERS */} + + + {"AI-based unique Walpapers"} + + + + {/* SLIDERS */} +
+
+ + {t("au.thermal_compatibility.result_title")} + + + {compatibilities.map((item, index) => ( + { + handleThermal(item); + }} + /> + ))} + +
+
+ {/* END SLIDERS */} + + + + {/*
{dailyForecast && dailyForecast.forecasts.map((forecast, index) => (
- {/* {forecast.category} */} {t("aura.personal_aura.button")}

@@ -259,7 +394,7 @@ function HomePage(): JSX.Element {

))} -
+
*/}
{/*
*/}
diff --git a/src/components/HomePage/styles.module.css b/src/components/HomePage/styles.module.css index b87bb9a..7c0632f 100644 --- a/src/components/HomePage/styles.module.css +++ b/src/components/HomePage/styles.module.css @@ -187,6 +187,7 @@ text-align: left; padding: 0 12px; margin-bottom: 12px; + width: 100%; } @keyframes pulse { diff --git a/src/components/MoonPhaseTracker/index.tsx b/src/components/MoonPhaseTracker/index.tsx new file mode 100644 index 0000000..48cc728 --- /dev/null +++ b/src/components/MoonPhaseTracker/index.tsx @@ -0,0 +1,53 @@ +import { useTranslation } from "react-i18next"; +import styles from "./styles.module.css"; +import { useApi } from "@/api"; +import { getRandomArbitrary } from "@/services/random-value"; +import { useEffect, useState } from "react"; + +interface IMoonPhaseTracker { + onClick: () => void; +} + +function MoonPhaseTracker({ onClick }: IMoonPhaseTracker): JSX.Element { + const api = useApi(); + const { t } = useTranslation(); + const [backgroundUrl, setBackgroundUrl] = useState(""); + + const getImage = async () => { + try { + const categoryId = "au.moonse_phase"; + 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); + } + }; + + useEffect(() => { + getImage(); + }, []); + + const removeLastWord = (sentence: string): string => { + const lastIndex = sentence.lastIndexOf(" "); + return sentence.substring(0, lastIndex); + }; + + return ( +
+

Today

+

+ Discover the {removeLastWord(t("au.moonse_phase.result_title"))} +

+ {/*
*/} +
+ ); +} + +export default MoonPhaseTracker; diff --git a/src/components/MoonPhaseTracker/styles.module.css b/src/components/MoonPhaseTracker/styles.module.css new file mode 100644 index 0000000..5a0ad0c --- /dev/null +++ b/src/components/MoonPhaseTracker/styles.module.css @@ -0,0 +1,38 @@ +.container { + position: relative; + width: 100%; + height: 300px; + display: flex; + flex-direction: column; + justify-content: flex-end; + align-items: flex-start; + gap: 16px; + background-color: #000; + background-position: center; + background-repeat: no-repeat; + background-size: cover; + border-radius: 17px; + padding: 16px; +} + +.text { + position: relative; + color: #fff; + font-size: 17px; + line-height: 20px; + font-weight: 500; + z-index: 2; +} + +.blur { + content: ""; + position: absolute; + left: 0; + bottom: 0; + height: 35%; + width: 100%; + filter: blur(10px); + z-index: 1; + -webkit-backdrop-filter: blur(50px); + backdrop-filter: blur(50px); +} diff --git a/src/components/NameHoroscopeSlider/index.tsx b/src/components/NameHoroscopeSlider/index.tsx new file mode 100644 index 0000000..481a969 --- /dev/null +++ b/src/components/NameHoroscopeSlider/index.tsx @@ -0,0 +1,67 @@ +import { useEffect, useState } from "react"; +import styles from "./styles.module.css"; +import { + getCategoryIdByZodiacSign, + getZodiacSignByDate, +} from "@/services/zodiac-sign"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import { useTranslation } from "react-i18next"; +import { useApi } from "@/api"; +import { getRandomArbitrary } from "@/services/random-value"; + +interface INameHoroscopeSliderProps { + data: INameHoroscope; + onClick: () => void; +} + +export interface INameHoroscope { + period: string; + name: string; +} + +function NameHoroscopeSlider({ + data, + onClick, +}: INameHoroscopeSliderProps): JSX.Element { + const api = useApi(); + const { i18n } = useTranslation(); + const locale = i18n.language; + const birthDate = useSelector(selectors.selectBirthdate); + const zodiacSign = getZodiacSignByDate(birthDate); + const [backgroundUrl, setBackgroundUrl] = useState(""); + + 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); + } + })(); + }, []); + + return ( +
+

{data.period}

+

+ {data.name} +

+
+ ); +} + +export default NameHoroscopeSlider; diff --git a/src/components/NameHoroscopeSlider/styles.module.css b/src/components/NameHoroscopeSlider/styles.module.css new file mode 100644 index 0000000..4213464 --- /dev/null +++ b/src/components/NameHoroscopeSlider/styles.module.css @@ -0,0 +1,35 @@ +.container { + width: 280px; + height: 200px; + border-radius: 100px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-end; + padding: 24px 12px; + background-repeat: no-repeat !important; + background-size: cover !important; + background-position: center !important; + background-color: #000 !important; + cursor: pointer; +} + +.period { + color: #b1b0b0; + font-weight: 500; + font-size: 17px; + line-height: 20px; + text-align: center; +} + +.period::first-letter { + text-transform: uppercase; +} + +.name { + color: #fff; + font-weight: 700; + font-size: 23px; + line-height: 27px; + text-align: center; +} diff --git a/src/components/PredictionMoonsSlider/index.tsx b/src/components/PredictionMoonsSlider/index.tsx new file mode 100644 index 0000000..330b54b --- /dev/null +++ b/src/components/PredictionMoonsSlider/index.tsx @@ -0,0 +1,31 @@ +import styles from "./styles.module.css"; + +interface IPredictionMoonsSliderProps { + image: string; + data: IPredictionMoon; + onClick: () => void; +} + +export interface IPredictionMoon { + period: string; +} + +function PredictionMoonsSlider({ + image, + data, + onClick, +}: IPredictionMoonsSliderProps): JSX.Element { + return ( +
+

+ {data.period} +

+
+ ); +} + +export default PredictionMoonsSlider; diff --git a/src/components/PredictionMoonsSlider/styles.module.css b/src/components/PredictionMoonsSlider/styles.module.css new file mode 100644 index 0000000..ee60751 --- /dev/null +++ b/src/components/PredictionMoonsSlider/styles.module.css @@ -0,0 +1,32 @@ +.container { + width: 200px; + height: 200px; + border-radius: 17px; + display: flex; + align-items: flex-end; + justify-content: start; + padding: 8px 12px; + background-repeat: no-repeat !important; + background-size: cover !important; + background-position: center !important; + background-color: #000 !important; + cursor: pointer; +} + +.text { + color: #fff; + font-weight: 400; + font-size: 17px; + line-height: 20px; + text-align: left; +} + +.text::first-letter { + text-transform: uppercase; +} + +.text > b { + color: #fff; + font-weight: 700; + font-size: 18px; +} diff --git a/src/components/ThermalSlider/index.tsx b/src/components/ThermalSlider/index.tsx new file mode 100644 index 0000000..398f3c7 --- /dev/null +++ b/src/components/ThermalSlider/index.tsx @@ -0,0 +1,61 @@ +import { useTranslation } from "react-i18next"; +import styles from "./styles.module.css"; +import { + getCategoryIdByZodiacSign, + getZodiacSignByDate, +} from "@/services/zodiac-sign"; +import { useEffect, useState } from "react"; +import { useApi } from "@/api"; +import { getRandomArbitrary } from "@/services/random-value"; + +interface ThermalProps { + data: Thermal; + onClick: () => void; +} + +export interface Thermal { + name: string; + birthDate: string; +} + +function ThermalSlider({ data, onClick }: ThermalProps): JSX.Element { + const api = useApi(); + const { i18n } = useTranslation(); + const locale = i18n.language; + const zodiacSign = getZodiacSignByDate(data.birthDate); + const [backgroundUrl, setBackgroundUrl] = useState(""); + + 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); + } + })(); + }, []); + + return ( +
+ background image +
+

with {data.name}

+
+ Today +
+ ); +} + +export default ThermalSlider; diff --git a/src/components/ThermalSlider/styles.module.css b/src/components/ThermalSlider/styles.module.css new file mode 100644 index 0000000..9d59b02 --- /dev/null +++ b/src/components/ThermalSlider/styles.module.css @@ -0,0 +1,58 @@ +.container { + position: relative; + padding: 4px; + margin: 26px 0; + width: 200px; + height: 200px; + border-radius: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + background-repeat: no-repeat !important; + background-size: cover !important; + background-position: center !important; + background-color: #000 !important; + cursor: pointer; +} + +.background { + position: absolute; + top: -15px; + left: -15px; + width: calc(100% + 30px); + height: calc(100% + 30px); + object-fit: cover; + border-radius: 100%; + filter: blur(9px); +} + +.name-container { + position: relative; + z-index: 100; + margin-top: 28px; + display: flex; + flex-direction: column; + justify-content: center; + width: 100%; + height: 64px; + text-align: center; + color: #000; + text-overflow: ellipsis; + overflow: hidden; + font-weight: 600; + font-size: 21px; + line-height: 25px; + background-color: #d9d9d985; + border-radius: 100px; +} + +.period { + position: relative; + z-index: 100; + color: #fff; + font-size: 17px; + line-height: 20px; + text-align: center; +} diff --git a/src/components/WallpapersZodiacSign/index.tsx b/src/components/WallpapersZodiacSign/index.tsx new file mode 100644 index 0000000..9a8c19c --- /dev/null +++ b/src/components/WallpapersZodiacSign/index.tsx @@ -0,0 +1,64 @@ +import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { useAuth } from "@/auth"; +import { useApi, Assets } from "@/api"; +import { saveFile, buildFilename } from "../WallpaperPage/utils"; +import styles from "./styles.module.css"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import { + getCategoryIdByZodiacSign, + getZodiacSignByDate, +} from "@/services/zodiac-sign"; +import { getRandomArbitrary } from "@/services/random-value"; + +function WallpapersZodiacSign(): JSX.Element { + const api = useApi(); + const { i18n } = useTranslation(); + const locale = i18n.language; + const [asset, setAsset] = useState(); + + const { + user, + // token + } = useAuth(); + const birthdate = useSelector(selectors.selectBirthdate); + const zodiacSign = getZodiacSignByDate(birthdate); + const category = user?.profile.sign?.sign || ""; + + const getZodiacWallpaper = async () => { + const { asset_categories } = await api.getAssetCategories({ locale }); + const categoryId = getCategoryIdByZodiacSign(zodiacSign, asset_categories); + const { assets } = await api.getAssets({ + category: String(categoryId || "1"), + }); + const asset = assets[getRandomArbitrary(0, assets.length - 1)]; + setAsset(asset); + }; + + useEffect(() => { + getZodiacWallpaper(); + }, []); + + const saveImage = () => + asset && + saveFile(asset.url.replace("http://", "https://"), buildFilename(category)); + + return ( +
+
+ Save image + Get new image +
+
+ ); +} + +export default WallpapersZodiacSign; diff --git a/src/components/WallpapersZodiacSign/styles.module.css b/src/components/WallpapersZodiacSign/styles.module.css new file mode 100644 index 0000000..e4f97c2 --- /dev/null +++ b/src/components/WallpapersZodiacSign/styles.module.css @@ -0,0 +1,22 @@ +.wallpaper { + width: 100%; + height: 520px; + border-radius: 17px; + background-color: #000; + background-position: center; + background-repeat: no-repeat; + background-size: cover; + padding: 26px 0; + display: flex; + flex-direction: column; + justify-content: flex-end; + align-items: center; +} + +.buttons-container { + width: fit-content; + display: flex; + flex-direction: column; + align-items: center; + gap: 18px; +} diff --git a/src/components/pages/BestiesHoroscopeResult/styles.module.css b/src/components/pages/BestiesHoroscopeResult/styles.module.css index b58138b..1a2f086 100644 --- a/src/components/pages/BestiesHoroscopeResult/styles.module.css +++ b/src/components/pages/BestiesHoroscopeResult/styles.module.css @@ -50,7 +50,7 @@ .cross { width: 24px; height: 24px; - border: solid 2px #bdbdbd; + /* border: solid 2px #bdbdbd; */ border-radius: 100%; rotate: 45deg; cursor: pointer; @@ -62,7 +62,7 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - width: 10px; + width: 20px; height: 2px; background-color: #bdbdbd; } @@ -74,6 +74,6 @@ left: 50%; transform: translate(-50%, -50%); width: 2px; - height: 10px; + height: 20px; background-color: #bdbdbd; } diff --git a/src/components/pages/EnergyVampirismResult/index.tsx b/src/components/pages/EnergyVampirismResult/index.tsx new file mode 100644 index 0000000..5d97782 --- /dev/null +++ b/src/components/pages/EnergyVampirismResult/index.tsx @@ -0,0 +1,165 @@ +import { useTranslation } from "react-i18next"; +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { AIRequestsV2, useApi, useApiCall } from "@/api"; +import { useNavigate } 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 EnergyVampirismResult(): 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 name = useSelector(selectors.selectUser).username; + const birthDate = useSelector(selectors.selectBirthdate); + const zodiacSign = getZodiacSignByDate(birthDate); + const [backgroundUrl, setBackgroundUrl] = useState(""); + const timeoutRef = useRef(); + + const progressBars: ProgressBar[] = [ + { + label: t("au.energy_vampirism.loading1"), + }, + { + label: t("au.energy_vampirism.loading2"), + }, + { + label: t("au.energy_vampirism.loading3"), + }, + { + label: t("au.energy_vampirism.loading4"), + }, + ]; + + useEffect(() => { + return () => { + clearTimeout(timeoutRef.current); + }; + }, []); + + const handleNext = () => { + return navigate(routes.client.home()); + }; + + const loadData = useCallback(async () => { + const payload: AIRequestsV2.Payload = { + aiRequest: { + birthDate, + sign: getZodiacSignByDate(birthDate).toLowerCase(), + }, + promptKey: "energy_vampirism", + 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") { + timeoutRef.current = 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(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 ( +
+ + { + setIsVisualLoading(false); + checkLoading(); + }} + > + + {t("au.energy_vampirism.loading_title")}{" "} + <span className={styles["loading-name"]}>{name}</span> + + <> + + +
+
+
+
+ + <span className={styles["loading-name"]}>{name}</span>{" "} + {t("au.energy_vampirism.result_title")} + +
+

{text}

+
+ ); +} + +export default EnergyVampirismResult; diff --git a/src/components/pages/EnergyVampirismResult/styles.module.css b/src/components/pages/EnergyVampirismResult/styles.module.css new file mode 100644 index 0000000..1a2f086 --- /dev/null +++ b/src/components/pages/EnergyVampirismResult/styles.module.css @@ -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: 20px; + height: 2px; + background-color: #bdbdbd; +} + +.cross::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 2px; + height: 20px; + background-color: #bdbdbd; +} diff --git a/src/components/pages/MoonPhaseTrackerResult/index.tsx b/src/components/pages/MoonPhaseTrackerResult/index.tsx new file mode 100644 index 0000000..c50b2af --- /dev/null +++ b/src/components/pages/MoonPhaseTrackerResult/index.tsx @@ -0,0 +1,157 @@ +import { useTranslation } from "react-i18next"; +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { 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 { getZodiacSignByDate } from "@/services/zodiac-sign"; +import { getRandomArbitrary } from "@/services/random-value"; + +function MoonPhaseTrackerResult(): 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 period = searchParams.get("period"); + const birthDate = useSelector(selectors.selectBirthdate); + const zodiacSign = getZodiacSignByDate(birthDate); + const [backgroundUrl, setBackgroundUrl] = useState(""); + const timeoutRef = useRef(); + + const progressBars: ProgressBar[] = [ + { + label: t("au.moonse_phase.loading1"), + }, + { + label: t("au.moonse_phase.loading2"), + }, + { + label: t("au.moonse_phase.loading3"), + }, + { + label: t("au.moonse_phase.loading4"), + }, + ]; + + useEffect(() => { + return () => { + clearTimeout(timeoutRef.current); + }; + }, []); + + const handleNext = () => { + return navigate(routes.client.home()); + }; + + const loadData = useCallback(async () => { + const payload: AIRequestsV2.Payload = { + aiRequest: { + birthDate, + sign: getZodiacSignByDate(birthDate).toLowerCase(), + period: period?.toLowerCase() || "today", + }, + promptKey: "moonse_phase", + 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") { + timeoutRef.current = 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, period]); + + useApiCall(loadData); + + useEffect(() => { + (async () => { + try { + const categoryId = "au.moonse_phase"; + 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 ( +
+ + { + setIsVisualLoading(false); + checkLoading(); + }} + > + {t("au.moonse_phase.loading_title")} + <> + + +
+
+
+
+ + {t("au.moonse_phase.loading_title")} {period} + +
+

{text}

+
+ ); +} + +export default MoonPhaseTrackerResult; diff --git a/src/components/pages/MoonPhaseTrackerResult/styles.module.css b/src/components/pages/MoonPhaseTrackerResult/styles.module.css new file mode 100644 index 0000000..1a2f086 --- /dev/null +++ b/src/components/pages/MoonPhaseTrackerResult/styles.module.css @@ -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: 20px; + height: 2px; + background-color: #bdbdbd; +} + +.cross::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 2px; + height: 20px; + background-color: #bdbdbd; +} diff --git a/src/components/pages/MyHoroscopeResult/index.tsx b/src/components/pages/MyHoroscopeResult/index.tsx new file mode 100644 index 0000000..5d8ad42 --- /dev/null +++ b/src/components/pages/MyHoroscopeResult/index.tsx @@ -0,0 +1,161 @@ +import { useTranslation } from "react-i18next"; +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { AIRequestsV2, useApi, useApiCall } from "@/api"; +import { useNavigate } 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 MyHoroscopeResult(): 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 birthDate = useSelector(selectors.selectBirthdate); + const zodiacSign = getZodiacSignByDate(birthDate); + const [backgroundUrl, setBackgroundUrl] = useState(""); + const timeoutRef = useRef(); + + const progressBars: ProgressBar[] = [ + { + label: t("au.my_horoscope.loading1"), + }, + { + label: t("au.my_horoscope.loading2"), + }, + { + label: t("au.my_horoscope.loading3"), + }, + { + label: t("au.my_horoscope.loading4"), + }, + ]; + + useEffect(() => { + return () => { + clearTimeout(timeoutRef.current); + }; + }, []); + + const handleNext = () => { + return navigate(routes.client.home()); + }; + + const loadData = useCallback(async () => { + const payload: AIRequestsV2.Payload = { + aiRequest: { + birthDate, + sign: getZodiacSignByDate(birthDate).toLowerCase(), + period: "today", + }, + promptKey: "horoscope_name", + 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") { + timeoutRef.current = 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(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 ( +
+ + { + setIsVisualLoading(false); + checkLoading(); + }} + > + {t("au.my_horoscope.loading_title")} + <> + + +
+
+
+
+ + {t("Receive an In-Depth Analysis and Today’s Horoscope")} + +
+

{text}

+
+ ); +} + +export default MyHoroscopeResult; diff --git a/src/components/pages/MyHoroscopeResult/styles.module.css b/src/components/pages/MyHoroscopeResult/styles.module.css new file mode 100644 index 0000000..1a2f086 --- /dev/null +++ b/src/components/pages/MyHoroscopeResult/styles.module.css @@ -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: 20px; + height: 2px; + background-color: #bdbdbd; +} + +.cross::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 2px; + height: 20px; + background-color: #bdbdbd; +} diff --git a/src/components/pages/NameHoroscopeResult/index.tsx b/src/components/pages/NameHoroscopeResult/index.tsx new file mode 100644 index 0000000..b3a076f --- /dev/null +++ b/src/components/pages/NameHoroscopeResult/index.tsx @@ -0,0 +1,163 @@ +import { useTranslation } from "react-i18next"; +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { 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 NameHoroscopeResult(): 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 period = searchParams.get("period") || "today"; + const birthDate = useSelector(selectors.selectBirthdate); + const zodiacSign = getZodiacSignByDate(birthDate); + const [backgroundUrl, setBackgroundUrl] = useState(""); + const timeoutRef = useRef(); + + const progressBars: ProgressBar[] = [ + { + label: t("au.name_horoscope.loading1"), + }, + { + label: t("au.name_horoscope.loading2"), + }, + { + label: t("au.name_horoscope.loading3"), + }, + { + label: t("au.name_horoscope.loading4"), + }, + ]; + + useEffect(() => { + return () => { + clearTimeout(timeoutRef.current); + }; + }, []); + + const handleNext = () => { + return navigate(routes.client.home()); + }; + + const loadData = useCallback(async () => { + const payload: AIRequestsV2.Payload = { + aiRequest: { + birthDate, + sign: getZodiacSignByDate(birthDate).toLowerCase(), + period, + }, + promptKey: "horoscope_name", + 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") { + timeoutRef.current = 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(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 ( +
+ + { + setIsVisualLoading(false); + checkLoading(); + }} + > + {t("au.name_horoscope.loading_title")} + <> + + +
+
+
+
+ + {t("Receive an In-Depth Analysis and Today’s Horoscope")} + +
+

{text}

+
+ ); +} + +export default NameHoroscopeResult; diff --git a/src/components/pages/NameHoroscopeResult/styles.module.css b/src/components/pages/NameHoroscopeResult/styles.module.css new file mode 100644 index 0000000..1a2f086 --- /dev/null +++ b/src/components/pages/NameHoroscopeResult/styles.module.css @@ -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: 20px; + height: 2px; + background-color: #bdbdbd; +} + +.cross::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 2px; + height: 20px; + background-color: #bdbdbd; +} diff --git a/src/components/pages/PredictionMoonResult/index.tsx b/src/components/pages/PredictionMoonResult/index.tsx new file mode 100644 index 0000000..0aaddb5 --- /dev/null +++ b/src/components/pages/PredictionMoonResult/index.tsx @@ -0,0 +1,156 @@ +import { useTranslation } from "react-i18next"; +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import { useCallback, useEffect, useState } from "react"; +import { 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 PredictionMoonResult(): 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 period = searchParams.get("period"); + const birthDate = useSelector(selectors.selectBirthdate); + const zodiacSign = getZodiacSignByDate(birthDate); + const [backgroundUrl, setBackgroundUrl] = useState(""); + + const progressBars: ProgressBar[] = [ + { + label: t("au.prediction_moons.loading1"), + }, + { + label: t("au.prediction_moons.loading2"), + }, + { + label: t("au.prediction_moons.loading3"), + }, + { + label: t("au.prediction_moons.loading4"), + }, + ]; + + const handleNext = () => { + return navigate(routes.client.home()); + }; + + const loadData = useCallback(async () => { + const payload: AIRequestsV2.Payload = { + aiRequest: { + birthDate, + sign: getZodiacSignByDate(birthDate).toLowerCase(), + period: period?.toLowerCase() || "today", + }, + promptKey: "prediction_moons", + 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, period]); + + useApiCall(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 ( +
+ + { + setIsVisualLoading(false); + checkLoading(); + }} + > + {t("au.prediction_moons.loading_title")} + <> + + +
+
+
+
+ + {t("au.prediction_moons.result_title")} {period} + +
+

{text}

+
+ ); +} + +export default PredictionMoonResult; diff --git a/src/components/pages/PredictionMoonResult/styles.module.css b/src/components/pages/PredictionMoonResult/styles.module.css new file mode 100644 index 0000000..1a2f086 --- /dev/null +++ b/src/components/pages/PredictionMoonResult/styles.module.css @@ -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: 20px; + height: 2px; + background-color: #bdbdbd; +} + +.cross::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 2px; + height: 20px; + background-color: #bdbdbd; +} diff --git a/src/components/pages/ThermalResult/index.tsx b/src/components/pages/ThermalResult/index.tsx new file mode 100644 index 0000000..96f888e --- /dev/null +++ b/src/components/pages/ThermalResult/index.tsx @@ -0,0 +1,160 @@ +import { useTranslation } from "react-i18next"; +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import { useCallback, useEffect, useState } from "react"; +import { 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 ThermalResult(): 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.thermal_compatibility.loading1"), + }, + { + label: t("au.thermal_compatibility.loading2"), + }, + { + label: t("au.thermal_compatibility.loading3"), + }, + { + label: t("au.thermal_compatibility.loading4"), + }, + ]; + + const handleNext = () => { + return navigate(routes.client.home()); + }; + + const loadData = useCallback(async () => { + const payload: AIRequestsV2.Payload = { + aiRequest: { + birthDate, + sign: getZodiacSignByDate(birthDate).toLowerCase(), + period: 'today' + }, + promptKey: "compatibility_thermal", + 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(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 ( +
+ + { + setIsVisualLoading(false); + checkLoading(); + }} + > + + {t("au.thermal_compatibility.loading_title")} + + <> + + +
+
+
+
+ background image + + {t("au.thermal_compatibility.result_title")}{" "} + <span className={styles["loading-name"]}>{name}</span> + +
+

{text}

+
+ ); +} + +export default ThermalResult; diff --git a/src/components/pages/ThermalResult/styles.module.css b/src/components/pages/ThermalResult/styles.module.css new file mode 100644 index 0000000..5a8703a --- /dev/null +++ b/src/components/pages/ThermalResult/styles.module.css @@ -0,0 +1,94 @@ +.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 { + position: relative; + width: 100%; + aspect-ratio: 1 / 1; + color: #fd433f; + border-radius: 100%; + display: flex; + flex-direction: column; + justify-content: flex-end; +} + +.thermal-image { + position: absolute; + top: -15px; + left: -15px; + width: calc(100% + 30px); + height: calc(100% + 30px); + object-fit: cover; + border-radius: 100%; + filter: blur(36px); + z-index: 99; +} + +.title { + position: relative; + z-index: 100; +} + +.text { + font-size: 18px; + line-height: 22px; + font-weight: 400; + padding: 0 8px; + text-align: left; + margin-top: 30px; +} + +.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: 20px; + height: 2px; + background-color: #bdbdbd; +} + +.cross::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 2px; + height: 20px; + background-color: #bdbdbd; +} diff --git a/src/data.ts b/src/data.ts new file mode 100644 index 0000000..63d6478 --- /dev/null +++ b/src/data.ts @@ -0,0 +1,16 @@ +import { IPredictionMoon } from "./components/PredictionMoonsSlider"; + +export const predictionMoonsPeriods: IPredictionMoon[] = [ + { + period: "today", + }, + { + period: "week", + }, + { + period: "month", + }, + { + period: "year", + }, +]; diff --git a/src/routes.ts b/src/routes.ts index 4db4d3d..ed4a9a6 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -34,11 +34,19 @@ const routes = { home: () => [host, "home"].join("/"), breathResult: () => [host, "breath", "result"].join("/"), magicBall: () => [host, "magic-ball"].join("/"), - horoscopeBestiesResult: () => [host, "horoscope", "besties"].join("/"), + horoscopeBestiesResult: () => [host, "horoscope-besties"].join("/"), + predictionMoonResult: () => [host, "prediction-moon"].join("/"), + myHoroscopeResult: () => [host, "my-horoscope"].join("/"), + thermalResult: () => [host, "thermal"].join("/"), + moonPhaseTracker: () => [host, "moon-phase-tracker"].join("/"), + energyVampirismResult: () => [host, "energy-vampirism"].join("/"), + nameHoroscopeResult: () => [host, "name-horoscope"].join("/"), }, server: { - appleAuth: (origin: string) => [apiHost, "auth", "apple", `gate?origin=${origin}`].join("/"), - googleAuth: (origin: string) => [apiHost, "auth", "google", `gate?origin=${origin}`].join("/"), + appleAuth: (origin: string) => + [apiHost, "auth", "apple", `gate?origin=${origin}`].join("/"), + googleAuth: (origin: string) => + [apiHost, "auth", "google", `gate?origin=${origin}`].join("/"), user: () => [apiHost, prefix, "user.json"].join("/"), token: () => [apiHost, prefix, "auth", "token.json"].join("/"), elements: () => [apiHost, prefix, "elements.json"].join("/"), @@ -82,8 +90,12 @@ const routes = { getUserCallbacks: (id: string) => [apiHost, prefix, "user", "callbacks", `${id}.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("/"), + aiRequestsV2: (promptKey: string) => + [apiHost, "api/v2", "ai", "prompts", promptKey, "requests.json"].join( + "/" + ), + getAiRequestsV2: (id: string) => + [apiHost, "api/v2", "ai", "requests", `${id}.json`].join("/"), }, }; @@ -137,6 +149,12 @@ export const withoutFooterRoutes = [ routes.client.paymentStripe(), routes.client.magicBall(), routes.client.horoscopeBestiesResult(), + routes.client.predictionMoonResult(), + routes.client.myHoroscopeResult(), + routes.client.thermalResult(), + routes.client.moonPhaseTracker(), + routes.client.energyVampirismResult(), + routes.client.nameHoroscopeResult(), ]; export const hasNoFooter = (path: string) => !withoutFooterRoutes.includes(path); @@ -161,6 +179,13 @@ export const withoutHeaderRoutes = [ routes.client.paymentFail(), routes.client.magicBall(), routes.client.horoscopeBestiesResult(), + routes.client.predictionMoonResult(), + routes.client.myHoroscopeResult(), + routes.client.myHoroscopeResult(), + routes.client.thermalResult(), + routes.client.moonPhaseTracker(), + routes.client.energyVampirismResult(), + routes.client.nameHoroscopeResult(), ]; export const hasNoHeader = (path: string) => !withoutHeaderRoutes.includes(path);