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, 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

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 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'

View File

@ -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 />} />

View File

@ -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(() => {

View File

@ -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 {

View File

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

View File

@ -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`}

View File

@ -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 (

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('/'), 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)