feat: add breath result page and fix numbers problem

This commit is contained in:
gofnnp 2023-09-07 05:01:19 +04:00
parent 0e430e472f
commit f650878f3e
12 changed files with 189 additions and 22 deletions

View File

@ -16,7 +16,8 @@ import {
PaymentIntents,
AICompatCategories,
AICompats,
AIRequests
AIRequests,
UserCallbacks
} from './resources'
const api = {
@ -39,6 +40,8 @@ const api = {
getAiCompatCategories: createMethod<AICompatCategories.Payload, AICompatCategories.Response>(AICompatCategories.createRequest),
getAiCompat: createMethod<AICompats.Payload, AICompats.Response>(AICompats.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

View 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) });
};

View File

@ -15,3 +15,4 @@ export * as PaymentIntents from './UserPaymentIntents'
export * as AICompatCategories from './AICompatCategories'
export * as AICompats from './AICompats'
export * as AIRequests from './AIRequests'
export * as UserCallbacks from './UserCallbacks'

View File

@ -28,6 +28,7 @@ import BreathPage from '../BreathPage'
import PriceListPage from '../PriceListPage'
import CompatResultPage from '../CompatResultPage'
import HomePage from '../HomePage'
import UserCallbacksPage from '../UserCallbacksPage'
function App(): JSX.Element {
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.priceList()} element={<PriceListPage />} />
<Route path={routes.client.home()} element={<HomePage />} />
<Route path={routes.client.breathResult()} element={<UserCallbacksPage />} />
<Route element={<PrivateOutlet />}>
<Route path={routes.client.subscription()} element={<SubscriptionPage />} />
<Route path={routes.client.paymentMethod()} element={<PaymentPage />} />

View File

@ -11,7 +11,7 @@ function BreathCircle(): JSX.Element {
const navigate = useNavigate()
const [text, setText] = useState(t(''))
const [counter, setCounter] = useState(0)
const handleNext = () => navigate(routes.client.compatibility())
const handleNext = () => navigate(routes.client.breathResult())
useEffect(() => {

View File

@ -14,7 +14,7 @@ const colors: Record<string, string> = {
}
const valueFormatter = (value: number) => {
return `${value * 100}%`
return `${(value * 100).toFixed()}%`
}
function EnergyValues({ className, values }: IEnergyValues): JSX.Element {

View File

@ -8,7 +8,11 @@
.energy-value {
display: flex;
flex-direction: row;
gap: 2px;
gap: 4px;
font-size: 14px;
font-weight: 600;
}
.energy-value__label {
font-weight: 500;
}

View File

@ -2,16 +2,9 @@ import { useNavigate } from "react-router-dom";
import { useTranslation } from "react-i18next";
import routes from "@/routes";
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 { Asset } from "@/api/resources/Assets";
import { useCallback, useEffect, useState } from "react";
import { getRandomArbitrary } from "@/services/random-value";
import BlurringSubstrate from "../BlurringSubstrate";
import EnergyValues from "../EnergyValues";
import { UserAura } from "@/api/resources/Auras";
@ -39,19 +32,19 @@ function HomePage(): JSX.Element {
navigate(routes.client.breath());
};
const { i18n } = useTranslation();
const locale = i18n.language;
const birthdate = useSelector(selectors.selectBirthdate);
const zodiacSign = getZodiacSignByDate(birthdate);
// const { i18n } = useTranslation();
// const locale = i18n.language;
// const birthdate = useSelector(selectors.selectBirthdate);
// const zodiacSign = getZodiacSignByDate(birthdate);
const [asset, setAsset] = useState<Asset>();
const api = useApi();
const assetsData = useCallback(async () => {
const { asset_categories } = await api.getAssetCategories({ locale });
const categoryId = getCategoryIdByZodiacSign(zodiacSign, asset_categories);
// const { asset_categories } = await api.getAssetCategories({ locale });
// const categoryId = getCategoryIdByZodiacSign(zodiacSign, asset_categories);
const { assets } = await api.getAssets({ category: String("10" || "1") });
return assets;
}, [api, locale, zodiacSign]);
}, [api]);
const {
data: assets,
@ -74,9 +67,6 @@ function HomePage(): JSX.Element {
// isPending
} = useApiCall<UserAura>(auraData);
console.log(aura);
return (
<section
className={`${styles.page} page`}

View File

@ -16,7 +16,7 @@ function PriceListPage(): JSX.Element {
const email = useSelector(selectors.selectEmail)
const handleNext = () => {
navigate(routes.client.breath())
navigate(routes.client.home())
}
return (

View 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;

View 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;
}

View File

@ -25,6 +25,7 @@ const routes = {
breath: () => [host, 'breath'].join('/'),
priceList: () => [host, 'price-list'].join('/'),
home: () => [host, 'home'].join('/'),
breathResult: () => [host, 'breath', 'result'].join('/'),
},
server: {
user: () => [apiHost, prefix, 'user.json'].join('/'),
@ -60,6 +61,7 @@ export const entrypoints = [
routes.client.breath(),
routes.client.compatibilityResult(),
routes.client.home(),
routes.client.breathResult(),
]
export const isEntrypoint = (path: string) => entrypoints.includes(path)
export const isNotEntrypoint = (path: string) => !isEntrypoint(path)
@ -81,6 +83,7 @@ export const withoutFooterRoutes = [
routes.client.priceList(),
routes.client.compatibilityResult(),
routes.client.home(),
routes.client.breathResult(),
]
export const hasNoFooter = (path: string) => !withoutFooterRoutes.includes(path)