feat: attention page buttons, special welcome offer
This commit is contained in:
parent
deb354e29b
commit
5ee5385ef9
BIN
public/your-friends.png
Normal file
BIN
public/your-friends.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@ -5,6 +5,7 @@ import routes from '@/routes'
|
|||||||
import styles from './styles.module.css'
|
import styles from './styles.module.css'
|
||||||
// import CheckboxWithText from '../CheckboxWithText'
|
// import CheckboxWithText from '../CheckboxWithText'
|
||||||
import SpecialWelcomeOffer from '../SpecialWelcomeOffer'
|
import SpecialWelcomeOffer from '../SpecialWelcomeOffer'
|
||||||
|
import MainButton from '../MainButton'
|
||||||
// import MainButton from '../MainButton'
|
// import MainButton from '../MainButton'
|
||||||
|
|
||||||
interface AttentionPageProps {
|
interface AttentionPageProps {
|
||||||
@ -29,9 +30,11 @@ function AttentionPage({ isOpenModal, onCloseSpecialOffer }: AttentionPageProps)
|
|||||||
<img className={styles.icon} src="/stop-icon.png" alt="stop" />
|
<img className={styles.icon} src="/stop-icon.png" alt="stop" />
|
||||||
<Title variant='h2'>{t('aura.attention.title')}</Title>
|
<Title variant='h2'>{t('aura.attention.title')}</Title>
|
||||||
<p className={styles.text}>{t('aura.warming_up.body')}</p>
|
<p className={styles.text}>{t('aura.warming_up.body')}</p>
|
||||||
<div className={styles['checkbox-container']}>
|
<div className={styles['buttons-container']}>
|
||||||
{/* <CheckboxWithText text={t('not_ready_for_information')} onChange={onChangeCheckbox} /> */}
|
{/* <CheckboxWithText text={t('not_ready_for_information')} onChange={onChangeCheckbox} /> */}
|
||||||
<Title variant='h2' className={styles.button} onClick={handleNext}>{t('aura.warming_up.button')}</Title>
|
{/* <Title variant='h2' className={styles.button} onClick={handleNext}>{t('aura.warming_up.button')}</Title> */}
|
||||||
|
<MainButton onClick={handleNext}>{t('aura.warmin_good.button')}</MainButton>
|
||||||
|
<MainButton onClick={handleNext}>{t('aura.warmin_bad.button')}</MainButton>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -17,9 +17,12 @@
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-container {
|
.buttons-container {
|
||||||
width: 80%;
|
width: 100%;
|
||||||
margin: 64px auto 0;
|
margin: 64px auto 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
|||||||
@ -1,60 +1,84 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from "react";
|
||||||
import { useNavigate, useLocation } from 'react-router-dom'
|
import { useNavigate, useLocation } from "react-router-dom";
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from "react-i18next";
|
||||||
import routes, { hasCrossButton, hasNavigation, isNotEntrypoint } from '@/routes'
|
import routes, {
|
||||||
import BackButton from './BackButton'
|
hasCrossButton,
|
||||||
import iconUrl from './icon.png'
|
hasNavigation,
|
||||||
import menuUrl from './menu.png'
|
isNotEntrypoint,
|
||||||
import styles from './styles.module.css'
|
} from "@/routes";
|
||||||
|
import BackButton from "./BackButton";
|
||||||
|
import iconUrl from "./icon.png";
|
||||||
|
import menuUrl from "./menu.png";
|
||||||
|
import styles from "./styles.module.css";
|
||||||
|
|
||||||
type HeaderProps = {
|
type HeaderProps = {
|
||||||
openMenu?: () => void
|
openMenu?: () => void;
|
||||||
showBack?: boolean
|
showBack?: boolean;
|
||||||
showCross?: boolean
|
showCross?: boolean;
|
||||||
classCross?: CSSModuleClasses | string
|
classCross?: CSSModuleClasses | string;
|
||||||
clickCross?: () => void
|
clickCross?: () => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
function Header({ openMenu, showBack, showCross, classCross, clickCross = () => {undefined}, ...props }: HeaderProps & React.HTMLAttributes<HTMLDivElement>): JSX.Element {
|
function Header({
|
||||||
const { t } = useTranslation()
|
openMenu,
|
||||||
const navigate = useNavigate()
|
showBack,
|
||||||
const location = useLocation()
|
showCross = true,
|
||||||
|
classCross,
|
||||||
|
clickCross = () => {
|
||||||
|
undefined;
|
||||||
|
},
|
||||||
|
...props
|
||||||
|
}: HeaderProps & React.HTMLAttributes<HTMLDivElement>): JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
const [initialPath, setInitialPath] = useState<string | null>(null);
|
const [initialPath, setInitialPath] = useState<string | null>(null);
|
||||||
const [isNavigated, setIsNavigated] = useState<boolean>(false);
|
const [isNavigated, setIsNavigated] = useState<boolean>(false);
|
||||||
const showBackButton = isNotEntrypoint(location.pathname)
|
const showBackButton = isNotEntrypoint(location.pathname);
|
||||||
const showMenuButton = hasNavigation(location.pathname)
|
const showMenuButton = hasNavigation(location.pathname);
|
||||||
const showCrossButton = hasCrossButton(location.pathname)
|
const showCrossButton = hasCrossButton(location.pathname);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!initialPath) {
|
if (!initialPath) {
|
||||||
setInitialPath(location.pathname)
|
setInitialPath(location.pathname);
|
||||||
}
|
}
|
||||||
if (initialPath && location.pathname !== initialPath && !isNavigated) {
|
if (initialPath && location.pathname !== initialPath && !isNavigated) {
|
||||||
setIsNavigated(true)
|
setIsNavigated(true);
|
||||||
}
|
}
|
||||||
}, [location.pathname, initialPath, isNavigated])
|
}, [location.pathname, initialPath, isNavigated]);
|
||||||
|
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
if (initialPath && isNotEntrypoint(initialPath) && !isNavigated) {
|
if (initialPath && isNotEntrypoint(initialPath) && !isNavigated) {
|
||||||
navigate(routes.client.root())
|
navigate(routes.client.root());
|
||||||
} else {
|
} else {
|
||||||
navigate(-1)
|
navigate(-1);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className={styles.header} {...props}>
|
<header className={styles.header} {...props}>
|
||||||
{ (showBackButton || showBack) ? <BackButton className='pa' onClick={goBack} /> : null }
|
{showBackButton || showBack ? (
|
||||||
<div className={styles['header__logo-container']}>
|
<BackButton className="pa" onClick={goBack} />
|
||||||
|
) : null}
|
||||||
|
<div className={styles["header__logo-container"]}>
|
||||||
<img src={iconUrl} alt="logo" width="40" height="40" />
|
<img src={iconUrl} alt="logo" width="40" height="40" />
|
||||||
<span className={styles["header__title"]}>{t('app_name')}</span>
|
<span className={styles["header__title"]}>{t("app_name")}</span>
|
||||||
</div>
|
</div>
|
||||||
{(showCrossButton || showCross) ? <img className={`${styles.cross} ${classCross || ''}`} src="/cross.png" alt="Cross" onClick={clickCross} /> : null}
|
{showCrossButton && showCross ? (
|
||||||
{showMenuButton ? <div className={styles["header__menu-btn"]} onClick={openMenu}>
|
<img
|
||||||
<img src={menuUrl} alt="menu" width="40" height="40" />
|
className={`${styles.cross} ${classCross || ""}`}
|
||||||
</div> : null}
|
src="/cross.png"
|
||||||
|
alt="Cross"
|
||||||
|
onClick={clickCross}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{showMenuButton ? (
|
||||||
|
<div className={styles["header__menu-btn"]} onClick={openMenu}>
|
||||||
|
<img src={menuUrl} alt="menu" width="40" height="40" />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</header>
|
</header>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Header
|
export default Header;
|
||||||
|
|||||||
@ -1,52 +1,86 @@
|
|||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from "react-i18next";
|
||||||
import Title from '../Title'
|
import Title from "../Title";
|
||||||
import routes from '@/routes'
|
import routes from "@/routes";
|
||||||
import styles from './styles.module.css'
|
import styles from "./styles.module.css";
|
||||||
import ModalTop from '../ModalTop'
|
import ModalTop from "../ModalTop";
|
||||||
import Header from '../Header'
|
import Header from "../Header";
|
||||||
import { useCallback } from 'react'
|
import { useCallback } from "react";
|
||||||
import { useDispatch } from 'react-redux'
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { actions } from '@/store'
|
import { actions, selectors } from "@/store";
|
||||||
|
import MainButton from "../MainButton";
|
||||||
|
|
||||||
interface ModalTopProps {
|
interface ModalTopProps {
|
||||||
open: boolean
|
open: boolean;
|
||||||
onClose?: () => void
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SpecialWelcomeOffer({ open, onClose }: ModalTopProps): JSX.Element {
|
function SpecialWelcomeOffer({ open, onClose }: ModalTopProps): JSX.Element {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate();
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch();
|
||||||
const updateSelectedPrice = useCallback((selectedPrice: number | null) => {
|
const selectedPrice = useSelector(selectors.selectSelectedPrice);
|
||||||
dispatch(actions.payment.update({
|
const halfPrice = (Math.round(selectedPrice || 0) / 2).toFixed(2);
|
||||||
selectedPrice
|
const updateIsDiscount = useCallback((isDiscount: boolean) => {
|
||||||
}))
|
dispatch(
|
||||||
}, [dispatch])
|
actions.payment.update({
|
||||||
|
isDiscount
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}, [dispatch]);
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
updateSelectedPrice(1)
|
updateIsDiscount(true);
|
||||||
navigate(routes.client.emailEnter())
|
navigate(routes.client.paymentMethod());
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{open ? (
|
{open ? (
|
||||||
<ModalTop open={open} onClose={onClose || handleNext}>
|
<ModalTop open={open} onClose={onClose || handleNext}>
|
||||||
<Header showBack={false} showCross={true} clickCross={onClose || handleNext} />
|
<Header
|
||||||
|
showBack={false}
|
||||||
|
showCross={false}
|
||||||
|
clickCross={onClose || handleNext}
|
||||||
|
/>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<span className={styles['welcome-offer']}>{t('special_welcome_offer')}</span>
|
{/* <span className={styles['welcome-offer']}>{t('special_welcome_offer')}</span> */}
|
||||||
<Title variant='h1'>{t('get_100_off')}</Title>
|
<img src="/your-friends.png" alt="Your friends" />
|
||||||
<div className={styles['discount-container']}>
|
<Title variant="h2" className={styles["your-friends"]}>
|
||||||
<span className={styles['red-price']}>$9.99</span>
|
{t("au.friends.window")}
|
||||||
<span className={styles['price']}>$0.00</span>
|
</Title>
|
||||||
|
<Title variant="h2" className={styles["get-50-only"]}>
|
||||||
|
{t("au.get50.only")}
|
||||||
|
</Title>
|
||||||
|
<div className={styles["discount-container"]}>
|
||||||
|
{Number(halfPrice) > 0 &&
|
||||||
|
<>
|
||||||
|
<span className={styles["red-price"]}>${selectedPrice}</span>{" "}
|
||||||
|
–<span className={styles["price"]}>${halfPrice}</span>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{!Number(halfPrice) && <span className={styles["free-trial"]}>{t('au.free_trial_web.7_14')}</span>}
|
||||||
</div>
|
</div>
|
||||||
<button className={styles.button} onClick={onClose || handleNext}>{t('get_discount')}</button>
|
<MainButton
|
||||||
|
className={styles["button-green"]}
|
||||||
|
onClick={handleNext}
|
||||||
|
>
|
||||||
|
$ {halfPrice} – {t("au.try_for.button")}
|
||||||
|
</MainButton>
|
||||||
|
<MainButton
|
||||||
|
// disabled
|
||||||
|
className={styles["button-black"]}
|
||||||
|
onClick={() => {
|
||||||
|
console.log("click");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img className={styles["button-icon"]} src="/leo.png" alt="Leo" />
|
||||||
|
{t("au.more_llc.button")}
|
||||||
|
</MainButton>
|
||||||
</div>
|
</div>
|
||||||
</ModalTop>
|
</ModalTop>
|
||||||
): null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SpecialWelcomeOffer
|
export default SpecialWelcomeOffer;
|
||||||
|
|||||||
@ -1,40 +1,73 @@
|
|||||||
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding: 0 24px 64px 24px;
|
padding: 0 24px 64px 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding-top: 28px;
|
padding-top: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.welcome-offer {
|
.welcome-offer {
|
||||||
color: #717171;
|
color: #717171;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.discount-container {
|
.discount-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
font-size: 24px;
|
font-size: 32px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
margin-bottom: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.red-price {
|
.red-price {
|
||||||
color: red;
|
color: red;
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button-green {
|
||||||
background-color: #69e573;
|
background-color: #18d136;
|
||||||
color: #fff;
|
font-weight: 600;
|
||||||
|
font-size: 21px;
|
||||||
|
/* color: #fff;
|
||||||
border-radius: 26px;
|
border-radius: 26px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
padding: 12px 0;
|
padding: 12px 0;
|
||||||
border: none;
|
border: none;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-top: 16px;
|
margin-top: 16px; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-black {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 21px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.your-friends {
|
||||||
|
width: 80%;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.get-50-only {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #ff003d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-icon {
|
||||||
|
position: absolute;
|
||||||
|
width: 48px;
|
||||||
|
top: 50%;
|
||||||
|
left: 13px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.free-trial {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
@ -4,7 +4,7 @@ import user, { actions as userActions, selectUser } from './user'
|
|||||||
import form, { actions as formActions, selectors as formSelectors } from './form'
|
import form, { actions as formActions, selectors as formSelectors } from './form'
|
||||||
import aura, { actions as auraActions } from './aura'
|
import aura, { actions as auraActions } from './aura'
|
||||||
import siteConfig, { selectHome, actions as siteConfigActions } from './siteConfig'
|
import siteConfig, { selectHome, actions as siteConfigActions } from './siteConfig'
|
||||||
import payment, { actions as paymentActions } from './payment'
|
import payment, { actions as paymentActions, selectIsDiscount } from './payment'
|
||||||
import subscriptionPlans, { actions as subscriptionPlasActions, selectPlanById } from './subscriptionPlan'
|
import subscriptionPlans, { actions as subscriptionPlasActions, selectPlanById } from './subscriptionPlan'
|
||||||
import status, { actions as userStatusActions, selectStatus } from './status'
|
import status, { actions as userStatusActions, selectStatus } from './status'
|
||||||
import compatibility, { actions as compatibilityActions } from './compatibility'
|
import compatibility, { actions as compatibilityActions } from './compatibility'
|
||||||
@ -44,6 +44,7 @@ export const selectors = {
|
|||||||
selectUserCallbacksDescription,
|
selectUserCallbacksDescription,
|
||||||
selectUserCallbacksPrevStat,
|
selectUserCallbacksPrevStat,
|
||||||
selectHome,
|
selectHome,
|
||||||
|
selectIsDiscount,
|
||||||
...formSelectors,
|
...formSelectors,
|
||||||
}
|
}
|
||||||
export type RootState = ReturnType<typeof reducer>
|
export type RootState = ReturnType<typeof reducer>
|
||||||
|
|||||||
@ -1,28 +1,34 @@
|
|||||||
import { createSlice, createSelector } from '@reduxjs/toolkit'
|
import { createSlice, createSelector } from "@reduxjs/toolkit";
|
||||||
import type { PayloadAction } from '@reduxjs/toolkit'
|
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
interface IPayment {
|
interface IPayment {
|
||||||
selectedPrice: number | null
|
selectedPrice: number | null;
|
||||||
|
isDiscount: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: IPayment = {
|
const initialState: IPayment = {
|
||||||
selectedPrice: null
|
selectedPrice: null,
|
||||||
}
|
isDiscount: false,
|
||||||
|
};
|
||||||
|
|
||||||
const paymentSlice = createSlice({
|
const paymentSlice = createSlice({
|
||||||
name: 'payment',
|
name: "payment",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
update(state, action: PayloadAction<Partial<IPayment>>) {
|
update(state, action: PayloadAction<Partial<IPayment>>) {
|
||||||
return { ...state, ...action.payload }
|
return { ...state, ...action.payload };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraReducers: (builder) => builder.addCase('reset', () => initialState),
|
extraReducers: (builder) => builder.addCase("reset", () => initialState),
|
||||||
})
|
});
|
||||||
|
|
||||||
export const { actions } = paymentSlice
|
export const { actions } = paymentSlice;
|
||||||
export const selectSelectedPrice = createSelector(
|
export const selectSelectedPrice = createSelector(
|
||||||
(state: { payment: IPayment }) => state.payment.selectedPrice,
|
(state: { payment: IPayment }) => state.payment.selectedPrice,
|
||||||
(payment) => payment
|
(payment) => payment
|
||||||
)
|
);
|
||||||
export default paymentSlice.reducer
|
export const selectIsDiscount = createSelector(
|
||||||
|
(state: { payment: IPayment }) => state.payment.isDiscount,
|
||||||
|
(payment) => payment
|
||||||
|
);
|
||||||
|
export default paymentSlice.reducer;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user