feat: infinity months date picker, texts from translate v2, prices from chosen
This commit is contained in:
parent
87eba58d0e
commit
64bdc08452
@ -34,7 +34,7 @@ function App(): JSX.Element {
|
||||
const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState<boolean>(false)
|
||||
const navigate = useNavigate()
|
||||
|
||||
const closeSpecialOffer = () => {
|
||||
const closeSpecialOfferAttention = () => {
|
||||
setIsSpecialOfferOpen(false)
|
||||
navigate(routes.client.emailEnter())
|
||||
}
|
||||
@ -46,7 +46,7 @@ function App(): JSX.Element {
|
||||
<Route path={routes.client.birthday()} element={<BirthdayPage />} />
|
||||
<Route path={routes.client.didYouKnow()} element={<DidYouKnowPage />} />
|
||||
<Route path={routes.client.freePeriodInfo()} element={<FreePeriodInfoPage />} />
|
||||
<Route path={routes.client.attention()} element={<AttentionPage isOpenModal={isSpecialOfferOpen} onCloseSpecialOffer={closeSpecialOffer} />} />
|
||||
<Route path={routes.client.attention()} element={<AttentionPage isOpenModal={isSpecialOfferOpen} onCloseSpecialOffer={closeSpecialOfferAttention} />} />
|
||||
<Route path={routes.client.feedback()} element={<FeedbackPage />} />
|
||||
<Route path={routes.client.birthtime()} element={<BirthtimePage />} />
|
||||
<Route path={routes.client.createProfile()} element={<SkipStep />} />
|
||||
|
||||
@ -3,11 +3,13 @@ import Title from "../Title";
|
||||
import styles from "./styles.module.css";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { actions, selectors } from "@/store";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { AICompats, AIRequests, useApi, useApiCall } from "@/api";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import { EPathsFromHome } from "@/store/siteConfig";
|
||||
import FullScreenModal from "../FullScreenModal";
|
||||
import CompatibilityLoading from "../CompatibilityLoading";
|
||||
|
||||
function CompatResultPage(): JSX.Element {
|
||||
const token =
|
||||
@ -21,6 +23,18 @@ function CompatResultPage(): JSX.Element {
|
||||
const categoryId = useSelector(selectors.selectCategoryId);
|
||||
const homeConfig = useSelector(selectors.selectHome);
|
||||
const [text, setText] = useState("Loading...");
|
||||
const [isOpenModal, setIsOpenModal] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const timeOut = setTimeout(() => {
|
||||
setIsOpenModal(false)
|
||||
}, 5000)
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeOut)
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
const handleNext = () => {
|
||||
dispatch(
|
||||
@ -75,6 +89,9 @@ function CompatResultPage(): JSX.Element {
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
<FullScreenModal isOpen={isOpenModal}>
|
||||
<CompatibilityLoading />
|
||||
</FullScreenModal>
|
||||
{text !== "Loading..." && (
|
||||
<div className={styles.cross} onClick={handleNext}></div>
|
||||
)}
|
||||
@ -103,7 +120,7 @@ function CompatResultPage(): JSX.Element {
|
||||
)}
|
||||
{text !== "Loading..." &&
|
||||
homeConfig.pathFromHome === EPathsFromHome.breath && (
|
||||
<button className={styles['button-green']} onClick={handleNext}>
|
||||
<button className={styles["button-green"]} onClick={handleNext}>
|
||||
{t("use-all-power")}
|
||||
</button>
|
||||
)}
|
||||
|
||||
@ -79,7 +79,8 @@
|
||||
color: #a09b9b;
|
||||
}
|
||||
.date-picker-item.selected {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Header and confirm button */
|
||||
|
||||
@ -19,7 +19,7 @@ const DatePicker: React.FC<DatePickerProps> = ({
|
||||
);
|
||||
|
||||
const days = Array.from({ length: 31 }, (_, index) => (index + 1).toString());
|
||||
const months = Array.from({ length: 12 }, (_, index) =>
|
||||
const months = Array.from({ length: 36 }, (_, index) =>
|
||||
new Date(0, index).toLocaleDateString(undefined, { month: "long" })
|
||||
);
|
||||
const years = Array.from({ length: 81 }, (_, index) =>
|
||||
@ -27,19 +27,19 @@ const DatePicker: React.FC<DatePickerProps> = ({
|
||||
);
|
||||
|
||||
const handleDaySelect = (day: string) => {
|
||||
const newDate = new Date(getDateAsString(selectedDate));
|
||||
const newDate = new Date(getDateAsString(selectedDate).replace(/-/g, "/"));
|
||||
newDate.setDate(parseInt(day));
|
||||
setSelectedDate(getDateAsString(newDate));
|
||||
};
|
||||
|
||||
const handleMonthSelect = (month: string) => {
|
||||
const newDate = new Date(getDateAsString(selectedDate));
|
||||
const newDate = new Date(getDateAsString(selectedDate).replace(/-/g, "/"));
|
||||
newDate.setMonth(months.indexOf(month));
|
||||
setSelectedDate(getDateAsString(newDate));
|
||||
};
|
||||
|
||||
const handleYearSelect = (year: string) => {
|
||||
const newDate = new Date(getDateAsString(selectedDate));
|
||||
const newDate = new Date(getDateAsString(selectedDate).replace(/-/g, "/"));
|
||||
newDate.setFullYear(parseInt(year));
|
||||
setSelectedDate(getDateAsString(newDate));
|
||||
};
|
||||
|
||||
@ -24,17 +24,14 @@ const DatePickerItem: React.FC<DatePickerItemProps> = ({
|
||||
data,
|
||||
selectedValue,
|
||||
onSelect,
|
||||
unit
|
||||
unit,
|
||||
}) => {
|
||||
console.log(selectedValue);
|
||||
console.log(data);
|
||||
|
||||
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
|
||||
(data.indexOf(selectedValue) + (unit === "month" ? 12 : 0)) * -ITEM_HEIGHT
|
||||
);
|
||||
|
||||
const handleTouchStart = (event: React.TouchEvent<HTMLDivElement>) => {
|
||||
@ -45,10 +42,26 @@ const DatePickerItem: React.FC<DatePickerItemProps> = ({
|
||||
if (isMobile && touchY !== null) {
|
||||
const deltaY = event.touches[0].clientY - touchY;
|
||||
wheelScroll(deltaY);
|
||||
|
||||
setTouchY(event.touches[0].clientY);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTranslateY((data.indexOf(selectedValue) + (unit === "month" ? 12 : 0)) * -ITEM_HEIGHT)
|
||||
}, [selectedValue, data, unit])
|
||||
|
||||
useEffect(() => {
|
||||
if (unit === "month") {
|
||||
if (
|
||||
data.indexOf(selectedValue) < 12 ||
|
||||
data.indexOf(selectedValue) > 23
|
||||
) {
|
||||
setTranslateY((data.indexOf(selectedValue) + 12) * -ITEM_HEIGHT);
|
||||
}
|
||||
}
|
||||
}, [unit, data, selectedValue]);
|
||||
|
||||
const handleTouchEnd = () => {
|
||||
if (isMobile && scrollRef.current) {
|
||||
const selectedIndex = Math.round(-translateY / ITEM_HEIGHT);
|
||||
|
||||
@ -53,7 +53,7 @@ function CompatibilityPage(): JSX.Element {
|
||||
};
|
||||
|
||||
const handleValidDate = (date: string | IDate) => {
|
||||
setIsDisabledDate(date === '');
|
||||
setIsDisabledDate(date === "");
|
||||
setSelectedDate(date);
|
||||
checkAllDisabled();
|
||||
};
|
||||
|
||||
@ -127,7 +127,7 @@
|
||||
.compatibility-categories {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32px;
|
||||
gap: 16px;
|
||||
margin-top: 72px;
|
||||
}
|
||||
|
||||
|
||||
18
src/components/CompatibilityLoading/index.tsx
Normal file
18
src/components/CompatibilityLoading/index.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import Title from "../Title";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
// interface ICompatibilityLoadingProps {
|
||||
// onEndLoading: () => void;
|
||||
// }
|
||||
|
||||
function CompatibilityLoading(): JSX.Element {
|
||||
return (
|
||||
<section className={`${styles.container}`}>
|
||||
<Title variant="h3" className={styles.title}>
|
||||
Analysis of you with Viktor Ershov
|
||||
</Title>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default CompatibilityLoading;
|
||||
12
src/components/CompatibilityLoading/styles.module.css
Normal file
12
src/components/CompatibilityLoading/styles.module.css
Normal file
@ -0,0 +1,12 @@
|
||||
.container {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fd3761;
|
||||
font-weight: 600;
|
||||
}
|
||||
@ -7,24 +7,24 @@ import styles from './styles.module.css'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { selectors } from '@/store'
|
||||
import { getZodiacSignByDate } from '@/services/zodiac-sign'
|
||||
import SpecialWelcomeOffer from '../SpecialWelcomeOffer'
|
||||
import { useState } from 'react'
|
||||
// import SpecialWelcomeOffer from '../SpecialWelcomeOffer'
|
||||
// import { useState } from 'react'
|
||||
|
||||
function DidYouKnowPage(): JSX.Element {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const handleNext = () => navigate(routes.client.freePeriodInfo())
|
||||
const [isOpenModal, setIsOpenModal] = useState(false)
|
||||
const handleSpecialOffer = () => {
|
||||
setIsOpenModal(true)
|
||||
}
|
||||
// const [isOpenModal, setIsOpenModal] = useState(false)
|
||||
// const handleSpecialOffer = () => {
|
||||
// setIsOpenModal(true)
|
||||
// }
|
||||
const birthdate = useSelector(selectors.selectBirthdate)
|
||||
const zodiacSign = getZodiacSignByDate(birthdate)
|
||||
|
||||
|
||||
return (
|
||||
<section className={`${styles.page} page`}>
|
||||
<SpecialWelcomeOffer open={isOpenModal} />
|
||||
{/* <SpecialWelcomeOffer open={isOpenModal} /> */}
|
||||
<div className={styles.content}>
|
||||
<Title variant='h1'>{t('did_you_know')}</Title>
|
||||
<p className={styles.zodiacInfo}>
|
||||
@ -35,7 +35,7 @@ function DidYouKnowPage(): JSX.Element {
|
||||
<MainButton onClick={handleNext}>
|
||||
{t('learn_about_my_energy')}
|
||||
</MainButton>
|
||||
<span className={styles.skip} onClick={handleSpecialOffer}>{t('skip_for_now')}</span>
|
||||
<span className={styles.skip} onClick={handleNext}>{t('skip_for_now')}</span>
|
||||
</footer>
|
||||
</section>
|
||||
)
|
||||
|
||||
@ -61,7 +61,7 @@ function EmailEnterPage(): JSX.Element {
|
||||
|
||||
return (
|
||||
<section className='page'>
|
||||
<Title variant='h2' className='mt-24'>{t('we_will_email_you')}</Title>
|
||||
<Title variant='h2' className='mt-24'>{t('aura.web.email_title')}</Title>
|
||||
<EmailInput
|
||||
name="email"
|
||||
value={email}
|
||||
@ -69,16 +69,16 @@ function EmailEnterPage(): JSX.Element {
|
||||
onValid={handleValidEmail}
|
||||
onInvalid={() => setIsDisabled(true)}
|
||||
/>
|
||||
<p>{t('we_dont_share')}</p>
|
||||
<p style={{ marginBottom: '8px' }}>{t('we_dont_share')}</p>
|
||||
<MainButton onClick={handleClick} disabled={isDisabled}>
|
||||
{isLoading ? <Loader color={LoaderColor.White} /> : t('_continue')}
|
||||
</MainButton>
|
||||
<Policy sizing='medium'>
|
||||
{t('_continue_agree', {
|
||||
eulaLink: <a href='https://aura.wit.life/terms' target='_blank' rel='noopener noreferrer'>{t('eula')}</a>,
|
||||
privacyLink: <a href='https://aura.wit.life/privacy' target='_blank' rel='noopener noreferrer'>{t('privacy_policy')}</a>,
|
||||
})}
|
||||
</Policy>
|
||||
<MainButton onClick={handleClick} disabled={isDisabled}>
|
||||
{isLoading ? <Loader color={LoaderColor.White} /> : t('_continue')}
|
||||
</MainButton>
|
||||
<ErrorText size='medium' isShown={Boolean(error)} message={error ? extractErrorMessage(error) : null} />
|
||||
</section>
|
||||
)
|
||||
|
||||
@ -162,7 +162,7 @@ function HomePage(): JSX.Element {
|
||||
{/* <a href={asset?.url.replace('http://', 'https://')} download></a> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<div className={styles.content} style={{ marginTop: isShowNavbar ? "calc(100vh - 570px)" : "calc(100vh - 500px)"}}>
|
||||
<div className={styles["content__buttons"]}>
|
||||
<BlurringSubstrate
|
||||
style={{ color: "#fa71ea" }}
|
||||
|
||||
@ -28,7 +28,7 @@ function PaymentTable({ currency, locale, items }: PaymentTableProps): JSX.Eleme
|
||||
</div>
|
||||
<div className='payment__item-description'>
|
||||
<p>{item.description}</p>
|
||||
<p>One dollar thirty six cents per day</p>
|
||||
{/* <p>One dollar thirty six cents per day</p> */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@ -44,7 +44,7 @@ function PaymentTable({ currency, locale, items }: PaymentTableProps): JSX.Eleme
|
||||
{items.map(toItem)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='payment__information'>{t('charged_only')}</div>
|
||||
<div className='payment__information'>{t('charged_only', {price: totalPrice.format()})}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
flex-direction: column;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ const removeAfterDot = (value: string): string => {
|
||||
|
||||
interface PriceItemProps {
|
||||
active: boolean;
|
||||
click: () => void;
|
||||
click: (id: number) => void;
|
||||
}
|
||||
|
||||
function PriceItem({
|
||||
@ -38,12 +38,16 @@ function PriceItem({
|
||||
|
||||
const compatClassName = () => {
|
||||
const isPopular = id === 3;
|
||||
// const isActive = active;
|
||||
return `${styles.container} ${isPopular ? styles.popular : ""}`;
|
||||
const isActive = active;
|
||||
return `${styles.container} ${isPopular ? styles.popular : ""} ${isActive ? styles.active : ""}`;
|
||||
};
|
||||
|
||||
const itemClick = () => {
|
||||
click(id);
|
||||
}
|
||||
|
||||
return (
|
||||
<div onClick={click} className={compatClassName()}>
|
||||
<div onClick={itemClick} className={compatClassName()}>
|
||||
{removeAfterDot(_price.format())}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -10,11 +10,12 @@
|
||||
border: solid #d6d2d2 2px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: #30bf52;
|
||||
border-color: #30bf52;
|
||||
background-color: #d6d2d2;
|
||||
border-color: #d6d2d2 !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import { useState } from 'react'
|
||||
import PriceItem from '../PriceItem'
|
||||
import styles from './styles.module.css'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { actions } from '@/store'
|
||||
|
||||
export interface IPrice {
|
||||
id: number
|
||||
@ -31,11 +34,29 @@ interface PriceListProps {
|
||||
}
|
||||
|
||||
function PriceList({click, activeItem}: PriceListProps): JSX.Element {
|
||||
const dispatch = useDispatch();
|
||||
const [activePriceItem, setActivePriceItem] = useState<number | null>(activeItem)
|
||||
|
||||
const priceItemClick = (id: number) => {
|
||||
console.log(id);
|
||||
setActivePriceItem(id)
|
||||
const activePriceItem = prices.find((item) => item.id === Number(id))
|
||||
if (activePriceItem) {
|
||||
dispatch(
|
||||
actions.payment.update({
|
||||
selectedPrice: activePriceItem.value
|
||||
})
|
||||
);
|
||||
}
|
||||
setTimeout(() => {
|
||||
click()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${styles.container}`}>
|
||||
{prices.map((price, idx) => (
|
||||
<PriceItem active={price.id === activeItem} key={idx} value={price.value} id={price.id} click={click} />
|
||||
<PriceItem active={price.id === activePriceItem} key={idx} value={price.value} id={price.id} click={priceItemClick} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -31,7 +31,7 @@ function PriceListPage(): JSX.Element {
|
||||
<UserHeader email={email} />
|
||||
<section className={`${styles.page} page`}>
|
||||
<Title className={styles.title} variant='h2'>{t('choose_your_own_fee')}</Title>
|
||||
<p className={styles.slogan}>{t('should_not_get', { strongText: <strong>{t('money')}</strong> })}</p>
|
||||
<p className={styles.slogan}>{t('should_not_get', { strongText: <strong>{t('aura.web.price_selection')}</strong> })}</p>
|
||||
<div className={styles['emails-list-container']}>
|
||||
<EmailsList />
|
||||
</div>
|
||||
|
||||
@ -11,6 +11,8 @@ import CallToAction from "../CallToAction";
|
||||
import routes from "@/routes";
|
||||
import styles from "./styles.module.css";
|
||||
import Header from "../Header";
|
||||
import SpecialWelcomeOffer from "../SpecialWelcomeOffer";
|
||||
import { useState } from "react";
|
||||
|
||||
const currency = Currency.USD;
|
||||
const locale = Locale.EN;
|
||||
@ -24,12 +26,17 @@ const paymentItems = [
|
||||
];
|
||||
|
||||
function SubscriptionPage(): JSX.Element {
|
||||
const [isOpenModal, setIsOpenModal] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const email = useSelector(selectors.selectEmail);
|
||||
const itemPrice = useSelector(selectors.selectPlanById(itemPriceId));
|
||||
const selectedPrice = useSelector(selectors.selectSelectedPrice);
|
||||
if (selectedPrice || selectedPrice === 0) {
|
||||
paymentItems[0].price = selectedPrice;
|
||||
}
|
||||
const handleClick = () => navigate(routes.client.paymentMethod());
|
||||
const handleCross = () => navigate(routes.client.home());
|
||||
const handleCross = () => setIsOpenModal(true);
|
||||
const policyLink = (
|
||||
<a href="https://aura.wit.life/" target="_blank" rel="noopener noreferrer">
|
||||
{t("subscription_policy")}
|
||||
@ -38,6 +45,7 @@ function SubscriptionPage(): JSX.Element {
|
||||
console.log({ itemPrice });
|
||||
return (
|
||||
<>
|
||||
<SpecialWelcomeOffer open={isOpenModal} onClose={handleClick} />
|
||||
<Header classCross={styles.cross} clickCross={handleCross} />
|
||||
<UserHeader email={email} />
|
||||
<section className="page">
|
||||
|
||||
@ -32,12 +32,12 @@ export default {
|
||||
unexpected_error: 'Sorry, an unexpected error has occurred.',
|
||||
oops: "Oops!",
|
||||
total_today: "Total today",
|
||||
charged_only: "You will be charged only $1 for your 7-day trial. We'll email you a reminder before your trial period ends. Cancel anytime.",
|
||||
charged_only: "You will be charged only <price> for your 7-day trial. We'll email you a reminder before your trial period ends. Cancel anytime.",
|
||||
purposes: "For entertaiment purposes only.",
|
||||
get_access: "Get access",
|
||||
subscription_text: "By proceeding, you agree that if you do not cancel your subscription before the end of the 7-day trial period, you will be automatically charged nineteen US dollars zero cents every 2 weeks until you cancel the subscription in the settings. Learn more about cancellation and refund policy in <policyLink>",
|
||||
subscription_policy: "Subscription policy",
|
||||
company_name: "Wit LLC, California, US",
|
||||
company_name: "Wit Apps LLC, California, US",
|
||||
choose_payment: "Choose Payment Method",
|
||||
or: "OR",
|
||||
card: "Credit / Debit Card",
|
||||
|
||||
@ -93,7 +93,7 @@ export const hasNavigation = (path: string) =>
|
||||
export const hasNoNavigation = (path: string) => !hasNavigation(path);
|
||||
|
||||
export const withCrossButtonRoutes = [
|
||||
routes.client.attention(),
|
||||
// routes.client.attention(),
|
||||
routes.client.subscription(),
|
||||
];
|
||||
export const hasCrossButton = (path: string) =>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user