From 415d9c4d7b0bba0461c91386ffe40901edbdb6f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BD=D0=B8=D1=81=20=D0=9A=D0=B0=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D0=B2?= Date: Tue, 30 Jan 2024 17:59:31 +0000 Subject: [PATCH] feat: add and change pages after questionnaire path --- public/arrow.svg | 5 + public/check-mark-purple.svg | 6 + public/darts-purple.svg | 11 + public/guard.svg | 6 + public/night_eye.webp | Bin 0 -> 6290 bytes public/opostrafs.svg | 6 + public/question.svg | 16 + public/question.webp | Bin 0 -> 14424 bytes src/components/App/index.tsx | 25 ++ src/components/EmailEnterPage/EmailInput.tsx | 49 +-- src/components/EmailEnterPage/NameInput.tsx | 48 +++ src/components/EmailEnterPage/index.tsx | 285 +++++++++++++----- .../EmailEnterPage/styles.module.css | 102 +++++++ src/components/EmailItem/index.tsx | 47 +-- src/components/EmailsList/index.tsx | 45 ++- src/components/EmailsList/styles.module.css | 21 +- src/components/Header/index.tsx | 3 +- .../LoadingProfileModalChild/index.tsx | 24 ++ .../styles.module.css | 48 +++ src/components/PriceItem/index.tsx | 15 +- src/components/PriceList/index.tsx | 15 +- src/components/PriceList/styles.module.css | 9 +- src/components/PriceListPage/index.tsx | 6 +- src/components/pages/AboutUs/index.tsx | 2 +- src/components/pages/EmailConfirm/index.tsx | 44 +++ .../pages/EmailConfirm/styles.module.css | 75 +++++ src/components/pages/LoadingProfile/index.tsx | 109 +++++++ .../pages/LoadingProfile/styles.module.css | 55 ++++ src/components/pages/Onboarding/index.tsx | 49 +++ .../pages/Onboarding/styles.module.css | 51 ++++ src/components/pages/TrialChoice/index.tsx | 114 +++++++ .../pages/TrialChoice/styles.module.css | 123 ++++++++ .../components/CustomButton/index.tsx | 14 + .../components/CustomButton/styles.module.css | 28 ++ .../components/DiscountExpires/index.tsx | 58 ++++ .../DiscountExpires/styles.module.css | 48 +++ .../TrialPayment/components/Goal/index.tsx | 27 ++ .../components/Goal/styles.module.css | 23 ++ .../components/GuardPayments/index.tsx | 12 + .../GuardPayments/styles.module.css | 16 + .../TrialPayment/components/Header/index.tsx | 20 ++ .../components/Header/styles.module.css | 19 ++ .../components/OftenAsk/Question.tsx | 25 ++ .../components/OftenAsk/index.tsx | 23 ++ .../components/OftenAsk/styles.module.css | 48 +++ .../components/PaymentTable/index.tsx | 53 ++++ .../components/PaymentTable/styles.module.css | 83 +++++ .../components/PersonalInformation/index.tsx | 51 ++++ .../PersonalInformation/styles.module.css | 64 ++++ .../components/Reviews/Review.tsx | 45 +++ .../TrialPayment/components/Reviews/index.tsx | 23 ++ .../components/Reviews/styles.module.css | 96 ++++++ .../TrialPayment/components/YouGet/index.tsx | 37 +++ .../components/YouGet/styles.module.css | 30 ++ .../components/YourReading/index.tsx | 77 +++++ .../components/YourReading/styles.module.css | 137 +++++++++ src/components/pages/TrialPayment/index.tsx | 59 ++++ .../pages/TrialPayment/styles.module.css | 16 + src/components/ui/ProgressBarLine/index.tsx | 25 +- src/data/loadingProfile.ts | 37 +++ src/data/oftenAsk.ts | 20 ++ src/data/onboarding.ts | 7 + src/data/reviews.ts | 27 ++ src/locales/dev.ts | 6 +- src/routes.ts | 14 +- src/services/price/index.ts | 9 + 66 files changed, 2541 insertions(+), 150 deletions(-) create mode 100755 public/arrow.svg create mode 100755 public/check-mark-purple.svg create mode 100755 public/darts-purple.svg create mode 100755 public/guard.svg create mode 100755 public/night_eye.webp create mode 100755 public/opostrafs.svg create mode 100755 public/question.svg create mode 100755 public/question.webp mode change 100644 => 100755 src/components/EmailEnterPage/EmailInput.tsx create mode 100755 src/components/EmailEnterPage/NameInput.tsx mode change 100644 => 100755 src/components/EmailEnterPage/index.tsx create mode 100755 src/components/EmailEnterPage/styles.module.css mode change 100644 => 100755 src/components/EmailItem/index.tsx mode change 100644 => 100755 src/components/EmailsList/index.tsx mode change 100644 => 100755 src/components/EmailsList/styles.module.css create mode 100755 src/components/LoadingProfileModalChild/index.tsx create mode 100755 src/components/LoadingProfileModalChild/styles.module.css mode change 100644 => 100755 src/components/PriceItem/index.tsx mode change 100644 => 100755 src/components/PriceList/index.tsx mode change 100644 => 100755 src/components/PriceList/styles.module.css mode change 100644 => 100755 src/components/PriceListPage/index.tsx create mode 100755 src/components/pages/EmailConfirm/index.tsx create mode 100755 src/components/pages/EmailConfirm/styles.module.css create mode 100755 src/components/pages/LoadingProfile/index.tsx create mode 100755 src/components/pages/LoadingProfile/styles.module.css create mode 100755 src/components/pages/Onboarding/index.tsx create mode 100755 src/components/pages/Onboarding/styles.module.css create mode 100755 src/components/pages/TrialChoice/index.tsx create mode 100755 src/components/pages/TrialChoice/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/CustomButton/index.tsx create mode 100755 src/components/pages/TrialPayment/components/CustomButton/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/DiscountExpires/index.tsx create mode 100755 src/components/pages/TrialPayment/components/DiscountExpires/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/Goal/index.tsx create mode 100755 src/components/pages/TrialPayment/components/Goal/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/GuardPayments/index.tsx create mode 100755 src/components/pages/TrialPayment/components/GuardPayments/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/Header/index.tsx create mode 100755 src/components/pages/TrialPayment/components/Header/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/OftenAsk/Question.tsx create mode 100755 src/components/pages/TrialPayment/components/OftenAsk/index.tsx create mode 100755 src/components/pages/TrialPayment/components/OftenAsk/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/PaymentTable/index.tsx create mode 100755 src/components/pages/TrialPayment/components/PaymentTable/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/PersonalInformation/index.tsx create mode 100755 src/components/pages/TrialPayment/components/PersonalInformation/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/Reviews/Review.tsx create mode 100755 src/components/pages/TrialPayment/components/Reviews/index.tsx create mode 100755 src/components/pages/TrialPayment/components/Reviews/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/YouGet/index.tsx create mode 100755 src/components/pages/TrialPayment/components/YouGet/styles.module.css create mode 100755 src/components/pages/TrialPayment/components/YourReading/index.tsx create mode 100755 src/components/pages/TrialPayment/components/YourReading/styles.module.css create mode 100755 src/components/pages/TrialPayment/index.tsx create mode 100755 src/components/pages/TrialPayment/styles.module.css mode change 100644 => 100755 src/components/ui/ProgressBarLine/index.tsx create mode 100755 src/data/loadingProfile.ts create mode 100755 src/data/oftenAsk.ts create mode 100755 src/data/onboarding.ts create mode 100755 src/data/reviews.ts mode change 100644 => 100755 src/locales/dev.ts mode change 100644 => 100755 src/services/price/index.ts diff --git a/public/arrow.svg b/public/arrow.svg new file mode 100755 index 0000000..db6d800 --- /dev/null +++ b/public/arrow.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/public/check-mark-purple.svg b/public/check-mark-purple.svg new file mode 100755 index 0000000..b0e69b6 --- /dev/null +++ b/public/check-mark-purple.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/public/darts-purple.svg b/public/darts-purple.svg new file mode 100755 index 0000000..5a81ddd --- /dev/null +++ b/public/darts-purple.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/guard.svg b/public/guard.svg new file mode 100755 index 0000000..3187bdd --- /dev/null +++ b/public/guard.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/public/night_eye.webp b/public/night_eye.webp new file mode 100755 index 0000000000000000000000000000000000000000..6552bf75c57bcc5751f75d6e1c2811aa471f6a58 GIT binary patch literal 6290 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 I0000002sJYCjbBd literal 0 HcmV?d00001 diff --git a/public/opostrafs.svg b/public/opostrafs.svg new file mode 100755 index 0000000..78fdedb --- /dev/null +++ b/public/opostrafs.svg @@ -0,0 +1,6 @@ + + + \ No newline at end of file diff --git a/public/question.svg b/public/question.svg new file mode 100755 index 0000000..9005190 --- /dev/null +++ b/public/question.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/public/question.webp b/public/question.webp new file mode 100755 index 0000000000000000000000000000000000000000..e94b9fc8bdcf2455f6c9ed9bdbbd200dfd870879 GIT binary patch literal 14424 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 literal 0 HcmV?d00001 diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index d5420a9..070f3df 100755 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -85,6 +85,11 @@ import BothPage from "../pages/Both"; import RelationshipZodiacInfoPage from "../pages/RelationshipZodiacInfo"; import Satisfied from "../pages/Satisfied"; import AboutUsPage from "../pages/AboutUs"; +import LoadingProfilePage from "../pages/LoadingProfile"; +import EmailConfirmPage from "../pages/EmailConfirm"; +import OnboardingPage from "../pages/Onboarding"; +import TrialChoicePage from "../pages/TrialChoice"; +import TrialPaymentPage from "../pages/TrialPayment"; function App(): JSX.Element { const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState(false); @@ -227,6 +232,26 @@ function App(): JSX.Element { /> } /> } /> + } + /> + } + /> + } + /> + } + /> + } + /> {/* Test Routes End */} { - const re = /^(([^<>()[\]\\.,:\s@"]+(\.[^<>()[\]\\.,:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ - return re.test(String(email).toLowerCase().trim()) -} + const re = + /^(([^<>()[\]\\.,:\s@"]+(\.[^<>()[\]\\.,:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase().trim()); +}; function EmailInput(props: FormField): JSX.Element { - const { name, value, placeholder, onValid, onInvalid } = props - const [email, setEmail] = useState(value) - const handleChange = (event: React.ChangeEvent) => { - setEmail(event.target.value) - } + const { name, value, placeholder, onValid, onInvalid } = props; + const [email, setEmail] = useState(value); + + const handleChangeEmail = (event: React.ChangeEvent) => { + const email = event.target.value; + if (!isValidEmail(email)) { + onInvalid(); + } else { + onValid(email); + } + setEmail(email); + }; useEffect(() => { if (isValidEmail(email)) { - onValid(email) + onValid(email); } else { - onInvalid() + onInvalid(); } - }, [email, onInvalid, onValid]) + }, [email, onInvalid, onValid]); return ( -
+
- {placeholder} + {placeholder}
- ) + ); } -export default EmailInput +export default EmailInput; diff --git a/src/components/EmailEnterPage/NameInput.tsx b/src/components/EmailEnterPage/NameInput.tsx new file mode 100755 index 0000000..c403686 --- /dev/null +++ b/src/components/EmailEnterPage/NameInput.tsx @@ -0,0 +1,48 @@ +import { useState } from "react"; +import styles from "./styles.module.css"; + +interface INameInputProps { + value: string; + placeholder: string; + onValid: (value: string) => void; + onInvalid: () => void; +} + +const isValidName = (name: string) => { + return !!(name.length > 0 && name.length < 30); +}; + +function NameInput({ + value, + placeholder, + onValid, + onInvalid, +}: INameInputProps) { + const [name, setName] = useState(value); + + const handleChangeName = (event: React.ChangeEvent) => { + const name = event.target.value; + if (!isValidName(name)) { + onInvalid(); + } else { + onValid(name); + } + setName(name); + }; + + return ( +
+ + {placeholder} +
+ ); +} + +export default NameInput; diff --git a/src/components/EmailEnterPage/index.tsx b/src/components/EmailEnterPage/index.tsx old mode 100644 new mode 100755 index fe885a1..7e40fdb --- a/src/components/EmailEnterPage/index.tsx +++ b/src/components/EmailEnterPage/index.tsx @@ -1,87 +1,228 @@ -import { useState } from 'react' -import { useNavigate } from 'react-router-dom' -import { useTranslation } from 'react-i18next' -import { useDispatch, useSelector } from 'react-redux' -import { actions, selectors } from '@/store' -import { getClientTimezone } from '@/locales' -import { useAuth } from '@/auth' -import { useApi, ApiError, extractErrorMessage } from '@/api' -import Title from '../Title' -import Policy from '../Policy' -import EmailInput from './EmailInput' -import MainButton from '../MainButton' -import Loader, { LoaderColor } from '../Loader' -import ErrorText from '../ErrorText' -import routes from '@/routes' +import styles from "./styles.module.css"; +import { useEffect, useState } from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { useDispatch, useSelector } from "react-redux"; +import { actions, selectors } from "@/store"; +import { getClientTimezone } from "@/locales"; +import { useAuth } from "@/auth"; +import { useApi, ApiError, extractErrorMessage } from "@/api"; +import Title from "../Title"; +import Policy from "../Policy"; +import EmailInput from "./EmailInput"; +import MainButton from "../MainButton"; +import Loader, { LoaderColor } from "../Loader"; +import ErrorText from "../ErrorText"; +import routes from "@/routes"; +import NameInput from "./NameInput"; +import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans"; function EmailEnterPage(): JSX.Element { - const api = useApi() - const { user, signUp } = useAuth() - const { t, i18n } = useTranslation() - const dispatch = useDispatch() - const navigate = useNavigate() - const email = useSelector(selectors.selectEmail) - const birthday = useSelector(selectors.selectBirthday) - const [isDisabled, setIsDisabled] = useState(true) - const [isLoading, setIsLoading] = useState(false) - const [error, setError] = useState(null) - const timezone = getClientTimezone() - const locale = i18n.language - const handleValidEmail = (email: string) => { - dispatch(actions.form.addEmail(email)) - setIsDisabled(false) - } - const handleClick = () => { - // TODO: fix backend error 422 auth with email - return navigate(routes.client.priceList()) - if (user) { - return - } - setError(null) - setIsLoading(true) - api.auth({ email, timezone, locale }) - .then(({ auth: { token, user } }) => signUp(token, user)) - .then((token) => { - const payload = { user: { profile_attributes: { birthday } }, token } - return Promise.all([ - api.updateUser(payload), - api.getSubscriptionItems({ locale, token }) - ]) - }) - .then(([{ user }, { item_prices }]) => { - dispatch(actions.user.update(user)) - dispatch(actions.status.update('registred')) - dispatch(actions.subscriptionPlan.setAll(item_prices)) - }) - .then(() => navigate(routes.client.subscription())) - .catch((error: ApiError) => setError(error)) - .finally(() => setIsLoading(false)) - } + const api = useApi(); + const { signUp } = useAuth(); + const { t, i18n } = useTranslation(); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const [email, setEmail] = useState(""); + const [name, setName] = useState(""); + const birthday = useSelector(selectors.selectBirthday); + const [isDisabled, setIsDisabled] = useState(true); + const [isValidEmail, setIsValidEmail] = useState(false); + const [isValidName, setIsValidName] = useState(true); + const [isLoading, setIsLoading] = useState(false); + const [isAuth, setIsAuth] = useState(false); + const [apiError, setApiError] = useState(null); + const [error, setError] = useState(false); + const [subPlans, setSubPlans] = useState([]); + const activeSubPlanFromStore = useSelector(selectors.selectActiveSubPlan); + const [activeSubPlan, setActiveSubPlan] = useState( + activeSubPlanFromStore + ); + const timezone = getClientTimezone(); + const locale = i18n.language; + const { subPlan } = useParams(); + useEffect(() => { + if (subPlan) { + const targetSubPlan = subPlans.find( + (sub_plan) => + String( + sub_plan?.trial?.price_cents + ? Math.floor((sub_plan?.trial?.price_cents + 1) / 100) + : sub_plan.id.replace(".", "") + ) === subPlan + ); + if (targetSubPlan) { + setActiveSubPlan(targetSubPlan); + } + } + }, [subPlan, subPlans]); + + useEffect(() => { + (async () => { + const { sub_plans } = await api.getSubscriptionPlans({ locale }); + const plans = sub_plans + .filter((plan: ISubscriptionPlan) => plan.provider === "stripe") + .sort((a, b) => { + if (!a.trial || !b.trial) { + return 0; + } + if (a?.trial?.price_cents < b?.trial?.price_cents) { + return -1; + } + if (a?.trial?.price_cents > b?.trial?.price_cents) { + return 1; + } + return 0; + }); + setSubPlans(plans); + })(); + }, [api, locale]); + + const handleValidEmail = (email: string) => { + dispatch(actions.form.addEmail(email)); + setEmail(email); + setIsValidEmail(true); + }; + + const handleValidName = (name: string) => { + setName(name); + setIsValidName(true); + }; + + useEffect(() => { + if (isValidName && isValidEmail) { + setIsDisabled(false); + } else { + setIsDisabled(true); + } + }, [isValidEmail, isValidName, email, name]); + + const handleClick = () => { + authorization(); + }; + + const authorization = async () => { + try { + setIsLoading(true); + const auth = await api.auth({ email, timezone, locale }); + const { + auth: { token, user }, + } = auth; + signUp(token, user); + const payload = { + user: { profile_attributes: { birthday } }, + token, + }; + const updatedUser = await api.updateUser(payload).catch((error) => { + console.log("Error: ", error); + }); + if (updatedUser?.user) { + dispatch(actions.user.update(updatedUser.user)); + } + if (name) { + dispatch( + actions.user.update({ + username: name, + }) + ); + } + dispatch(actions.status.update("registred")); + dispatch( + actions.payment.update({ + activeSubPlan, + }) + ); + setIsLoading(false); + setIsAuth(true); + setTimeout(() => { + navigate(routes.client.emailConfirm()); + }, 1000); + } catch (error) { + console.error(error); + if (error instanceof ApiError) { + setApiError(error as ApiError); + } else { + setError(true); + } + setIsLoading(false); + } + }; return ( -
- {t('aura.web.email_title')} +
+ + Enter your email to see how you can find your perfect partner + +

