feat: attention page buttons, special welcome offer

This commit is contained in:
gofnnp 2023-09-23 22:16:27 +04:00
parent deb354e29b
commit 5ee5385ef9
8 changed files with 213 additions and 109 deletions

BIN
public/your-friends.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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