-
{t('reserved_for')}{formatTime(time)}
+
+
+ {t("reserved_for")}
+ {formatTime(time)}
+
- )
+ );
}
-export default Countdown
+export default Countdown;
diff --git a/src/components/Countdown/styles.css b/src/components/Countdown/styles.module.css
similarity index 100%
rename from src/components/Countdown/styles.css
rename to src/components/Countdown/styles.module.css
diff --git a/src/components/DidYouKnowPage/index.tsx b/src/components/DidYouKnowPage/index.tsx
index f6da302..abe97f7 100644
--- a/src/components/DidYouKnowPage/index.tsx
+++ b/src/components/DidYouKnowPage/index.tsx
@@ -11,7 +11,7 @@ import { getZodiacSignByDate } from "@/services/zodiac-sign";
function DidYouKnowPage(): JSX.Element {
const { t } = useTranslation();
const navigate = useNavigate();
- const handleNext = () => navigate(routes.client.freePeriodInfo());
+ const handleNext = () => navigate(routes.client.feedback());
const birthdate = useSelector(selectors.selectBirthdate);
const zodiacSign = getZodiacSignByDate(birthdate);
diff --git a/src/components/FeedbackPage/index.tsx b/src/components/FeedbackPage/index.tsx
index 3b26183..bdabda9 100644
--- a/src/components/FeedbackPage/index.tsx
+++ b/src/components/FeedbackPage/index.tsx
@@ -12,7 +12,7 @@ function FeedbackPage(): JSX.Element {
const { t } = useTranslation();
const navigate = useNavigate();
const api = useApi();
- const handleNext = () => navigate(routes.client.auth());
+ const handleNext = () => navigate(routes.client.freePeriodInfo());
const assetsData = useCallback(async () => {
const { assets } = await api.getAssets({ category: String("au") });
diff --git a/src/components/PaymentTable/index.tsx b/src/components/PaymentTable/index.tsx
index 2b2b6d6..55ab9b7 100644
--- a/src/components/PaymentTable/index.tsx
+++ b/src/components/PaymentTable/index.tsx
@@ -1,53 +1,63 @@
-import { useTranslation } from 'react-i18next'
-import Price, { Currency, Locale } from './Price'
-import './styles.css'
+import { useTranslation } from "react-i18next";
+import Price, { Currency, Locale } from "./Price";
+import styles from "./styles.module.css";
+import Countdown from "../Countdown";
type PaymentItem = {
- title: string
- price: number
- description: string
-}
+ title: string;
+ price: number;
+ description: string;
+};
type PaymentTableProps = {
- currency: Currency
- locale: Locale
- items: PaymentItem[]
-}
+ currency: Currency;
+ locale: Locale;
+ items: PaymentItem[];
+};
-function PaymentTable({ currency, locale, items }: PaymentTableProps): JSX.Element {
- const { t } = useTranslation()
- const total = items.reduce((acc, item) => acc + item.price, 0)
- const totalPrice = new Price(total, currency, locale)
- const toItem = (item: typeof items[0], idx: number) => {
- const price = new Price(item.price, currency, locale)
+function PaymentTable({
+ currency,
+ locale,
+ items,
+}: PaymentTableProps): JSX.Element {
+ const { t } = useTranslation();
+ const total = items.reduce((acc, item) => acc + item.price, 0);
+ const totalPrice = new Price(total, currency, locale);
+ const toItem = (item: (typeof items)[0], idx: number) => {
+ const price = new Price(item.price, currency, locale);
return (
-
-
-
{item.title}
-
{price.format()}
+
+
+
{item.title}
+
{price.format()}
-
+
{item.description}
{/*
One dollar thirty six cents per day
*/}
- )
- }
+ );
+ };
return (
-
-
-
-
{t('total_today')}
-
{totalPrice.format()}
-
-
- {items.map(toItem)}
+
+
+
+
+
+ {t("total_today")}
+
+
+ {totalPrice.format()}
+
+
{items.map(toItem)}
+
+
+ {t("charged_only", { price: totalPrice.format() })}
-
{t('charged_only', {price: totalPrice.format()})}
- )
+ );
}
-export default PaymentTable
-export { Price, Currency, Locale }
+export default PaymentTable;
+export { Price, Currency, Locale };
diff --git a/src/components/PaymentTable/styles.css b/src/components/PaymentTable/styles.module.css
similarity index 73%
rename from src/components/PaymentTable/styles.css
rename to src/components/PaymentTable/styles.module.css
index 0bfd7f6..9a3cfaa 100644
--- a/src/components/PaymentTable/styles.css
+++ b/src/components/PaymentTable/styles.module.css
@@ -1,6 +1,22 @@
.payment {
position: relative;
width: 100%;
+ margin-top: 16px;
+}
+
+.payment__countdown {
+ position: absolute;
+ border-radius: 14px !important;
+ padding: 10px 16px !important;
+ width: fit-content;
+ right: 12px;
+ top: -18px;
+ font-size: 14px !important;
+}
+
+.payment__countdown-time {
+ font-size: 16px;
+ font-weight: 700;
}
.payment__table {
@@ -11,6 +27,7 @@
padding: 0 25px;
width: 100%;
margin-bottom: 10px;
+ padding-top: 6px;
}
.payment__total,
@@ -57,4 +74,4 @@
font-size: 12px;
line-height: 1.5;
text-align: center;
-}
\ No newline at end of file
+}
diff --git a/src/components/SubscriptionPage/index.tsx b/src/components/SubscriptionPage/index.tsx
index 11b7161..a016295 100644
--- a/src/components/SubscriptionPage/index.tsx
+++ b/src/components/SubscriptionPage/index.tsx
@@ -1,7 +1,7 @@
-import { useSelector } from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
-import { selectors } from "@/store";
+import { actions, selectors } from "@/store";
import MainButton from "../MainButton";
import Policy from "../Policy";
import Countdown from "../Countdown";
@@ -14,9 +14,15 @@ import Header from "../Header";
import SpecialWelcomeOffer from "../SpecialWelcomeOffer";
import { useState } from "react";
import { ITrial } from "@/api/resources/SubscriptionPlans";
+import { ApiError, extractErrorMessage, useApi } from "@/api";
+import { useAuth } from "@/auth";
+import { getClientLocale, getClientTimezone } from "@/locales";
+import Loader from "../Loader";
+import Title from "../Title";
+import ErrorText from "../ErrorText";
const currency = Currency.USD;
-const locale = Locale.EN;
+const locale = getClientLocale() as Locale;
const getPriceFromTrial = (trial: ITrial | null) => {
if (!trial) {
@@ -26,9 +32,26 @@ const getPriceFromTrial = (trial: ITrial | null) => {
};
function SubscriptionPage(): JSX.Element {
- const [isOpenModal, setIsOpenModal] = useState(false);
+ const api = useApi();
+ const timezone = getClientTimezone();
+ const { signUp } = useAuth();
const { t } = useTranslation();
+ const navigate = useNavigate();
+ const dispatch = useDispatch();
+ const [isOpenModal, setIsOpenModal] = useState(false);
+ const [email, setEmail] = useState("");
+ const [emailError, setEmailError] = useState
(
+ "Email is invalid"
+ );
+ const [name, setName] = useState("");
+ const [nameError, setNameError] = useState("Name is invalid");
+ const [isSubmit, setIsSubmit] = useState(false);
+ const [isAuth, setIsAuth] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+ const [apiError, setApiError] = useState(null);
+ const [error, setError] = useState(false);
const activeSubPlan = useSelector(selectors.selectActiveSubPlan);
+ const birthday = useSelector(selectors.selectBirthday);
const paymentItems = [
{
title: activeSubPlan?.name || "Per 7-Day Trial For",
@@ -39,9 +62,52 @@ function SubscriptionPage(): JSX.Element {
},
];
- const navigate = useNavigate();
- const email = useSelector(selectors.selectEmail);
- const handleClick = () => navigate(routes.client.paymentMethod());
+ 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));
+ }
+ dispatch(actions.status.update("registred"));
+ setIsLoading(false);
+ setIsAuth(true);
+ setTimeout(() => {
+ navigate(routes.client.paymentMethod());
+ }, 1000);
+ } catch (error) {
+ console.error(error);
+ if (error instanceof ApiError) {
+ setApiError(error as ApiError);
+ } else {
+ setError(true);
+ }
+ setIsLoading(false);
+ }
+ };
+
+ // const email = useSelector(selectors.selectEmail);
+ const handleClick = async () => {
+ setIsSubmit(true);
+
+ if (!isValidEmail(email) || !isValidName(name)) {
+ return;
+ }
+ await authorization();
+ console.log("sdvdvsdv");
+ // navigate(routes.client.paymentMethod())
+ };
const handleCross = () => setIsOpenModal(true);
const policyLink = (
@@ -49,21 +115,110 @@ function SubscriptionPage(): JSX.Element {
);
+ const isValidEmail = (email: string) => {
+ return /\S+@\S+\.\S+/.test(email);
+ };
+
+ const isValidName = (name: string) => {
+ return !!(name.length > 0 && name.length < 30);
+ };
+
+ const handleChangeEmail = (event: React.ChangeEvent) => {
+ const email = event.target.value;
+ if (!isValidEmail(email)) {
+ setEmailError("Email is invalid");
+ } else {
+ setEmailError(null);
+ }
+ setEmail(email);
+ };
+
+ const handleChangeName = (event: React.ChangeEvent) => {
+ const name = event.target.value;
+ if (!isValidName(name)) {
+ setNameError("Name is invalid");
+ } else {
+ setNameError(null);
+ }
+ setName(name);
+ };
+
return (
<>
-
+ {/* */}
-
+ {/* */}
+
+
+
+ {isSubmit && !!nameError && (
+
+ {nameError}
+
+ )}
+
+
+
+ {isSubmit && !!emailError && (
+
+ {emailError}
+
+ )}
+
+ {isLoading &&
+ isSubmit &&
+ isValidEmail(email) &&
+ isValidName(name) &&
}
+ {(error || apiError) && (
+
+ Something went wrong
+
+ )}
+ {apiError && (
+
+ )}
+ {!apiError && !error && !isLoading && isAuth && (
+

+ )}
+
- {t("get_access")}
+
+ Start ${getPriceFromTrial(activeSubPlan?.trial || null)}
+
<>
diff --git a/src/components/SubscriptionPage/styles.module.css b/src/components/SubscriptionPage/styles.module.css
index 15061b0..d25d4da 100644
--- a/src/components/SubscriptionPage/styles.module.css
+++ b/src/components/SubscriptionPage/styles.module.css
@@ -15,4 +15,45 @@
.cross {
left: 28px;
-}
\ No newline at end of file
+}
+
+.inputs-container {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 16px;
+ margin-top: 16px;
+}
+
+.inputs-container__input {
+ width: 100%;
+ border: solid #000 2px;
+ border-radius: 20px;
+ font-size: 17px;
+ font-weight: 400;
+ line-height: 20px;
+ padding: 16px 25px;
+}
+
+.inputs-container__input-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ width: 100%;
+}
+
+.inputs-container__input-error {
+ border-color: red;
+}
+
+.inputs-container__label-error {
+ font-size: 12px;
+ font-weight: 400;
+ color: red;
+ width: 100%;
+ padding-left: 25px;
+}
diff --git a/src/locales/dev.ts b/src/locales/dev.ts
index b428b63..917a9da 100644
--- a/src/locales/dev.ts
+++ b/src/locales/dev.ts
@@ -11,7 +11,7 @@ export default {
nasa_data_using: "We use NASA data to determine the exact position of the planets in the sky at the time of your birth to create wallpapers that are just right for you.",
cta_title: "Start your 7-day trial",
cta_subtitle: "No pressure. Cancel anytime.",
- reserved_for: "Reserved for ",
+ reserved_for: "RESERVED for ",
creating_profile: "Creating your profile",
zodiac_analysis: "Zodiac data analysis",
drawing_wallpaper: "Drawing Wallpapers",