{t("we_dont_share")}

+ setIsValidName(true)} + /> setIsDisabled(true)} + onInvalid={() => setIsValidEmail(false)} /> -

{t('we_dont_share')}

- - {isLoading ? : t('_continue')} - - - {t('_continue_agree', { - eulaLink: {t('eula')}, - privacyLink: {t('privacy_policy')}, + + {t("_continue_agree", { + eulaLink: ( + + {t("eula")} + + ), + privacyLink: ( + + {t("privacy_policy")} + + ), })} - + + {isLoading && } + {!isLoading && + !(!apiError && !error && !isLoading && isAuth) && + t("_continue")} + {!apiError && !error && !isLoading && isAuth && ( + Success Icon + )} + + {(error || apiError) && ( + + Something went wrong + + )} + {apiError && ( + + )}
- ) + ); } -export default EmailEnterPage +export default EmailEnterPage; diff --git a/src/components/EmailEnterPage/styles.module.css b/src/components/EmailEnterPage/styles.module.css new file mode 100755 index 0000000..be97ae9 --- /dev/null +++ b/src/components/EmailEnterPage/styles.module.css @@ -0,0 +1,102 @@ +.title { + font-size: 18px; + line-height: 28px; + font-weight: 700; + color: #333333; +} + +.not-share { + font-size: 12px; + line-height: 16px; + text-align: center; + max-width: 330px; + margin-left: auto; + margin-right: auto; + margin-bottom: 32px; + color: #333333; +} + +.button { + border-radius: 12px; + margin-top: 24px; + box-shadow: rgba(0, 0, 0, 0.25) 0px 4px 4px 0px; + height: 50px; + min-height: 0; + background: linear-gradient( + 165.54deg, + rgb(20, 19, 51) -33.39%, + rgb(32, 34, 97) 15.89%, + rgb(84, 60, 151) 55.84%, + rgb(105, 57, 162) 74.96% + ); + font-size: 18px; + line-height: 21px; +} + +.policy { + max-width: 240px; +} + +.policy > p { + font-size: 12px; +} + +.link { + font-size: 12px !important; + color: #9974f6 !important; +} + +.success-icon { + height: 100%; + width: auto; +} + +.input-container { + width: 100%; + position: relative; + text-align: center; + margin-bottom: 20px; + max-width: 400px; + min-width: 250px; +} + +.input-container > input { + appearance: none; + border: 1px solid #c7c7c7; + border-radius: 25px; + color: #121620; + font-size: 16px; + height: 48px; + line-height: 18px; + outline: none; + padding: 12px 24px 5px; + transition: border-color 0.3s ease; + width: 100%; +} + +.input-container > input:focus { + border-color: #000; + transition-delay: 0.1s; +} + +.input-container > input:focus + .input__placeholder, +.input-container > input:not(:placeholder-shown) + .input__placeholder { + font-size: 12px; + top: 12px; + width: auto; +} + +.input__placeholder { + color: #8e8e93; + font-size: 16px; + left: 24px; + overflow: hidden; + text-overflow: ellipsis; + transition: top 0.3s ease, color 0.3s ease, font-size 0.3s ease; + white-space: nowrap; + position: absolute; + top: 50%; + transform: translateY(-50%); + user-select: none; + pointer-events: none; +} diff --git a/src/components/EmailItem/index.tsx b/src/components/EmailItem/index.tsx old mode 100644 new mode 100755 index 56447a1..3f47172 --- a/src/components/EmailItem/index.tsx +++ b/src/components/EmailItem/index.tsx @@ -1,34 +1,39 @@ -import { Currency, Locale, Price } from '../PaymentTable' -import styles from './styles.module.css' +import { Currency, Locale, Price } from "../PaymentTable"; +import styles from "./styles.module.css"; -export interface IEmailItem { - email: string - price: number +export interface IEmailItemProps { + email: string; + price: number; + className?: string; } -const currency = Currency.USD -const locale = Locale.EN +const currency = Currency.USD; +const locale = Locale.EN; const roundToWhole = (value: string | number): number => { - value = Number(value) - if (value % Math.floor(value) !== 0) { - return value - } - return Math.floor(value) -} + value = Number(value); + if (value % Math.floor(value) !== 0) { + return value; + } + return Math.floor(value); +}; const formatEmail = (email: string): string => { - return `${email.slice(0, 4)}****` -} + return `${email.slice(0, 4)}****`; +}; -function EmailItem({email, price}: IEmailItem): JSX.Element { - const _price = new Price(roundToWhole(price), currency, locale) +function EmailItem({ + email, + price, + className = "", +}: IEmailItemProps): JSX.Element { + const _price = new Price(roundToWhole(price), currency, locale); return ( - - {formatEmail(email)} chose {_price.format()} + + {formatEmail(email)} chose {_price.format()} - ) + ); } -export default EmailItem +export default EmailItem; diff --git a/src/components/EmailsList/index.tsx b/src/components/EmailsList/index.tsx old mode 100644 new mode 100755 index 6a9dd07..12ee577 --- a/src/components/EmailsList/index.tsx +++ b/src/components/EmailsList/index.tsx @@ -1,11 +1,11 @@ import { getRandomArbitrary, getRandomName } from "@/services/random-value"; -import EmailItem, { IEmailItem } from "../EmailItem"; +import EmailItem, { IEmailItemProps } from "../EmailItem"; import styles from "./styles.module.css"; import { useTranslation } from "react-i18next"; import { useEffect, useRef, useState } from "react"; -const getEmails = (): IEmailItem[] => { - const emails: IEmailItem[] = []; +const getEmails = (): IEmailItemProps[] => { + const emails: IEmailItemProps[] = []; for (let index = 0; index < 5; index++) { emails.push({ @@ -17,7 +17,19 @@ const getEmails = (): IEmailItem[] => { return emails; }; -function EmailsList(): JSX.Element { +interface IEmailsListProps { + classNameContainer?: string; + classNameTitle?: string; + classNameEmailItem?: string; + direction?: "up-down" | "down-up" | "left-right" | "right-left"; +} + +function EmailsList({ + classNameContainer = "", + classNameTitle = "", + classNameEmailItem = "", + direction = "up-down", +}: IEmailsListProps): JSX.Element { const { t } = useTranslation(); const [countUsers, setCountUsers] = useState(752); const [emails, setEmails] = useState(getEmails()); @@ -56,21 +68,34 @@ function EmailsList(): JSX.Element { }, [emails, elementIdx]); return ( -
- +
+ {t("people_joined_today", { countPeoples: {countUsers}, })} -
+
{emails.map(({ email, price }, idx) => (
idx ? styles["hidden"] : ""}`} - style={{ display: elementIdx - 1 > idx ? "none" : "" }} + className={`${styles["email-item"]} ${ + elementIdx > idx ? styles["hidden"] : "" + }`} + style={{ + display: elementIdx - 1 > idx ? "none" : "", + marginLeft: + direction === "right-left" && elementIdx > idx + ? `-${itemsRef.current[idx]?.offsetWidth + 10}px` + : 0, + }} ref={(el: HTMLDivElement) => (itemsRef.current[idx] = el)} + data-width={`-${itemsRef.current[idx]?.offsetWidth}px`} key={idx} > - +
))}
diff --git a/src/components/EmailsList/styles.module.css b/src/components/EmailsList/styles.module.css old mode 100644 new mode 100755 index 94361ff..131d319 --- a/src/components/EmailsList/styles.module.css +++ b/src/components/EmailsList/styles.module.css @@ -21,12 +21,24 @@ height: 108px; padding: 8px 0; display: flex; - flex-direction: column-reverse; align-items: center; overflow: hidden; gap: 10px; } +.emails-container.up-down { + flex-direction: column-reverse; +} + +.emails-container.right-left { + flex-direction: row; + height: fit-content; +} + +.emails-container.up-down { + flex-direction: column-reverse; +} + .email-item { margin-top: 0; opacity: 1; @@ -34,7 +46,10 @@ } .hidden { - transition: all .5s ease; - margin-bottom: -26px; + transition: all 0.5s ease; opacity: 0; } + +.up-down .hidden { + margin-bottom: -26px; +} diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 82b501b..f55b780 100755 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -50,7 +50,8 @@ function Header({ const goBack = () => { if ( location.pathname.includes("/questionnaire") || - location.pathname.includes("/about-us") + location.pathname.includes("/about-us") || + location.pathname.includes("/payment/stripe") ) { return navigate(-1); } diff --git a/src/components/LoadingProfileModalChild/index.tsx b/src/components/LoadingProfileModalChild/index.tsx new file mode 100755 index 0000000..b5ed052 --- /dev/null +++ b/src/components/LoadingProfileModalChild/index.tsx @@ -0,0 +1,24 @@ +import MainButton from "../MainButton"; +import Title from "../Title"; +import styles from "./styles.module.css"; + +interface ILoadingProfileModalChildProps { + title: string; + handleClick: () => void; +} + +function LoadingProfileModalChild({ title, handleClick }: ILoadingProfileModalChildProps) { + return ( +
+ Question +
+ {title} +
+ No + Yes +
+
+ ); +} + +export default LoadingProfileModalChild; diff --git a/src/components/LoadingProfileModalChild/styles.module.css b/src/components/LoadingProfileModalChild/styles.module.css new file mode 100755 index 0000000..d6460e8 --- /dev/null +++ b/src/components/LoadingProfileModalChild/styles.module.css @@ -0,0 +1,48 @@ +.modal { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; +} + +.title { + font-weight: 700; +} + +.question-img { + width: 100%; + max-width: 134px; +} + +.horizontal-line { + width: 100%; + height: 1px; +} + +.buttons-container { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.button { + background-color: #eaeef7; + color: #000; + width: calc(50% - 8px); + min-width: 0; + height: 40px; + min-height: 0; + border-radius: 12px; +} + +.button:nth-child(2) { + background: linear-gradient( + 165.54deg, + rgb(20, 19, 51) -33.39%, + rgb(32, 34, 97) 15.89%, + rgb(84, 60, 151) 55.84%, + rgb(105, 57, 162) 74.96% + ); + color: #fff; +} diff --git a/src/components/PriceItem/index.tsx b/src/components/PriceItem/index.tsx old mode 100644 new mode 100755 index 7eec7c5..8bd28a3 --- a/src/components/PriceItem/index.tsx +++ b/src/components/PriceItem/index.tsx @@ -9,10 +9,19 @@ interface PriceItemProps { id: string; value: number; active: boolean; + className?: string; + classNameActive?: string; click: (id: string) => void; } -function PriceItem({ id, value, active, click }: PriceItemProps): JSX.Element { +function PriceItem({ + id, + value, + active, + className = "", + classNameActive = "", + click, +}: PriceItemProps): JSX.Element { const _price = new Price( roundToWhole(value === 1 ? 0.99 : value), currency, @@ -23,8 +32,8 @@ function PriceItem({ id, value, active, click }: PriceItemProps): JSX.Element { const isPopular = id === "stripe.7"; const isActive = active; return `${styles.container} ${isPopular ? styles.popular : ""} ${ - isActive ? styles.active : "" - }`; + isActive ? `${styles.active} ${classNameActive}` : "" + } ${className}`; }; const itemClick = () => { diff --git a/src/components/PriceList/index.tsx b/src/components/PriceList/index.tsx old mode 100644 new mode 100755 index b3a1bd9..b086f0a --- a/src/components/PriceList/index.tsx +++ b/src/components/PriceList/index.tsx @@ -8,6 +8,8 @@ import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans"; interface PriceListProps { subPlans: ISubscriptionPlan[]; activeItem: number | null; + classNameItem?: string; + classNameItemActive?: string; click: () => void; } @@ -15,7 +17,12 @@ const getPrice = (plan: ISubscriptionPlan) => { return (plan.trial?.price_cents || 0) / 100; }; -function PriceList({ click, subPlans }: PriceListProps): JSX.Element { +function PriceList({ + click, + subPlans, + classNameItem = "", + classNameItemActive = "", +}: PriceListProps): JSX.Element { const dispatch = useDispatch(); const [activePlanItem, setActivePlanItem] = useState(null); @@ -30,9 +37,7 @@ function PriceList({ click, subPlans }: PriceListProps): JSX.Element { }) ); } - setTimeout(() => { - click(); - }, 1000); + click(); }; return ( @@ -43,6 +48,8 @@ function PriceList({ click, subPlans }: PriceListProps): JSX.Element { key={idx} value={getPrice(plan)} id={plan.id} + className={classNameItem} + classNameActive={classNameItemActive} click={priceItemClick} /> ))} diff --git a/src/components/PriceList/styles.module.css b/src/components/PriceList/styles.module.css old mode 100644 new mode 100755 index e65220e..de269fd --- a/src/components/PriceList/styles.module.css +++ b/src/components/PriceList/styles.module.css @@ -1,5 +1,6 @@ .container { - display: flex; - flex-direction: row; - justify-content: space-between; -} \ No newline at end of file + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; +} diff --git a/src/components/PriceListPage/index.tsx b/src/components/PriceListPage/index.tsx old mode 100644 new mode 100755 index 018eca6..68972d0 --- a/src/components/PriceListPage/index.tsx +++ b/src/components/PriceListPage/index.tsx @@ -43,7 +43,7 @@ function PriceListPage(): JSX.Element { }); setSubPlans(plans); })(); - }, [api]); + }, [api, locale]); const handleNext = () => { dispatch( @@ -51,7 +51,9 @@ function PriceListPage(): JSX.Element { home: { pathFromHome: homeConfig.pathFromHome, isShowNavbar: false }, }) ); - navigate(routes.client.subscription()); + setTimeout(() => { + navigate(routes.client.subscription()); + }, 1000); }; return ( diff --git a/src/components/pages/AboutUs/index.tsx b/src/components/pages/AboutUs/index.tsx index a45d7bf..0a4b069 100755 --- a/src/components/pages/AboutUs/index.tsx +++ b/src/components/pages/AboutUs/index.tsx @@ -17,7 +17,7 @@ function AboutUsPage() { aboutUs: answer.id, }) ); - navigate(routes.client.priceList()); + navigate(routes.client.loadingProfile()); }; return ( diff --git a/src/components/pages/EmailConfirm/index.tsx b/src/components/pages/EmailConfirm/index.tsx new file mode 100755 index 0000000..99cfa50 --- /dev/null +++ b/src/components/pages/EmailConfirm/index.tsx @@ -0,0 +1,44 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import MainButton from "@/components/MainButton"; +import { useNavigate } from "react-router-dom"; +import routes from "@/routes"; + +function EmailConfirmPage() { + const navigate = useNavigate(); + + const handleNext = () => { + navigate(routes.client.onboarding()); + }; + + return ( +
+
+ Night eye + + Get access to your{" "} + <span className={styles.purple}>exclusive reading</span>, special + offers, updates, astrology & relationship tips, recipes, and free + gifts. + +

+ Get it all! Confirm receiving emails so you don't miss anything +

+
+
+ + Confirm + +

+ I know everything about astrology & relationship +

+
+
+ ); +} + +export default EmailConfirmPage; diff --git a/src/components/pages/EmailConfirm/styles.module.css b/src/components/pages/EmailConfirm/styles.module.css new file mode 100755 index 0000000..48634da --- /dev/null +++ b/src/components/pages/EmailConfirm/styles.module.css @@ -0,0 +1,75 @@ +.page { + position: relative; + height: fit-content; + min-height: calc(100vh - 50px); + min-height: calc(100dvh - 50px); + display: flex; + justify-content: start; + align-items: center; + justify-content: space-between; + flex-direction: column; + padding-top: 16px; + color: #333333; +} + +.title { + margin: 0; + text-align: left; + font-size: 24px; + line-height: 1.3; +} + +.description { + margin: 0; + text-align: left; + font-size: 18px; + font-weight: 400; + line-height: 1.3; +} + +.purple { + color: #6a3aa2; +} + +.eye-image { + width: 100%; + max-width: 180px; +} + +.top-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 30px; + padding: 0 24px; +} + +.bottom-section { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + padding: 0 24px; +} + +.text-link { + color: #828282; + text-align: center; + font-weight: 600; + line-height: 1.2; + text-decoration: underline; +} + +.button { + background: linear-gradient( + 165.54deg, + rgb(20, 19, 51) -33.39%, + rgb(32, 34, 97) 15.89%, + rgb(84, 60, 151) 55.84%, + rgb(105, 57, 162) 74.96% + ); + min-height: 0; + height: 50px; + font-size: 16px; + font-weight: 400; +} diff --git a/src/components/pages/LoadingProfile/index.tsx b/src/components/pages/LoadingProfile/index.tsx new file mode 100755 index 0000000..01613ab --- /dev/null +++ b/src/components/pages/LoadingProfile/index.tsx @@ -0,0 +1,109 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import ProgressBarLine from "@/components/ui/ProgressBarLine"; +import { + loadingProfilePoints, + modalTitlesLoadingProfile, + titlesLoadingProfile, +} from "@/data/loadingProfile"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { useNavigate } from "react-router-dom"; +import routes from "@/routes"; +import Modal from "@/components/Modal"; +import LoadingProfileModalChild from "@/components/LoadingProfileModalChild"; + +function LoadingProfilePage() { + const navigate = useNavigate(); + const [progress, setProgress] = useState(0); + const [isPause, setIsPause] = useState(false); + const interval = useRef(); + + const onEndLoading = useCallback(() => { + navigate(routes.client.emailEnter()); + }, [navigate]); + + const getProgressValue = useCallback( + (index: number) => { + const integerDivision = Math.floor(progress / 100); + if (integerDivision > index) { + return 100; + } + if (integerDivision === index) { + return progress % 100; + } + return 0; + }, + [progress] + ); + + useEffect(() => { + if (progress % 98 === 97) { + setIsPause(true); + } + }, [progress]); + + useEffect(() => { + if (progress >= loadingProfilePoints.length * 100) { + return onEndLoading(); + } + interval.current = setTimeout(() => { + setProgress((prevProgress) => { + if (!isPause) return prevProgress + 1; + return prevProgress; + }); + }, 100); + return () => { + clearTimeout(interval.current); + }; + }, [progress, onEndLoading, isPause]); + + return ( +
+ {isPause && ( + setIsPause(false)}> + setIsPause(false)} + /> + + )} + + {titlesLoadingProfile[Math.floor(progress / 34)]} + +
+ {loadingProfilePoints.map(({ title, color }, index) => ( +
+
+ + {title} + +

+ {getProgressValue(index)}% +

+
+ +
+ ))} +
+

