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 CheckboxWithText from '../CheckboxWithText'
|
||||
import SpecialWelcomeOffer from '../SpecialWelcomeOffer'
|
||||
import MainButton from '../MainButton'
|
||||
// import MainButton from '../MainButton'
|
||||
|
||||
interface AttentionPageProps {
|
||||
@ -29,9 +30,11 @@ function AttentionPage({ isOpenModal, onCloseSpecialOffer }: AttentionPageProps)
|
||||
<img className={styles.icon} src="/stop-icon.png" alt="stop" />
|
||||
<Title variant='h2'>{t('aura.attention.title')}</Title>
|
||||
<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} /> */}
|
||||
<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>
|
||||
</section>
|
||||
)
|
||||
|
||||
@ -17,9 +17,12 @@
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
width: 80%;
|
||||
.buttons-container {
|
||||
width: 100%;
|
||||
margin: 64px auto 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 13px;
|
||||
}
|
||||
|
||||
.button {
|
||||
|
||||
@ -1,60 +1,84 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useNavigate, useLocation } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import routes, { hasCrossButton, hasNavigation, isNotEntrypoint } from '@/routes'
|
||||
import BackButton from './BackButton'
|
||||
import iconUrl from './icon.png'
|
||||
import menuUrl from './menu.png'
|
||||
import styles from './styles.module.css'
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import routes, {
|
||||
hasCrossButton,
|
||||
hasNavigation,
|
||||
isNotEntrypoint,
|
||||
} from "@/routes";
|
||||
import BackButton from "./BackButton";
|
||||
import iconUrl from "./icon.png";
|
||||
import menuUrl from "./menu.png";
|
||||
import styles from "./styles.module.css";
|
||||
|
||||
type HeaderProps = {
|
||||
openMenu?: () => void
|
||||
showBack?: boolean
|
||||
showCross?: boolean
|
||||
classCross?: CSSModuleClasses | string
|
||||
clickCross?: () => void
|
||||
}
|
||||
openMenu?: () => void;
|
||||
showBack?: boolean;
|
||||
showCross?: boolean;
|
||||
classCross?: CSSModuleClasses | string;
|
||||
clickCross?: () => void;
|
||||
};
|
||||
|
||||
function Header({ openMenu, showBack, showCross, classCross, clickCross = () => {undefined}, ...props }: HeaderProps & React.HTMLAttributes<HTMLDivElement>): JSX.Element {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
function Header({
|
||||
openMenu,
|
||||
showBack,
|
||||
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 [isNavigated, setIsNavigated] = useState<boolean>(false);
|
||||
const showBackButton = isNotEntrypoint(location.pathname)
|
||||
const showMenuButton = hasNavigation(location.pathname)
|
||||
const showCrossButton = hasCrossButton(location.pathname)
|
||||
const showBackButton = isNotEntrypoint(location.pathname);
|
||||
const showMenuButton = hasNavigation(location.pathname);
|
||||
const showCrossButton = hasCrossButton(location.pathname);
|
||||
|
||||
useEffect(() => {
|
||||
if (!initialPath) {
|
||||
setInitialPath(location.pathname)
|
||||
setInitialPath(location.pathname);
|
||||
}
|
||||
if (initialPath && location.pathname !== initialPath && !isNavigated) {
|
||||
setIsNavigated(true)
|
||||
setIsNavigated(true);
|
||||
}
|
||||
}, [location.pathname, initialPath, isNavigated])
|
||||
}, [location.pathname, initialPath, isNavigated]);
|
||||
|
||||
const goBack = () => {
|
||||
if (initialPath && isNotEntrypoint(initialPath) && !isNavigated) {
|
||||
navigate(routes.client.root())
|
||||
navigate(routes.client.root());
|
||||
} else {
|
||||
navigate(-1)
|
||||
navigate(-1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<header className={styles.header} {...props}>
|
||||
{ (showBackButton || showBack) ? <BackButton className='pa' onClick={goBack} /> : null }
|
||||
<div className={styles['header__logo-container']}>
|
||||
{showBackButton || showBack ? (
|
||||
<BackButton className="pa" onClick={goBack} />
|
||||
) : null}
|
||||
<div className={styles["header__logo-container"]}>
|
||||
<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>
|
||||
{(showCrossButton || showCross) ? <img className={`${styles.cross} ${classCross || ''}`} 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}
|
||||
{showCrossButton && showCross ? (
|
||||
<img
|
||||
className={`${styles.cross} ${classCross || ""}`}
|
||||
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>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Header
|
||||
export default Header;
|
||||
|
||||
@ -1,52 +1,86 @@
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Title from '../Title'
|
||||
import routes from '@/routes'
|
||||
import styles from './styles.module.css'
|
||||
import ModalTop from '../ModalTop'
|
||||
import Header from '../Header'
|
||||
import { useCallback } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { actions } from '@/store'
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Title from "../Title";
|
||||
import routes from "@/routes";
|
||||
import styles from "./styles.module.css";
|
||||
import ModalTop from "../ModalTop";
|
||||
import Header from "../Header";
|
||||
import { useCallback } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { actions, selectors } from "@/store";
|
||||
import MainButton from "../MainButton";
|
||||
|
||||
interface ModalTopProps {
|
||||
open: boolean
|
||||
onClose?: () => void
|
||||
open: boolean;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
function SpecialWelcomeOffer({ open, onClose }: ModalTopProps): JSX.Element {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const dispatch = useDispatch()
|
||||
const updateSelectedPrice = useCallback((selectedPrice: number | null) => {
|
||||
dispatch(actions.payment.update({
|
||||
selectedPrice
|
||||
}))
|
||||
}, [dispatch])
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const selectedPrice = useSelector(selectors.selectSelectedPrice);
|
||||
const halfPrice = (Math.round(selectedPrice || 0) / 2).toFixed(2);
|
||||
const updateIsDiscount = useCallback((isDiscount: boolean) => {
|
||||
dispatch(
|
||||
actions.payment.update({
|
||||
isDiscount
|
||||
})
|
||||
);
|
||||
}, [dispatch]);
|
||||
const handleNext = () => {
|
||||
updateSelectedPrice(1)
|
||||
navigate(routes.client.emailEnter())
|
||||
}
|
||||
|
||||
updateIsDiscount(true);
|
||||
navigate(routes.client.paymentMethod());
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{open ? (
|
||||
<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}>
|
||||
<span className={styles['welcome-offer']}>{t('special_welcome_offer')}</span>
|
||||
<Title variant='h1'>{t('get_100_off')}</Title>
|
||||
<div className={styles['discount-container']}>
|
||||
<span className={styles['red-price']}>$9.99</span>
|
||||
<span className={styles['price']}>$0.00</span>
|
||||
{/* <span className={styles['welcome-offer']}>{t('special_welcome_offer')}</span> */}
|
||||
<img src="/your-friends.png" alt="Your friends" />
|
||||
<Title variant="h2" className={styles["your-friends"]}>
|
||||
{t("au.friends.window")}
|
||||
</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>
|
||||
<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>
|
||||
</ModalTop>
|
||||
): null}
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default SpecialWelcomeOffer
|
||||
export default SpecialWelcomeOffer;
|
||||
|
||||
@ -1,40 +1,73 @@
|
||||
|
||||
|
||||
.content {
|
||||
padding: 0 24px 64px 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding-top: 28px;
|
||||
padding: 0 24px 64px 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding-top: 28px;
|
||||
}
|
||||
|
||||
.welcome-offer {
|
||||
color: #717171;
|
||||
font-weight: 500;
|
||||
color: #717171;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.discount-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.red-price {
|
||||
color: red;
|
||||
text-decoration: line-through;
|
||||
color: red;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: #69e573;
|
||||
color: #fff;
|
||||
.button-green {
|
||||
background-color: #18d136;
|
||||
font-weight: 600;
|
||||
font-size: 21px;
|
||||
/* color: #fff;
|
||||
border-radius: 26px;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
padding: 12px 0;
|
||||
border: none;
|
||||
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 aura, { actions as auraActions } from './aura'
|
||||
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 status, { actions as userStatusActions, selectStatus } from './status'
|
||||
import compatibility, { actions as compatibilityActions } from './compatibility'
|
||||
@ -44,6 +44,7 @@ export const selectors = {
|
||||
selectUserCallbacksDescription,
|
||||
selectUserCallbacksPrevStat,
|
||||
selectHome,
|
||||
selectIsDiscount,
|
||||
...formSelectors,
|
||||
}
|
||||
export type RootState = ReturnType<typeof reducer>
|
||||
|
||||
@ -1,28 +1,34 @@
|
||||
import { createSlice, createSelector } from '@reduxjs/toolkit'
|
||||
import type { PayloadAction } from '@reduxjs/toolkit'
|
||||
import { createSlice, createSelector } from "@reduxjs/toolkit";
|
||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
interface IPayment {
|
||||
selectedPrice: number | null
|
||||
selectedPrice: number | null;
|
||||
isDiscount: boolean;
|
||||
}
|
||||
|
||||
const initialState: IPayment = {
|
||||
selectedPrice: null
|
||||
}
|
||||
selectedPrice: null,
|
||||
isDiscount: false,
|
||||
};
|
||||
|
||||
const paymentSlice = createSlice({
|
||||
name: 'payment',
|
||||
name: "payment",
|
||||
initialState,
|
||||
reducers: {
|
||||
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(
|
||||
(state: { payment: IPayment }) => state.payment.selectedPrice,
|
||||
(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