fix: paths from home page, reviews, profile create, attention page
This commit is contained in:
parent
7fec68157f
commit
22d6c9e1fe
@ -3,8 +3,9 @@ import { useTranslation } from 'react-i18next'
|
||||
import Title from '../Title'
|
||||
import routes from '@/routes'
|
||||
import styles from './styles.module.css'
|
||||
import CheckboxWithText from '../CheckboxWithText'
|
||||
// import CheckboxWithText from '../CheckboxWithText'
|
||||
import SpecialWelcomeOffer from '../SpecialWelcomeOffer'
|
||||
import MainButton from '../MainButton'
|
||||
|
||||
interface AttentionPageProps {
|
||||
isOpenModal: boolean
|
||||
@ -16,20 +17,21 @@ function AttentionPage({ isOpenModal, onCloseSpecialOffer }: AttentionPageProps)
|
||||
const navigate = useNavigate()
|
||||
const handleNext = () => navigate(routes.client.feedback())
|
||||
|
||||
const onChangeCheckbox = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
if (e.currentTarget.checked) {
|
||||
handleNext()
|
||||
}
|
||||
}
|
||||
// const onChangeCheckbox = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
// if (e.currentTarget.checked) {
|
||||
// handleNext()
|
||||
// }
|
||||
// }
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
<SpecialWelcomeOffer open={isOpenModal} onClose={onCloseSpecialOffer} />
|
||||
<img className={styles.icon} src="/stop-icon.png" alt="stop" />
|
||||
<Title variant='h2'>{t('attention')}</Title>
|
||||
<p className={styles.text}>{t('attention_page_text')}</p>
|
||||
<Title variant='h2'>{t('aura.attention.title')}</Title>
|
||||
<p className={styles.text}>{t('aura.warming_up.body')}</p>
|
||||
<div className={styles['checkbox-container']}>
|
||||
<CheckboxWithText text={t('not_ready_for_information')} onChange={onChangeCheckbox} />
|
||||
{/* <CheckboxWithText text={t('not_ready_for_information')} onChange={onChangeCheckbox} /> */}
|
||||
<MainButton className={styles.button} onClick={handleNext}>{t('aura.warming_up.button')}</MainButton>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
@ -1,23 +1,27 @@
|
||||
.page {
|
||||
position: relative;
|
||||
flex: auto;
|
||||
height: calc(100vh - 50px);
|
||||
max-height: -webkit-fill-available;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
position: relative;
|
||||
flex: auto;
|
||||
height: calc(100vh - 50px);
|
||||
max-height: -webkit-fill-available;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 96px;
|
||||
width: 96px;
|
||||
}
|
||||
|
||||
.text {
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
width: 80%;
|
||||
margin: 64px auto 0;
|
||||
width: 80%;
|
||||
margin: 64px auto 0;
|
||||
}
|
||||
|
||||
.button {
|
||||
white-space: pre;
|
||||
}
|
||||
@ -37,8 +37,6 @@ function BreathPage(): JSX.Element {
|
||||
if (isOpenModal) return;
|
||||
const previewTimeOut = setTimeout(() => {
|
||||
setIsShowPreview(false);
|
||||
console.log(apngPlayer);
|
||||
|
||||
apngPlayer?.play()
|
||||
}, 10_000);
|
||||
|
||||
@ -54,26 +52,17 @@ function BreathPage(): JSX.Element {
|
||||
|
||||
useEffect(() => {
|
||||
async function test() {
|
||||
console.log(data);
|
||||
|
||||
if (!data) return;
|
||||
console.log(1);
|
||||
|
||||
const response = await fetch(
|
||||
data.find((item) => item.key === "au.apng.leo")?.url || ""
|
||||
);
|
||||
console.log(2);
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
console.log(3);
|
||||
const apng = parseAPNG(arrayBuffer);
|
||||
console.log(4);
|
||||
const context = leoCanvasRef.current?.getContext("2d");
|
||||
console.log("context: ", context);
|
||||
if (context && !(apng instanceof Error)) {
|
||||
context.canvas.height = apng.height;
|
||||
context.canvas.width = apng.width;
|
||||
const _apngPlayer = await apng.getPlayer(context);
|
||||
console.log(5);
|
||||
setApngPlayer(_apngPlayer);
|
||||
if (apngPlayer) {
|
||||
apngPlayer.stop();
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #01010b;
|
||||
padding-bottom: 60px;
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
|
||||
@ -75,7 +75,9 @@ function CompatResultPage(): JSX.Element {
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
{text !== "Loading..." && <div className={styles.cross} onClick={handleNext}></div>}
|
||||
{text !== "Loading..." && (
|
||||
<div className={styles.cross} onClick={handleNext}></div>
|
||||
)}
|
||||
<div className={styles["title-container"]}>
|
||||
<Title variant="h2">{t("you_and", { user: rightUser.name })}</Title>
|
||||
</div>
|
||||
@ -85,19 +87,26 @@ function CompatResultPage(): JSX.Element {
|
||||
</Title>
|
||||
<p className={styles["result-container__text"]}>{text}</p>
|
||||
</div>
|
||||
{text !== "Loading..." && (
|
||||
<div className={styles["button-container"]}>
|
||||
<p className={styles["button-container__text"]}>
|
||||
{t("now-you-know")}
|
||||
</p>
|
||||
<button
|
||||
className={styles["button-container__button"]}
|
||||
onClick={handleNext}
|
||||
>
|
||||
{t("go-through")}
|
||||
{text !== "Loading..." &&
|
||||
homeConfig.pathFromHome === EPathsFromHome.compatibility && (
|
||||
<div className={styles["button-container"]}>
|
||||
<p className={styles["button-container__text"]}>
|
||||
{t("now-you-know")}
|
||||
</p>
|
||||
<button
|
||||
className={styles["button-container__button"]}
|
||||
onClick={handleNext}
|
||||
>
|
||||
{t("go-through")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{text !== "Loading..." &&
|
||||
homeConfig.pathFromHome === EPathsFromHome.breath && (
|
||||
<button className={styles['button-green']} onClick={handleNext}>
|
||||
{t("use-all-power")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@ -67,6 +67,21 @@
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button-green {
|
||||
position: fixed;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
bottom: 24px;
|
||||
width: calc(100% - 64px);
|
||||
max-width: 496px;
|
||||
border-radius: 32px;
|
||||
padding: 24px 0;
|
||||
background: -moz-linear-gradient(to bottom, #01d400, #196e17);
|
||||
background: -webkit-linear-gradient(to bottom, #01d400, #196e17);
|
||||
background: linear-gradient(to bottom, #01d400, #196e17);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.cross {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
|
||||
118
src/components/Compatibility/DatePicker.css
Normal file
118
src/components/Compatibility/DatePicker.css
Normal file
@ -0,0 +1,118 @@
|
||||
/* DatePicker.css */
|
||||
|
||||
/* Container for the entire date picker */
|
||||
.date-picker-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 0 auto;
|
||||
color: white;
|
||||
/* background: transparent; */
|
||||
/* box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); */
|
||||
}
|
||||
|
||||
.date-picker-viewport {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
height: 160px; /* Adjust the max height as needed */
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
.date-picker-viewport::-webkit-scrollbar {
|
||||
display: none; /* Chrome, Safari and Opera */
|
||||
}
|
||||
|
||||
.date-picker-viewport:first-child .date-picker-wheel {
|
||||
border-bottom-left-radius: 10px;
|
||||
border-top-left-radius: 10px;
|
||||
}
|
||||
.date-picker-viewport:last-child .date-picker-wheel {
|
||||
border-bottom-right-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
}
|
||||
|
||||
.date-picker-viewport::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
/* background: linear-gradient(
|
||||
#f7f7f7,
|
||||
rgba(245, 245, 245, 0) 52%,
|
||||
rgba(245, 245, 245, 0) 48%,
|
||||
#f7f7f7
|
||||
); */
|
||||
}
|
||||
|
||||
.date-picker-wheel {
|
||||
position: absolute;
|
||||
height: 40px;
|
||||
top: 50%;
|
||||
background: #7a7979;
|
||||
margin-top: -20px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Scrollable components for day, month, and year */
|
||||
.date-picker-scroll {
|
||||
padding: 0;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
/* Individual items in the scrollable components */
|
||||
.date-picker-item {
|
||||
padding: 10px 0;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #a09b9b;
|
||||
}
|
||||
.date-picker-item.selected {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Header and confirm button */
|
||||
.date-picker-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.date-picker-confirm {
|
||||
background-color: #007aff;
|
||||
color: #ffffff;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.date-picker-confirm:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
/* Display selected date */
|
||||
.date-picker-selected-date {
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
color: #007aff; /* Change the color to your desired style */
|
||||
white-space: nowrap; /* Prevent selected date from wrapping */
|
||||
text-overflow: ellipsis; /* Show ellipsis for long selected date */
|
||||
width: 100%; /* Ensure the width is 100% for centering */
|
||||
position: absolute; /* Position it absolutely for centering */
|
||||
left: 50%; /* Center horizontally */
|
||||
transform: translateX(-50%); /* Center horizontally */
|
||||
}
|
||||
82
src/components/Compatibility/DatePicker.tsx
Normal file
82
src/components/Compatibility/DatePicker.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import "./DatePicker.css";
|
||||
import DatePickerItem from "./DatePickerItem";
|
||||
import { IDate, getDateAsString } from "@/services/date";
|
||||
|
||||
interface DatePickerProps {
|
||||
onDateChange: (selectedDate: string | IDate) => void;
|
||||
}
|
||||
|
||||
const DatePicker: React.FC<DatePickerProps> = ({ onDateChange }) => {
|
||||
const currentDate = new Date();
|
||||
const [selectedDate, setSelectedDate] = useState<string | IDate>(getDateAsString(currentDate));
|
||||
|
||||
const days = Array.from({ length: 31 }, (_, index) => (index + 1).toString());
|
||||
const months = Array.from({ length: 12 }, (_, index) =>
|
||||
new Date(0, index).toLocaleDateString(undefined, { month: "long" })
|
||||
);
|
||||
const years = Array.from({ length: 81 }, (_, index) =>
|
||||
(currentDate.getFullYear() - 80 + index).toString()
|
||||
);
|
||||
|
||||
const handleDaySelect = (day: string) => {
|
||||
const newDate = new Date(getDateAsString(selectedDate));
|
||||
newDate.setDate(parseInt(day));
|
||||
setSelectedDate(getDateAsString(newDate));
|
||||
};
|
||||
|
||||
const handleMonthSelect = (month: string) => {
|
||||
const newDate = new Date(getDateAsString(selectedDate));
|
||||
newDate.setMonth(months.indexOf(month));
|
||||
setSelectedDate(getDateAsString(newDate));
|
||||
};
|
||||
|
||||
const handleYearSelect = (year: string) => {
|
||||
const newDate = new Date(getDateAsString(selectedDate));
|
||||
newDate.setFullYear(parseInt(year));
|
||||
setSelectedDate(getDateAsString(newDate));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (onDateChange) {
|
||||
onDateChange(selectedDate); // Call the callback when selectedDate changes
|
||||
}
|
||||
}, [selectedDate, onDateChange]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="date-picker-container">
|
||||
<DatePickerItem
|
||||
data={days}
|
||||
selectedValue={new Date(getDateAsString(selectedDate)).getDate().toString()}
|
||||
onSelect={handleDaySelect}
|
||||
unit="day"
|
||||
/>
|
||||
<DatePickerItem
|
||||
data={months}
|
||||
selectedValue={new Date(
|
||||
0,
|
||||
new Date(getDateAsString(selectedDate)).getMonth()
|
||||
).toLocaleDateString(undefined, { month: "long" })}
|
||||
onSelect={handleMonthSelect}
|
||||
unit="month"
|
||||
/>
|
||||
<DatePickerItem
|
||||
data={years}
|
||||
selectedValue={new Date(getDateAsString(selectedDate)).getFullYear().toString()}
|
||||
onSelect={handleYearSelect}
|
||||
unit="year"
|
||||
/>
|
||||
</div>
|
||||
<div className="date-picker-selected-date">
|
||||
{/* {selectedDate.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})} */}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatePicker;
|
||||
148
src/components/Compatibility/DatePickerItem.tsx
Normal file
148
src/components/Compatibility/DatePickerItem.tsx
Normal file
@ -0,0 +1,148 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
|
||||
const ITEM_HEIGHT = 40; // Height of each date item
|
||||
|
||||
interface DatePickerItemProps {
|
||||
data: string[];
|
||||
selectedValue: string;
|
||||
onSelect: (value: string) => void;
|
||||
unit: string;
|
||||
}
|
||||
|
||||
function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsMobile(window.matchMedia("(max-width: 600px)").matches);
|
||||
}, []);
|
||||
|
||||
return isMobile;
|
||||
}
|
||||
|
||||
const DatePickerItem: React.FC<DatePickerItemProps> = ({
|
||||
data,
|
||||
selectedValue,
|
||||
onSelect,
|
||||
unit,
|
||||
}) => {
|
||||
const isMobile = useIsMobile();
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const [touchY, setTouchY] = useState<number>(0);
|
||||
const [translateY, setTranslateY] = useState<number>(
|
||||
data.indexOf(selectedValue) * -ITEM_HEIGHT
|
||||
);
|
||||
|
||||
const handleTouchStart = (event: React.TouchEvent<HTMLDivElement>) => {
|
||||
setTouchY(event.touches[0].clientY);
|
||||
};
|
||||
|
||||
const handleTouchMove = (event: React.TouchEvent<HTMLDivElement>) => {
|
||||
if (isMobile && touchY !== null) {
|
||||
const deltaY = event.touches[0].clientY - touchY;
|
||||
wheelScroll(deltaY);
|
||||
setTouchY(event.touches[0].clientY);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchEnd = () => {
|
||||
if (isMobile && scrollRef.current) {
|
||||
const selectedIndex = Math.round(-translateY / ITEM_HEIGHT);
|
||||
onSelect(data[selectedIndex]);
|
||||
|
||||
// Limit the translateY to ensure it aligns with a valid item
|
||||
setTranslateY(-selectedIndex * ITEM_HEIGHT);
|
||||
setTouchY(0);
|
||||
}
|
||||
};
|
||||
|
||||
const wheelScroll = (deltaY: number) => {
|
||||
const newTranslateY = translateY + deltaY;
|
||||
|
||||
// Calculate the maximum and minimum translateY values based on the data length
|
||||
const minTranslateY = -ITEM_HEIGHT * (data.length - 1);
|
||||
const maxTranslateY = 0;
|
||||
|
||||
// Ensure the newTranslateY stays within the valid range
|
||||
if (newTranslateY < minTranslateY) {
|
||||
setTranslateY(minTranslateY);
|
||||
} else if (newTranslateY > maxTranslateY) {
|
||||
setTranslateY(maxTranslateY);
|
||||
} else {
|
||||
setTranslateY(newTranslateY);
|
||||
}
|
||||
};
|
||||
|
||||
// const handleMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
// if (!isMobile) {
|
||||
// setTouchY(event.clientY);
|
||||
// document.addEventListener("mousemove", handleMouseMove);
|
||||
// document.addEventListener("mouseup", handleMouseUp);
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleMouseMove = (event: MouseEvent) => {
|
||||
// const deltaY = event.clientY - touchY;
|
||||
// handleScroll(deltaY);
|
||||
// setTouchY(event.clientY);
|
||||
// };
|
||||
|
||||
// const handleMouseUp = () => {
|
||||
// resetMouseState();
|
||||
// };
|
||||
|
||||
// const resetTouchState = () => {
|
||||
// if (isMobile && scrollRef.current) {
|
||||
// const selectedIndex = Math.round(-translateY / ITEM_HEIGHT);
|
||||
// onSelect(data[selectedIndex]);
|
||||
|
||||
// // Limit the translateY to ensure it aligns with a valid item
|
||||
// setTranslateY(-selectedIndex * ITEM_HEIGHT);
|
||||
// setTouchY(0);
|
||||
// }
|
||||
// };
|
||||
|
||||
// const resetMouseState = () => {
|
||||
// document.removeEventListener("mousemove", handleMouseMove);
|
||||
// document.removeEventListener("mouseup", handleMouseUp);
|
||||
// resetTouchState();
|
||||
// };
|
||||
|
||||
// useEffect(() => {
|
||||
// // Clean up mouse event listeners when the component unmounts
|
||||
// return () => {
|
||||
// resetMouseState();
|
||||
// };
|
||||
// }, []);
|
||||
|
||||
return (
|
||||
<div className="date-picker-viewport">
|
||||
<div className="date-picker-wheel">
|
||||
<div
|
||||
className="date-picker-scroll"
|
||||
onTouchStart={handleTouchStart}
|
||||
onTouchMove={handleTouchMove}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
// onMouseDown={handleMouseDown}
|
||||
ref={scrollRef}
|
||||
style={{
|
||||
transform: `translateY(${translateY}px)`,
|
||||
}}
|
||||
>
|
||||
{data.map((item, index) => (
|
||||
<div
|
||||
className={`date-picker-item ${
|
||||
item === selectedValue ? "selected" : ""
|
||||
}`}
|
||||
key={`${unit}-${index}`}
|
||||
>
|
||||
{item}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DatePickerItem;
|
||||
@ -4,8 +4,8 @@ import Title from "../Title";
|
||||
import styles from "./styles.module.css";
|
||||
import { useCallback, useState } from "react";
|
||||
import NameInput from "./nameInput";
|
||||
import { DatePicker } from "../DateTimePicker";
|
||||
import { IDate, getDateAsString } from "@/services/date";
|
||||
import DatePicker from "./DatePicker";
|
||||
import { IDate } from "@/services/date";
|
||||
import { AICompatCategories, useApi, useApiCall } from "@/api";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
@ -14,57 +14,61 @@ import { actions } from "@/store";
|
||||
|
||||
function CompatibilityPage(): JSX.Element {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate()
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const [isDisabled, setIsDisabled] = useState(true);
|
||||
const [isDisabledName, setIsDisabledName] = useState(true);
|
||||
const [isDisabledDate, setIsDisabledDate] = useState(true);
|
||||
const [name, setName] = useState<string>('');
|
||||
const [date, setDate] = useState<string | IDate>('');
|
||||
const [name, setName] = useState<string>("");
|
||||
const [selectedDate, setSelectedDate] = useState<string | IDate>("");
|
||||
const [compatCategory, setCompatCategory] = useState(1);
|
||||
|
||||
const handleNext = () => {
|
||||
dispatch(actions.compatibility.update({
|
||||
rightUser: {
|
||||
name,
|
||||
birthDate: date
|
||||
},
|
||||
categoryId: compatCategory
|
||||
}))
|
||||
navigate(routes.client.compatibilityResult())
|
||||
if (isDisabled) return;
|
||||
dispatch(
|
||||
actions.compatibility.update({
|
||||
rightUser: {
|
||||
name,
|
||||
birthDate: selectedDate,
|
||||
},
|
||||
categoryId: compatCategory,
|
||||
})
|
||||
);
|
||||
navigate(routes.client.compatibilityResult());
|
||||
};
|
||||
|
||||
const api = useApi()
|
||||
const locale = i18n.language
|
||||
|
||||
const api = useApi();
|
||||
const locale = i18n.language;
|
||||
const loadData = useCallback(() => {
|
||||
return api.getAiCompatCategories({ locale })
|
||||
.then((resp: AICompatCategories.Response) => resp.compat_categories)
|
||||
}, [api, locale])
|
||||
const { data } = useApiCall<AICompatCategories.CompatCategory[]>(loadData)
|
||||
return api
|
||||
.getAiCompatCategories({ locale })
|
||||
.then((resp: AICompatCategories.Response) => resp.compat_categories);
|
||||
}, [api, locale]);
|
||||
const { data } = useApiCall<AICompatCategories.CompatCategory[]>(loadData);
|
||||
|
||||
const handleValidName = (name: string) => {
|
||||
setIsDisabledName(!name.length);
|
||||
setName(name)
|
||||
checkAllDisabled()
|
||||
}
|
||||
setName(name);
|
||||
checkAllDisabled();
|
||||
};
|
||||
|
||||
const handleValidDate = (date: IDate | string) => {
|
||||
const handleValidDate = (date: string | IDate) => {
|
||||
setIsDisabledDate(date === '');
|
||||
setDate(date)
|
||||
checkAllDisabled()
|
||||
}
|
||||
setSelectedDate(date);
|
||||
checkAllDisabled();
|
||||
};
|
||||
|
||||
const checkAllDisabled = () => {
|
||||
setIsDisabled(isDisabledName || isDisabledDate);
|
||||
}
|
||||
};
|
||||
|
||||
const changeCompatCategory = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setCompatCategory(parseInt(event.target.value))
|
||||
}
|
||||
|
||||
setCompatCategory(parseInt(event.target.value));
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
<div className={styles.cross} onClick={handleNext}></div>
|
||||
<Title variant="h1" className={styles.title}>
|
||||
{t("compatibility")}
|
||||
</Title>
|
||||
@ -75,47 +79,47 @@ function CompatibilityPage(): JSX.Element {
|
||||
<Title variant="h3" className={styles.plus}>
|
||||
+
|
||||
</Title>
|
||||
<div className={styles['inputs-container']}>
|
||||
<div className={styles['input-container__name-container']}>
|
||||
<div className={styles["inputs-container"]}>
|
||||
<div className={styles["input-container__name-container"]}>
|
||||
<NameInput
|
||||
name="name"
|
||||
value={name}
|
||||
placeholder={t('name')}
|
||||
placeholder={t("name")}
|
||||
onValid={handleValidName}
|
||||
onInvalid={() => setIsDisabledName(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles['input-container__date-container']}>
|
||||
<DatePicker
|
||||
name='birthdate'
|
||||
value={getDateAsString(date)}
|
||||
inputClassName={styles['date-input']}
|
||||
onValid={handleValidDate}
|
||||
onInvalid={() => setIsDisabledDate(true)}
|
||||
/>
|
||||
<div className={styles["input-container__date-container"]}>
|
||||
<DatePicker onDateChange={handleValidDate} />
|
||||
</div>
|
||||
</div>
|
||||
{data && data.length && (
|
||||
<div className={styles['compatibility-categories']}>
|
||||
{
|
||||
data.map((item, index) => (
|
||||
<div className="compatibility-categories__item" key={index}>
|
||||
<input className={`${styles["compatibility-categories__input"]} ${compatCategory === item.id ? styles["compatibility-categories__input--checked"] : ''}`}
|
||||
type="radio"
|
||||
name="compatCategory"
|
||||
id={String(item.id)}
|
||||
value={item.id}
|
||||
checked={compatCategory === item.id}
|
||||
onChange={changeCompatCategory} />
|
||||
<label htmlFor={String(item.id)}>
|
||||
{item.name}
|
||||
</label>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
<div className={styles["compatibility-categories"]}>
|
||||
{data.map((item, index) => (
|
||||
<div className="compatibility-categories__item" key={index}>
|
||||
<input
|
||||
className={`${styles["compatibility-categories__input"]} ${
|
||||
compatCategory === item.id
|
||||
? styles["compatibility-categories__input--checked"]
|
||||
: ""
|
||||
}`}
|
||||
type="radio"
|
||||
name="compatCategory"
|
||||
id={String(item.id)}
|
||||
value={item.id}
|
||||
checked={compatCategory === item.id}
|
||||
onChange={changeCompatCategory}
|
||||
/>
|
||||
<label htmlFor={String(item.id)}>{item.name}</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<MainButton className={styles['check-btn']} onClick={handleNext} disabled={isDisabled}>
|
||||
<MainButton
|
||||
className={styles["check-btn"]}
|
||||
onClick={handleNext}
|
||||
disabled={isDisabled}
|
||||
>
|
||||
{t("check")}
|
||||
</MainButton>
|
||||
</div>
|
||||
|
||||
@ -6,8 +6,42 @@
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.cross {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: solid 2px #bdbdbd;
|
||||
border-radius: 100%;
|
||||
rotate: 45deg;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cross::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 10px;
|
||||
height: 2px;
|
||||
background-color: #bdbdbd;
|
||||
}
|
||||
|
||||
.cross::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 2px;
|
||||
height: 10px;
|
||||
background-color: #bdbdbd;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
@ -84,10 +118,10 @@
|
||||
}
|
||||
|
||||
.check-btn {
|
||||
border-radius: 30px;
|
||||
background-color: #ea445a;
|
||||
max-width: 200px;
|
||||
margin: 48px auto;
|
||||
border-radius: 30px;
|
||||
background-color: #ea445a;
|
||||
max-width: 200px;
|
||||
margin: 48px auto;
|
||||
}
|
||||
|
||||
.compatibility-categories {
|
||||
|
||||
@ -34,10 +34,10 @@ function CreateProfilePage(): JSX.Element {
|
||||
}, [progressbarRef])
|
||||
|
||||
const processItems = [
|
||||
{ task: () => sleep(3300).then(() => setProgress(23)), label: t('money_energy') },
|
||||
{ task: () => sleep(2550).then(() => setProgress(48)), label: t('sexual_energy') },
|
||||
{ task: () => sleep(3789).then(() => setProgress(65)), label: t('black_energy') },
|
||||
{ task: () => sleep(3789).then(() => setProgress(98)), label: t('relations_energy') },
|
||||
{ task: () => sleep(3300).then(() => setProgress(23)), label: t('aura.1.loading') },
|
||||
{ task: () => sleep(2550).then(() => setProgress(48)), label: t('aura.2.loading') },
|
||||
{ task: () => sleep(3789).then(() => setProgress(65)), label: t('aura.3.loading') },
|
||||
{ task: () => sleep(3789).then(() => setProgress(98)), label: t('aura.4.loading') },
|
||||
]
|
||||
const handleDone = () => Promise.resolve()
|
||||
.then(() => setProgress(100))
|
||||
|
||||
@ -1,26 +1,65 @@
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import routes from '@/routes'
|
||||
import styles from './styles.module.css'
|
||||
import MainButton from '../MainButton'
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import routes from "@/routes";
|
||||
import styles from "./styles.module.css";
|
||||
import MainButton from "../MainButton";
|
||||
import Title from "../Title";
|
||||
import { useApi, useApiCall } from "@/api";
|
||||
import { useCallback } from "react";
|
||||
import { Asset } from "@/api/resources/Assets";
|
||||
|
||||
function FeedbackPage(): JSX.Element {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const handleNext = () => navigate(routes.client.emailEnter())
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const api = useApi();
|
||||
const handleNext = () => navigate(routes.client.emailEnter());
|
||||
|
||||
const assetsData = useCallback(async () => {
|
||||
const { assets } = await api.getAssets({ category: String("au") });
|
||||
return assets;
|
||||
}, [api]);
|
||||
|
||||
const {
|
||||
data: assets,
|
||||
// isPending
|
||||
} = useApiCall<Asset[]>(assetsData);
|
||||
|
||||
const reviews = [
|
||||
{
|
||||
title: t("aura.name_1.review"),
|
||||
text: t("aura.review_1.content"),
|
||||
imageKey: "au.1.avatar",
|
||||
},
|
||||
{
|
||||
title: t("aura.name_2.review"),
|
||||
text: t("aura.review_2.content"),
|
||||
imageKey: "au.2.avatar",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
<div className={styles.images}>
|
||||
<img className={styles['profile-picture']} src="/profile-picture-feedback.png" alt="profile picture" />
|
||||
<img className={styles.stars} src="/5-stars.png" alt="stop" />
|
||||
<div className={styles.reviews}>
|
||||
{reviews.map(({ title, text, imageKey }, index) => (
|
||||
<div className={styles.review} key={index}>
|
||||
<div className={styles.images}>
|
||||
<img
|
||||
className={styles["profile-picture"]}
|
||||
src={assets?.find((asset) => asset.key === imageKey)?.url}
|
||||
alt="profile picture"
|
||||
/>
|
||||
<div className={styles["header-container"]}>
|
||||
<Title variant="h3">{title}</Title>
|
||||
<img className={styles.stars} src="/5-stars.png" alt="stop" />
|
||||
</div>
|
||||
</div>
|
||||
<p className={styles.text}>{text}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className={styles.text}>{t('feedback')}</p>
|
||||
<MainButton onClick={handleNext}>
|
||||
{t('next')}
|
||||
</MainButton>
|
||||
<MainButton onClick={handleNext}>{t("next")}</MainButton>
|
||||
</section>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default FeedbackPage
|
||||
export default FeedbackPage;
|
||||
|
||||
@ -6,6 +6,23 @@
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.reviews {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: scroll;
|
||||
width: 100vw;
|
||||
max-width: 560px;
|
||||
padding: 0 32px;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.review {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.images {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
@ -16,6 +33,9 @@
|
||||
|
||||
.profile-picture {
|
||||
width: 96px;
|
||||
border-radius: 100%;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.stars {
|
||||
@ -31,3 +51,8 @@
|
||||
border-radius: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-top: calc(100vh - 432px);
|
||||
margin-top: calc(100vh - 478px);
|
||||
}
|
||||
|
||||
.content__buttons {
|
||||
|
||||
@ -13,23 +13,40 @@ function StartBreathModalChild({
|
||||
|
||||
return (
|
||||
<section className={`${styles["start-breath"]} page`}>
|
||||
<div className={styles.cross} onClick={handleBegin}></div>
|
||||
<div className={styles.text}>
|
||||
<Title variant="h4" className={styles["breathe-title"]}>
|
||||
{t("breathe-title").split("").map((symbol, index) => (
|
||||
<span className={styles["symbol"]} style={{ animationDelay: `${index * 0.05}s` }} key={index}>{symbol}</span>
|
||||
))}
|
||||
{t("breathe-title")
|
||||
.split("")
|
||||
.map((symbol, index) => (
|
||||
<span
|
||||
className={styles["symbol"]}
|
||||
style={{ animationDelay: `${index * 0.05}s` }}
|
||||
key={index}
|
||||
>
|
||||
{symbol}
|
||||
</span>
|
||||
))}
|
||||
</Title>
|
||||
<Title variant="h4" className={styles["breathe-subtitle"]}>
|
||||
{t("breathe-subtitle").split("").map((symbol, index) => (
|
||||
<span className={styles["symbol"]} style={{ animationDelay: `${(t("breathe-title").split("").length + index) * 0.05}s` }} key={index}>{symbol}</span>
|
||||
))}
|
||||
{t("breathe-subtitle")
|
||||
.split("")
|
||||
.map((symbol, index) => (
|
||||
<span
|
||||
className={styles["symbol"]}
|
||||
style={{
|
||||
animationDelay: `${
|
||||
(t("breathe-title").split("").length + index) * 0.05
|
||||
}s`,
|
||||
}}
|
||||
key={index}
|
||||
>
|
||||
{symbol}
|
||||
</span>
|
||||
))}
|
||||
</Title>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.begin}
|
||||
onClick={handleBegin}
|
||||
>
|
||||
<button type="button" className={styles.begin} onClick={handleBegin}>
|
||||
{t("aura-begin_breathe-button")}
|
||||
</button>
|
||||
</section>
|
||||
|
||||
@ -29,10 +29,44 @@
|
||||
will-change: opacity;
|
||||
animation-name: appearance;
|
||||
animation-timing-function: ease;
|
||||
animation-duration: .3s;
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.cross {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: solid 2px #bdbdbd;
|
||||
border-radius: 100%;
|
||||
rotate: 45deg;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cross::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 10px;
|
||||
height: 2px;
|
||||
background-color: #bdbdbd;
|
||||
}
|
||||
|
||||
.cross::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 2px;
|
||||
height: 10px;
|
||||
background-color: #bdbdbd;
|
||||
}
|
||||
|
||||
@keyframes appearance {
|
||||
0% {
|
||||
opacity: 0;
|
||||
|
||||
@ -16,7 +16,11 @@ function UserCallbacksPage(): JSX.Element {
|
||||
const homeConfig = useSelector(selectors.selectHome);
|
||||
|
||||
const handleNext = () => {
|
||||
dispatch(actions.siteConfig.update({ home: { pathFromHome: homeConfig.pathFromHome, isShowNavbar: true } }));
|
||||
dispatch(
|
||||
actions.siteConfig.update({
|
||||
home: { pathFromHome: homeConfig.pathFromHome, isShowNavbar: true },
|
||||
})
|
||||
);
|
||||
if (homeConfig.pathFromHome === EPathsFromHome.compatibility) {
|
||||
return navigate(routes.client.home());
|
||||
}
|
||||
@ -55,9 +59,16 @@ function UserCallbacksPage(): JSX.Element {
|
||||
</div>
|
||||
<p className={styles["result-container__text"]}>{text}</p>
|
||||
</div>
|
||||
<button className={styles.button} onClick={handleNext}>
|
||||
{t("use-all-power")}
|
||||
</button>
|
||||
{homeConfig.pathFromHome === EPathsFromHome.breath && (
|
||||
<button className={`${styles.button} ${styles['button-red']}`} onClick={handleNext}>
|
||||
{t("aura.breath_compatibility.button")}
|
||||
</button>
|
||||
)}
|
||||
{homeConfig.pathFromHome === EPathsFromHome.compatibility && (
|
||||
<button className={styles.button} onClick={handleNext}>
|
||||
{t("use-all-power")}
|
||||
</button>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@ -52,6 +52,13 @@
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.button-red {
|
||||
padding: 12px 0;
|
||||
background: -moz-linear-gradient(to bottom, #FF003D, #6B174A);
|
||||
background: -webkit-linear-gradient(to bottom, #FF003D, #6B174A);
|
||||
background: linear-gradient(to bottom, #FF003D, #6B174A);
|
||||
}
|
||||
|
||||
.cross {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
|
||||
@ -89,5 +89,17 @@ export default {
|
||||
"use-all-power": "Use all the power of your Aura",
|
||||
"go-through": "Go through practicing recovery",
|
||||
"now-you-know": "Now you know who`s causing your financial energy loss.",
|
||||
"aura.breath_compatibility.button": "Discover who is causing your financial energy loss.",
|
||||
"aura.attention.title": "ATTENTION",
|
||||
"aura.warming_up.body": "The analysis of your Aura revealed a negative energy influence on you and a partial positive one. Such a combination is very rare! Which one would you like to start with?",
|
||||
"aura.warming_up.button": "GOOD BAD",
|
||||
"aura.1.loading": "MONEY energy test1",
|
||||
"aura.2.loading": "SEXUAL energy test",
|
||||
"aura.3.loading": "BLACK energy",
|
||||
"aura.4.loading": "RELATIONS energy",
|
||||
"aura.name_1.review": "Samantha Green",
|
||||
"aura.name_2.review": "James Wilson",
|
||||
"aura.review_1.content": "As for me, money just doesn't stick with me at all. It turned out that it wasn't because I was bad or irresponsible, but it was because of my partner. We delved deeply into this issue thanks to Aura, and what do you think? Everything changed the very next day. Now we are happy and wealthy.",
|
||||
"aura.review_2.content": "I don't know why, but I always had bad experiences in relationships and couldn't find the one who would understand and love me. So, I took a special extended test and immediately figured everything out. It turns out that the birth date of the chosen one and number coincidences are very important in relationships. Now I consider this in all areas, and it helps me a lot."
|
||||
},
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user