+ Sit tight! We`re building your perfect guidance plane based on your + unique astrological blueprint and data of millions users. +

+
+ ); +} + +export default LoadingProfilePage; diff --git a/src/components/pages/LoadingProfile/styles.module.css b/src/components/pages/LoadingProfile/styles.module.css new file mode 100755 index 0000000..a488eeb --- /dev/null +++ b/src/components/pages/LoadingProfile/styles.module.css @@ -0,0 +1,55 @@ +.title { + font-size: 20px; + color: #1c1c1c; + min-height: 60px; +} + +.points-container, +.point { + width: 100%; + display: flex; + flex-direction: column; +} + +.points-container { + gap: 24px; +} + +.point { + gap: 12px; +} + +.point__text-container { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; +} + +.point__title { + font-size: 14px; + margin: 0; +} + +.point__percentage { + font-size: 14px; + font-weight: 600; +} + +.progress-bar__container { + background-color: #e6e6e6; + height: 8px; +} + +.progress-bar__line { + background-color: #908cf2; +} + +.description { + margin-top: 24px; + font-size: 14px; + text-align: center; + line-height: 1.5; + width: 90%; +} diff --git a/src/components/pages/Onboarding/index.tsx b/src/components/pages/Onboarding/index.tsx new file mode 100755 index 0000000..7aa883a --- /dev/null +++ b/src/components/pages/Onboarding/index.tsx @@ -0,0 +1,49 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { onboardingTitles } from "@/data/onboarding"; +import routes from "@/routes"; +import { useNavigate } from "react-router-dom"; + +function OnboardingPage() { + const navigate = useNavigate(); + const [activeIndexTitle, setIndexTitle] = useState(0); + const [periodClassName, setPeriodClassName] = useState(""); + const titleInterval = useRef(); + const classNameTimeOut = useRef(); + + const handleNext = useCallback(() => { + navigate(routes.client.trialChoice()); + }, [navigate]); + + useEffect(() => { + console.log("onboardingTitles", activeIndexTitle); + setPeriodClassName("to-nontransparent"); + classNameTimeOut.current = setTimeout(() => { + setPeriodClassName("to-transparent"); + }, 4000); + titleInterval.current = setTimeout(() => { + if (activeIndexTitle < onboardingTitles.length - 1) { + setIndexTitle((prev) => prev + 1); + } else { + handleNext(); + } + }, 5000); + return () => { + if (titleInterval.current) clearTimeout(titleInterval.current); + if (classNameTimeOut.current) clearTimeout(classNameTimeOut.current); + }; + }, [activeIndexTitle, handleNext]); + + return ( +
+ {onboardingTitles[activeIndexTitle] && ( + + {onboardingTitles[activeIndexTitle]} + + )} +
+ ); +} + +export default OnboardingPage; diff --git a/src/components/pages/Onboarding/styles.module.css b/src/components/pages/Onboarding/styles.module.css new file mode 100755 index 0000000..dd280dd --- /dev/null +++ b/src/components/pages/Onboarding/styles.module.css @@ -0,0 +1,51 @@ +.page { + display: flex; + justify-content: center; + align-items: flex-start; + height: fit-content; + min-height: 100vh; + min-height: 100dvh; + background: linear-gradient( + 165.54deg, + rgb(20, 19, 51) -33.39%, + rgb(32, 34, 97) 15.89%, + rgb(84, 60, 151) 55.84%, + rgb(105, 57, 162) 74.96% + ); +} + +.title { + color: #fff; + text-align: left; + max-width: 300px; + font-size: 32px; + opacity: 0; + transition: opacity 1s; + /* animation-name: show-up; + animation-duration: 5s; + animation-iteration-count: infinite; + animation-timing-function: linear; */ +} + +.to-transparent { + opacity: 0; +} + +.to-nontransparent { + opacity: 1; +} + +@keyframes show-up { + 0% { + opacity: 0; + } + 20% { + opacity: 1; + } + 80% { + opacity: 1; + } + 100% { + opacity: 0; + } +} diff --git a/src/components/pages/TrialChoice/index.tsx b/src/components/pages/TrialChoice/index.tsx new file mode 100755 index 0000000..797733f --- /dev/null +++ b/src/components/pages/TrialChoice/index.tsx @@ -0,0 +1,114 @@ +import PriceList from "@/components/PriceList"; +import styles from "./styles.module.css"; +import { useEffect, useState } from "react"; +import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans"; +import { useTranslation } from "react-i18next"; +import { useApi } from "@/api"; +import { useDispatch, useSelector } from "react-redux"; +import { actions, selectors } from "@/store"; +import { useNavigate } from "react-router-dom"; +import routes from "@/routes"; +import EmailsList from "@/components/EmailsList"; +import MainButton from "@/components/MainButton"; + +function TrialChoicePage() { + const { i18n } = useTranslation(); + const locale = i18n.language; + const api = useApi(); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const selectedPrice = useSelector(selectors.selectSelectedPrice); + const homeConfig = useSelector(selectors.selectHome); + const [subPlans, setSubPlans] = useState([]); + const [isDisabled, setIsDisabled] = useState(true); + + useEffect(() => { + (async () => { + const { sub_plans } = await api.getSubscriptionPlans({ locale }); + const plans = sub_plans + .filter((plan: ISubscriptionPlan) => plan.provider === "stripe") + .sort((a, b) => { + if (!a.trial || !b.trial) { + return 0; + } + if (a.trial?.price_cents < b.trial?.price_cents) { + return -1; + } + if (a.trial?.price_cents > b.trial?.price_cents) { + return 1; + } + return 0; + }); + setSubPlans(plans); + })(); + }, [api, locale]); + + const handlePriceItem = () => { + setIsDisabled(false); + }; + + const handleNext = () => { + dispatch( + actions.siteConfig.update({ + home: { pathFromHome: homeConfig.pathFromHome, isShowNavbar: false }, + }) + ); + navigate(routes.client.trialPayment()); + }; + + return ( +
+

+ We've helped millions of people to have happier lives and better + relationships, and we want to help you too. +

+

+ Money shouldn’t stand in the way of finding astrology guidance that + finally works. So, choose an amount that you think is reasonable to try + us out for one week. +

+

+ It costs us $13.67 to offer a 7-day trial, but please choose the amount + you are comfortable with. +

+
+ +

+ This option will help us support those who need to select the lowest + trial prices! +

+ {`Arrow +
+
+ +
+ + See my plan + +

