diff --git a/src/api/resources/AuthTokens.ts b/src/api/resources/AuthTokens.ts index 4b15ac0..748631d 100644 --- a/src/api/resources/AuthTokens.ts +++ b/src/api/resources/AuthTokens.ts @@ -1,37 +1,43 @@ -import routes from "@/routes" -import { AuthToken } from "../types" -import { User } from "./User" -import { getBaseHeaders } from "../utils" +import routes from "@/routes"; +import { AuthToken } from "../types"; +import { User } from "./User"; +import { getBaseHeaders } from "../utils"; -export interface Payload { - email: string - timezone: string - locale: string +export interface PayloadRegisterByEmail { + email: string; + timezone: string; + locale: string; } +export interface PayloadAuthWithJWT { + jwt: string; +} + +export type Payload = PayloadRegisterByEmail | PayloadAuthWithJWT; + export interface Response { auth: { - token: AuthToken - payload: JwtPayload - user: User - } + token: AuthToken; + payload: JwtPayload; + user: User; + }; } export interface JwtPayload { - sub: number - email: string - loc: string - tz: number - state: string - iat: number - exp: number - jti: string - type: string - iss: string + sub: number; + email: string; + loc: string; + tz: number; + state: string; + iat: number; + exp: number; + jti: string; + type: string; + iss: string; } -export const createRequest = ({ locale, timezone, email }: Payload): Request => { - const url = new URL(routes.server.token()) - const body = JSON.stringify({ auth: { locale, timezone, email }}) - return new Request(url, { method: 'POST', headers: getBaseHeaders(), body }) -} +export const createRequest = (payload: Payload): Request => { + const url = new URL(routes.server.token()); + const body = JSON.stringify({ auth: { ...payload } }); + return new Request(url, { method: "POST", headers: getBaseHeaders(), body }); +}; diff --git a/src/auth/AuthProvider.tsx b/src/auth/AuthProvider.tsx index c861e85..3e85add 100644 --- a/src/auth/AuthProvider.tsx +++ b/src/auth/AuthProvider.tsx @@ -1,28 +1,41 @@ -import { useCallback, useMemo } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { actions, selectors } from '../store' -import { AuthToken, User } from '../api' -import { AuthContext } from './AuthContext' +import { useCallback, useMemo } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { actions, selectors } from "../store"; +import { AuthToken, User } from "../api"; +import { AuthContext } from "./AuthContext"; -export function AuthProvider({ children }: React.PropsWithChildren): JSX.Element { - const dispatch = useDispatch() - const token = useSelector(selectors.selectToken) - const user = useSelector(selectors.selectUser) - const signUp = useCallback((token: AuthToken, user: User.User): AuthToken => { - dispatch(actions.token.update(token)) - dispatch(actions.user.update(user)) - return token - }, [dispatch]) - const logout = useCallback(() => dispatch(actions.reset()), [dispatch]) - const auth = useMemo(() => ({ - signUp, - logout, - token, - user: user.id ? user : null - }), [token, user, signUp, logout]) - return ( - - {children} - - ) +export function AuthProvider({ + children, +}: React.PropsWithChildren): JSX.Element { + const dispatch = useDispatch(); + const token = useSelector(selectors.selectToken); + const user = useSelector(selectors.selectUser); + const signUp = useCallback( + (token: AuthToken, user: User.User): AuthToken => { + dispatch(actions.token.update(token)); + dispatch(actions.user.update(user)); + dispatch(actions.form.addEmail(user.email)); + if (user.profile.birthday?.length) { + dispatch(actions.form.addDate(user.profile.birthday.split(" ")[0])); + dispatch( + actions.form.addTime( + new Date(user.profile.birthday).toLocaleTimeString() + ) + ); + } + return token; + }, + [dispatch] + ); + const logout = useCallback(() => dispatch(actions.reset()), [dispatch]); + const auth = useMemo( + () => ({ + signUp, + logout, + token, + user: user.id ? user : null, + }), + [token, user, signUp, logout] + ); + return {children}; } diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 9f8e059..89f8bab 100755 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -6,6 +6,7 @@ import { Outlet, useLocation, useNavigate, + useSearchParams, } from "react-router-dom"; import { useAuth } from "@/auth"; import { useDispatch, useSelector } from "react-redux"; @@ -112,7 +113,9 @@ function App(): JSX.Element { const navigate = useNavigate(); const api = useApi(); const dispatch = useDispatch(); - const { token, user } = useAuth(); + const { token, user, signUp } = useAuth(); + const [searchParams] = useSearchParams(); + const jwtToken = searchParams.get("token"); useEffect(() => { if (!isProduction) return; @@ -137,6 +140,18 @@ function App(): JSX.Element { const { data } = useApiCall(assetsData); + // jwt auth + useEffect(() => { + (async () => { + if (!jwtToken) return; + const auth = await api.auth({ jwt: jwtToken }); + const { + auth: { token, user }, + } = auth; + signUp(token, user); + })(); + }, [api, jwtToken, signUp]); + useEffect(() => { (async () => { if (!token.length || !user) return; @@ -185,8 +200,11 @@ function App(): JSX.Element { // set user device type useEffect(() => { - if (/iPhone|iPad|iPod/i.test(navigator.userAgent)) { + const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent); + if (isIOS) { dispatch(actions.userConfig.addDeviceType(EUserDeviceType.ios)); + } else { + dispatch(actions.userConfig.addDeviceType(EUserDeviceType.android)); } }, [dispatch]); @@ -473,7 +491,6 @@ function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element { }, [dataItems]); const onCloseFullDataModal = (_birthDate: string) => { - console.log("onCloseFullDataModal", _birthDate); dispatch(actions.form.addDate(_birthDate)); setIsShowFullDataModal(getIsShowFullDataModal(dataItems)); }; @@ -584,8 +601,7 @@ function PrivateOutlet(): JSX.Element { } function PrivateSubscriptionOutlet(): JSX.Element { - // const isProduction = import.meta.env.MODE === "production"; - const isProduction = false; + const isProduction = import.meta.env.MODE === "production"; const status = useSelector(selectors.selectStatus); return status === "subscribed" || !isProduction ? ( diff --git a/src/components/EmailEnterPage/index.tsx b/src/components/EmailEnterPage/index.tsx index ff5669b..c262d42 100755 --- a/src/components/EmailEnterPage/index.tsx +++ b/src/components/EmailEnterPage/index.tsx @@ -41,6 +41,9 @@ function EmailEnterPage(): JSX.Element { const timezone = getClientTimezone(); const locale = i18n.language; const { subPlan } = useParams(); + const { gender, flowChoice, birthPlace } = useSelector( + selectors.selectQuestionnaire + ); useEffect(() => { if (subPlan) { @@ -111,7 +114,15 @@ function EmailEnterPage(): JSX.Element { } = auth; signUp(token, user); const payload = { - user: { profile_attributes: { birthday } }, + user: { + profile_attributes: { + birthday, + gender, + full_name: name, + relationship_status: flowChoice, + }, + birthplace_attributes: { address: birthPlace }, + }, token, }; const updatedUser = await api.updateUser(payload).catch((error) => { diff --git a/src/components/HomePage/index.tsx b/src/components/HomePage/index.tsx index fc1c08b..f288b91 100644 --- a/src/components/HomePage/index.tsx +++ b/src/components/HomePage/index.tsx @@ -15,23 +15,23 @@ import { } from "@/services/zodiac-sign"; import { actions, selectors } from "@/store"; import { getRandomArbitrary } from "@/services/random-value"; -import Title from "../Title"; +// import Title from "../Title"; // import { UserDailyForecast } from "@/api/resources/UserDailyForecasts"; import { EPathsFromHome } from "@/store/siteConfig"; import { buildFilename, saveFile } from "../WallpaperPage/utils"; import Onboarding from "../Onboarding"; import TextWithFinger from "../TextWithFinger"; -import Slider from "../Slider"; -import BestiesHoroscopeSlider, { Horoscope } from "../BestiesHoroscopeSlider"; -import PredictionMoonsSlider, { - IPredictionMoon, -} from "../PredictionMoonsSlider"; +// import Slider from "../Slider"; +// import BestiesHoroscopeSlider, { Horoscope } from "../BestiesHoroscopeSlider"; +// import PredictionMoonsSlider, { +// IPredictionMoon, +// } from "../PredictionMoonsSlider"; import { predictionMoonsPeriods } from "@/data"; -import WallpapersZodiacSign from "../WallpapersZodiacSign"; -import ThermalSlider from "../ThermalSlider"; -import MoonPhaseTracker from "../MoonPhaseTracker"; -import EnergyVampirism from "../EnergyVampirism"; -import NameHoroscopeSlider from "../NameHoroscopeSlider"; +// import WallpapersZodiacSign from "../WallpapersZodiacSign"; +// import ThermalSlider from "../ThermalSlider"; +// import MoonPhaseTracker from "../MoonPhaseTracker"; +// import EnergyVampirism from "../EnergyVampirism"; +// import NameHoroscopeSlider from "../NameHoroscopeSlider"; const buttonTextFormatter = (text: string): JSX.Element => { const sentences = text.split("."); @@ -56,6 +56,7 @@ function HomePage(): JSX.Element { const zodiacSign = getZodiacSignByDate(birthdate); const [asset, setAsset] = useState(); const [moonsAssets, setMoonsAssets] = useState([]); + moonsAssets const api = useApi(); const homeConfig = useSelector(selectors.selectHome); @@ -63,8 +64,10 @@ function HomePage(): JSX.Element { const onboardingConfigHome = useSelector(selectors.selectOnboardingHome); const compatibilities = useSelector(selectors.selectCompatibilities); + compatibilities const user = useSelector(selectors.selectUser); + user const [isShowOnboardingHome, setIsShowOnboardingHome] = useState( !onboardingConfigHome?.isShown @@ -168,36 +171,36 @@ function HomePage(): JSX.Element { saveFile(asset.url.replace("http://", "https://"), buildFilename("1")); }; - const handleBestiesHoroscope = (item: Horoscope) => { - const { name, birthDate } = item; - navigate( - `${routes.client.horoscopeBestiesResult()}?name=${name}&birthDate=${birthDate}` - ); - }; + // const handleBestiesHoroscope = (item: Horoscope) => { + // const { name, birthDate } = item; + // navigate( + // `${routes.client.horoscopeBestiesResult()}?name=${name}&birthDate=${birthDate}` + // ); + // }; - const handleThermal = (item: Horoscope) => { - const { name, birthDate } = item; - navigate( - `${routes.client.thermalResult()}?name=${name}&birthDate=${birthDate}` - ); - }; + // const handleThermal = (item: Horoscope) => { + // const { name, birthDate } = item; + // navigate( + // `${routes.client.thermalResult()}?name=${name}&birthDate=${birthDate}` + // ); + // }; - const handlePredictionMoon = (item: IPredictionMoon) => { - const { period } = item; - navigate(`${routes.client.predictionMoonResult()}?period=${period}`); - }; + // const handlePredictionMoon = (item: IPredictionMoon) => { + // const { period } = item; + // navigate(`${routes.client.predictionMoonResult()}?period=${period}`); + // }; - const handleMoonPhaseTracker = () => { - navigate(`${routes.client.moonPhaseTracker()}?period=today`); - }; + // const handleMoonPhaseTracker = () => { + // navigate(`${routes.client.moonPhaseTracker()}?period=today`); + // }; - const handleEnergyVampirism = () => { - navigate(`${routes.client.energyVampirismResult()}?period=today`); - }; + // const handleEnergyVampirism = () => { + // navigate(`${routes.client.energyVampirismResult()}?period=today`); + // }; - const handleNameHoroscope = (item: IPredictionMoon) => { - navigate(`${routes.client.nameHoroscopeResult()}?period=${item.period}`); - }; + // const handleNameHoroscope = (item: IPredictionMoon) => { + // navigate(`${routes.client.nameHoroscopeResult()}?period=${item.period}`); + // }; return (
{/* SLIDERS */} -
+ {/*
{!!compatibilities.length && (
@@ -323,16 +326,16 @@ function HomePage(): JSX.Element { ))} </Slider> </div> - </div> + </div> */} {/* END SLIDERS */} - <Title variant="h2" className={styles["sliders__title"]}> + {/* <Title variant="h2" className={styles["sliders__title"]}> {"Energy Vampirism"} - + */} {/* SLIDERS */} -
+ {/*
{t("Your Name's Horoscope")} @@ -349,16 +352,16 @@ function HomePage(): JSX.Element { ))} </Slider> </div> - </div> + </div> */} {/* END SLIDERS */} - <Title variant="h2" className={styles["sliders__title"]}> + {/* <Title variant="h2" className={styles["sliders__title"]}> {"AI-based unique Walpapers"} - + */} {/* SLIDERS */} -
+ {/*
{!!compatibilities.length && (
@@ -377,10 +380,10 @@ function HomePage(): JSX.Element { </Slider> </div> )} - </div> + </div> */} {/* END SLIDERS */} - <MoonPhaseTracker onClick={handleMoonPhaseTracker} /> + {/* <MoonPhaseTracker onClick={handleMoonPhaseTracker} /> */} {/* <div className={styles["content__daily-forecast"]}> {dailyForecast && diff --git a/src/components/pages/Onboarding/index.tsx b/src/components/pages/Onboarding/index.tsx index 7aa883a..6d0fbb0 100755 --- a/src/components/pages/Onboarding/index.tsx +++ b/src/components/pages/Onboarding/index.tsx @@ -17,7 +17,6 @@ function OnboardingPage() { }, [navigate]); useEffect(() => { - console.log("onboardingTitles", activeIndexTitle); setPeriodClassName("to-nontransparent"); classNameTimeOut.current = setTimeout(() => { setPeriodClassName("to-transparent"); diff --git a/src/components/pages/Questionnaire/CustomAnswers/Birthdate/index.tsx b/src/components/pages/Questionnaire/CustomAnswers/Birthdate/index.tsx index 0fa2dc4..513d325 100755 --- a/src/components/pages/Questionnaire/CustomAnswers/Birthdate/index.tsx +++ b/src/components/pages/Questionnaire/CustomAnswers/Birthdate/index.tsx @@ -22,6 +22,7 @@ function BirthdateCustomAnswer({ const questionnaire = useSelector(selectors.selectQuestionnaire); const birthdateFromStore = affiliation === "self" ? selfBirthdate : questionnaire.partnerBirthdate; + const [birthdate, setBirthdate] = useState(birthdateFromStore); const [isDisabled, setIsDisabled] = useState(true); const handleValid = (_birthdate: string) => { diff --git a/src/components/pages/Questionnaire/CustomAnswers/Birthtime/index.tsx b/src/components/pages/Questionnaire/CustomAnswers/Birthtime/index.tsx index aabd810..72a06a6 100644 --- a/src/components/pages/Questionnaire/CustomAnswers/Birthtime/index.tsx +++ b/src/components/pages/Questionnaire/CustomAnswers/Birthtime/index.tsx @@ -22,6 +22,7 @@ function BirthtimeCustomAnswer({ const questionnaire = useSelector(selectors.selectQuestionnaire); const birthtimeFromStore = affiliation === "self" ? selfBirthtime : questionnaire.partnerBirthtime; + const [birthtime, setBirthtime] = useState(birthtimeFromStore); const handleChange = (_birthtime: string) => { diff --git a/src/components/pages/TrialChoice/index.tsx b/src/components/pages/TrialChoice/index.tsx index 8799ef0..7cf1ecb 100755 --- a/src/components/pages/TrialChoice/index.tsx +++ b/src/components/pages/TrialChoice/index.tsx @@ -23,6 +23,7 @@ function TrialChoicePage() { const navigate = useNavigate(); const selectedPrice = useSelector(selectors.selectSelectedPrice); const homeConfig = useSelector(selectors.selectHome); + const email = useSelector(selectors.selectEmail); const [subPlans, setSubPlans] = useState<ISubscriptionPlan[]>([]); const [isDisabled, setIsDisabled] = useState(true); @@ -117,6 +118,7 @@ function TrialChoicePage() { direction="right-left" /> </div> + <p className={styles.email}>{email}</p> <MainButton className={styles.button} disabled={isDisabled} diff --git a/src/components/pages/TrialChoice/styles.module.css b/src/components/pages/TrialChoice/styles.module.css index f082e4c..73e3d4c 100755 --- a/src/components/pages/TrialChoice/styles.module.css +++ b/src/components/pages/TrialChoice/styles.module.css @@ -121,3 +121,7 @@ min-height: 0; height: 50px; } + +.email { + font-weight: 500; +} \ No newline at end of file diff --git a/src/components/pages/TrialPayment/components/Header/index.tsx b/src/components/pages/TrialPayment/components/Header/index.tsx index 75bff06..0616dff 100755 --- a/src/components/pages/TrialPayment/components/Header/index.tsx +++ b/src/components/pages/TrialPayment/components/Header/index.tsx @@ -3,15 +3,24 @@ import DiscountExpires from "../DiscountExpires"; import styles from "./styles.module.css"; interface IHeaderProps { + buttonText?: string; + buttonClassName?: string; buttonClick: () => void; } -function Header({ buttonClick }: IHeaderProps) { +function Header({ + buttonClick, + buttonText = "get my reading", + buttonClassName = "", +}: IHeaderProps) { return ( <header className={styles.header}> <DiscountExpires /> - <CustomButton className={styles.button} onClick={buttonClick}> - get my reading + <CustomButton + className={`${styles.button} ${buttonClassName}`} + onClick={buttonClick} + > + {buttonText} </CustomButton> </header> ); diff --git a/src/components/pages/TrialPayment/components/YourReading/index.tsx b/src/components/pages/TrialPayment/components/YourReading/index.tsx index 1c3d1ca..3ddb4d8 100755 --- a/src/components/pages/TrialPayment/components/YourReading/index.tsx +++ b/src/components/pages/TrialPayment/components/YourReading/index.tsx @@ -8,6 +8,8 @@ interface IYourReadingProps { gender: string; zodiacSign: string; singleOrWithPartner: "single" | "partner"; + buttonText?: string; + callToActionText?: string; buttonClick: () => void; } @@ -15,6 +17,8 @@ function YourReading({ gender, zodiacSign, singleOrWithPartner = "single", + buttonText = "get my reading", + callToActionText = "To read the full reading you need get access", buttonClick, }: IYourReadingProps) { const [points, setPoints] = useState(yourReadingList); @@ -60,9 +64,9 @@ function YourReading({ </div> <div className={styles["cover-container"]}> <div className={styles["cover"]}></div> - <p>To read the full reading you need get access</p> + <p>{callToActionText}</p> <CustomButton className={styles.button} onClick={buttonClick}> - get my reading + {buttonText} </CustomButton> </div> </div> diff --git a/src/components/pages/TryApp/index.tsx b/src/components/pages/TryApp/index.tsx index 202b132..c005082 100644 --- a/src/components/pages/TryApp/index.tsx +++ b/src/components/pages/TryApp/index.tsx @@ -52,7 +52,11 @@ function TryAppPage() { return ( <section className={`${styles.page} page`}> - <Header buttonClick={downloadApp} /> + <Header + buttonClick={downloadApp} + buttonText="get my reading in the app" + buttonClassName={styles["header-button"]} + /> {singleOrWithPartner === "partner" && ( <WithPartnerInformation zodiacSign={zodiacSign} @@ -90,7 +94,7 @@ function TryAppPage() { alt="Download on the app store" onClick={downloadApp} /> - <span className={styles["download-app-title"]}>1. Download App</span> + <span className={styles["download-app-title"]}>2. Enter Your Access Code</span> <div className={styles["code-container"]}>FV1HBP</div> <p className={styles["code-description"]}> Enter your access code in the app to access Your Personalized Reading. @@ -102,6 +106,8 @@ function TryAppPage() { zodiacSign={zodiacSign} buttonClick={downloadApp} singleOrWithPartner={singleOrWithPartner} + callToActionText="To read the full reading you need get access through the app for your iPhone" + buttonText="get my reading in the app" /> <Title variant="h2" diff --git a/src/components/pages/TryApp/styles.module.css b/src/components/pages/TryApp/styles.module.css index 2b74882..7791955 100644 --- a/src/components/pages/TryApp/styles.module.css +++ b/src/components/pages/TryApp/styles.module.css @@ -22,6 +22,7 @@ max-width: 270px; height: 80px; object-fit: cover; + animation: 1.5s ease 0s infinite normal none running pulse; } .code-container { @@ -46,3 +47,19 @@ .title { font-weight: 700; } + +.header-button { + height: 100%; +} + +@keyframes pulse { + 0% { + transform: scale(0.9); + } + 70% { + transform: scale(1); + } + 100% { + transform: scale(0.9); + } +}