feat: add breath result page and fix numbers problem
This commit is contained in:
parent
0e430e472f
commit
f650878f3e
@ -16,7 +16,8 @@ import {
|
|||||||
PaymentIntents,
|
PaymentIntents,
|
||||||
AICompatCategories,
|
AICompatCategories,
|
||||||
AICompats,
|
AICompats,
|
||||||
AIRequests
|
AIRequests,
|
||||||
|
UserCallbacks
|
||||||
} from './resources'
|
} from './resources'
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
@ -39,6 +40,8 @@ const api = {
|
|||||||
getAiCompatCategories: createMethod<AICompatCategories.Payload, AICompatCategories.Response>(AICompatCategories.createRequest),
|
getAiCompatCategories: createMethod<AICompatCategories.Payload, AICompatCategories.Response>(AICompatCategories.createRequest),
|
||||||
getAiCompat: createMethod<AICompats.Payload, AICompats.Response>(AICompats.createRequest),
|
getAiCompat: createMethod<AICompats.Payload, AICompats.Response>(AICompats.createRequest),
|
||||||
getAiRequest: createMethod<AIRequests.Payload, AIRequests.Response>(AIRequests.createRequest),
|
getAiRequest: createMethod<AIRequests.Payload, AIRequests.Response>(AIRequests.createRequest),
|
||||||
|
createUserCallbacks: createMethod<UserCallbacks.PayloadPost, UserCallbacks.Response>(UserCallbacks.createRequestPost),
|
||||||
|
getUserCallbacks: createMethod<UserCallbacks.PayloadGet, UserCallbacks.Response>(UserCallbacks.createRequestGet),
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiContextValue = typeof api
|
export type ApiContextValue = typeof api
|
||||||
|
|||||||
48
src/api/resources/UserCallbacks.ts
Normal file
48
src/api/resources/UserCallbacks.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import routes from "@/routes";
|
||||||
|
import { getAuthHeaders } from "../utils";
|
||||||
|
|
||||||
|
export interface PayloadGet {
|
||||||
|
id: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PayloadPost {
|
||||||
|
data: {
|
||||||
|
user_callback: {
|
||||||
|
kind: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Response {
|
||||||
|
user_callback: IUserCallbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserCallbacks {
|
||||||
|
id: string;
|
||||||
|
user_id: number;
|
||||||
|
kind: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
prev_stat_changes: IPrevStateChanges[];
|
||||||
|
description: string;
|
||||||
|
is_complete: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPrevStateChanges {
|
||||||
|
stat: string;
|
||||||
|
value: number;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createRequestPost = ({ data, token }: PayloadPost): Request => {
|
||||||
|
const url = new URL(routes.server.createUserCallbacks());
|
||||||
|
const body = JSON.stringify(data);
|
||||||
|
return new Request(url, { method: "POST", headers: getAuthHeaders(token), body });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createRequestGet = ({ id, token }: PayloadGet): Request => {
|
||||||
|
const url = new URL(routes.server.getUserCallbacks(id));
|
||||||
|
return new Request(url, { method: "GET", headers: getAuthHeaders(token) });
|
||||||
|
};
|
||||||
@ -15,3 +15,4 @@ export * as PaymentIntents from './UserPaymentIntents'
|
|||||||
export * as AICompatCategories from './AICompatCategories'
|
export * as AICompatCategories from './AICompatCategories'
|
||||||
export * as AICompats from './AICompats'
|
export * as AICompats from './AICompats'
|
||||||
export * as AIRequests from './AIRequests'
|
export * as AIRequests from './AIRequests'
|
||||||
|
export * as UserCallbacks from './UserCallbacks'
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import BreathPage from '../BreathPage'
|
|||||||
import PriceListPage from '../PriceListPage'
|
import PriceListPage from '../PriceListPage'
|
||||||
import CompatResultPage from '../CompatResultPage'
|
import CompatResultPage from '../CompatResultPage'
|
||||||
import HomePage from '../HomePage'
|
import HomePage from '../HomePage'
|
||||||
|
import UserCallbacksPage from '../UserCallbacksPage'
|
||||||
|
|
||||||
function App(): JSX.Element {
|
function App(): JSX.Element {
|
||||||
const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState<boolean>(false)
|
const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState<boolean>(false)
|
||||||
@ -56,6 +57,7 @@ function App(): JSX.Element {
|
|||||||
<Route path={routes.client.breath()} element={<BreathPage />} />
|
<Route path={routes.client.breath()} element={<BreathPage />} />
|
||||||
<Route path={routes.client.priceList()} element={<PriceListPage />} />
|
<Route path={routes.client.priceList()} element={<PriceListPage />} />
|
||||||
<Route path={routes.client.home()} element={<HomePage />} />
|
<Route path={routes.client.home()} element={<HomePage />} />
|
||||||
|
<Route path={routes.client.breathResult()} element={<UserCallbacksPage />} />
|
||||||
<Route element={<PrivateOutlet />}>
|
<Route element={<PrivateOutlet />}>
|
||||||
<Route path={routes.client.subscription()} element={<SubscriptionPage />} />
|
<Route path={routes.client.subscription()} element={<SubscriptionPage />} />
|
||||||
<Route path={routes.client.paymentMethod()} element={<PaymentPage />} />
|
<Route path={routes.client.paymentMethod()} element={<PaymentPage />} />
|
||||||
|
|||||||
@ -11,7 +11,7 @@ function BreathCircle(): JSX.Element {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [text, setText] = useState(t(''))
|
const [text, setText] = useState(t(''))
|
||||||
const [counter, setCounter] = useState(0)
|
const [counter, setCounter] = useState(0)
|
||||||
const handleNext = () => navigate(routes.client.compatibility())
|
const handleNext = () => navigate(routes.client.breathResult())
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const colors: Record<string, string> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const valueFormatter = (value: number) => {
|
const valueFormatter = (value: number) => {
|
||||||
return `${value * 100}%`
|
return `${(value * 100).toFixed()}%`
|
||||||
}
|
}
|
||||||
|
|
||||||
function EnergyValues({ className, values }: IEnergyValues): JSX.Element {
|
function EnergyValues({ className, values }: IEnergyValues): JSX.Element {
|
||||||
|
|||||||
@ -8,7 +8,11 @@
|
|||||||
.energy-value {
|
.energy-value {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 2px;
|
gap: 4px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.energy-value__label {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
@ -2,16 +2,9 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import routes from "@/routes";
|
import routes from "@/routes";
|
||||||
import styles from "./styles.module.css";
|
import styles from "./styles.module.css";
|
||||||
import { useSelector } from "react-redux";
|
|
||||||
import { selectors } from "@/store";
|
|
||||||
import {
|
|
||||||
getCategoryIdByZodiacSign,
|
|
||||||
getZodiacSignByDate,
|
|
||||||
} from "@/services/zodiac-sign";
|
|
||||||
import { useApi, useApiCall } from "@/api";
|
import { useApi, useApiCall } from "@/api";
|
||||||
import { Asset } from "@/api/resources/Assets";
|
import { Asset } from "@/api/resources/Assets";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { getRandomArbitrary } from "@/services/random-value";
|
|
||||||
import BlurringSubstrate from "../BlurringSubstrate";
|
import BlurringSubstrate from "../BlurringSubstrate";
|
||||||
import EnergyValues from "../EnergyValues";
|
import EnergyValues from "../EnergyValues";
|
||||||
import { UserAura } from "@/api/resources/Auras";
|
import { UserAura } from "@/api/resources/Auras";
|
||||||
@ -39,19 +32,19 @@ function HomePage(): JSX.Element {
|
|||||||
navigate(routes.client.breath());
|
navigate(routes.client.breath());
|
||||||
};
|
};
|
||||||
|
|
||||||
const { i18n } = useTranslation();
|
// const { i18n } = useTranslation();
|
||||||
const locale = i18n.language;
|
// const locale = i18n.language;
|
||||||
const birthdate = useSelector(selectors.selectBirthdate);
|
// const birthdate = useSelector(selectors.selectBirthdate);
|
||||||
const zodiacSign = getZodiacSignByDate(birthdate);
|
// const zodiacSign = getZodiacSignByDate(birthdate);
|
||||||
const [asset, setAsset] = useState<Asset>();
|
const [asset, setAsset] = useState<Asset>();
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
|
|
||||||
const assetsData = useCallback(async () => {
|
const assetsData = useCallback(async () => {
|
||||||
const { asset_categories } = await api.getAssetCategories({ locale });
|
// const { asset_categories } = await api.getAssetCategories({ locale });
|
||||||
const categoryId = getCategoryIdByZodiacSign(zodiacSign, asset_categories);
|
// const categoryId = getCategoryIdByZodiacSign(zodiacSign, asset_categories);
|
||||||
const { assets } = await api.getAssets({ category: String("10" || "1") });
|
const { assets } = await api.getAssets({ category: String("10" || "1") });
|
||||||
return assets;
|
return assets;
|
||||||
}, [api, locale, zodiacSign]);
|
}, [api]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: assets,
|
data: assets,
|
||||||
@ -74,9 +67,6 @@ function HomePage(): JSX.Element {
|
|||||||
// isPending
|
// isPending
|
||||||
} = useApiCall<UserAura>(auraData);
|
} = useApiCall<UserAura>(auraData);
|
||||||
|
|
||||||
console.log(aura);
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={`${styles.page} page`}
|
className={`${styles.page} page`}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ function PriceListPage(): JSX.Element {
|
|||||||
|
|
||||||
const email = useSelector(selectors.selectEmail)
|
const email = useSelector(selectors.selectEmail)
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
navigate(routes.client.breath())
|
navigate(routes.client.home())
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
79
src/components/UserCallbacksPage/index.tsx
Normal file
79
src/components/UserCallbacksPage/index.tsx
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import Title from "../Title";
|
||||||
|
import styles from "./styles.module.css";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { UserCallbacks, useApi, useApiCall } from "@/api";
|
||||||
|
import { IPrevStateChanges } from "@/api/resources/UserCallbacks";
|
||||||
|
|
||||||
|
function UserCallbacksPage(): JSX.Element {
|
||||||
|
const token =
|
||||||
|
"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjIzNjEyLCJpYXQiOjE2OTM0MTg5MTAsImV4cCI6MTcwMjA1ODkxMCwianRpIjoiNzg5MjkwYWItODg0YS00MGUyLTkyNjEtOWI2OGEyNjkwNmE0IiwiZW1haWwiOiJvdGhlckBleGFtcGxlLmNvbSIsInN0YXRlIjoicHJvdmVuIiwibG9jIjoiZW4iLCJ0eiI6LTI4ODAwLCJ0eXBlIjoiZW1haWwiLCJpc3MiOiJjb20ubGlmZS5hdXJhIn0.J2ocWIv5jKzuKMcwMgWMiNMyGg5qLlMAeln-bQm_9lw";
|
||||||
|
const api = useApi();
|
||||||
|
const [text, setText] = useState("Loading...");
|
||||||
|
const [statChanges, setStatChanges] = useState([] as IPrevStateChanges[]);
|
||||||
|
|
||||||
|
const createCallback = useCallback(async () => {
|
||||||
|
const data: UserCallbacks.PayloadPost = {
|
||||||
|
data: {
|
||||||
|
user_callback: {
|
||||||
|
kind: "breathing_end",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
const createCallbackRequest = await api.createUserCallbacks(data);
|
||||||
|
setStatChanges(createCallbackRequest.user_callback.prev_stat_changes);
|
||||||
|
if (!createCallbackRequest.user_callback.is_complete) {
|
||||||
|
const getUserCallbacksRequest = async () => {
|
||||||
|
const getCallback = await api.getUserCallbacks({
|
||||||
|
id: createCallbackRequest.user_callback.id,
|
||||||
|
token,
|
||||||
|
});
|
||||||
|
if (!getCallback.user_callback.is_complete) {
|
||||||
|
setTimeout(getUserCallbacksRequest, 3000);
|
||||||
|
}
|
||||||
|
setText(getCallback.user_callback.description || "Loading...");
|
||||||
|
return getCallback.user_callback;
|
||||||
|
};
|
||||||
|
return await getUserCallbacksRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
return createCallbackRequest.user_callback;
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
useApiCall<UserCallbacks.IUserCallbacks>(createCallback);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={`${styles.page} page`}>
|
||||||
|
<div className={styles["title-container"]}>
|
||||||
|
<Title variant="h3" className={styles.percent}>
|
||||||
|
<>
|
||||||
|
<p>Well done!</p>
|
||||||
|
<p>Your results has changes...</p>
|
||||||
|
</>
|
||||||
|
</Title>
|
||||||
|
{/* <Title variant="h2">{t("you_and", { user: rightUser.name })}</Title> */}
|
||||||
|
</div>
|
||||||
|
<div className={styles["result-container"]}>
|
||||||
|
<div className={styles["result-container__values"]}>
|
||||||
|
{statChanges.map((change, index) => (
|
||||||
|
<div className={styles["result-container__value"]} key={index}>
|
||||||
|
<span className={styles["result-container__value-label"]}>
|
||||||
|
{change.label}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
style={{ color: change.value > 0 ? "#00ea00" : "red" }}
|
||||||
|
className={styles["result-container__value-value"]}
|
||||||
|
>
|
||||||
|
{change.value > 0 ? "+" : ""}
|
||||||
|
{(change.value * 100).toFixed()}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<p className={styles["result-container__text"]}>{text}</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserCallbacksPage;
|
||||||
37
src/components/UserCallbacksPage/styles.module.css
Normal file
37
src/components/UserCallbacksPage/styles.module.css
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
.page {
|
||||||
|
position: relative;
|
||||||
|
height: calc(100vh - 50px);
|
||||||
|
max-height: -webkit-fill-available;
|
||||||
|
flex: auto;
|
||||||
|
background-color: #000;
|
||||||
|
color: #bababb;
|
||||||
|
overflow-y: scroll;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-container__values {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
max-width: 250px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-container__value {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-container__value-label {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-container__value-value {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-container__text {
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
@ -25,6 +25,7 @@ const routes = {
|
|||||||
breath: () => [host, 'breath'].join('/'),
|
breath: () => [host, 'breath'].join('/'),
|
||||||
priceList: () => [host, 'price-list'].join('/'),
|
priceList: () => [host, 'price-list'].join('/'),
|
||||||
home: () => [host, 'home'].join('/'),
|
home: () => [host, 'home'].join('/'),
|
||||||
|
breathResult: () => [host, 'breath', 'result'].join('/'),
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
user: () => [apiHost, prefix, 'user.json'].join('/'),
|
user: () => [apiHost, prefix, 'user.json'].join('/'),
|
||||||
@ -60,6 +61,7 @@ export const entrypoints = [
|
|||||||
routes.client.breath(),
|
routes.client.breath(),
|
||||||
routes.client.compatibilityResult(),
|
routes.client.compatibilityResult(),
|
||||||
routes.client.home(),
|
routes.client.home(),
|
||||||
|
routes.client.breathResult(),
|
||||||
]
|
]
|
||||||
export const isEntrypoint = (path: string) => entrypoints.includes(path)
|
export const isEntrypoint = (path: string) => entrypoints.includes(path)
|
||||||
export const isNotEntrypoint = (path: string) => !isEntrypoint(path)
|
export const isNotEntrypoint = (path: string) => !isEntrypoint(path)
|
||||||
@ -81,6 +83,7 @@ export const withoutFooterRoutes = [
|
|||||||
routes.client.priceList(),
|
routes.client.priceList(),
|
||||||
routes.client.compatibilityResult(),
|
routes.client.compatibilityResult(),
|
||||||
routes.client.home(),
|
routes.client.home(),
|
||||||
|
routes.client.breathResult(),
|
||||||
]
|
]
|
||||||
export const hasNoFooter = (path: string) => !withoutFooterRoutes.includes(path)
|
export const hasNoFooter = (path: string) => !withoutFooterRoutes.includes(path)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user