+ *Cost of trial as of February 2023 +

+
+ ); +} + +export default TrialChoicePage; diff --git a/src/components/pages/TrialChoice/styles.module.css b/src/components/pages/TrialChoice/styles.module.css new file mode 100755 index 0000000..f082e4c --- /dev/null +++ b/src/components/pages/TrialChoice/styles.module.css @@ -0,0 +1,123 @@ +.page { + display: flex; + flex-direction: column; + gap: 10px; + min-height: calc(100vh - 50px); + min-height: calc(100dvh - 50px); + height: fit-content; + background-color: #fff0f0; + padding: 15px 42px 60px; +} + +.text { + font-size: 14px; + line-height: 180%; + font-weight: 400; +} + +.text.bold { + font-weight: 600; +} + +.purple { + color: #6a3aa2; +} + +.auxiliary-text { + font-size: 12px; + line-height: 16px; + color: rgb(52, 52, 52); + width: 100%; +} + +.price-container { + position: relative; + width: 100%; + margin-top: 10px; + display: flex; + flex-direction: column; + gap: 20px; +} + +.price-item { + background: rgb(234, 238, 247); + color: rgb(51, 51, 51); + border: 1px solid rgb(224, 224, 224); + box-shadow: rgba(84, 60, 151, 0.25) 2px 2px 6px; + border-radius: 12px; + display: flex; + -webkit-box-align: center; + align-items: center; + -webkit-box-pack: center; + justify-content: center; + font-weight: 600; + width: calc((100% - 30px) / 4); + max-width: 72px; + max-height: 72px; + height: auto; + aspect-ratio: 1 / 1; + position: relative; + z-index: 1; +} + +.price-item-active { + background: linear-gradient( + 165.54deg, + rgb(20, 19, 51) -33.39%, + rgb(32, 34, 97) 15.89%, + rgb(84, 60, 151) 55.84%, + rgb(105, 57, 162) 74.96% + ); + color: rgb(251, 251, 255); +} + +.arrow-image { + position: absolute; + width: 26px; + height: 33px; + top: 92px; + right: 32px; +} + +.emails-list-container { + width: 100%; + margin-top: 20px; +} + +.emails-container { + background-color: #3c38d70d; +} + +.emails-title { + font-weight: 600; + line-height: 24px; + font-size: 16px; + color: rgb(69, 72, 149); + margin-bottom: 6px; + text-align: center; +} + +.email-item { + background: rgb(251, 251, 255); + border-radius: 4px; + padding: 5px 7px; + font-size: 12px; + line-height: 130%; + display: flex; + width: max-content; + color: rgb(79, 79, 79); +} + +.button { + background: linear-gradient( + 165.54deg, + rgb(20, 19, 51) -33.39%, + rgb(32, 34, 97) 15.89%, + rgb(84, 60, 151) 55.84%, + rgb(105, 57, 162) 74.96% + ); + color: #fbfbff; + border-radius: 12px; + min-height: 0; + height: 50px; +} diff --git a/src/components/pages/TrialPayment/components/CustomButton/index.tsx b/src/components/pages/TrialPayment/components/CustomButton/index.tsx new file mode 100755 index 0000000..00f232a --- /dev/null +++ b/src/components/pages/TrialPayment/components/CustomButton/index.tsx @@ -0,0 +1,14 @@ +import styles from "./styles.module.css"; +import MainButton from "@/components/MainButton"; + +function CustomButton(props: typeof MainButton.arguments) { + const { className } = props; + return ( + + ); +} + +export default CustomButton; diff --git a/src/components/pages/TrialPayment/components/CustomButton/styles.module.css b/src/components/pages/TrialPayment/components/CustomButton/styles.module.css new file mode 100755 index 0000000..ec98e1f --- /dev/null +++ b/src/components/pages/TrialPayment/components/CustomButton/styles.module.css @@ -0,0 +1,28 @@ +.button { + height: 100%; + min-height: 0; + width: 100%; + min-width: 0; + text-transform: uppercase; + text-align: center; + border-radius: 6px; + background: rgb(153, 116, 246); + box-shadow: rgba(153, 116, 246, 0.5) 0px 0px 0px 0px; + font-size: 14px; + font-weight: 700; + animation: 1.5s ease 0s infinite normal none running pulse; +} + +@keyframes pulse { + 0% { + transform: scale(0.9); + } + 70% { + transform: scale(1); + box-shadow: rgba(153, 116, 246, 0.2) 0px 0px 0px 4px; + } + 100% { + transform: scale(0.9); + box-shadow: rgba(153, 116, 246, 0.2) 0px 0px 0px 0px; + } +} diff --git a/src/components/pages/TrialPayment/components/DiscountExpires/index.tsx b/src/components/pages/TrialPayment/components/DiscountExpires/index.tsx new file mode 100755 index 0000000..b368c04 --- /dev/null +++ b/src/components/pages/TrialPayment/components/DiscountExpires/index.tsx @@ -0,0 +1,58 @@ +import { useEffect, useMemo, useState } from "react"; +import styles from "./styles.module.css"; +import Title from "@/components/Title"; + +function DiscountExpires() { + const [currentDate, setCurrentDate] = useState(new Date()); + const endDate = useMemo( + () => new Date().setMinutes(currentDate.getMinutes() + 10), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + useEffect(() => { + const interval = setInterval(() => { + setCurrentDate(new Date()); + }, 1000); + return () => clearInterval(interval); + }, [endDate, currentDate]); + + const getMinutes = () => { + const diff = endDate - currentDate.getTime(); + let minutes = Math.floor(diff / 1000 / 60); + if (minutes < 0) { + minutes = 0; + } + return minutes.toString().padStart(2, "0"); + }; + + const getSeconds = () => { + const diff = endDate - currentDate.getTime(); + let seconds = Math.floor(diff / 1000) % 60; + if (seconds < 0) { + seconds = 0; + } + return seconds.toString().padStart(2, "0"); + }; + + return ( +
+ + Discount expires + +
+
+ {getMinutes()} + min +
+

:

+
+ {getSeconds()} + sec +
+
+
+ ); +} + +export default DiscountExpires; diff --git a/src/components/pages/TrialPayment/components/DiscountExpires/styles.module.css b/src/components/pages/TrialPayment/components/DiscountExpires/styles.module.css new file mode 100755 index 0000000..7570e5e --- /dev/null +++ b/src/components/pages/TrialPayment/components/DiscountExpires/styles.module.css @@ -0,0 +1,48 @@ +.discount-expires { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.title { + font-size: 12px; + font-weight: 600; + line-height: 130%; + color: rgb(51, 51, 51); + margin: 0; +} + +.values { + display: flex; + flex-direction: row; +} + +.value-container { + display: flex; + flex-direction: column; + -webkit-box-align: center; + align-items: center; +} + +.value { + font-weight: 700; + font-size: 18px; + line-height: 110%; + color: #333333; +} + +.value-symbol { + font-weight: 600; + font-size: 8px; + line-height: 130%; + color: rgb(130, 130, 130); +} + +.colon { + margin: 0 4px; + font-weight: 700; + font-size: 18px; + line-height: 110%; + color: rgb(51, 51, 51); +} diff --git a/src/components/pages/TrialPayment/components/Goal/index.tsx b/src/components/pages/TrialPayment/components/Goal/index.tsx new file mode 100755 index 0000000..ec687fd --- /dev/null +++ b/src/components/pages/TrialPayment/components/Goal/index.tsx @@ -0,0 +1,27 @@ +import { useSelector } from "react-redux"; +import styles from "./styles.module.css"; +import { selectors } from "@/store"; +import { stepsQuestionary } from "@/data"; + +function Goal() { + const { goal } = useSelector(selectors.selectQuestionnaire); + + const getGoal = () => { + const question = stepsQuestionary[0].questions.find( + (question) => question.id === "goal" + ); + return question?.answers?.find((answer) => answer.id === goal)?.answer; + }; + + return ( +
+ Darts icon +
+ Goal +

{getGoal() || ""}

+
+
+ ); +} + +export default Goal; diff --git a/src/components/pages/TrialPayment/components/Goal/styles.module.css b/src/components/pages/TrialPayment/components/Goal/styles.module.css new file mode 100755 index 0000000..0ff4199 --- /dev/null +++ b/src/components/pages/TrialPayment/components/Goal/styles.module.css @@ -0,0 +1,23 @@ +.goal { + width: 100%; + display: flex; + align-items: center; + flex-direction: row; + gap: 10px; +} + +.text-container { + display: flex; + flex-direction: column; +} + +.text-container > span { + font-size: 12px; + line-height: 1; +} + +.text-container > p { + font-weight: 600; + line-height: 130%; + font-size: 18px; +} diff --git a/src/components/pages/TrialPayment/components/GuardPayments/index.tsx b/src/components/pages/TrialPayment/components/GuardPayments/index.tsx new file mode 100755 index 0000000..cc3550b --- /dev/null +++ b/src/components/pages/TrialPayment/components/GuardPayments/index.tsx @@ -0,0 +1,12 @@ +import styles from "./styles.module.css"; + +function GuardPayments() { + return ( +
+ Guaranteed security +

Guaranteed security payments

+
+ ); +} + +export default GuardPayments; diff --git a/src/components/pages/TrialPayment/components/GuardPayments/styles.module.css b/src/components/pages/TrialPayment/components/GuardPayments/styles.module.css new file mode 100755 index 0000000..ebfd926 --- /dev/null +++ b/src/components/pages/TrialPayment/components/GuardPayments/styles.module.css @@ -0,0 +1,16 @@ +.guard-payments { + width: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 6px; + margin-top: 26px; +} + +.text { + font-size: 14px; + font-weight: 400; + line-height: 24px; + color: rgb(79, 79, 79); +} diff --git a/src/components/pages/TrialPayment/components/Header/index.tsx b/src/components/pages/TrialPayment/components/Header/index.tsx new file mode 100755 index 0000000..75bff06 --- /dev/null +++ b/src/components/pages/TrialPayment/components/Header/index.tsx @@ -0,0 +1,20 @@ +import CustomButton from "../CustomButton"; +import DiscountExpires from "../DiscountExpires"; +import styles from "./styles.module.css"; + +interface IHeaderProps { + buttonClick: () => void; +} + +function Header({ buttonClick }: IHeaderProps) { + return ( +
+ + + get my reading + +
+ ); +} + +export default Header; diff --git a/src/components/pages/TrialPayment/components/Header/styles.module.css b/src/components/pages/TrialPayment/components/Header/styles.module.css new file mode 100755 index 0000000..a6baf9c --- /dev/null +++ b/src/components/pages/TrialPayment/components/Header/styles.module.css @@ -0,0 +1,19 @@ +.header { + position: fixed; + z-index: 10; + top: 0px; + left: 0px; + height: 62px; + width: 100%; + background-color: rgb(251, 251, 255); + display: flex; + -webkit-box-align: center; + align-items: center; + justify-content: space-between; + padding: 8px 15px; +} + +.button { + width: 50%; + height: 32px; +} diff --git a/src/components/pages/TrialPayment/components/OftenAsk/Question.tsx b/src/components/pages/TrialPayment/components/OftenAsk/Question.tsx new file mode 100755 index 0000000..46aee10 --- /dev/null +++ b/src/components/pages/TrialPayment/components/OftenAsk/Question.tsx @@ -0,0 +1,25 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; + +interface IQuestionProps { + title: string; + text: string; +} + +function Question({ title, text }: IQuestionProps) { + return ( + <> +
+
+ Question icon +
+ + {title} + +
+

{text}

+ + ); +} + +export default Question; diff --git a/src/components/pages/TrialPayment/components/OftenAsk/index.tsx b/src/components/pages/TrialPayment/components/OftenAsk/index.tsx new file mode 100755 index 0000000..29147b1 --- /dev/null +++ b/src/components/pages/TrialPayment/components/OftenAsk/index.tsx @@ -0,0 +1,23 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import Question from "./Question"; +import { questions } from "@/data/oftenAsk"; + +function OftenAsk() { + return ( +
+ + People often ask + +
    + {questions.map((question, index) => ( +
  • + +
  • + ))} +
+
+ ); +} + +export default OftenAsk; diff --git a/src/components/pages/TrialPayment/components/OftenAsk/styles.module.css b/src/components/pages/TrialPayment/components/OftenAsk/styles.module.css new file mode 100755 index 0000000..d0499eb --- /dev/null +++ b/src/components/pages/TrialPayment/components/OftenAsk/styles.module.css @@ -0,0 +1,48 @@ +.often-ask { + display: flex; + flex-direction: column; + -webkit-box-align: center; + align-items: center; +} + +.often-ask > ul > li { + width: 100%; + margin-top: 20px; +} + +.title { + margin-bottom: 20px; + font-size: 24px; + line-height: 145%; + text-align: center; + color: #333333; + font-weight: 700; +} + +.header { + display: flex; +} + +.image-container { + width: 32px; + align-self: center; +} + +.secondary-title { + flex: 1 1 0%; + font-weight: 600; + font-size: 16px; + line-height: 140%; + color: #0f0f0f; + margin: 0; + text-align: left; +} + +.text { + font-weight: 400; + font-size: 14px; + line-height: 140%; + margin-top: 8px; + margin-left: 32px; + color: #333333; +} diff --git a/src/components/pages/TrialPayment/components/PaymentTable/index.tsx b/src/components/pages/TrialPayment/components/PaymentTable/index.tsx new file mode 100755 index 0000000..c6e4327 --- /dev/null +++ b/src/components/pages/TrialPayment/components/PaymentTable/index.tsx @@ -0,0 +1,53 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import { getPriceFromTrial } from "@/services/price"; +import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans"; +import CustomButton from "../CustomButton"; +import GuardPayments from "../GuardPayments"; + +interface IPaymentTableProps { + subPlan: ISubscriptionPlan; + buttonClick: () => void; +} + +function PaymentTable({ subPlan, buttonClick }: IPaymentTableProps) { + return ( + <> +
+
+ Special offer +
+
+ + Personalized reading for{" "} + <span className={styles.purple}> + ${getPriceFromTrial(subPlan?.trial)} + </span> + +
+

Total today:

+ ${getPriceFromTrial(subPlan?.trial)} +
+
+
+

Your cost per 2 weeks after trial

+ ${subPlan.price_cents / 100} +
+
+
+ + get my reading + + +

+ You are enrolling in 2 weeks subscription. By continuing you agree that + if you don't cancel prior to the end of the 7-day trial for the $5 you + will automatically be charged $29 every 2 weeks until you cancel in + settings. Learn more about cancellation and refund policy in{" "} + Subscription policy +

+ + ); +} + +export default PaymentTable; diff --git a/src/components/pages/TrialPayment/components/PaymentTable/styles.module.css b/src/components/pages/TrialPayment/components/PaymentTable/styles.module.css new file mode 100755 index 0000000..c148706 --- /dev/null +++ b/src/components/pages/TrialPayment/components/PaymentTable/styles.module.css @@ -0,0 +1,83 @@ +.payment-table { + width: 100%; + margin-top: 24px; +} + +.header { + width: 100%; + background: #9974f6; + border-radius: 20px 20px 0px 0px; + padding: 4px 0px; +} + +.header > span { + display: block; + width: 100%; + font-weight: 700; + font-size: 14px; + line-height: 120%; + text-align: center; + color: #fbfbff; +} + +.table-container { + background: #fbfbff; + border-radius: 0px 0px 20px 20px; + padding: 9px 20px 16px; +} + +.table-container > hr { + margin: 10px 0; +} + +.title { + font-weight: 700; + font-size: 18px; + line-height: 28px; + color: #0f0f0f; +} + +.purple { + color: #7270c0; +} + +.table-element { + display: flex; + -webkit-box-pack: justify; + justify-content: space-between; + -webkit-box-align: center; + align-items: center; +} + +.table-element > p { + font-weight: 400; + font-size: 14px; + line-height: 24px; + color: #0f0f0f; +} + +.table-element > span { + font-weight: 600; + font-size: 16px; + line-height: 22px; + margin-left: 4px; + color: rgb(51, 51, 51); +} + +.button { + height: 48px; + margin-top: 26px; +} + +.policy { + font-size: 12px; + line-height: 20px; + margin-top: 26px; + color: rgb(51, 51, 51); + text-align: center; +} + +.policy > a { + text-decoration: underline; + font-weight: 600; +} diff --git a/src/components/pages/TrialPayment/components/PersonalInformation/index.tsx b/src/components/pages/TrialPayment/components/PersonalInformation/index.tsx new file mode 100755 index 0000000..71128dd --- /dev/null +++ b/src/components/pages/TrialPayment/components/PersonalInformation/index.tsx @@ -0,0 +1,51 @@ +import styles from "./styles.module.css"; + +interface IPersonalInformationProps { + birthdate: string; + zodiacSign: string; + gender: string; + birthPlace: string; +} + +function PersonalInformation({ + birthdate, + zodiacSign, + gender, + birthPlace, +}: IPersonalInformationProps) { + + return ( +
+
+ {`${gender} +
+
+
    +
  • +
    Zodiac sign
    +

    {zodiacSign}

    +
  • +
  • +
    Gender
    +

    {gender}

    +
  • +
+
    +
  • +
    Date of birth
    +

    {birthdate}

    +
  • +
  • +
    Place of birth
    +

    {birthPlace}

    +
  • +
+
+
+ ); +} + +export default PersonalInformation; diff --git a/src/components/pages/TrialPayment/components/PersonalInformation/styles.module.css b/src/components/pages/TrialPayment/components/PersonalInformation/styles.module.css new file mode 100755 index 0000000..5a9a8b5 --- /dev/null +++ b/src/components/pages/TrialPayment/components/PersonalInformation/styles.module.css @@ -0,0 +1,64 @@ +.personal-information { + position: absolute; + width: 100%; + height: 340px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 10px; + background-color: #eaeef7; + padding: 10px 15px; +} + +.image-container { + background: #fbfbff; + width: 100%; + min-height: 100px; + border-top-left-radius: 15px; + border-top-right-radius: 15px; + display: flex; + justify-content: space-around; + padding-top: 10px; + padding-bottom: 6px; +} + +.image-container > img { + height: 196px; +} + +.text-information { + background: #fbfbff; + border-radius: 0px 0px 20px 20px; + width: 100%; + display: flex; + position: relative; + padding: 8px 12px; +} + +.text-information > ul { + flex: 1 1 0%; + display: flex; + flex-wrap: wrap; + gap: 8px; + position: relative; +} + +.text-information > ul > li { + width: 100%; +} + +.text-information > ul > li > h6 { + font-weight: 700; + font-size: 12px; + line-height: 130%; + color: #454895; +} + +.text-information > ul > li > p { + font-size: 14px; + line-height: 130%; + margin-top: 4px; + text-transform: capitalize; + color: #0f0f0f; +} diff --git a/src/components/pages/TrialPayment/components/Reviews/Review.tsx b/src/components/pages/TrialPayment/components/Reviews/Review.tsx new file mode 100755 index 0000000..cebf9fb --- /dev/null +++ b/src/components/pages/TrialPayment/components/Reviews/Review.tsx @@ -0,0 +1,45 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; + +interface IReviewProps { + username: string; + date: string; + text: string; + mark: number; +} + +function Review({ username, date, text, mark }: IReviewProps) { + return ( +
+
+
+ {username.slice(0, 2)} +
+
+ + {username} + +

{date}

+
+ +
+
+
+ Opostrafs +
+

{text}

+
+
+ ); +} + +export default Review; diff --git a/src/components/pages/TrialPayment/components/Reviews/index.tsx b/src/components/pages/TrialPayment/components/Reviews/index.tsx new file mode 100755 index 0000000..2af3efd --- /dev/null +++ b/src/components/pages/TrialPayment/components/Reviews/index.tsx @@ -0,0 +1,23 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import { reviews } from "@/data/reviews"; +import Review from "./Review"; + +function Reviews() { + return ( +
+ + Users love us + +
    + {reviews.map((review, index) => ( +
  • + +
  • + ))} +
+
+ ); +} + +export default Reviews; diff --git a/src/components/pages/TrialPayment/components/Reviews/styles.module.css b/src/components/pages/TrialPayment/components/Reviews/styles.module.css new file mode 100755 index 0000000..e839398 --- /dev/null +++ b/src/components/pages/TrialPayment/components/Reviews/styles.module.css @@ -0,0 +1,96 @@ +.reviews { + display: flex; + flex-direction: column; + justify-content: center; + margin-top: 88px; +} + +.reviews > ul > li { + width: 100%; + padding: 10px 0; +} + +.review { + height: 100%; + padding: 16px; + background: rgb(255, 255, 255); + border-radius: 16px; +} + +.header { + display: flex; +} + +.avatar { + width: 32px; + height: 32px; + background-color: rgb(210, 209, 249); + border-radius: 50%; + display: flex; + align-self: center; + -webkit-box-align: center; + align-items: center; + -webkit-box-pack: center; + justify-content: center; +} + +.avatar > span { + font-weight: 600; + font-size: 16px; + line-height: 24px; + background: linear-gradient( + rgb(20, 19, 51) 0%, + rgb(32, 34, 97) 70.63%, + rgb(58, 35, 122) 100% + ) + text; + -webkit-text-fill-color: transparent; +} + +.info { + flex: 1 1 0%; + margin-left: 6px; +} + +.info > .title { + font-weight: 600; + font-size: 16px; + line-height: 130%; + color: #6a3aa2; + margin: 0; + text-align: left; +} + +.info > p { + font-size: 12px; + line-height: 130%; + margin-top: 2px; + color: #9b9b9b; +} + +.stars { + max-width: 5em; + font-size: 1rem; + height: 1em; + width: 100%; + mask-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTciIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNyAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwKSI+CjxwYXRoIGQ9Ik04LjQ5OTc4IDEyLjE3NEwzLjc5Nzc4IDE0LjgwNkw0Ljg0Nzc4IDkuNTIwNjVMMC44OTExMTMgNS44NjE5OEw2LjI0MjQ1IDUuMjI3MzJMOC40OTk3OCAwLjMzMzk4NEwxMC43NTcxIDUuMjI3MzJMMTYuMTA4NCA1Ljg2MTk4TDEyLjE1MTggOS41MjA2NUwxMy4yMDE4IDE0LjgwNkw4LjQ5OTc4IDEyLjE3NFoiIGZpbGw9IiNGMkM5NEMiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMCI+CjxyZWN0IHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuNSkiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K); + mask-position: 0px 0px; + mask-size: 1em 1em; +} + +.content { + display: flex; + margin-top: 10px; +} + +.opostrafs { + width: 32px; +} + +.text { + flex: 1 1 0%; + font-size: 14px; + margin-left: 6px; + line-height: 140%; + color: rg#0f0f0f; +} diff --git a/src/components/pages/TrialPayment/components/YouGet/index.tsx b/src/components/pages/TrialPayment/components/YouGet/index.tsx new file mode 100755 index 0000000..323b6af --- /dev/null +++ b/src/components/pages/TrialPayment/components/YouGet/index.tsx @@ -0,0 +1,37 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; + +function YouGet() { + return ( +
+ + What you get + +
    +
  • + Check mark + Your personalised plan +
  • +
  • + Check mark + 1:1 advice from your own astrologer +
  • +
  • + Check mark + Finding the most compatible partner +
  • +
  • + Check mark + Insights into your relationship patterns, and emotional and sexual + needs +
  • +
  • + Check mark + Better understanding of yourself +
  • +
+
+ ); +} + +export default YouGet; diff --git a/src/components/pages/TrialPayment/components/YouGet/styles.module.css b/src/components/pages/TrialPayment/components/YouGet/styles.module.css new file mode 100755 index 0000000..8e25698 --- /dev/null +++ b/src/components/pages/TrialPayment/components/YouGet/styles.module.css @@ -0,0 +1,30 @@ +.you-get { + display: flex; + flex-direction: column; + -webkit-box-align: center; + align-items: center; + width: 100%; + box-sizing: border-box; + margin-bottom: 40px; + padding: 0px 15px; + margin-top: 40px; + max-width: unset; +} + +.title { + margin-bottom: 20px; + font-size: 24px; + font-weight: 700; + line-height: 145%; + text-align: center; + color: #333333; +} + +.you-get > ul > li { + display: flex; + align-items: center; + gap: 6px; + font-size: 15px; + line-height: 140%; + margin-bottom: 10px; +} diff --git a/src/components/pages/TrialPayment/components/YourReading/index.tsx b/src/components/pages/TrialPayment/components/YourReading/index.tsx new file mode 100755 index 0000000..abb0faa --- /dev/null +++ b/src/components/pages/TrialPayment/components/YourReading/index.tsx @@ -0,0 +1,77 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import CustomButton from "../CustomButton"; + +interface IYourReadingProps { + gender: string; + zodiacSign: string; + buttonClick: () => void; +} + +function YourReading({ gender, zodiacSign, buttonClick }: IYourReadingProps) { + return ( +
+ + Your reading + +
+ {`${gender} +
+
+ + Content + +
    +
  1. +

    Compatibility with your partner in other areas of your life.

    +
  2. +
  3. +

    + Deep analysis of the relationships with your partner based on a + unique birth chart matching system +

    +
  4. +
  5. +

    + Simple and actionable guide to improving your relationship with + your partner +

    +
  6. +
  7. +

    + Warning about astrological events and practical advice that will + help you get through this period well +

    +
  8. +
  9. +

    Your horoscope and upcoming events for 2023

    +
  10. +
  11. +

    + Your unique strengths and weaknesses and how to get the most out + of them +

    +
  12. +
+ + Personality + +

+ Personality information +

+
+
+
+

To read the full reading you need get access

+ + get my reading + +
+
+ ); +} + +export default YourReading; diff --git a/src/components/pages/TrialPayment/components/YourReading/styles.module.css b/src/components/pages/TrialPayment/components/YourReading/styles.module.css new file mode 100755 index 0000000..6306936 --- /dev/null +++ b/src/components/pages/TrialPayment/components/YourReading/styles.module.css @@ -0,0 +1,137 @@ +.your-reading { + position: relative; + width: 100%; + background: #fbfbff; + border-radius: 20px; + padding-top: 20px; + padding-left: 6px; + padding-right: 6px; + margin-top: 40px; +} + +.title { + font-weight: 700; + font-size: 24px; + line-height: 135%; + width: 100%; + text-align: center; + color: #333333; +} + +.image-container { + display: flex; + -webkit-box-align: center; + align-items: center; + justify-content: center; + background: #eaeef7; + border-radius: 20px; + padding: 16px 20px; + margin-top: 20px; +} + +.image-container > img { + height: 146px; +} + +.text-container { + width: 100%; + padding: 0px 20px 20px; + margin-top: 20px; +} + +.secondary-title { + font-weight: 700; + font-size: 18px; + line-height: 140%; + width: 100%; + text-align: center; + color: #333333; +} + +.text-container > ol { + list-style: none; + counter-reset: item 0; + margin-top: 12px; + margin-bottom: 20px; +} + +.text-container > ol > li::before { + content: counter(item) ". "; + counter-increment: item 1; + font-weight: 600; + font-size: 14px; + color: #333333; +} + +.text-container > ol > li > p { + font-weight: 600; + font-size: 14px; + line-height: 150%; + display: inline; + color: #333333; +} + +.personality-information { + font-weight: 400; + font-size: 14px; + line-height: 150%; + margin-top: 12px; + color: #333333; +} + +.cover-container { + position: absolute; + bottom: 0px; + left: 0px; + width: 100%; + height: 50%; + background: linear-gradient( + rgba(251, 251, 255, 0) 3.34%, + rgba(251, 251, 255, 0.8) 24.99%, + rgba(251, 251, 255, 0.9) 45.63%, + rgba(255, 251, 251, 0.9) 69.74%, + rgb(255, 240, 240) 89.93% + ); + border-radius: 20px; + padding-top: 34px; + display: flex; + flex-direction: column; + -webkit-box-pack: end; + justify-content: end; + z-index: 1; +} + +.cover { + position: absolute; + width: 100%; + background: linear-gradient( + rgba(251, 251, 255, 0) 0%, + rgba(251, 251, 255, 0.18) 22.4%, + rgba(251, 251, 255, 0.16) 43.75%, + rgba(251, 251, 255, 0.14) 75%, + rgba(251, 251, 255, 0.196) 89.58% + ); + backdrop-filter: blur(1.5px); + border-radius: 20px; + height: 100%; + padding-bottom: 48px; +} + +.cover-container > p { + position: relative; + font-weight: 600; + line-height: 140%; + width: 240px; + margin: 0px auto 28px; + text-align: center; + color: #6a3aa2; + z-index: 2; +} + +.button { + position: relative; + height: 48px; + z-index: 2; + font-size: 18px; + font-weight: 700; +} diff --git a/src/components/pages/TrialPayment/index.tsx b/src/components/pages/TrialPayment/index.tsx new file mode 100755 index 0000000..571364f --- /dev/null +++ b/src/components/pages/TrialPayment/index.tsx @@ -0,0 +1,59 @@ +import Title from "@/components/Title"; +import Header from "./components/Header"; +import PersonalInformation from "./components/PersonalInformation"; +import styles from "./styles.module.css"; +import Goal from "./components/Goal"; +import PaymentTable from "./components/PaymentTable"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import { Navigate, useNavigate } from "react-router-dom"; +import routes from "@/routes"; +import { getZodiacSignByDate } from "@/services/zodiac-sign"; +import YourReading from "./components/YourReading"; +import Reviews from "./components/Reviews"; +import YouGet from "./components/YouGet"; +import OftenAsk from "./components/OftenAsk"; + +function TrialPaymentPage() { + const navigate = useNavigate(); + const subPlan = useSelector(selectors.selectActiveSubPlan); + const birthdate = useSelector(selectors.selectBirthdate); + const zodiacSign = getZodiacSignByDate(birthdate); + const { gender, birthPlace } = useSelector(selectors.selectQuestionnaire); + + if (!subPlan) { + return ; + } + + const handleNext = () => { + navigate(routes.client.paymentStripe()); + }; + + return ( +
+
+ + + Your Personalized Clarity & Love Reading is ready! + + + + + + + + +
+ ); +} + +export default TrialPaymentPage; diff --git a/src/components/pages/TrialPayment/styles.module.css b/src/components/pages/TrialPayment/styles.module.css new file mode 100755 index 0000000..2abe954 --- /dev/null +++ b/src/components/pages/TrialPayment/styles.module.css @@ -0,0 +1,16 @@ +.page { + background-color: #fff0f0; + height: fit-content; + min-height: 100vh; + min-height: 100dvh; + padding-top: 62px; + padding-bottom: 62px; +} + +.title { + margin-top: 360px; + font-size: 24px; + line-height: 145%; + color: #333333; + font-weight: 700; +} diff --git a/src/components/ui/ProgressBarLine/index.tsx b/src/components/ui/ProgressBarLine/index.tsx old mode 100644 new mode 100755 index 38e738c..5b3e0fc --- a/src/components/ui/ProgressBarLine/index.tsx +++ b/src/components/ui/ProgressBarLine/index.tsx @@ -1,15 +1,30 @@ import styles from "./styles.module.css"; interface IProgressBarLineProps { - value: number - delay: number - containerClassName?: string + value: number; + delay: number; + containerClassName?: string; + lineClassName?: string; + lineColor?: string; } -function ProgressBarLine({ value, delay, containerClassName }: IProgressBarLineProps): JSX.Element { +function ProgressBarLine({ + value, + delay, + containerClassName, + lineClassName, + lineColor, +}: IProgressBarLineProps): JSX.Element { return (
-
+
); } diff --git a/src/data/loadingProfile.ts b/src/data/loadingProfile.ts new file mode 100755 index 0000000..012d713 --- /dev/null +++ b/src/data/loadingProfile.ts @@ -0,0 +1,37 @@ +interface IPoint { + title: string; + color: string; +} + +export const loadingProfilePoints: IPoint[] = [ + { + title: "Your profile", + color: "#908cf2", + }, + { + title: "Personality traits", + color: "#55cdf2", + }, + { + title: "Relationship Pattern", + color: "#b86ada", + }, +]; + +export const titlesLoadingProfile = [ + "Analyzing your profile...", + "Identifying the planetary positions when you were born...", + "Creating your astrological blueprint...", + "Assessing personality profile...", + "Identifying your strengths and weaknesses...", + "Analyzing your compatibility...", + "Analyzing relationship needs...", + "Charting best guidance plan...", + "Predicting future results...", +]; + +export const modalTitlesLoadingProfile = [ + "Do you enjoy time spent alone?", + "Are you adventurous person?", + "Have you ever tried any remedies/rituals?", +]; diff --git a/src/data/oftenAsk.ts b/src/data/oftenAsk.ts new file mode 100755 index 0000000..bb2fe2f --- /dev/null +++ b/src/data/oftenAsk.ts @@ -0,0 +1,20 @@ +interface IQuestion { + title: string; + text: string; +} + +export const questions: IQuestion[] = [ + { + title: "How accurate is the astrology reading on this platform?", + text: "The accuracy of an astrology reading can vary and is subjective. Astrology is not an exact science, but many find that it can provide valuable insights and perspectives. Our platform uses advanced algorithms and expert astrologers to provide the most accurate readings possible.", + }, + { + title: "Can I get a compatibility reading for relationships?", + text: "Yes, you can get a compatibility reading for relationships. This type of reading includes a comprehensive astrological analysis of two people to assess their compatibility in various areas, including love, communication and shared values.", + }, + { + title: + "Are the astrology readings on this platform confidential and private?", + text: "Yes, all readings on our platform are strictly confidential. We respect our users' privacy and ensure that all personal data and readings are securely stored and not shared with third parties without consent.", + }, +]; diff --git a/src/data/onboarding.ts b/src/data/onboarding.ts new file mode 100755 index 0000000..f6c150d --- /dev/null +++ b/src/data/onboarding.ts @@ -0,0 +1,7 @@ +export const onboardingTitles = [ + "Based on your answers", + "We’ve created your astrological blueprint and guidance plan", + "To help you find your perfect partner", + "And to improve your relationship – for good.", + "Let’s get started.", +]; diff --git a/src/data/reviews.ts b/src/data/reviews.ts new file mode 100755 index 0000000..33689a3 --- /dev/null +++ b/src/data/reviews.ts @@ -0,0 +1,27 @@ +interface IReview { + username: string; + date: string; + text: string; + mark: number; +} + +export const reviews: IReview[] = [ + { + username: "ria._.panwar", + date: "02/17/2023", + text: "It was really helpful and had provided me the clarity that I needed for my current relationship situation. It gives me hope that my relationship could still be save. Thank you. Highly recommended!", + mark: 5, + }, + { + username: "jp63_", + date: "02/17/2023", + text: "Amazing, absolutely amazing! The affirmations I received and nurturing advice, was worth everything ! Truly, thank you !!", + mark: 5, + }, + { + username: "therealslimmazi", + date: "02/17/2023", + text: "It helps me be able to trust my self and my choices for the future by giving me reassurance with the information i get. My goals and dreams are going to happen and and now i trust myself to do as a need and wish", + mark: 4.6, + }, +]; diff --git a/src/locales/dev.ts b/src/locales/dev.ts old mode 100644 new mode 100755 index 917a9da..48ca5ee --- a/src/locales/dev.ts +++ b/src/locales/dev.ts @@ -4,7 +4,7 @@ export default { next: "Next", date_of_birth: "What's your date of birth?", privacy_text: "By continuing, you agree to our and . Have a question? Reach our support team ", - eula: 'EULA', + eula: "Hint's EULA", here: 'here', privacy_notice: 'Privacy Notice', born_time_question: "What time were you born?", @@ -22,9 +22,9 @@ export default { day: "Day", we_will_email_you: "We will email you a copy of your wallpaper for easy access.", your_email: "Your email", - we_dont_share: "We don't share any personal information.", + we_dont_share: "*We don't share any personal information. We'll email you a copy of your program for convenient access.", continue_agree: 'By clicking "Continue" below, you agree to our and .', - _continue_agree: `By clicking "Continue" below you agree to Hint's EULA and Privacy Policy.`, + _continue_agree: `By clicking "Continue" below you agree to and .`, privacy_policy: "Privacy Policy", continue: 'Continue', _continue: 'Continue', diff --git a/src/routes.ts b/src/routes.ts index d49f354..db4156c 100755 --- a/src/routes.ts +++ b/src/routes.ts @@ -1,7 +1,7 @@ import type { UserStatus } from "./types"; const host = ""; -export const apiHost = "https://api-web.aura.wit.life"; +export const apiHost = "https://api-web-test.aura.wit.life"; const siteHost = "https://aura.wit.life"; const prefix = "api/v1"; @@ -69,6 +69,11 @@ const routes = { relationshipZodiacInfo: () => [host, "relationship-zodiac-info"].join("/"), satisfiedResult: () => [host, "satisfied-result"].join("/"), aboutUs: () => [host, "about-us"].join("/"), + loadingProfile: () => [host, "loading-profile"].join("/"), + emailConfirm: () => [host, "email-confirm"].join("/"), + onboarding: () => [host, "onboarding"].join("/"), + trialChoice: () => [host, "trial-choice"].join("/"), + trialPayment: () => [host, "trial-payment"].join("/"), notFound: () => [host, "404"].join("/"), }, server: { @@ -141,6 +146,7 @@ export const entrypoints = [ routes.client.home(), routes.client.breathResult(), routes.client.magicBall(), + routes.client.trialChoice(), ]; export const isEntrypoint = (path: string) => entrypoints.includes(path); export const isNotEntrypoint = (path: string) => !isEntrypoint(path); @@ -211,6 +217,10 @@ export const withoutFooterRoutes = [ routes.client.relationshipZodiacInfo(), routes.client.satisfiedResult(), routes.client.aboutUs(), + routes.client.emailConfirm(), + routes.client.onboarding(), + routes.client.trialChoice(), + routes.client.trialPayment(), ]; export const withoutFooterPartOfRoutes = [routes.client.questionnaire()]; @@ -269,6 +279,8 @@ export const withoutHeaderRoutes = [ routes.client.both(), routes.client.relationshipZodiacInfo(), routes.client.satisfiedResult(), + routes.client.onboarding(), + routes.client.trialPayment(), ]; export const hasNoHeader = (path: string) => { return !withoutHeaderRoutes.includes(`/${path.split("/")[1]}`); diff --git a/src/services/price/index.ts b/src/services/price/index.ts old mode 100644 new mode 100755 index 52afca5..053aec7 --- a/src/services/price/index.ts +++ b/src/services/price/index.ts @@ -1,3 +1,5 @@ +import { ITrial } from "@/api/resources/SubscriptionPlans"; + export const roundToWhole = (value: string | number): number => { value = Number(value); if (value % Math.floor(value) !== 0) { @@ -13,3 +15,10 @@ export const removeAfterDot = (value: string): string => { } return value.split(".")[0]; }; + +export const getPriceFromTrial = (trial: ITrial | null) => { + if (!trial) { + return 0; + } + return (trial.price_cents === 100 ? 99 : trial.price_cents || 0) / 100; +};