Merge branch 'develop' into 'main'
develop See merge request witapp/aura-webapp!555
This commit is contained in:
commit
8dd0670c69
62
public/locales/email-marketing-comp-v1/en/male_en.json
Normal file
62
public/locales/email-marketing-comp-v1/en/male_en.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"marketing-landing": {
|
||||
"title": "Special Offer!",
|
||||
"description": "Everything for free. Trial include!",
|
||||
"title-hey": "Hey, Sun 👋",
|
||||
"description-hey": "Your wellness and happiness are key for us!",
|
||||
"title-highlights": "Highlights of your plan:",
|
||||
"button-continue": "Continue",
|
||||
"plan-includes-title": "Your plan also includes:",
|
||||
"plan-includes-unlimited-horoscopes": "Unlimited daily / weekly / monthly / yearly horoscopes",
|
||||
"plan-includes-astrology-lessons": "Astrology lessons and articles inside the app",
|
||||
"plan-includes-lifestyle-calendars": "Beauty / health / travel and more calendars",
|
||||
"plan-includes-compatibility-check": "Compatibility check with zodiac signs inside the app",
|
||||
"personalized-plan-title": "So we decided to give you your personalized plan and access to the trial of our app",
|
||||
"personalized-plan-free": "FOR FREE!",
|
||||
"personal-astrologer-advice": "1:1 Advice from your personal astrologer",
|
||||
"finding-compatible-partner": "Finding the most compatible partner",
|
||||
"relationship-patterns-insights": "Insights into your relationship patterns, and emotional and sexual needs",
|
||||
"better-understanding-yourself": "Better understanding of yourself and your needs",
|
||||
"old-price-label": "OLD PRICE",
|
||||
"new-price-label": "NEW PRICE",
|
||||
"review-1": "Horoscope tells realistic facts about day to day life, which can be easily relatable. It shows direction.",
|
||||
"review-2": "It makes me feel safe, seeing, warm and smart.",
|
||||
"review-3": "I love that we have the \"my profile\" option. I love learning about myself.",
|
||||
"circular-text-image": "circular-text-en.png",
|
||||
"statistics-banner-text": "Last week alone <b> got this relationship guide",
|
||||
"statistics-banner-count": "<count>+ people",
|
||||
"money-back-guarantee-title": "Money-back guarantee",
|
||||
"money-back-guarantee-text": "We are convinced that we will help you get a deeper understanding of your partner and how you can improve your relationship. After all of our stellar customer reviews, we are ready to return your money if you feel that this report doesn't provide any value. Find more about applicable limitations in our Money-back policy.",
|
||||
"guaranteed-security-payments": "Guaranteed security payments"
|
||||
},
|
||||
"special-offer": {
|
||||
"title": "Special Offer!",
|
||||
"start-trial": "Start your <days>-day trial",
|
||||
"cancel-anytime": "No pressure. Cancel anytime",
|
||||
"policy": "By continuing you agree that if you don't cancel prior to the end of the <days>-days trial, you will automatically be charged <price> every 2 weeks until you cancel in settings. Learn more about cancellation and refund policy in Subscription terms",
|
||||
"button-continue": "Continue",
|
||||
"pricing-summary-total-today": "Total today:",
|
||||
"pricing-summary-code-applied": "Code applied!",
|
||||
"pricing-summary-cost-after-trial": "Your cost per 2 weeks after trial",
|
||||
"pricing-summary-trial-description": "You will be charged only <totalToday> for your <trialDuration>-day trial. Subscription renews automatically until cancelled. You can cancel at any time before the end of the trial."
|
||||
},
|
||||
"save-off": {
|
||||
"title": "SAVE <discount>% OFF!",
|
||||
"instead": "<price> instead <oldPrice>",
|
||||
"instead-old-price": "of <oldPrice>",
|
||||
"trial-duration": "<days>-day trial",
|
||||
"discount-offer": "<discount>% off on your personalized plan",
|
||||
"button-trial": "GET <days>-day trial"
|
||||
},
|
||||
"secret-discount": {
|
||||
"title": "You get a secret discount!",
|
||||
"button-trial": "GET <days>-DAY TRIAL",
|
||||
"policy": "By continuing you agree that if you don't cancel prior to the end of the <days>-days trial, you will automatically be charged <price> for the introductory period of 14 days thereafter the standard rate of <price> every 14 days until you cancel in settings. Learn more about cancellation and refund policy in Subscription terms.",
|
||||
"secret-discount-table_title": "You get a secret discount!",
|
||||
"secret-discount-table_subtitle": "No pressure. Cancel anytime.",
|
||||
"secret-discount-table_discount-applied": "Secret discount applied!",
|
||||
"secret-discount-table_cost-after-trial": "Your cost per <days> days after trial:",
|
||||
"secret-discount-table_you-save": "You save <amount>",
|
||||
"secret-discount-table_total-today": "Total today"
|
||||
}
|
||||
}
|
||||
80
public/locales/email-marketing-palmistry-v2/en/male_en.json
Normal file
80
public/locales/email-marketing-palmistry-v2/en/male_en.json
Normal file
@ -0,0 +1,80 @@
|
||||
{
|
||||
"trial-payment": {
|
||||
"transform_your_life": "Transform your\nlife with the Power\nof Palmistry — Get your\npersonalized plan today",
|
||||
"get_trial_days": "GET <days>-day trial",
|
||||
"personalized_reading_ready": "Personalized Palmistry\nReading is ready!",
|
||||
"hands_reveal_title": "What your hands and fingers can reveal about you",
|
||||
"hands_reveal_description": "Your hands and fingers are like a personal storybook, with each line representing a chapter of your life.",
|
||||
"prepare_personalized_plan": "We will prepare your\npersonalized plan",
|
||||
"aura_users_stories": "Here are a few stories from\nAURA Users",
|
||||
"as_featured_in": "As featured in",
|
||||
"discount_expires": "Discount expires",
|
||||
"min": "min",
|
||||
"sec": "sec",
|
||||
"payment_table_special_offer": "Special offer",
|
||||
"payment_table_title": "Personalized plan for <price>",
|
||||
"payment_table_total_today": "Total today",
|
||||
"payment_table_cost": "Including 1-week trial personal reading",
|
||||
"guaranteed_security_payments": "Guaranteed security payments",
|
||||
"policy_link": "Subscription policy",
|
||||
"subscription_policy": "You are enrolling in 1 week subscription. By continuing you agree that if you don't cancel prior to the end of the <trialDuration>-day trial for the <trialPrice> you will automatically be charged <price> every 1 week until you cancel in settings. Learn more about cancellation and refund policy in <policyLink>",
|
||||
"money_back_guarantee_title": "100% Money-back Guarantee",
|
||||
"money_back_guarantee_text": "We are convinced that we will help you get a deeper understanding of your partner and how you can improve your relationship. After all of our stellar customer reviews, we are ready to return your money if you feel that this report doesn't provide any value. Find more about applicable limitations in our Money-back policy.",
|
||||
"palms_say_about_point5": "<color> is a symbol of vitality, holds clues about your health, energy, and passion for life",
|
||||
"palms_say_about_point5_color": "Life line",
|
||||
"palms_say_about_point3": "<color> reflects intellectual pursuits and mental strengths",
|
||||
"palms_say_about_point3_color": "Head line",
|
||||
"palms_say_about_point7": "<color> reveals insights into your financial potential and approach towards wealth",
|
||||
"palms_say_about_point7_color": "Fate line",
|
||||
"palms_say_about_point1": "<color> can reveal insights into your romantic journey, mapping your heart's desires",
|
||||
"palms_say_about_point1_color": "Love line",
|
||||
"palms_say_about_point9": "Each finger is a pillar of your personality, from leadership and ambition to creativity and self- expression",
|
||||
"zodiac_sign": "Zodiac sign",
|
||||
"zodiac_signs_aries": "Aries",
|
||||
"zodiac_signs_taurus": "Taurus",
|
||||
"zodiac_signs_gemini": "Gemini",
|
||||
"zodiac_signs_cancer": "Cancer",
|
||||
"zodiac_signs_leo": "Leo",
|
||||
"zodiac_signs_virgo": "Virgo",
|
||||
"zodiac_signs_libra": "Libra",
|
||||
"zodiac_signs_scorpio": "Scorpio",
|
||||
"zodiac_signs_sagittarius": "Sagittarius",
|
||||
"zodiac_signs_capricorn": "Capricorn",
|
||||
"zodiac_signs_aquarius": "Aquarius",
|
||||
"zodiac_signs_pisces": "Pisces",
|
||||
"gender": "Gender",
|
||||
"male": "Male",
|
||||
"female": "Female",
|
||||
"date_of_birth": "Date of birth",
|
||||
"place_of_birth": "Place of birth",
|
||||
"reviews_username1": "Rebecca Bauman",
|
||||
"reviews_tagline1": "\"It’s changed my life!\"",
|
||||
"reviews_text1": "I'm thankful for this app and Akho! She's an excellent palm reader and astrologer—clear, thorough, and reassuring. I eagerly look forward to more sessions with her!",
|
||||
"reviews_username2": "Mika Ryan",
|
||||
"reviews_tagline2": "\"After years of seeking, I’ve finally found a true love.\"",
|
||||
"reviews_text2": "I was hesitant about whether it was really worth trying, but now I have no regrets and I'm enjoying my new relationships!",
|
||||
"reviews_username3": "Amanda Holmes",
|
||||
"reviews_tagline3": "\"I’ve found a job I really enjoy.\"",
|
||||
"reviews_text3": "Thanks to Vladana, I've finally discovered a clue about what my life's purpose really is and what kind of job resonates with me better!",
|
||||
"copyright": "<year>, Wit Apps LLC, <br>2108 N ST STE 5446 SACRAMENTO, CA 95816, US"
|
||||
},
|
||||
"save-off": {
|
||||
"title": "SAVE <discount>% OFF!",
|
||||
"instead": "<price> instead <oldPrice>",
|
||||
"instead-old-price": "of <oldPrice>",
|
||||
"trial-duration": "<days>-day trial",
|
||||
"discount-offer": "<discount>% off on your personalized plan",
|
||||
"button-trial": "GET <days>-day trial"
|
||||
},
|
||||
"secret-discount": {
|
||||
"title": "You get a secret discount!",
|
||||
"button-trial": "GET <days>-DAY TRIAL",
|
||||
"policy": "By continuing you agree that if you don't cancel prior to the end of the <days>-days trial, you will automatically be charged <price> for the introductory period of 14 days thereafter the standard rate of <price> every 14 days until you cancel in settings. Learn more about cancellation and refund policy in Subscription terms.",
|
||||
"secret-discount-table_title": "You get a secret discount!",
|
||||
"secret-discount-table_subtitle": "No pressure. Cancel anytime.",
|
||||
"secret-discount-table_discount-applied": "Secret discount applied!",
|
||||
"secret-discount-table_cost-after-trial": "Your cost per <days> days after trial:",
|
||||
"secret-discount-table_you-save": "You save <amount>",
|
||||
"secret-discount-table_total-today": "Total today"
|
||||
}
|
||||
}
|
||||
BIN
public/v1/email-marketing/circular-text-en.png
Normal file
BIN
public/v1/email-marketing/circular-text-en.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
BIN
public/v1/email-marketing/fire.png
Normal file
BIN
public/v1/email-marketing/fire.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
public/v1/email-marketing/gift.png
Normal file
BIN
public/v1/email-marketing/gift.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
23
public/v1/email-marketing/gift.svg
Normal file
23
public/v1/email-marketing/gift.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<svg width="280" height="241" viewBox="0 0 280 241" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M132.73 66.5252C119.181 61.0832 91.4158 51.9763 90.3052 34.3178C89.861 28.8759 90.4163 29.7643 94.9697 31.2081C98.9679 32.5408 102.744 35.5395 105.854 38.316C116.404 47.645 122.402 60.639 126.067 73.9662C129.731 87.7376 151.166 81.8514 147.39 68.08C141.948 47.5339 131.175 28.8759 113.739 16.3261C100.412 6.77495 81.5315 3.33208 71.5361 18.7694C61.7628 33.8736 71.7582 53.6422 83.0863 64.1929C95.3029 75.521 111.629 81.8514 126.844 87.9597C140.171 93.1796 145.946 71.6339 132.73 66.5252Z" fill="#FFD86E"/>
|
||||
<path d="M155.275 87.9596C170.935 81.7402 186.039 75.1877 199.033 64.1927C210.805 54.1973 220.023 33.4291 210.583 18.7692C200.255 2.88766 182.041 7.33008 168.381 16.3259C150.722 27.9872 140.06 48.089 134.729 68.0798C130.953 81.8513 152.388 87.7374 156.053 73.966C159.829 60.1946 165.937 48.3112 176.266 38.3158C179.264 35.3172 183.152 32.8739 187.15 31.208C192.036 29.2089 191.703 29.3199 191.814 34.3176C192.481 51.4208 161.717 61.6384 149.389 66.525C136.173 71.6338 141.948 93.1794 155.275 87.9596Z" fill="#FFD86E"/>
|
||||
<path d="M174.378 69.9678H149.945H30.0001V103.286H52.7673H149.945H174.378H235.905H252.12V69.9678H174.378Z" fill="#ED4C4C"/>
|
||||
<path d="M132.397 103.286H149.723V86.1829V69.1907H132.397V86.1829V103.286Z" fill="#FFD86E"/>
|
||||
<path d="M236.572 132.161H45.5488V241H236.572V132.161Z" fill="#ED4C4C"/>
|
||||
<mask id="mask0_1_7976" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="45" y="132" width="192" height="109">
|
||||
<path d="M236.572 132.161H45.5488V241H236.572V132.161Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1_7976)">
|
||||
<g opacity="0.4">
|
||||
<path d="M-20.5872 191.634V181.528H300.487V191.634H-20.5872Z" fill="#FAE5E5" stroke="#ED4C4C"/>
|
||||
<path d="M-20.5872 222.73V212.624H300.487V222.73H-20.5872Z" fill="#FAE5E5" stroke="#ED4C4C"/>
|
||||
</g>
|
||||
</g>
|
||||
<path d="M132.397 241H149.723V186.58V132.161H132.397V186.58V241Z" fill="#FFD86E"/>
|
||||
<path d="M279.325 7.77419C279.325 11.8294 276.106 15.0484 272.05 15.0484C267.995 15.0484 264.776 11.8294 264.776 7.77419C264.776 3.719 267.995 0.5 272.05 0.5C276.106 0.5 279.325 3.719 279.325 7.77419Z" fill="#FC6524" stroke="#ED4C4C"/>
|
||||
<path d="M238.288 56.6405C238.288 58.8617 236.511 60.6387 234.29 60.6387C232.069 60.6387 230.292 58.8617 230.292 56.6405C230.292 54.4193 232.069 52.6423 234.29 52.6423C236.511 52.6423 238.288 54.4193 238.288 56.6405Z" fill="#FFD86E"/>
|
||||
<path d="M118.344 118.834C118.344 121.055 116.567 122.832 114.346 122.832C112.125 122.832 110.348 121.055 110.348 118.834C110.348 116.613 112.125 114.836 114.346 114.836C116.567 114.836 118.344 116.613 118.344 118.834Z" fill="#C14040"/>
|
||||
<path d="M17.7232 56.6405C17.7232 60.9719 14.2803 64.4147 9.949 64.4147C5.61766 64.4147 2.1748 60.9719 2.1748 56.6405C2.1748 52.3092 5.61766 48.8663 9.949 48.8663C14.2803 48.8663 17.7232 52.3092 17.7232 56.6405Z" stroke="#ED4C4C" stroke-width="4" stroke-miterlimit="10"/>
|
||||
<path d="M266.164 138.825C266.164 141.712 263.832 144.045 260.944 144.045C258.057 144.045 255.725 141.712 255.725 138.825C255.725 135.937 258.057 133.605 260.944 133.605C263.832 133.605 266.164 135.937 266.164 138.825Z" stroke="#ED4C4C" stroke-width="4" stroke-miterlimit="10"/>
|
||||
<path d="M43.2666 124.276L34.1597 140.046L25.0527 124.276H43.2666Z" fill="#FFD86E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@ -19,7 +19,8 @@ export enum EPlacementKeys {
|
||||
"aura.placement.palmistry.redesign" = "aura.placement.palmistry.redesign",
|
||||
"aura.placement.chat" = "aura.placement.chat",
|
||||
"aura.placement.email.palmistry" = "aura.placement.email.palmistry",
|
||||
"aura.placement.email.palmistry.discount" = "aura.placement.email.palmistry.discount"
|
||||
"aura.placement.email.palmistry.discount" = "aura.placement.email.palmistry.discount",
|
||||
"aura.placement.email.compatibility.discount" = "aura.placement.email.compatibility.discount"
|
||||
}
|
||||
|
||||
export interface ResponseGetSuccess {
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import { createContext } from 'react'
|
||||
import { AuthToken, User } from '../api'
|
||||
import { IUser } from '@/api/resources/User'
|
||||
|
||||
export interface AuthContextValue {
|
||||
user: User.User | null
|
||||
token: AuthToken
|
||||
logout: () => void
|
||||
signUp: (token: AuthToken, user: User.User) => AuthToken
|
||||
signUp: (token: AuthToken, user: User.User, newUser?: IUser) => AuthToken
|
||||
}
|
||||
|
||||
export const AuthContext = createContext<AuthContextValue>({} as AuthContextValue)
|
||||
|
||||
@ -3,6 +3,7 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
import { actions, selectors } from "../store";
|
||||
import { AuthToken, User } from "../api";
|
||||
import { AuthContext } from "./AuthContext";
|
||||
import { IUser } from "@/api/resources/User";
|
||||
|
||||
export function AuthProvider({
|
||||
children,
|
||||
@ -11,7 +12,7 @@ export function AuthProvider({
|
||||
const token = useSelector(selectors.selectToken);
|
||||
const user = useSelector(selectors.selectUser);
|
||||
const signUp = useCallback(
|
||||
(token: AuthToken, user: User.User): AuthToken => {
|
||||
(token: AuthToken, user: User.User, newUser?: IUser): AuthToken => {
|
||||
dispatch(actions.token.update(token));
|
||||
dispatch(actions.user.update(user));
|
||||
dispatch(actions.form.addEmail(user.email));
|
||||
@ -23,6 +24,20 @@ export function AuthProvider({
|
||||
)
|
||||
);
|
||||
}
|
||||
if (newUser) {
|
||||
dispatch(actions.questionnaire.update({
|
||||
gender: newUser.profile.gender ?? undefined,
|
||||
birthPlace: newUser.profile.birthplace?.address ?? undefined,
|
||||
birthdate: newUser.profile.birthdate ?? undefined,
|
||||
partnerBirthPlace: newUser.partner?.birthplace?.address ?? undefined,
|
||||
partnerBirthdate: newUser.partner?.birthdate ?? undefined,
|
||||
partnerGender: newUser.partner?.gender ?? undefined,
|
||||
}))
|
||||
|
||||
dispatch(actions.user.update({
|
||||
username: newUser.profile.name ?? undefined,
|
||||
}));
|
||||
}
|
||||
return token;
|
||||
},
|
||||
[dispatch]
|
||||
|
||||
@ -242,21 +242,7 @@ function App(): JSX.Element {
|
||||
const { token } = await api.getRealToken({ token: jwtToken });
|
||||
const { user } = await api.getUser({ token });
|
||||
const { user: userMe } = await api.getMe({ token });
|
||||
signUp(token, user);
|
||||
|
||||
dispatch(actions.questionnaire.update({
|
||||
gender: userMe.profile.gender ?? undefined,
|
||||
birthPlace: userMe.profile.birthplace?.address ?? undefined,
|
||||
birthdate: userMe.profile.birthdate ?? undefined,
|
||||
partnerBirthPlace: userMe.partner?.birthplace?.address ?? undefined,
|
||||
partnerBirthdate: userMe.partner?.birthdate ?? undefined,
|
||||
partnerGender: userMe.partner?.gender ?? undefined,
|
||||
}))
|
||||
|
||||
dispatch(actions.user.update({
|
||||
username: userMe.profile.name ?? undefined,
|
||||
}));
|
||||
|
||||
signUp(token, user, userMe);
|
||||
} catch (error) {
|
||||
console.log("Error of get real token or get user: ");
|
||||
console.error(error);
|
||||
@ -331,7 +317,7 @@ function App(): JSX.Element {
|
||||
<CookieYesController isDelete={subscriptionStatus === "subscribed"} />
|
||||
}
|
||||
>
|
||||
<Route path={routes.client.auth()} element={<Auth />} />
|
||||
<Route path={routes.client.auth()} element={<Auth redirectUrl={routes.client.trialPaymentV1()} />} />
|
||||
<Route path="*" element={<ABDesignV1Routes />} />
|
||||
<Route path={`${chatsPrefix}/*`} element={<ChatsRoutes />} />
|
||||
<Route
|
||||
|
||||
@ -296,6 +296,7 @@ function ExpertChat() {
|
||||
placementKey={placementKey}
|
||||
onPaymentError={onPaymentError}
|
||||
onPaymentSuccess={onPaymentSuccess}
|
||||
isSinglePayment={true}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
|
||||
@ -13,10 +13,14 @@
|
||||
|
||||
.modal {
|
||||
max-height: calc(100dvh - 32px);
|
||||
height: 100%;
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
transform: translate(-50%, 0);
|
||||
// height: 100%;
|
||||
// top: auto;
|
||||
// bottom: 0;
|
||||
// transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
color: #2f2e37;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
|
||||
@ -1,12 +1,23 @@
|
||||
import { images } from "../../data";
|
||||
import TextWithEmoji from "../TextWithEmoji";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function AdviceFromAstrologer() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<TextWithEmoji text="1:1 Advice from your personal astrologer" emoji="sparkling-heart.svg" />
|
||||
<img className={styles.messages} src={images("messages.svg")} alt="messages" />
|
||||
<TextWithEmoji
|
||||
text={translate("marketing-landing.personal-astrologer-advice")}
|
||||
emoji="sparkling-heart.svg"
|
||||
/>
|
||||
<img
|
||||
className={styles.messages}
|
||||
src={images("messages.svg")}
|
||||
alt="messages"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import { useTranslations } from '@/hooks/translations';
|
||||
import { images } from '../../data';
|
||||
import styles from './styles.module.scss';
|
||||
import { ELocalesPlacement } from '@/locales';
|
||||
|
||||
interface CustomerCounterProps {
|
||||
count: number;
|
||||
}
|
||||
|
||||
function CustomerCounter({ count }: CustomerCounterProps) {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{/* <div className={styles.circularText}>
|
||||
@ -27,7 +31,7 @@ function CustomerCounter({ count }: CustomerCounterProps) {
|
||||
</text>
|
||||
</svg>
|
||||
</div> */}
|
||||
<img className={styles.circularText} src={images("circular-text.png")} alt="" />
|
||||
<img className={styles.circularText} src={images(translate("marketing-landing.circular-text-image"))} alt="" />
|
||||
<div className={styles.count}>{count}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
import { images } from "../../data";
|
||||
import TextWithEmoji from "../TextWithEmoji";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function FindingPartner() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<TextWithEmoji text="Finding the most compatible partner" emoji="revolving-hearts.svg" />
|
||||
<TextWithEmoji
|
||||
text={translate("marketing-landing.finding-compatible-partner")}
|
||||
emoji="revolving-hearts.svg"
|
||||
/>
|
||||
<img className={styles.smartphone} src={images("smartphone.svg")} alt="smartphone" />
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
import Title from "@/components/Title";
|
||||
import styles from "./styles.module.scss";
|
||||
import { images } from "../../data";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function GivePersonalizedPlan() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Title className={styles.title} variant="h2">
|
||||
So we decided to give you your
|
||||
personalized plan and access to
|
||||
the trial of our app <span>FOR FREE!</span>
|
||||
{translate("marketing-landing.personalized-plan-title")}
|
||||
<span> {translate("marketing-landing.personalized-plan-free")}</span>
|
||||
</Title>
|
||||
<img className={styles.stars} src={images("stars.svg")} alt="stars" />
|
||||
<img className={styles.zodiacs} src={images("zodiac-in-space.svg")} alt="stars" />
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import { images } from "../../data";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function GuaranteedSecurityPayments() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<img src={images("guaranteed.svg")} alt="guaranteed" />
|
||||
<p>Guaranteed security payments</p>
|
||||
<p>{translate("marketing-landing.guaranteed-security-payments")}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
import { images } from "../../data";
|
||||
import TextWithEmoji from "../TextWithEmoji";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function InsightsRelationship() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<TextWithEmoji
|
||||
text="Insights into your relationship patterns, and emotional and sexual needs"
|
||||
text={translate("marketing-landing.relationship-patterns-insights")}
|
||||
emoji="yellow-heart.svg"
|
||||
/>
|
||||
<img className={styles.smartphone} src={images("relationships.svg")} alt="relationships" />
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
import TextWithEmoji from "../TextWithEmoji";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function UnderstandingYourself() {
|
||||
function MoneyBackGuarantee() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<TextWithEmoji
|
||||
text="Money-back guarantee"
|
||||
text={translate("marketing-landing.money-back-guarantee-title")}
|
||||
emoji="guarantee.svg"
|
||||
/>
|
||||
<p className={styles.text}>
|
||||
We are convinced that we will help you get a deeper understanding of
|
||||
your partner and how you can improve your relationship.
|
||||
After all of our stellar customer reviews, we are ready to return your money
|
||||
if you feel that this report doesn’t provide any value. Find more
|
||||
about applicable limitations in our Money-back policy.
|
||||
{translate("marketing-landing.money-back-guarantee-text")}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UnderstandingYourself
|
||||
export default MoneyBackGuarantee
|
||||
@ -1,5 +1,7 @@
|
||||
import { combineStyles } from "@/services/styles";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
interface PriceComparisonProps {
|
||||
oldPrice: number | string;
|
||||
@ -7,11 +9,13 @@ interface PriceComparisonProps {
|
||||
}
|
||||
|
||||
function PriceComparison({ oldPrice, newPrice }: PriceComparisonProps) {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={combineStyles(styles.priceCard, styles.oldPriceCard)}>
|
||||
<div className={styles.header}>
|
||||
OLD PRICE
|
||||
{translate("marketing-landing.old-price-label")}
|
||||
</div>
|
||||
<div className={styles.priceContent}>
|
||||
<div className={styles.price}>{oldPrice}</div>
|
||||
@ -20,7 +24,7 @@ function PriceComparison({ oldPrice, newPrice }: PriceComparisonProps) {
|
||||
|
||||
<div className={combineStyles(styles.priceCard, styles.newPriceCard)}>
|
||||
<div className={styles.header}>
|
||||
NEW PRICE
|
||||
{translate("marketing-landing.new-price-label")}
|
||||
</div>
|
||||
<div className={styles.priceContent}>
|
||||
<div className={styles.price}>${newPrice}</div>
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import { combineStyles } from '@/services/styles';
|
||||
import CountdownTimer from '../CountdownTimer';
|
||||
import styles from './styles.module.scss';
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { addCurrency, ELocalesPlacement } from "@/locales";
|
||||
import { Currency } from '@/components/PaymentTable';
|
||||
|
||||
interface PricingSummaryProps {
|
||||
totalToday: number | string;
|
||||
@ -8,6 +11,7 @@ interface PricingSummaryProps {
|
||||
discountedPrice: number | string;
|
||||
trialDuration: number | string;
|
||||
saveText: string;
|
||||
currency: Currency;
|
||||
}
|
||||
|
||||
function PricingSummary({
|
||||
@ -15,21 +19,24 @@ function PricingSummary({
|
||||
originalPrice,
|
||||
discountedPrice,
|
||||
trialDuration,
|
||||
saveText
|
||||
saveText,
|
||||
currency
|
||||
}: PricingSummaryProps) {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.table}>
|
||||
<div className={combineStyles(styles.row, styles.totalRow)}>
|
||||
<span className={styles.label}>Total today:</span>
|
||||
<span className={styles.label}>{translate("special-offer.pricing-summary-total-today")}</span>
|
||||
<span className={styles.price}>${totalToday}</span>
|
||||
</div>
|
||||
<div className={combineStyles(styles.row, styles.codeRow)}>
|
||||
<span className={styles.codeApplied}>Code applied!</span>
|
||||
<span className={styles.codeApplied}>{translate("special-offer.pricing-summary-code-applied")}</span>
|
||||
<CountdownTimer />
|
||||
</div>
|
||||
<div className={combineStyles(styles.row, styles.costRow)}>
|
||||
<span className={styles.label}>Your cost per 2 weeks after trial</span>
|
||||
<span className={styles.label}>{translate("special-offer.pricing-summary-cost-after-trial")}</span>
|
||||
<div className={styles.prices}>
|
||||
<span className={styles.originalPrice}>${originalPrice}</span>
|
||||
<span className={styles.discountedPrice}>${discountedPrice}</span>
|
||||
@ -40,7 +47,10 @@ function PricingSummary({
|
||||
</div>
|
||||
</div>
|
||||
<p className={styles.description}>
|
||||
You will be charged only ${totalToday} for your {trialDuration}-day trial. Subscription renews automatically until cancelled. You can cancel at any time before the end of the trial.
|
||||
{translate("special-offer.pricing-summary-trial-description", {
|
||||
totalToday: addCurrency(totalToday, currency),
|
||||
trialDuration: trialDuration
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -6,7 +6,7 @@ import { ELocalesPlacement } from "@/locales";
|
||||
import { images } from "../../data";
|
||||
|
||||
function Reviews() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
const reviews: IReviewProps[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
@ -14,21 +14,21 @@ function Reviews() {
|
||||
username: "@andi36_11",
|
||||
date: "06/05/2024",
|
||||
gender: "male",
|
||||
text: "Horoscope tells realistic facts about day to day life, which can be easily relatable. It shows direction.",
|
||||
text: translate("marketing-landing.review-1"),
|
||||
},
|
||||
{
|
||||
avatar: images("aramaska.png"),
|
||||
username: "@aramaska",
|
||||
date: "04/17/2024",
|
||||
gender: "female",
|
||||
text: "It makes me feel safe, seeing, warm and smart.",
|
||||
text: translate("marketing-landing.review-2"),
|
||||
},
|
||||
{
|
||||
avatar: images("patterso.png"),
|
||||
username: "@patterso",
|
||||
date: "03/01/2024",
|
||||
gender: "female",
|
||||
text: "I love that we have the “my profile” option. I love learning about myself.",
|
||||
text: translate("marketing-landing.review-3"),
|
||||
},
|
||||
],
|
||||
[translate]
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
import Title from "@/components/Title";
|
||||
import styles from "./styles.module.scss";
|
||||
import { images } from "../../data";
|
||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||
import { addCurrency, ELocalesPlacement } from "@/locales";
|
||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||
import { Currency } from "@/components/PaymentTable";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
|
||||
const placementKey = EPlacementKeys["aura.placement.email.compatibility.discount"]
|
||||
|
||||
const getPrice = (price: number, currency: Currency) => {
|
||||
if (price % 100 === 0) {
|
||||
return addCurrency(price / 100, currency);
|
||||
}
|
||||
return addCurrency(
|
||||
(price / 100).toFixed(2),
|
||||
currency
|
||||
);
|
||||
}
|
||||
|
||||
function SecretDiscountTable() {
|
||||
const { products, currency, getText } = usePaywall({
|
||||
placementKey,
|
||||
localesPlacement: ELocalesPlacement.EmailMarketingCompatibilityV1,
|
||||
});
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
const activeProduct = products[0];
|
||||
const price = activeProduct?.price || 0;
|
||||
const trialPrice = activeProduct?.trialPrice || 0;
|
||||
const trialDuration = activeProduct?.trialDuration || 7;
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Title className={styles.title} variant="h3">
|
||||
{translate("secret-discount.secret-discount-table_title")}
|
||||
</Title>
|
||||
<p className={styles.subtitle}>
|
||||
{translate("secret-discount.secret-discount-table_subtitle")}
|
||||
</p>
|
||||
<div className={styles.applied}>
|
||||
<img src={images("gift.png")} alt="Gift" />
|
||||
<Title className={styles.title} variant="h4">
|
||||
{translate("secret-discount.secret-discount-table_discount-applied")}
|
||||
</Title>
|
||||
<span className={styles["old-discount"]}>{getText("old.discount")}</span>
|
||||
<span className={styles["new-discount"]}>{getText("new.discount")}</span>
|
||||
</div>
|
||||
<div className={`${styles["grid-line"]} ${styles["days-14"]}`}>
|
||||
<p>{translate("secret-discount.secret-discount-table_cost-after-trial", { days: trialDuration })}</p>
|
||||
<span className={styles["old-price"]}>{addCurrency(Number(getText("old.price")), currency)}</span>
|
||||
<span className={styles["new-price"]}>{getPrice(price, currency)}</span>
|
||||
</div>
|
||||
<div className={`${styles["grid-line"]} ${styles["save"]}`}>
|
||||
<p>{translate("secret-discount.secret-discount-table_you-save", { amount: addCurrency(Number(getText("save")), currency) })}</p>
|
||||
</div>
|
||||
<hr />
|
||||
<div className={`${styles["grid-line"]} ${styles["total-today"]}`}>
|
||||
<p>{translate("secret-discount.secret-discount-table_total-today")}</p>
|
||||
<span>{getPrice(trialPrice, currency)}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SecretDiscountTable
|
||||
@ -0,0 +1,110 @@
|
||||
.container {
|
||||
background-color: #fff;
|
||||
border-radius: 13px;
|
||||
width: calc(100% + 24px);
|
||||
padding: 16px 0 22px;
|
||||
box-shadow: 2px 11px 17px -1px rgba(0, 0, 0, 0.13);
|
||||
margin-top: 42px;
|
||||
color: #363636;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.applied {
|
||||
width: 100%;
|
||||
background-color: #293D68;
|
||||
padding: 7px 10px;
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
gap: 10px;
|
||||
color: #fff;
|
||||
|
||||
&>img {
|
||||
width: 17px;
|
||||
}
|
||||
|
||||
&>.title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
&>.old-discount {
|
||||
font-size: 15px;
|
||||
color: #B2B2B2;
|
||||
text-decoration: line-through;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
&>.new-discount {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
line-height: 19px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-line {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 22px 22px;
|
||||
align-items: center;
|
||||
gap: 28px;
|
||||
margin-top: 8px;
|
||||
padding: 0 24px 0 10px;
|
||||
|
||||
&>p {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 130%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.days-14>span {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 130%;
|
||||
|
||||
&.old-price {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
&.save {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
&.total-today {
|
||||
|
||||
&>p,
|
||||
span {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 130%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
background-color: #363636;
|
||||
margin-top: 6px;
|
||||
}
|
||||
@ -1,15 +1,19 @@
|
||||
import styles from './styles.module.scss';
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
interface StatisticsBannerProps {
|
||||
count: number;
|
||||
}
|
||||
|
||||
function StatisticsBanner({ count }: StatisticsBannerProps) {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<span className={styles.text}>
|
||||
Last week alone <b>{count}+ people</b> got this relationship guide
|
||||
{translate("marketing-landing.statistics-banner-text", { b: <b>{translate("marketing-landing.statistics-banner-count", { count })}</b> })}
|
||||
</span>
|
||||
<div className={styles.infoIcon}>
|
||||
<svg width="27" height="27" viewBox="0 0 27 27" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
import { images } from "../../data";
|
||||
import TextWithEmoji from "../TextWithEmoji";
|
||||
import styles from "./styles.module.scss";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function UnderstandingYourself() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<TextWithEmoji
|
||||
text="Better understanding of yourself and your needs"
|
||||
text={translate("marketing-landing.better-understanding-yourself")}
|
||||
emoji="relieved-face.svg"
|
||||
/>
|
||||
<img className={styles.hand} src={images("hand-with-eye.svg")} alt="hand with eye" />
|
||||
|
||||
18
src/components/EmailMarketing/v1/images/SVG/Blob/index.tsx
Normal file
18
src/components/EmailMarketing/v1/images/SVG/Blob/index.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { SVGProps } from "react"
|
||||
|
||||
function Blob(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width="216" height="150" viewBox="0 0 216 150" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M303.878 -65.5499C282.778 -127.95 170.378 -59.0499 127.378 -68.8499C79.078 -79.7499 25.378 -76.5499 4.67803 -24.4499C-6.12197 2.95011 -0.321915 63.4501 44.4781 56.3501C67.8781 52.6501 89.578 27.2501 113.378 32.9501C140.878 39.4501 139.178 66.0501 145.778 86.2501C160.678 132.15 220.678 171.55 264.478 134.25C289.978 112.55 288.078 74.4501 290.278 45.1501C292.778 11.3501 311.978 -21.9499 306.178 -56.3499C305.645 -59.6832 304.878 -62.7499 303.878 -65.5499Z" fill="url(#paint0_linear_1_7947)" />
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1_7947" x1="227.33" y1="-166.848" x2="364.777" y2="7.71622" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#E3FEF9" />
|
||||
<stop offset="1" stopColor="#BCF0FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default Blob
|
||||
17
src/components/EmailMarketing/v1/images/SVG/Blob2/index.tsx
Normal file
17
src/components/EmailMarketing/v1/images/SVG/Blob2/index.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { SVGProps } from "react"
|
||||
|
||||
function Blob2(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width="197" height="173" viewBox="0 0 197 173" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M376.745 8.31103C313.484 -9.05387 250.755 2.50459 205.527 52.2953C171.122 90.2852 159.804 138.706 100.572 145.714C42.2871 152.666 -19.4446 181.366 6.01194 257.137C34.4467 341.884 139.028 318.246 203.745 298.11C229.74 289.96 254.95 280.876 281.311 274.066C329.257 261.653 377.408 245.774 415.485 213.013C440.246 191.695 461.108 118.086 457.281 86.0026C452.562 46.4328 423.44 24.1359 387.627 11.7445C384.09 10.5207 380.463 9.37619 376.745 8.31103Z" fill="url(#paint0_linear_1_7945)" />
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1_7945" x1="237.028" y1="-87.8634" x2="464.157" y2="55.102" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#FEF5E3" />
|
||||
<stop offset="1" stopColor="#BCFFBE" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default Blob2
|
||||
18
src/components/EmailMarketing/v1/images/SVG/Blob3/index.tsx
Normal file
18
src/components/EmailMarketing/v1/images/SVG/Blob3/index.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { SVGProps } from "react"
|
||||
|
||||
function Blob3(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg width="147" height="285" viewBox="0 0 147 285" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M-29.9172 282.953C56.8893 300.323 52.2376 123.186 92.5422 79.7173C137.689 30.8185 170.054 -33.8408 123.328 -92.9459C98.6956 -123.929 24.3829 -157.832 2.5382 -100.912C-8.88165 -71.187 6.0972 -28.8594 -16.5319 -4.98539C-42.5788 22.658 -72.3955 2.80487 -100.341 -3.08616C-163.777 -16.5852 -249.958 26.7819 -235.981 102.831C-227.863 147.096 -182.242 170.486 -149.619 192.736C-111.96 218.358 -86.1061 263.084 -42.1707 279.449C-37.9326 281.068 -33.8482 282.236 -29.9172 282.953Z" fill="url(#paint0_linear_1_8023)" />
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1_8023" x1="139.426" y1="261.917" x2="-156.124" y2="304.57" gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#E3FEF9" />
|
||||
<stop offset="1" stopColor="#BCF0FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default Blob3
|
||||
21
src/components/EmailMarketing/v1/images/SVG/Blob4/index.tsx
Normal file
21
src/components/EmailMarketing/v1/images/SVG/Blob4/index.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { SVGProps } from "react"
|
||||
|
||||
function Blob4(props: SVGProps<SVGSVGElement>) {
|
||||
const width = props.width ? Number(props.width) : 419;
|
||||
const height = props.height ? Number(props.height) : 193;
|
||||
return (
|
||||
<svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d={`M${width + 78} 37.2239C${width * .58} 116.789 ${width * .35} -66.6269 ${width * -0.186} 27.6975V${height}H${width}V37.2239Z`} fill="url(#paint0_linear_1_8022)" />
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1_8022" x1={1.186 * width} y1={height} x2={-0.186 * width} y2={height} gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#FFA1BA" />
|
||||
<stop offset="1" stopColor="#9A55FF" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default Blob4
|
||||
@ -21,23 +21,11 @@ import routes from "@/routes";
|
||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||
import BlurComponent from "@/components/BlurComponent";
|
||||
|
||||
const features = [
|
||||
{
|
||||
text: "Unlimited daily / weekly / monthly / yearly horoscopes"
|
||||
},
|
||||
{
|
||||
text: "Astrology lessons and articles inside the app"
|
||||
},
|
||||
{
|
||||
text: "Beauty / health / travel and more calendars"
|
||||
},
|
||||
{
|
||||
text: "Compatibility check with zodiac signs inside the app"
|
||||
}
|
||||
];
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function MarketingLanding() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
const navigate = useNavigate();
|
||||
const { products, getText } = usePaywall({
|
||||
placementKey: EPlacementKeys["aura.placement.email.marketing"],
|
||||
@ -47,6 +35,21 @@ function MarketingLanding() {
|
||||
navigate(routes.client.emailMarketingV1SpecialOffer());
|
||||
}
|
||||
|
||||
const features = [
|
||||
{
|
||||
text: translate("marketing-landing.plan-includes-unlimited-horoscopes")
|
||||
},
|
||||
{
|
||||
text: translate("marketing-landing.plan-includes-astrology-lessons")
|
||||
},
|
||||
{
|
||||
text: translate("marketing-landing.plan-includes-lifestyle-calendars")
|
||||
},
|
||||
{
|
||||
text: translate("marketing-landing.plan-includes-compatibility-check")
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{/* Background elements */}
|
||||
@ -58,25 +61,27 @@ function MarketingLanding() {
|
||||
<Title className={combineStyles(styles.title, styles.titleSpecialOffer)} variant="h1">
|
||||
<>
|
||||
<img className={combineStyles(styles.backgroundElement, styles.backgroundElement3)} src={images("gift-small.png")} alt="Gift" />
|
||||
Special Offer!
|
||||
{translate("marketing-landing.title")}
|
||||
</>
|
||||
</Title>
|
||||
<p className={combineStyles(styles.description, styles.descriptionSpecialOffer)}>
|
||||
<svg className={combineStyles(styles.backgroundElement, styles.backgroundElement4)} width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="9" cy="9" r="9" fill="#FFDC60" />
|
||||
</svg>
|
||||
Everything for free. Trial include!
|
||||
{translate("marketing-landing.description")}
|
||||
</p>
|
||||
<Title className={combineStyles(styles.title, styles.titleHey)} variant="h1">
|
||||
Hey, Sun <span>👋</span>
|
||||
{translate("marketing-landing.title-hey")}
|
||||
</Title>
|
||||
<p className={combineStyles(styles.description, styles.descriptionHey)}>Your wellness and happiness are key for us!</p>
|
||||
<p className={combineStyles(styles.description, styles.descriptionHey)}>
|
||||
{translate("marketing-landing.description-hey")}
|
||||
</p>
|
||||
<GivePersonalizedPlan />
|
||||
<Title className={combineStyles(styles.title, styles.titleHighlights)} variant="h1">
|
||||
<svg className={combineStyles(styles.backgroundElement, styles.backgroundElement5)} width="31" height="31" viewBox="0 0 31 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="15.5" cy="15.5" r="15.5" fill="#DEBA00" />
|
||||
</svg>
|
||||
Highlights of your plan:
|
||||
{translate("marketing-landing.title-highlights")}
|
||||
</Title>
|
||||
<div className={styles["relative-container"]}>
|
||||
<svg className={combineStyles(styles.backgroundElement, styles.backgroundElement6)} width="169" height="169" viewBox="0 0 169 169" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
@ -125,7 +130,10 @@ function MarketingLanding() {
|
||||
<UnderstandingYourself />
|
||||
</div>
|
||||
<PriceComparison oldPrice={getText("text.old.price", {}) as string} newPrice={`${((products[0]?.trialPrice || 0) / 100).toFixed(2)}`} />
|
||||
<PlanIncludes title="Your plan also includes:" features={features} />
|
||||
<PlanIncludes
|
||||
title={translate("marketing-landing.plan-includes-title")}
|
||||
features={features}
|
||||
/>
|
||||
<Reviews />
|
||||
<CustomerCounter count={267583} />
|
||||
<StatisticsBanner count={2000} />
|
||||
@ -175,7 +183,7 @@ function MarketingLanding() {
|
||||
<div className={styles.buttonContainer}>
|
||||
<BlurComponent isActiveBlur={true}>
|
||||
<Button className={styles.buttonContinue} onClick={handleContinue}>
|
||||
Continue
|
||||
{translate("marketing-landing.button-continue")}
|
||||
</Button>
|
||||
</BlurComponent>
|
||||
</div>
|
||||
|
||||
71
src/components/EmailMarketing/v1/pages/SaveOff/index.tsx
Normal file
71
src/components/EmailMarketing/v1/pages/SaveOff/index.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
import styles from "./styles.module.scss";
|
||||
import Blob from "../../images/SVG/Blob";
|
||||
import { images } from "../../data";
|
||||
import Title from "@/components/Title";
|
||||
import Button from "../../components/Button";
|
||||
import Blob2 from "../../images/SVG/Blob2";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||
import { addCurrency, ELocalesPlacement } from "@/locales";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import Header from "@/components/pages/ABDesign/v1/components/Header";
|
||||
|
||||
const placementKey = EPlacementKeys["aura.placement.email.compatibility.discount"]
|
||||
|
||||
function SaveOff() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
const { products, currency, getText } = usePaywall({
|
||||
placementKey,
|
||||
localesPlacement: ELocalesPlacement.EmailMarketingCompatibilityV1,
|
||||
});
|
||||
const activeProduct = products[0]
|
||||
const price = (activeProduct?.price || 0) / 100
|
||||
const trialDuration = activeProduct?.trialDuration || 7
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleNext = () => {
|
||||
navigate(routes.client.emailMarketingV1SecretDiscount());
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Header
|
||||
className={styles.header}
|
||||
classNameTitle={styles["header-title"]}
|
||||
isBackButtonVisible={true}
|
||||
/>
|
||||
<Blob className={styles.blob} />
|
||||
<Blob2 className={styles.blob2} />
|
||||
<img className={styles.gift} src={images("gift.svg")} alt="gift" />
|
||||
<Title className={styles.title}>
|
||||
{translate("save-off.title", { discount: getText("discount") })}
|
||||
</Title>
|
||||
<p className={styles.description}>
|
||||
{translate("save-off.instead", {
|
||||
price: <span className={styles.price}>{addCurrency(price, currency)}</span>,
|
||||
oldPrice: <span className={styles.discount}>
|
||||
{translate("save-off.instead-old-price", {
|
||||
oldPrice: addCurrency(getText("full.price") as string, currency)
|
||||
})}
|
||||
</span>
|
||||
})}
|
||||
</p>
|
||||
<p className={styles.point} style={{ marginTop: 12 }}>
|
||||
<img src={images("fire.png")} alt="fire" />
|
||||
{translate("save-off.trial-duration", { days: trialDuration })}
|
||||
</p>
|
||||
<p className={styles.point}>
|
||||
<img src={images("gift.png")} alt="gift" />
|
||||
{translate("save-off.discount-offer", { discount: getText("discount") })}
|
||||
</p>
|
||||
<Button className={styles.button} onClick={handleNext}>
|
||||
{translate("save-off.button-trial", { days: trialDuration })}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SaveOff
|
||||
@ -0,0 +1,85 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px 26px;
|
||||
min-height: 100dvh;
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 8px 0 30px;
|
||||
|
||||
&>button {
|
||||
margin-left: -12px;
|
||||
}
|
||||
}
|
||||
|
||||
.blob {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.blob2 {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.gift {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 32px;
|
||||
margin-bottom: 14px;
|
||||
font-size: 32px;
|
||||
line-height: 26px;
|
||||
font-weight: 600;
|
||||
color: #275CA7;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
color: #363636;
|
||||
|
||||
&>.price {
|
||||
font-weight: 600;
|
||||
background: linear-gradient(90deg, #FFA1BA 0%, #9A55FF 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
&>.discount {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
|
||||
.point {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 21px;
|
||||
font-weight: 600;
|
||||
color: #2c2c2c;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 54px;
|
||||
margin-top: 16px;
|
||||
font-size: 18px;
|
||||
line-height: 21px;
|
||||
font-weight: 600;
|
||||
}
|
||||
109
src/components/EmailMarketing/v1/pages/SecretDiscount/index.tsx
Normal file
109
src/components/EmailMarketing/v1/pages/SecretDiscount/index.tsx
Normal file
@ -0,0 +1,109 @@
|
||||
import styles from "./styles.module.scss";
|
||||
import Blob3 from "../../images/SVG/Blob3";
|
||||
import Blob4 from "../../images/SVG/Blob4";
|
||||
import Title from "@/components/Title";
|
||||
import SecretDiscountTable from "../../components/SecretDiscountTable";
|
||||
import Button from "../../components/Button";
|
||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||
import { addCurrency, ELocalesPlacement } from "@/locales";
|
||||
import Modal from "@/components/Modal";
|
||||
import { useEffect, useState } from "react";
|
||||
import PaymentForm from "@/components/Payment/nmi/PaymentForm";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { actions } from "@/store";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import Header from "@/components/pages/ABDesign/v1/components/Header";
|
||||
import { useDynamicSize } from "@/hooks/useDynamicSize";
|
||||
|
||||
const placementKey = EPlacementKeys["aura.placement.email.compatibility.discount"]
|
||||
|
||||
function SecretDiscount() {
|
||||
const { width, elementRef } = useDynamicSize({ defaultWidth: 560 });
|
||||
const { height, elementRef: policyContainerRef } = useDynamicSize<HTMLDivElement>({ defaultWidth: 560, defaultHeight: 193 });
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const { products, currency } = usePaywall({
|
||||
placementKey,
|
||||
localesPlacement: ELocalesPlacement.EmailMarketingCompatibilityV1,
|
||||
});
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
const activeProduct = products[0];
|
||||
const price = (activeProduct?.price || 0) / 100;
|
||||
const trialDuration = activeProduct?.trialDuration || 7;
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeProduct) return;
|
||||
dispatch(actions.payment.update({
|
||||
activeProduct
|
||||
}))
|
||||
}, [activeProduct])
|
||||
|
||||
const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false);
|
||||
|
||||
|
||||
const onPaymentSuccess = () => {
|
||||
return navigate(routes.client.paymentSuccess())
|
||||
}
|
||||
|
||||
const onModalClosed = () => {
|
||||
setIsPaymentModalOpen(false);
|
||||
}
|
||||
|
||||
const onPaymentError = () => {
|
||||
return navigate(routes.client.paymentFail())
|
||||
}
|
||||
|
||||
const openPaymentModal = () => {
|
||||
setIsPaymentModalOpen(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={styles.container} ref={elementRef} style={{
|
||||
paddingBottom: `${height + 42}px`
|
||||
}}>
|
||||
<Header
|
||||
className={styles.header}
|
||||
classNameTitle={styles["header-title"]}
|
||||
isBackButtonVisible={true}
|
||||
/>
|
||||
{activeProduct && (
|
||||
<Modal
|
||||
containerClassName={styles.modal}
|
||||
open={isPaymentModalOpen}
|
||||
onClose={onModalClosed}
|
||||
type="hidden"
|
||||
>
|
||||
<PaymentForm
|
||||
placementKey={placementKey}
|
||||
onPaymentError={onPaymentError}
|
||||
onPaymentSuccess={onPaymentSuccess}
|
||||
/>
|
||||
</Modal>
|
||||
)}
|
||||
<Blob3 className={styles.blob3} />
|
||||
<Title className={styles.title} variant="h1">
|
||||
{translate("secret-discount.title")}
|
||||
</Title>
|
||||
<SecretDiscountTable />
|
||||
<Button className={styles.button} onClick={openPaymentModal}>
|
||||
{translate("secret-discount.button-trial", { days: trialDuration })}
|
||||
</Button>
|
||||
|
||||
<div className={styles["policy-container"]} ref={policyContainerRef}>
|
||||
<p className={styles.policy}>
|
||||
{translate("secret-discount.policy", {
|
||||
days: trialDuration,
|
||||
price: addCurrency(price, currency)
|
||||
})}
|
||||
</p>
|
||||
<Blob4 className={styles.blob4} width={width} height={height + 34} />
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default SecretDiscount
|
||||
@ -0,0 +1,75 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px 26px;
|
||||
min-height: 100dvh;
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 8px 0 30px;
|
||||
|
||||
&>button {
|
||||
margin-left: -12px;
|
||||
}
|
||||
}
|
||||
|
||||
.blob3 {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding: 15px 0;
|
||||
background-color: #F096C4;
|
||||
margin: 0;
|
||||
margin-top: 32px;
|
||||
line-height: 100%;
|
||||
width: calc(100% + 52px);
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.button {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
padding: 16px 0;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.policy-container {
|
||||
position: absolute;
|
||||
width: calc(100%);
|
||||
max-width: 560px;
|
||||
height: fit-content;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
&>.policy {
|
||||
width: 100%;
|
||||
margin: 34px 0;
|
||||
padding: 0 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 130%;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.blob4 {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,8 @@ import PaymentForm from "@/components/Payment/nmi/PaymentForm";
|
||||
import routes from "@/routes";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import BlurComponent from "@/components/BlurComponent";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { addCurrency, ELocalesPlacement } from "@/locales";
|
||||
|
||||
const placementKey = EPlacementKeys["aura.placement.email.marketing"];
|
||||
|
||||
@ -20,8 +22,9 @@ function SpecialOffer() {
|
||||
const navigate = useNavigate();
|
||||
const [isOpenPaymentModal, setIsOpenPaymentModal] = useState<boolean>(false);
|
||||
const activeProduct = useSelector(selectors.selectActiveProduct);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingCompatibilityV1);
|
||||
|
||||
const { products, getText } = usePaywall({
|
||||
const { products, currency, getText } = usePaywall({
|
||||
placementKey,
|
||||
});
|
||||
|
||||
@ -39,6 +42,7 @@ function SpecialOffer() {
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setIsOpenPaymentModal(false);
|
||||
return navigate(routes.client.emailMarketingV1SaveOff())
|
||||
};
|
||||
|
||||
const onPaymentError = () => {
|
||||
@ -67,14 +71,14 @@ function SpecialOffer() {
|
||||
)}
|
||||
<div className={styles.container}>
|
||||
<Title className={styles.title} variant="h1">
|
||||
Special Offer!
|
||||
{translate("special-offer.title")}
|
||||
</Title>
|
||||
<div className={styles.content}>
|
||||
<Title className={styles.contentTitle} variant="h2">
|
||||
Start your {trialDuration}-day trial
|
||||
{translate("special-offer.start-trial", { days: trialDuration })}
|
||||
</Title>
|
||||
<p className={styles.contentDescription}>
|
||||
No pressure. Cancel anytime
|
||||
{translate("special-offer.cancel-anytime")}
|
||||
</p>
|
||||
<PricingSummary
|
||||
totalToday={trialPrice}
|
||||
@ -82,14 +86,18 @@ function SpecialOffer() {
|
||||
discountedPrice={price}
|
||||
trialDuration={trialDuration}
|
||||
saveText={getText("text.save") as string}
|
||||
currency={currency}
|
||||
/>
|
||||
<p className={styles.contentPolicy}>
|
||||
By continuing you agree that if you don't cancel prior to the end of the {trialDuration}-days trial, you will automatically be charged ${price} every 2 weeks until you cancel in settings. Learn more about cancellation and refund policy in Subscription terms
|
||||
{translate("special-offer.policy", {
|
||||
days: trialDuration,
|
||||
price: addCurrency(price, currency)
|
||||
})}
|
||||
</p>
|
||||
<div className={styles.buttonContainer}>
|
||||
<BlurComponent isActiveBlur={true}>
|
||||
<Button className={styles.button} onClick={openPaymentModal}>
|
||||
Continue
|
||||
{translate("special-offer.button-continue")}
|
||||
</Button>
|
||||
</BlurComponent>
|
||||
</div>
|
||||
|
||||
@ -6,8 +6,11 @@ import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function PalmIsReady() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.container} style={{
|
||||
"--palmistry-prefix-girl-image": `url(${palmistryV1Prefix}/trial-payment/girl.png)`
|
||||
} as React.CSSProperties}>
|
||||
<Title variant="h2" className={styles.title}>
|
||||
{translate("/trial-payment.palm_is_ready.title", {
|
||||
color: (
|
||||
@ -20,9 +23,6 @@ function PalmIsReady() {
|
||||
className={styles["girl-image"]}
|
||||
src={`${palmistryV1Prefix}/trial-payment/girl.png`}
|
||||
alt="Smile girl"
|
||||
style={{
|
||||
shapeOutside: `url(${palmistryV1Prefix}/trial-payment/girl.png)`,
|
||||
}}
|
||||
/>
|
||||
<p className={styles.description}>
|
||||
{translate("/trial-payment.palm_is_ready.description")}
|
||||
|
||||
@ -62,4 +62,10 @@
|
||||
margin-top: 33px;
|
||||
margin-right: -10px;
|
||||
width: 191px;
|
||||
shape-outside: var(--palmistry-prefix-girl-image);
|
||||
-webkit-shape-outside: var(--palmistry-prefix-girl-image);
|
||||
shape-image-threshold: 0;
|
||||
-webkit-shape-image-threshold: 0;
|
||||
shape-margin: 10px;
|
||||
-webkit-shape-margin: 10px;
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
export const answerTimeOut = 1000;
|
||||
export const answerTimeOut = 600;
|
||||
|
||||
@ -38,12 +38,12 @@ function Birthdate() {
|
||||
return age;
|
||||
};
|
||||
|
||||
const handleNext = async () => {
|
||||
const handleNext = () => {
|
||||
const age = getAge();
|
||||
metricService.userParams({
|
||||
age,
|
||||
});
|
||||
await updateSession(
|
||||
updateSession(
|
||||
{
|
||||
profile: {
|
||||
birthdate: `${birthdate} 00:00`,
|
||||
|
||||
@ -61,7 +61,7 @@ function ElementResonates() {
|
||||
|
||||
const handleClick = async (id: IAnswersSessionPalmistry["element_resonates"]) => {
|
||||
dispatch(actions.palmistryV1Answers.update({ elementResonates: id }));
|
||||
await updateSession({
|
||||
updateSession({
|
||||
answers: {
|
||||
element_resonates: id,
|
||||
},
|
||||
|
||||
@ -74,7 +74,7 @@ function Email() {
|
||||
};
|
||||
|
||||
const authorize = async () => {
|
||||
await updateSession(
|
||||
updateSession(
|
||||
{
|
||||
profile: {
|
||||
name,
|
||||
|
||||
@ -72,7 +72,7 @@ function FavoriteColor() {
|
||||
|
||||
const handleClick = async (id: IAnswersSessionPalmistry["favorite_color"]) => {
|
||||
dispatch(actions.palmistryV1Answers.update({ favoriteColor: id }));
|
||||
await updateSession({
|
||||
updateSession({
|
||||
answers: {
|
||||
favorite_color: id,
|
||||
},
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import styles from "./styles.module.scss";
|
||||
import Title from "@/components/Title";
|
||||
import ChooseGender from "@/components/pages/ABDesign/v1/components/ChooseGender";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { actions, selectors } from "@/store";
|
||||
import { Gender } from "@/data";
|
||||
@ -19,6 +18,7 @@ import { ELottieKeys, useLottie } from "@/hooks/lottie/useLottie";
|
||||
import { useSession } from "@/hooks/session/useSession";
|
||||
import { EGender, ESourceAuthorization } from "@/api/resources/User";
|
||||
import AlreadyHaveAccount from "@/components/ui/AlreadyHaveAccount";
|
||||
import Answer from "../../components/Answer";
|
||||
|
||||
function GenderPalmistry() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||
@ -28,6 +28,8 @@ function GenderPalmistry() {
|
||||
const { checked: privacyPolicyChecked } = useSelector(
|
||||
selectors.selectPrivacyPolicy
|
||||
);
|
||||
console.log(privacyPolicyChecked);
|
||||
|
||||
const { gender } = useSelector(selectors.selectQuestionnaire);
|
||||
const [isSelected, setIsSelected] = useState(false);
|
||||
usePreloadImages(["/v1/palmistry/hand-with-eye.svg"]);
|
||||
@ -35,13 +37,20 @@ function GenderPalmistry() {
|
||||
preloadKey: ELottieKeys.handSymbols,
|
||||
});
|
||||
|
||||
const selectGender = async (_gender: Gender | null) => {
|
||||
const localGenders = genders.map((gender) => ({
|
||||
id: gender.id,
|
||||
title: translate(gender.id, undefined, ELocalesPlacement.V1),
|
||||
}));
|
||||
|
||||
const selectGender = (_gender: Gender | null) => {
|
||||
dispatch(actions.privacyPolicy.updateChecked(true));
|
||||
setIsSelected(true);
|
||||
dispatch(actions.questionnaire.update({ gender: _gender?.id }));
|
||||
};
|
||||
|
||||
const handleNext = useCallback(async () => {
|
||||
if (!gender) return;
|
||||
dispatch(actions.privacyPolicy.updateChecked(true));
|
||||
await sleep(1000);
|
||||
metricService.userParams({
|
||||
gender: genders.find((g) => g.id === gender)?.name,
|
||||
@ -50,7 +59,7 @@ function GenderPalmistry() {
|
||||
ESourceAuthorization["aura.palmistry.new"]
|
||||
);
|
||||
if (session?.sessionId?.length) {
|
||||
await updateSession(
|
||||
updateSession(
|
||||
{
|
||||
profile: {
|
||||
gender: EGender[gender as keyof typeof EGender],
|
||||
@ -76,9 +85,19 @@ function GenderPalmistry() {
|
||||
{translate("/gender.title")}
|
||||
</Title>
|
||||
<p className={styles.description}>{translate("/gender.description")}</p>
|
||||
<ChooseGender onSelectGender={selectGender} />
|
||||
{/* <ChooseGender onSelectGender={selectGender} /> */}
|
||||
<PrivacyPolicy containerClassName={styles["privacy-policy"]} haveCheckbox={false} />
|
||||
<div className={styles["genders-container"]}>
|
||||
{localGenders.map((_gender, index) => (
|
||||
<Answer
|
||||
key={index}
|
||||
answer={_gender}
|
||||
isSelected={gender === _gender.id}
|
||||
onClick={() => selectGender(genders.find((g) => g.id === _gender.id) ?? null)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<AlreadyHaveAccount text={translate("/gender.already_have_account")} />
|
||||
<PrivacyPolicy containerClassName={styles["privacy-policy"]} />
|
||||
{gender && !privacyPolicyChecked && (
|
||||
<Toast classNameContainer={styles["toast-container"]} variant="error">
|
||||
{translate("/gender.toast", undefined, ELocalesPlacement.V1)}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
.privacy-policy {
|
||||
max-width: 316px;
|
||||
margin-top: 26px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toast-container {
|
||||
@ -26,3 +27,9 @@
|
||||
max-width: 460px;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.genders-container {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@ function HeadOrHeart() {
|
||||
|
||||
const handleClick = async (id: IAnswersSessionPalmistry["head_or_heart"]) => {
|
||||
dispatch(actions.palmistryV1Answers.update({ headOrHeart: id }));
|
||||
await updateSession(
|
||||
updateSession(
|
||||
{
|
||||
answers: {
|
||||
head_or_heart: id,
|
||||
|
||||
@ -58,7 +58,7 @@ function RelateFollowing() {
|
||||
|
||||
const handleClick = async (answerIndex: number) => {
|
||||
setActiveButton(answerIndex);
|
||||
await updateSession(
|
||||
updateSession(
|
||||
{
|
||||
answers: {
|
||||
[questions[parseInt(questionId) - 1].id]: answerIndex + 1,
|
||||
|
||||
@ -46,7 +46,7 @@ function RelationshipStatus() {
|
||||
|
||||
const handleClick = async (id: IAnswersSessionPalmistry["relationship_status"]) => {
|
||||
dispatch(actions.palmistryV1Answers.update({ relationshipStatus: id }));
|
||||
await updateSession({
|
||||
updateSession({
|
||||
answers: {
|
||||
relationship_status: id,
|
||||
},
|
||||
|
||||
@ -45,7 +45,7 @@
|
||||
|
||||
.paywall__get-prediction {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
bottom: 0dvh;
|
||||
left: 0;
|
||||
align-items: center;
|
||||
background: #eff2fd;
|
||||
|
||||
@ -46,7 +46,7 @@ function WhatAspects() {
|
||||
|
||||
const handleClick = async (id: IAnswersSessionPalmistry["what_aspects"]) => {
|
||||
dispatch(actions.palmistryV1Answers.update({ whatAspects: id }));
|
||||
await updateSession(
|
||||
updateSession(
|
||||
{
|
||||
answers: {
|
||||
what_aspects: id,
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import styles from "./styles.module.scss";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function Address() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
|
||||
return (
|
||||
<p className={styles.address}>
|
||||
2024, Wit Apps LLC, <br />
|
||||
2108 N ST STE 5446 SACRAMENTO, CA 95816, US
|
||||
{translate("trial-payment.copyright", {
|
||||
year: new Date().getFullYear(),
|
||||
br: "\n",
|
||||
})}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,4 +4,5 @@
|
||||
text-align: center;
|
||||
line-height: 16px;
|
||||
margin-top: 16px;
|
||||
white-space: pre-line;
|
||||
}
|
||||
@ -7,7 +7,7 @@ import { ELocalesPlacement } from "@/locales";
|
||||
type TDiscountExpiresProps = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
function DiscountExpires({ className = "", ...props }: TDiscountExpiresProps) {
|
||||
const { translate } = useTranslations(ELocalesPlacement.V1);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
const [currentDate, setCurrentDate] = useState(new Date());
|
||||
const endDate = useMemo(
|
||||
() => new Date().setMinutes(currentDate.getMinutes() + 10),
|
||||
@ -43,17 +43,17 @@ function DiscountExpires({ className = "", ...props }: TDiscountExpiresProps) {
|
||||
return (
|
||||
<div {...props} className={`${styles["discount-expires"]} ${className}`}>
|
||||
<Title variant="h6" className={styles.title}>
|
||||
{translate("/trial-payment.discount_expires")}
|
||||
{translate("trial-payment.discount_expires")}
|
||||
</Title>
|
||||
<div className={styles.values}>
|
||||
<div className={styles["value-container"]}>
|
||||
<span className={styles.value}>{getMinutes()}</span>
|
||||
<span className={styles["value-symbol"]}>{translate("min")}</span>
|
||||
<span className={styles["value-symbol"]}>{translate("trial-payment.min")}</span>
|
||||
</div>
|
||||
<p className={styles.colon}>:</p>
|
||||
<div className={styles["value-container"]}>
|
||||
<span className={styles.value}>{getSeconds()}</span>
|
||||
<span className={styles["value-symbol"]}>{translate("sec")}</span>
|
||||
<span className={styles["value-symbol"]}>{translate("trial-payment.sec")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,11 +4,11 @@ import { ELocalesPlacement } from "@/locales";
|
||||
import { images } from "../../data";
|
||||
|
||||
function GuardPayments() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.V1);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
return (
|
||||
<div className={styles["guard-payments"]}>
|
||||
<img src={images("guard.svg")} alt="Guaranteed security" />
|
||||
<p className={styles.text}>{translate("guaranteed_security_payments")}</p>
|
||||
<p className={styles.text}>{translate("trial-payment.guaranteed_security_payments")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,19 +5,15 @@ import { ELocalesPlacement } from "@/locales";
|
||||
import { images } from "../../data";
|
||||
|
||||
function MoneyBackGuarantee() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<img src={images("money-back.svg")} alt="Money back" />
|
||||
<Title className={styles.title} variant="h4">
|
||||
{translate("/trial-payment.money_back_guarantee.title")}
|
||||
{translate("trial-payment.money_back_guarantee_title")}
|
||||
</Title>
|
||||
<p className={styles.text}>
|
||||
We are convinced that this report will provide you with a deeper
|
||||
understanding of your partner and suggest ways to enhance your
|
||||
relationship. With a track record of stellar customer reviews, we are
|
||||
fully prepared to refund your money if you find that this report does not
|
||||
deliver any value.
|
||||
{translate("trial-payment.money_back_guarantee_text")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -4,7 +4,7 @@ import { useTranslations } from "@/hooks/translations";
|
||||
import { images } from "../../data";
|
||||
|
||||
function PalmsSayAbout() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
@ -17,16 +17,13 @@ function PalmsSayAbout() {
|
||||
<div className={styles.points}>
|
||||
<div className={styles.point}>
|
||||
<p className={styles.text}>
|
||||
{/* {translate("/trial-payment.palms_say_about.point5", {
|
||||
{translate("trial-payment.palms_say_about_point5", {
|
||||
color: (
|
||||
<span style={{ color: "#2A37D3" }}>
|
||||
{translate("/trial-payment.palms_say_about.point5_color")}
|
||||
{translate("trial-payment.palms_say_about_point5_color")}
|
||||
</span>
|
||||
),
|
||||
})} */}
|
||||
<span style={{ color: "#2A37D3" }}>
|
||||
{translate("/trial-payment.palms_say_about.point5_color")}
|
||||
</span> is a symbol of vitality, holds clues about your health, energy, and passion for life
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -39,16 +36,13 @@ function PalmsSayAbout() {
|
||||
<div className={styles.points}>
|
||||
<div className={styles.point}>
|
||||
<p className={styles.text}>
|
||||
{/* {translate("/trial-payment.palms_say_about.point3", {
|
||||
{translate("trial-payment.palms_say_about_point3", {
|
||||
color: (
|
||||
<span style={{ color: "#20B61D" }}>
|
||||
{translate("/trial-payment.palms_say_about.point3_color")}
|
||||
{translate("trial-payment.palms_say_about_point3_color")}
|
||||
</span>
|
||||
),
|
||||
})} */}
|
||||
<span style={{ color: "#20B61D" }}>
|
||||
{translate("/trial-payment.palms_say_about.point3_color")}
|
||||
</span>reflects intellectual pursuits and mental strengths
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -61,16 +55,13 @@ function PalmsSayAbout() {
|
||||
<div className={styles.points}>
|
||||
<div className={styles.point}>
|
||||
<p className={styles.text}>
|
||||
{/* {translate("/trial-payment.palms_say_about.point7", {
|
||||
{translate("trial-payment.palms_say_about_point7", {
|
||||
color: (
|
||||
<span style={{ color: "#BC23BF" }}>
|
||||
{translate("/trial-payment.palms_say_about.point7_color")}
|
||||
{translate("trial-payment.palms_say_about_point7_color")}
|
||||
</span>
|
||||
),
|
||||
})} */}
|
||||
<span style={{ color: "#BC23BF" }}>
|
||||
{translate("/trial-payment.palms_say_about.point7_color")}
|
||||
</span> reveals insights into your financial potential and approach towards wealth
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -83,17 +74,13 @@ function PalmsSayAbout() {
|
||||
<div className={styles.points}>
|
||||
<div className={styles.point}>
|
||||
<p className={styles.text}>
|
||||
{/* {translate("/trial-payment.palms_say_about.point1", {
|
||||
{translate("trial-payment.palms_say_about_point1", {
|
||||
color: (
|
||||
<span>
|
||||
{translate("/trial-payment.palms_say_about.point1_color")}
|
||||
{translate("trial-payment.palms_say_about_point1_color")}
|
||||
</span>
|
||||
),
|
||||
})} */}
|
||||
<span>
|
||||
{translate("/trial-payment.palms_say_about.point1_color")}
|
||||
</span> can reveal insights
|
||||
into your romantic journey, mapping your heart's desires
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -106,9 +93,7 @@ function PalmsSayAbout() {
|
||||
<div className={styles.points}>
|
||||
<div className={styles.point}>
|
||||
<p className={styles.text}>
|
||||
{/* {translate("/trial-payment.palms_say_about.point9")} */}
|
||||
Each finger is a pillar of your
|
||||
personality, from leadership and ambition to creativity and self- expression
|
||||
{translate("trial-payment.palms_say_about_point9")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -22,10 +22,10 @@ function PaymentTable({
|
||||
placementKey,
|
||||
buttonClick,
|
||||
}: IPaymentTableProps) {
|
||||
const { translate } = useTranslations(ELocalesPlacement.V1);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
const { getText, currency } = usePaywall({
|
||||
placementKey,
|
||||
localesPlacement: ELocalesPlacement.V1,
|
||||
localesPlacement: ELocalesPlacement.EmailMarketingPalmistryV2,
|
||||
});
|
||||
const [isOpenPrivacyModal, setIsOpenPrivacyModal] = useState<boolean>(false);
|
||||
const handleSubscriptionPolicyClick = (event: React.MouseEvent) => {
|
||||
@ -71,18 +71,17 @@ function PaymentTable({
|
||||
// backgroundColor: gender === "male" ? "#85B6FF" : "#D1ACF2",
|
||||
}}
|
||||
>
|
||||
<span>{translate("/trial-payment.payment_table.special_offer")}</span>
|
||||
<span>{translate("trial-payment.payment_table_special_offer")}</span>
|
||||
</div>
|
||||
<div className={styles["table-container"]}>
|
||||
<Title variant="h3" className={styles.title}>
|
||||
{/* {translate("/trial-payment.payment_table.title", {
|
||||
price: <span className={styles.purple}>{getPrice(product)}</span>,
|
||||
})} */}
|
||||
Personalized plan for <span className={styles.blue}>{getPrice(product.trialPrice || 0)}</span>
|
||||
{translate("trial-payment.payment_table_title", {
|
||||
price: <span className={styles.purple}>{getPrice(product.trialPrice || 0)}</span>,
|
||||
})}
|
||||
</Title>
|
||||
<div className={styles["table-element"]}>
|
||||
<p className={styles["total-today"]}>
|
||||
{translate("/trial-payment.payment_table.total_today")}
|
||||
{translate("trial-payment.payment_table_total_today")}
|
||||
</p>
|
||||
<span>{getPrice(product.trialPrice || 0)}</span>
|
||||
</div>
|
||||
@ -90,8 +89,7 @@ function PaymentTable({
|
||||
<div className={styles["table-element"]}>
|
||||
{/* {product.trialPrice !== 50 && ( */}
|
||||
<p>
|
||||
{/* {translate("/trial-payment.payment_table.cost")} */}
|
||||
Including 1-week trial personal reading
|
||||
{translate("trial-payment.payment_table_cost")}
|
||||
</p>
|
||||
{/* )} */}
|
||||
{/* {product.trialPrice === 50 && (
|
||||
@ -117,14 +115,14 @@ function PaymentTable({
|
||||
</div>
|
||||
</div>
|
||||
<Button className={styles.button} onClick={buttonClick}>
|
||||
GET {product.trialDuration}-DAY TRIAL
|
||||
{translate("trial-payment.get_trial_days", { days: product.trialDuration })}
|
||||
</Button>
|
||||
<GuardPayments />
|
||||
<p className={styles.policy}>
|
||||
{translate("/trial-payment.subscription_policy", {
|
||||
{translate("trial-payment.subscription_policy", {
|
||||
policyLink: (
|
||||
<a onClick={handleSubscriptionPolicyClick}>
|
||||
{translate("/trial-payment.policy_link")}
|
||||
{translate("trial-payment.policy_link")}
|
||||
</a>
|
||||
),
|
||||
trialDuration: product.trialDuration,
|
||||
|
||||
@ -80,6 +80,7 @@
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 21px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.policy {
|
||||
|
||||
@ -15,7 +15,7 @@ function PersonalInformation({
|
||||
gender,
|
||||
birthPlace,
|
||||
}: IPersonalInformationProps) {
|
||||
const { translate } = useTranslations(ELocalesPlacement.V1);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -30,21 +30,21 @@ function PersonalInformation({
|
||||
<div className={styles["text-information"]}>
|
||||
<ul>
|
||||
<li>
|
||||
<h6>{translate("/trial-payment.zodiac_sign")}</h6>
|
||||
<p>{translate(`zodiac_signs.${zodiacSign?.toLowerCase()}`)}</p>
|
||||
<h6>{translate("trial-payment.zodiac_sign")}</h6>
|
||||
<p>{translate(`trial-payment.zodiac_signs_${zodiacSign?.toLowerCase()}`)}</p>
|
||||
</li>
|
||||
<li>
|
||||
<h6>{translate("gender")}</h6>
|
||||
<p>{translate(gender?.toLowerCase())}</p>
|
||||
<h6>{translate("trial-payment.gender")}</h6>
|
||||
<p>{translate(`trial-payment.${gender?.toLowerCase()}`)}</p>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<h6>{translate("/trial-payment.date_of_birth")}</h6>
|
||||
<h6>{translate("trial-payment.date_of_birth")}</h6>
|
||||
<p>{birthdate}</p>
|
||||
</li>
|
||||
<li>
|
||||
<h6>{translate("/trial-payment.place_of_birth")}</h6>
|
||||
<h6>{translate("trial-payment.place_of_birth")}</h6>
|
||||
<p>{birthPlace}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -6,29 +6,29 @@ import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
function Reviews() {
|
||||
const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
const reviews: IReviewProps[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
avatar: `${palmistryV1Prefix}/reviews/rebecca.png`,
|
||||
username: translate("/trial-payment.reviews.username1"),
|
||||
username: translate("trial-payment.reviews_username1"),
|
||||
date: "01/12/2024",
|
||||
gender: "female",
|
||||
text: translate("/trial-payment.reviews.text1"),
|
||||
text: translate("trial-payment.reviews_text1"),
|
||||
},
|
||||
{
|
||||
avatar: `${palmistryV1Prefix}/reviews/mika.png`,
|
||||
username: translate("/trial-payment.reviews.username2"),
|
||||
username: translate("trial-payment.reviews_username2"),
|
||||
date: "07/12/2024",
|
||||
gender: "female",
|
||||
text: translate("/trial-payment.reviews.text2"),
|
||||
text: translate("trial-payment.reviews_text2"),
|
||||
},
|
||||
{
|
||||
avatar: `${palmistryV1Prefix}/reviews/amanda.png`,
|
||||
username: translate("/trial-payment.reviews.username3"),
|
||||
username: translate("trial-payment.reviews_username3"),
|
||||
date: "16/11/2024",
|
||||
gender: "female",
|
||||
text: translate("/trial-payment.reviews.text3"),
|
||||
text: translate("trial-payment.reviews_text3"),
|
||||
},
|
||||
],
|
||||
[translate]
|
||||
|
||||
@ -5,6 +5,7 @@ import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||
import { addCurrency, ELocalesPlacement } from "@/locales";
|
||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||
import { Currency } from "@/components/PaymentTable";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
|
||||
const placementKey = EPlacementKeys["aura.placement.email.palmistry.discount"]
|
||||
|
||||
@ -21,8 +22,9 @@ const getPrice = (price: number, currency: Currency) => {
|
||||
function SecretDiscountTable() {
|
||||
const { products, currency, getText } = usePaywall({
|
||||
placementKey,
|
||||
localesPlacement: ELocalesPlacement.PalmistryV1,
|
||||
localesPlacement: ELocalesPlacement.EmailMarketingPalmistryV2,
|
||||
});
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
|
||||
const activeProduct = products[0];
|
||||
const price = activeProduct?.price || 0;
|
||||
@ -32,28 +34,30 @@ function SecretDiscountTable() {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Title className={styles.title} variant="h3">
|
||||
You get a secret discount!
|
||||
{translate("secret-discount.secret-discount-table_title")}
|
||||
</Title>
|
||||
<p className={styles.subtitle}>No pressure. Cancel anytime.</p>
|
||||
<p className={styles.subtitle}>
|
||||
{translate("secret-discount.secret-discount-table_subtitle")}
|
||||
</p>
|
||||
<div className={styles.applied}>
|
||||
<img src={images("gift.png")} alt="Gift" />
|
||||
<Title className={styles.title} variant="h4">
|
||||
Secret discount applied!
|
||||
{translate("secret-discount.secret-discount-table_discount-applied")}
|
||||
</Title>
|
||||
<span className={styles["old-discount"]}>{getText("old.discount")}</span>
|
||||
<span className={styles["new-discount"]}>{getText("new.discount")}</span>
|
||||
</div>
|
||||
<div className={`${styles["grid-line"]} ${styles["days-14"]}`}>
|
||||
<p>Your cost per {trialDuration} days after trial:</p>
|
||||
<p>{translate("secret-discount.secret-discount-table_cost-after-trial", { days: trialDuration })}</p>
|
||||
<span className={styles["old-price"]}>{addCurrency(Number(getText("old.price")), currency)}</span>
|
||||
<span className={styles["new-price"]}>{getPrice(price, currency)}</span>
|
||||
</div>
|
||||
<div className={`${styles["grid-line"]} ${styles["save"]}`}>
|
||||
<p>You save {addCurrency(Number(getText("save")), currency)}</p>
|
||||
<p>{translate("secret-discount.secret-discount-table_you-save", { amount: addCurrency(Number(getText("save")), currency) })}</p>
|
||||
</div>
|
||||
<hr />
|
||||
<div className={`${styles["grid-line"]} ${styles["total-today"]}`}>
|
||||
<p>Total today</p>
|
||||
<p>{translate("secret-discount.secret-discount-table_total-today")}</p>
|
||||
<span>{getPrice(trialPrice, currency)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -25,7 +25,7 @@ function WithPartnerInformation(props: IWithPartnerInformationProps) {
|
||||
partnerGender,
|
||||
partnerBirthPlace,
|
||||
} = props;
|
||||
const { translate } = useTranslations(ELocalesPlacement.V1);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -51,45 +51,45 @@ function WithPartnerInformation(props: IWithPartnerInformationProps) {
|
||||
<div className={styles["text-information"]}>
|
||||
<ul>
|
||||
<li>
|
||||
<h6>{translate("/trial-payment.zodiac_sign")}</h6>
|
||||
<h6>{translate("trial-payment.zodiac_sign")}</h6>
|
||||
<p>
|
||||
{zodiacSign?.length
|
||||
? translate(`zodiac_signs.${zodiacSign?.toLowerCase()}`)
|
||||
? translate(`trial-payment.zodiac_signs_${zodiacSign?.toLowerCase()}`)
|
||||
: "-"}
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<h6>{translate("gender")}</h6>
|
||||
<p>{gender.length ? translate(gender?.toLowerCase()) : "-"}</p>
|
||||
<h6>{translate("trial-payment.gender")}</h6>
|
||||
<p>{gender.length ? translate(`trial-payment.${gender?.toLowerCase()}`) : "-"}</p>
|
||||
</li>
|
||||
<li>
|
||||
<h6>{translate("/trial-payment.date_of_birth")}</h6>
|
||||
<h6>{translate("trial-payment.date_of_birth")}</h6>
|
||||
<p>{birthdate.length ? birthdate : "-"}</p>
|
||||
</li>
|
||||
<li>
|
||||
<h6>{translate("/trial-payment.place_of_birth")}</h6>
|
||||
<h6>{translate("trial-payment.place_of_birth")}</h6>
|
||||
<p>{birthPlace.length ? birthPlace : "-"}</p>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<h6>{translate("/trial-payment.zodiac_sign")}</h6>
|
||||
<h6>{translate("trial-payment.zodiac_sign")}</h6>
|
||||
<p>
|
||||
{partnerZodiacSign?.length
|
||||
? translate(`zodiac_signs.${partnerZodiacSign?.toLowerCase()}`)
|
||||
? translate(`trial-payment.zodiac_signs_${partnerZodiacSign?.toLowerCase()}`)
|
||||
: "-"}
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<h6>{translate("gender")}</h6>
|
||||
<p>{partnerGender.length ? translate(partnerGender?.toLowerCase()) : "-"}</p>
|
||||
<h6>{translate("trial-payment.gender")}</h6>
|
||||
<p>{partnerGender.length ? translate(`trial-payment.${partnerGender?.toLowerCase()}`) : "-"}</p>
|
||||
</li>
|
||||
<li>
|
||||
<h6>{translate("/trial-payment.date_of_birth")}</h6>
|
||||
<h6>{translate("trial-payment.date_of_birth")}</h6>
|
||||
<p>{partnerBirthDate.length ? partnerBirthDate : "-"}</p>
|
||||
</li>
|
||||
<li>
|
||||
<h6>{translate("/trial-payment.place_of_birth")}</h6>
|
||||
<h6>{translate("trial-payment.place_of_birth")}</h6>
|
||||
<p>{partnerBirthPlace.length ? partnerBirthPlace : "-"}</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { SVGProps } from "react"
|
||||
|
||||
function Blob4(props: SVGProps<SVGSVGElement>) {
|
||||
const width = props.width ? Number(props.width) : 419;
|
||||
const height = props.height ? Number(props.height) : 193;
|
||||
return (
|
||||
<svg width="419" height="193" viewBox="0 0 419 193" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M497 37.2239C243.674 116.789 145.019 -66.6269 -78 27.6975V193H497V37.2239Z" fill="url(#paint0_linear_1_8022)" />
|
||||
<svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d={`M${width + 78} 37.2239C${width * .58} 116.789 ${width * .35} -66.6269 ${width * -0.186} 27.6975V${height}H${width}V37.2239Z`} fill="url(#paint0_linear_1_8022)" />
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1_8022" x1="497.092" y1="193" x2="-77.9896" y2="193" gradientUnits="userSpaceOnUse">
|
||||
<linearGradient id="paint0_linear_1_8022" x1={1.186 * width} y1={height} x2={-0.186 * width} y2={height} gradientUnits="userSpaceOnUse">
|
||||
<stop stopColor="#FFA1BA" />
|
||||
<stop offset="1" stopColor="#9A55FF" />
|
||||
</linearGradient>
|
||||
|
||||
@ -8,14 +8,16 @@ import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { addCurrency, ELocalesPlacement } from "@/locales";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
|
||||
const placementKey = EPlacementKeys["aura.placement.email.palmistry.discount"]
|
||||
|
||||
function SaveOff() {
|
||||
const { products, getText } = usePaywall({
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
const { products, currency, getText } = usePaywall({
|
||||
placementKey,
|
||||
localesPlacement: ELocalesPlacement.PalmistryV1,
|
||||
localesPlacement: ELocalesPlacement.EmailMarketingPalmistryV2,
|
||||
});
|
||||
const activeProduct = products[0]
|
||||
const price = (activeProduct?.price || 0) / 100
|
||||
@ -33,19 +35,28 @@ function SaveOff() {
|
||||
<Blob2 className={styles.blob2} />
|
||||
<img className={styles.gift} src={images("gift.svg")} alt="gift" />
|
||||
<Title className={styles.title}>
|
||||
SAVE {getText("discount")}% OFF!
|
||||
{translate("save-off.title", { discount: getText("discount") })}
|
||||
</Title>
|
||||
<p className={styles.description}>
|
||||
<span className={styles.price}>${price}</span> instead <span className={styles.discount}>of ${getText("full.price")}</span>
|
||||
{translate("save-off.instead", {
|
||||
price: <span className={styles.price}>{addCurrency(price, currency)}</span>,
|
||||
oldPrice: <span className={styles.discount}>
|
||||
{translate("save-off.instead-old-price", {
|
||||
oldPrice: addCurrency(getText("full.price") as string, currency)
|
||||
})}
|
||||
</span>
|
||||
})}
|
||||
</p>
|
||||
<p className={styles.point} style={{ marginTop: 12 }}>
|
||||
<img src={images("fire.png")} alt="fire" /> {trialDuration}-day trial
|
||||
<img src={images("fire.png")} alt="fire" />
|
||||
{translate("save-off.trial-duration", { days: trialDuration })}
|
||||
</p>
|
||||
<p className={styles.point}>
|
||||
<img src={images("gift.png")} alt="gift" /> {getText("discount")}% off on your personalized plan
|
||||
<img src={images("gift.png")} alt="gift" />
|
||||
{translate("save-off.discount-offer", { discount: getText("discount") })}
|
||||
</p>
|
||||
<Button className={styles.button} onClick={handleNext}>
|
||||
GET {trialDuration}-day trial
|
||||
{translate("save-off.button-trial", { days: trialDuration })}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -6,7 +6,7 @@ import SecretDiscountTable from "../../components/SecretDiscountTable";
|
||||
import Button from "../../components/Button";
|
||||
import { usePaywall } from "@/hooks/paywall/usePaywall";
|
||||
import { EPlacementKeys } from "@/api/resources/Paywall";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { addCurrency, ELocalesPlacement } from "@/locales";
|
||||
import Modal from "@/components/Modal";
|
||||
import { useEffect, useState } from "react";
|
||||
import PaymentForm from "@/components/Payment/nmi/PaymentForm";
|
||||
@ -14,16 +14,22 @@ import { useNavigate } from "react-router-dom";
|
||||
import routes from "@/routes";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { actions } from "@/store";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { useDynamicSize } from "@/hooks/useDynamicSize";
|
||||
import Header from "@/components/pages/ABDesign/v1/components/Header";
|
||||
|
||||
const placementKey = EPlacementKeys["aura.placement.email.palmistry.discount"]
|
||||
|
||||
function SecretDiscount() {
|
||||
const { width, elementRef } = useDynamicSize({ defaultWidth: 560 });
|
||||
const { height, elementRef: policyContainerRef } = useDynamicSize<HTMLDivElement>({ defaultWidth: 560, defaultHeight: 193 });
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const { products } = usePaywall({
|
||||
const { products, currency } = usePaywall({
|
||||
placementKey,
|
||||
localesPlacement: ELocalesPlacement.PalmistryV1,
|
||||
localesPlacement: ELocalesPlacement.EmailMarketingPalmistryV2,
|
||||
});
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
|
||||
const activeProduct = products[0];
|
||||
const price = (activeProduct?.price || 0) / 100;
|
||||
@ -56,7 +62,14 @@ function SecretDiscount() {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className={styles.container} ref={elementRef} style={{
|
||||
paddingBottom: `${height + 42}px`
|
||||
}}>
|
||||
<Header
|
||||
className={styles.header}
|
||||
classNameTitle={styles["header-title"]}
|
||||
isBackButtonVisible={true}
|
||||
/>
|
||||
{activeProduct && (
|
||||
<Modal
|
||||
containerClassName={styles.modal}
|
||||
@ -73,20 +86,23 @@ function SecretDiscount() {
|
||||
)}
|
||||
<Blob3 className={styles.blob3} />
|
||||
<Title className={styles.title} variant="h1">
|
||||
You get a secret discount!
|
||||
{translate("secret-discount.title")}
|
||||
</Title>
|
||||
<SecretDiscountTable />
|
||||
<Button className={styles.button} onClick={openPaymentModal}>
|
||||
GET {trialDuration}-DAY TRIAL
|
||||
{translate("secret-discount.button-trial", { days: trialDuration })}
|
||||
</Button>
|
||||
|
||||
<div className={styles["policy-container"]}>
|
||||
<div className={styles["policy-container"]} ref={policyContainerRef}>
|
||||
<p className={styles.policy}>
|
||||
By continuing you agree that if you don't cancel prior to the end of the {trialDuration}-days trial, you will automatically be charged ${price} for the introductory period of 14 days thereafter the standard rate of ${price} every 14 days until you cancel in settings. Learn more about cancellation and refund policy in Subscription terms.
|
||||
{translate("secret-discount.policy", {
|
||||
days: trialDuration,
|
||||
price: addCurrency(price, currency)
|
||||
})}
|
||||
</p>
|
||||
<Blob4 className={styles.blob4} />
|
||||
<Blob4 className={styles.blob4} width={width} height={height + 34} />
|
||||
</div>
|
||||
</>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,24 @@
|
||||
.container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px 26px;
|
||||
min-height: 100dvh;
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 8px 0 30px;
|
||||
|
||||
&>button {
|
||||
margin-left: -12px;
|
||||
}
|
||||
}
|
||||
|
||||
.blob3 {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -27,10 +48,13 @@
|
||||
}
|
||||
|
||||
.policy-container {
|
||||
position: relative;
|
||||
width: calc(100% + 52px);
|
||||
position: absolute;
|
||||
width: calc(100%);
|
||||
max-width: 560px;
|
||||
height: fit-content;
|
||||
bottom: -58px;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
&>.policy {
|
||||
width: 100%;
|
||||
|
||||
@ -3,7 +3,7 @@ import Title from "@/components/Title";
|
||||
import Button from "../../components/Button";
|
||||
import routes from "@/routes";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
// import { useTranslations } from "@/hooks/translations";
|
||||
import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { images } from "../../data";
|
||||
import DiscountExpires from "../../components/DiscountExpires";
|
||||
@ -30,7 +30,7 @@ function TrialPayment() {
|
||||
const api = useApi();
|
||||
const token = useSelector(selectors.selectToken);
|
||||
const dispatch = useDispatch();
|
||||
// const { translate } = useTranslations(ELocalesPlacement.PalmistryV1);
|
||||
const { translate } = useTranslations(ELocalesPlacement.EmailMarketingPalmistryV2);
|
||||
const { products } = usePaywall({
|
||||
placementKey,
|
||||
localesPlacement: ELocalesPlacement.PalmistryV1,
|
||||
@ -107,22 +107,18 @@ function TrialPayment() {
|
||||
<div className={styles.background} />
|
||||
<div className={styles.header}>
|
||||
<Title className={styles.title}>
|
||||
Transform your <br />
|
||||
life with the Power <br />
|
||||
of Palmistry — Get your
|
||||
personalized plan today
|
||||
{translate("trial-payment.transform_your_life")}
|
||||
</Title>
|
||||
<img src={images("hand.svg")} alt="Hand" />
|
||||
</div>
|
||||
<div className={styles.discount}>
|
||||
<DiscountExpires />
|
||||
<Button className={styles.button} onClick={openPaymentModal}>
|
||||
GET {trialDuration}-day trial
|
||||
{translate("trial-payment.get_trial_days", { days: trialDuration })}
|
||||
</Button>
|
||||
</div>
|
||||
<Title className={styles.title}>
|
||||
Personalized Palmistry <br />
|
||||
Reading is ready!
|
||||
{translate("trial-payment.personalized_reading_ready")}
|
||||
</Title>
|
||||
<PaymentTable
|
||||
product={activeProduct}
|
||||
@ -132,16 +128,14 @@ function TrialPayment() {
|
||||
/>
|
||||
<MoneyBackGuarantee />
|
||||
<Title className={styles["title-hands"]}>
|
||||
What your hands and fingers can reveal about you
|
||||
{translate("trial-payment.hands_reveal_title")}
|
||||
</Title>
|
||||
<p className={styles["text-hands"]}>
|
||||
Your hands and fingers are like a personal storybook, with each line
|
||||
representing a chapter of your life.
|
||||
{translate("trial-payment.hands_reveal_description")}
|
||||
</p>
|
||||
<PalmsSayAbout />
|
||||
<Title className={styles["title-prepare"]}>
|
||||
We will prepare your
|
||||
personalized plan
|
||||
{translate("trial-payment.prepare_personalized_plan")}
|
||||
</Title>
|
||||
{singleOrWithPartner === "partner" && (
|
||||
<WithPartnerInformation
|
||||
@ -164,12 +158,11 @@ function TrialPayment() {
|
||||
/>
|
||||
)}
|
||||
<Title className={styles["title-reviews"]}>
|
||||
Here are a few stories from
|
||||
AURA Users
|
||||
{translate("trial-payment.aura_users_stories")}
|
||||
</Title>
|
||||
<Reviews />
|
||||
<Title className={styles["title-featured"]}>
|
||||
As featured in
|
||||
{translate("trial-payment.as_featured_in")}
|
||||
</Title>
|
||||
<div className={styles["featured-container"]}>
|
||||
<img src={images("featured.svg")} alt="Featured" />
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
text-align: left;
|
||||
color: #2c2c2c;
|
||||
max-width: 215px;
|
||||
white-space: pre-line
|
||||
}
|
||||
|
||||
&>img {
|
||||
@ -60,6 +61,7 @@
|
||||
color: #2c2c2c;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.title-hands {
|
||||
@ -87,6 +89,7 @@
|
||||
text-align: center;
|
||||
color: #224e90;
|
||||
margin-bottom: 0;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.title-reviews {
|
||||
@ -97,6 +100,7 @@
|
||||
text-align: center;
|
||||
color: #224e90;
|
||||
margin-bottom: 0;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.title-featured {
|
||||
|
||||
@ -21,6 +21,7 @@ const getPrice = (product: IPaywallProduct | null) => {
|
||||
}
|
||||
|
||||
interface IPaymentFormProps {
|
||||
isSinglePayment?: boolean;
|
||||
className?: string;
|
||||
placementKey: EPlacementKeys;
|
||||
onPaymentError?: () => void;
|
||||
@ -28,7 +29,7 @@ interface IPaymentFormProps {
|
||||
onModalClosed?: () => void;
|
||||
}
|
||||
|
||||
function PaymentForm({ className, placementKey, onPaymentError, onPaymentSuccess, onModalClosed }: IPaymentFormProps) {
|
||||
function PaymentForm({ isSinglePayment = false, className, placementKey, onPaymentError, onPaymentSuccess, onModalClosed }: IPaymentFormProps) {
|
||||
const { translate } = useTranslations(ELocalesPlacement.V1);
|
||||
const currency = useSelector(selectors.selectCurrency);
|
||||
const activeProduct = useSelector(selectors.selectActiveProduct);
|
||||
@ -48,15 +49,15 @@ function PaymentForm({ className, placementKey, onPaymentError, onPaymentSuccess
|
||||
<div
|
||||
className={`${styles["payment-modal"]} ${isLoading ? styles.hide : ""} ${className}`}
|
||||
>
|
||||
<Title variant="h3" className={styles.title}>
|
||||
{!isSinglePayment && <Title variant="h3" className={styles.title}>
|
||||
{translate("payment_modal.title")}
|
||||
</Title>
|
||||
</Title>}
|
||||
<PaymentMethodsChoice
|
||||
paymentMethods={paymentMethodsButtons}
|
||||
selectedPaymentMethod={paymentMethodsButtons[0].id}
|
||||
onSelectPaymentMethod={() => { }}
|
||||
/>
|
||||
{activeProduct && (
|
||||
{!isSinglePayment && activeProduct && (
|
||||
<div>
|
||||
<p className={styles["sub-plan-description"]}>
|
||||
{translate("payment_modal.description", {
|
||||
|
||||
@ -7,9 +7,10 @@ import { ELocalesPlacement } from "@/locales";
|
||||
|
||||
interface IPrivacyPolicyProps {
|
||||
containerClassName?: string;
|
||||
haveCheckbox?: boolean;
|
||||
}
|
||||
|
||||
function PrivacyPolicy({ containerClassName = "" }: IPrivacyPolicyProps) {
|
||||
function PrivacyPolicy({ containerClassName = "", haveCheckbox = true }: IPrivacyPolicyProps) {
|
||||
const dispatch = useDispatch();
|
||||
const { translate } = useTranslations(ELocalesPlacement.V1);
|
||||
const { checked } = useSelector(selectors.selectPrivacyPolicy);
|
||||
@ -20,7 +21,7 @@ function PrivacyPolicy({ containerClassName = "" }: IPrivacyPolicyProps) {
|
||||
|
||||
return (
|
||||
<div className={`${styles.container} ${containerClassName}`}>
|
||||
<Checkbox checked={checked} onChange={handleChange} />
|
||||
{haveCheckbox && <Checkbox checked={checked} onChange={handleChange} />}
|
||||
<p className={styles.text}>
|
||||
{translate("policy", {
|
||||
privacyPolicy: (
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import CustomButton from "../CustomButton";
|
||||
import DiscountExpires from "../DiscountExpires";
|
||||
import styles from "./styles.module.css";
|
||||
import { selectors } from "@/store";
|
||||
|
||||
interface IHeaderProps {
|
||||
buttonText?: string;
|
||||
@ -15,12 +13,12 @@ function Header({
|
||||
buttonText = "get my reading",
|
||||
buttonClassName = "",
|
||||
}: IHeaderProps) {
|
||||
const { gender } = useSelector(selectors.selectQuestionnaire);
|
||||
// const { gender } = useSelector(selectors.selectQuestionnaire);
|
||||
return (
|
||||
<header
|
||||
className={styles.header}
|
||||
style={{
|
||||
backgroundColor: gender === "male" ? "#C1E5FF" : "#F7EBFF",
|
||||
// backgroundColor: gender === "male" ? "#C1E5FF" : "#F7EBFF",
|
||||
}}
|
||||
>
|
||||
<DiscountExpires />
|
||||
|
||||
@ -3,13 +3,15 @@
|
||||
top: 0;
|
||||
z-index: 30;
|
||||
height: 62px;
|
||||
width: 100%;
|
||||
width: calc(100% + 64px);
|
||||
max-width: 560px;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 15px;
|
||||
padding: 8px 47px;
|
||||
background-color: transparent;
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
.button {
|
||||
|
||||
@ -4,7 +4,7 @@ import { useTranslations } from "@/hooks/translations";
|
||||
import { ELocalesPlacement } from "@/locales";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useDynamicSize } from "@/hooks/useDynamicSize";
|
||||
import { useAuthentication } from "@/hooks/authentication/use-authentication";
|
||||
import { actions, selectors } from "@/store";
|
||||
@ -32,11 +32,17 @@ interface IAuthPage {
|
||||
}
|
||||
|
||||
function Auth({ redirectUrl = routes.client.home() }: IAuthPage) {
|
||||
const [passwordFromQuery, emailFromQuery] = useMemo(() => {
|
||||
const url = window.location.href;
|
||||
const [_password, _email] = url.split("?")?.[1]?.split("password=", 2)?.[1]?.split("&email=") || ["", ""]
|
||||
return [decodeURIComponent(_password), decodeURIComponent(_email)]
|
||||
}, [window.location.href]);
|
||||
|
||||
const { translate } = useTranslations(ELocalesPlacement.V1);
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [email, setEmail] = useState(emailFromQuery || "");
|
||||
const [password, setPassword] = useState(passwordFromQuery || "");
|
||||
const [isDisabled, setIsDisabled] = useState(true);
|
||||
const [isValidEmail, setIsValidEmail] = useState(false);
|
||||
const [isValidPassword, setIsValidPassword] = useState(false);
|
||||
@ -78,6 +84,16 @@ function Auth({ redirectUrl = routes.client.home() }: IAuthPage) {
|
||||
}
|
||||
}, [subPlan, products]);
|
||||
|
||||
useEffect(() => {
|
||||
if (emailFromQuery?.length) {
|
||||
setIsValidEmail(true);
|
||||
}
|
||||
if (passwordFromQuery?.length) {
|
||||
setIsValidPassword(true);
|
||||
}
|
||||
}, [emailFromQuery, passwordFromQuery]);
|
||||
|
||||
|
||||
const handleValidEmail = (email: string) => {
|
||||
dispatch(actions.form.addEmail(email));
|
||||
setEmail(email);
|
||||
|
||||
@ -42,26 +42,26 @@ export const useAuthentication = () => {
|
||||
const birthtimeFromForm = useSelector(selectors.selectBirthtime);
|
||||
|
||||
const birthdate = useMemo(() => {
|
||||
if (birthdateFromQuestionnaire.length) {
|
||||
if (birthdateFromQuestionnaire?.length) {
|
||||
return birthdateFromQuestionnaire;
|
||||
}
|
||||
if (birthdateFromForm.length) {
|
||||
if (birthdateFromForm?.length) {
|
||||
return birthdateFromForm;
|
||||
}
|
||||
}, [birthdateFromForm, birthdateFromQuestionnaire]);
|
||||
|
||||
const birthtime = useMemo(() => {
|
||||
if (birthtimeFromQuestionnaire.length) {
|
||||
if (birthtimeFromQuestionnaire?.length) {
|
||||
return birthtimeFromQuestionnaire;
|
||||
}
|
||||
if (birthtimeFromForm.length) {
|
||||
if (birthtimeFromForm?.length) {
|
||||
return birthtimeFromForm;
|
||||
}
|
||||
}, [birthtimeFromForm, birthtimeFromQuestionnaire]);
|
||||
|
||||
const partnerBirthdate = useMemo(() => {
|
||||
const fromQuestionnaire = `${partnerBirthdateFromQuestionnaire} ${partnerBirthtime}`
|
||||
if (partnerBirthdateFromQuestionnaire.length) {
|
||||
if (partnerBirthdateFromQuestionnaire?.length) {
|
||||
return fromQuestionnaire;
|
||||
}
|
||||
return getDateAsString(partnerBirthdateFromForm)
|
||||
@ -141,14 +141,15 @@ export const useAuthentication = () => {
|
||||
}
|
||||
}
|
||||
const { user } = await api.getUser({ token });
|
||||
const { user: userMe } = await api.getMe({ token });
|
||||
if (userId?.length) {
|
||||
metricService.userParams({
|
||||
email: user.email,
|
||||
email: user?.email,
|
||||
UserID: userId
|
||||
})
|
||||
metricService.setUserID(userId);
|
||||
}
|
||||
signUp(token, user);
|
||||
signUp(token, user, userMe);
|
||||
setToken(token);
|
||||
dispatch(actions.status.update("registred"));
|
||||
} catch (error: unknown) {
|
||||
@ -167,16 +168,17 @@ export const useAuthentication = () => {
|
||||
const payload = getAuthorizationPayload(email, source);
|
||||
const { token, userId, generatingVideo, videoId, authCode } = await api.authorization(payload);
|
||||
const { user } = await api.getUser({ token });
|
||||
const { user: userMe } = await api.getMe({ token });
|
||||
if (userId?.length) {
|
||||
dispatch(actions.userId.update({ userId }));
|
||||
metricService.userParams({
|
||||
hasPersonalVideo: generatingVideo || false,
|
||||
email: user.email,
|
||||
email: user?.email,
|
||||
UserID: userId
|
||||
})
|
||||
metricService.setUserID(userId);
|
||||
}
|
||||
signUp(token, user);
|
||||
signUp(token, user, userMe);
|
||||
setToken(token);
|
||||
if (authCode?.length) {
|
||||
dispatch(actions.userConfig.setAuthCode(authCode));
|
||||
|
||||
@ -126,15 +126,21 @@ const useChatSocket = (userId: string, chatId: string) => {
|
||||
}, [chatId, fetchBalance])
|
||||
|
||||
const receiveMessage = useCallback((message: IMessage[]) => {
|
||||
if (message[0]?.role === "user") {
|
||||
if (!message?.[0]) return;
|
||||
|
||||
if (message[0].role === "user") {
|
||||
setIsLoadingSelfMessage(false);
|
||||
}
|
||||
if (message[0]?.role === "assistant") {
|
||||
if (message[0].role === "assistant") {
|
||||
setIsLoadingAdvisorMessage(false);
|
||||
}
|
||||
setMessages((prev) => [...prev, message[0]]);
|
||||
|
||||
}, [])
|
||||
setMessages((prev) => {
|
||||
if (prev.some(msg => msg.id === message[0].id)) {
|
||||
return prev;
|
||||
}
|
||||
return [...prev, message[0]];
|
||||
});
|
||||
}, []);
|
||||
|
||||
const currentBalance = useCallback((balance: IResponse<ICurrentBalance>) => {
|
||||
setBalance(balance?.data);
|
||||
@ -208,11 +214,11 @@ const useChatSocket = (userId: string, chatId: string) => {
|
||||
init();
|
||||
}, [init]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket) return setIsAvailableChatting(false);
|
||||
joinChat(chatId);
|
||||
fetchBalance(chatId);
|
||||
}, [chatId, fetchBalance, joinChat, socket]);
|
||||
// useEffect(() => {
|
||||
// if (!socket) return setIsAvailableChatting(false);
|
||||
// joinChat(chatId);
|
||||
// fetchBalance(chatId);
|
||||
// }, [chatId, fetchBalance, joinChat, socket]);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
@ -258,6 +264,28 @@ const useChatSocket = (userId: string, chatId: string) => {
|
||||
}
|
||||
}, [dispatch, session]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket) return;
|
||||
|
||||
const handleReconnect = () => {
|
||||
setIsLoadingSelfMessage(false);
|
||||
setIsLoadingAdvisorMessage(false);
|
||||
joinChat(chatId);
|
||||
fetchBalance(chatId);
|
||||
};
|
||||
|
||||
socket.on('connect', handleReconnect);
|
||||
socket.on('disconnect', () => {
|
||||
setIsLoadingSelfMessage(false);
|
||||
setIsLoadingAdvisorMessage(false);
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.off('connect', handleReconnect);
|
||||
socket.off('disconnect');
|
||||
};
|
||||
}, [socket, chatId, joinChat, fetchBalance]);
|
||||
|
||||
// clean up
|
||||
useEffect(() => {
|
||||
const handleBeforeUnload = () => {
|
||||
|
||||
@ -762,5 +762,63 @@ export const defaultPaywalls: { [key in EPlacementKeys]: IPaywall } = {
|
||||
"currency": "usd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"aura.placement.email.compatibility.discount": {
|
||||
"_id": "678ed4d62f5ba6ddbc7dd318",
|
||||
"key": "aura.paywall.email.compatibility.discount",
|
||||
"name": "Email Compatibility Discount",
|
||||
"properties": [
|
||||
{
|
||||
"key": "full.price",
|
||||
"value": "45",
|
||||
"_id": "678be264aaa17756a1e517df"
|
||||
},
|
||||
{
|
||||
"key": "discount",
|
||||
"value": "70",
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"key": "old.discount",
|
||||
"value": "-30%",
|
||||
"_id": "678d4093783493141ac1e545"
|
||||
},
|
||||
{
|
||||
"key": "new.discount",
|
||||
"value": "-50%",
|
||||
"_id": "678d4093783493141ac1e546"
|
||||
},
|
||||
{
|
||||
"key": "old.price",
|
||||
"value": "19",
|
||||
"_id": "678d4093783493141ac1e547"
|
||||
},
|
||||
{
|
||||
"key": "save",
|
||||
"value": "30",
|
||||
"_id": "678d4093783493141ac1e548"
|
||||
}
|
||||
],
|
||||
"products": [
|
||||
{
|
||||
"_id": "66589439ef0d180993cdb72f",
|
||||
"key": "compatibility.secret.discount.trial.0",
|
||||
"productId": "prod_PnStTEBzrPLgvL",
|
||||
"name": "Сompatibility AURA Secret Discount | Trial $0.99",
|
||||
"priceId": "price_1PpFlMIlX4lgwUxrUTeWDFoI",
|
||||
"type": "subscription",
|
||||
"description": "Description",
|
||||
"discountPrice": null,
|
||||
"discountPriceId": null,
|
||||
"isDiscount": false,
|
||||
"isFreeTrial": false,
|
||||
"isTrial": true,
|
||||
"price": 900,
|
||||
"trialDuration": 3,
|
||||
"trialPrice": 100,
|
||||
"trialPriceId": "price_1PpFoNIlX4lgwUxrP4l0lbE5",
|
||||
"currency": "usd"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,8 @@ import locales from './locales.ts'
|
||||
import { Currency } from '@/components/PaymentTable/Price.ts'
|
||||
import { symbolByCurrency } from './currency.ts'
|
||||
|
||||
// const environments = import.meta.env;
|
||||
|
||||
// export const getClientLocale = () => {
|
||||
// return navigator.language
|
||||
// }
|
||||
@ -71,7 +73,9 @@ export enum ELocalesPlacement {
|
||||
PalmistryV01 = "palmistry-v0_1",
|
||||
PalmistryV1 = "palmistry-v1",
|
||||
PalmistryV11 = "palmistry-v1_1",
|
||||
Chats = "chats"
|
||||
Chats = "chats",
|
||||
EmailMarketingCompatibilityV1 = "email-marketing-comp-v1",
|
||||
EmailMarketingPalmistryV2 = "email-marketing-palmistry-v2",
|
||||
}
|
||||
|
||||
interface ITranslationJSON {
|
||||
@ -86,6 +90,7 @@ export type TTranslationPlacements = Partial<
|
||||
|
||||
export const getTranslationsJSON = async (language: string): Promise<TTranslationPlacements> => {
|
||||
const api = createApi();
|
||||
// const isProduction = environments.MODE === "production" && window.location.hostname !== "localhost";
|
||||
let defaultLanguage = getDefaultLocaleByLanguage(language).toLowerCase();
|
||||
if (defaultLanguage === "pt") {
|
||||
defaultLanguage = "pt-pt"
|
||||
@ -94,17 +99,32 @@ export const getTranslationsJSON = async (language: string): Promise<TTranslatio
|
||||
const placements = Object.values(ELocalesPlacement).filter(placement => placement !== ELocalesPlacement.V0);
|
||||
|
||||
try {
|
||||
const responses = await Promise.all(
|
||||
placements.map(place =>
|
||||
api.getLocaleTranslations({
|
||||
funnel: place,
|
||||
locale: defaultLanguage
|
||||
})
|
||||
)
|
||||
const responses = await Promise.allSettled(
|
||||
placements.map(async place => {
|
||||
try {
|
||||
// return isProduction
|
||||
// ?
|
||||
return await api.getLocaleTranslations({
|
||||
funnel: place,
|
||||
locale: defaultLanguage
|
||||
})
|
||||
// : await (await fetch(`/locales/${place}/${defaultLanguage}/male_${defaultLanguage}.json`)).json()
|
||||
} catch (error) {
|
||||
console.error(`Ошибка загрузки переводов для ${place}:`, error);
|
||||
return null; // или можно вернуть пустой объект {}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
const result = responses.reduce((merged, current, index) => ({
|
||||
// Фильтруем успешные результаты
|
||||
const successfulResponses = responses
|
||||
.filter(result => result.status === 'fulfilled')
|
||||
.map(result => (result as PromiseFulfilledResult<any>).value)
|
||||
.filter(Boolean);
|
||||
|
||||
const result = successfulResponses.reduce((merged, current, index) => ({
|
||||
...merged,
|
||||
// [placements[index]]: isProduction ? current : { male: current }
|
||||
[placements[index]]: current
|
||||
}), {});
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ function LayoutSession() {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (flowChoice.length) {
|
||||
if (flowChoice?.length) {
|
||||
updateSession(
|
||||
{
|
||||
profile: {
|
||||
@ -31,7 +31,7 @@ function LayoutSession() {
|
||||
}, [flowChoice]);
|
||||
|
||||
useEffect(() => {
|
||||
if (partnerGender.length) {
|
||||
if (partnerGender?.length) {
|
||||
updateSession(
|
||||
{
|
||||
partner: {
|
||||
|
||||
@ -4,6 +4,8 @@ import NotFoundPage from "@/components/NotFoundPage";
|
||||
import MarketingLanding from "@/components/EmailMarketing/v1/pages/MarketingLanding";
|
||||
import styles from "./styles.module.scss";
|
||||
import SpecialOffer from "@/components/EmailMarketing/v1/pages/SpecialOffer";
|
||||
import SecretDiscount from "@/components/EmailMarketing/v1/pages/SecretDiscount";
|
||||
import SaveOff from "@/components/EmailMarketing/v1/pages/SaveOff";
|
||||
const removePrefix = (path: string) => path.replace(emailMarketingV1Prefix, "");
|
||||
|
||||
function MarketingLandingV1Routes() {
|
||||
@ -19,6 +21,14 @@ function MarketingLandingV1Routes() {
|
||||
path={removePrefix(routes.client.emailMarketingV1SpecialOffer())}
|
||||
element={<SpecialOffer />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(routes.client.emailMarketingV1SaveOff())}
|
||||
element={<SaveOff />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(routes.client.emailMarketingV1SecretDiscount())}
|
||||
element={<SecretDiscount />}
|
||||
/>
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
@ -13,7 +13,7 @@ const removePrefix = (path: string) => path.replace(palmistryV2Prefix, "");
|
||||
|
||||
function PalmistryV2Routes() {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(actions.palmistry.update({ fromRedesign: true }));
|
||||
@ -21,6 +21,10 @@ function PalmistryV2Routes() {
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
<Route
|
||||
path={removePrefix(routes.client.palmistryV2SecretDiscount())}
|
||||
element={<SecretDiscount />}
|
||||
/>
|
||||
<Route element={<LayoutPalmistryV2 />}>
|
||||
{/* <Route
|
||||
element={
|
||||
@ -29,24 +33,20 @@ function PalmistryV2Routes() {
|
||||
/>
|
||||
}
|
||||
> */}
|
||||
<Route
|
||||
path={removePrefix(routes.client.palmistryV2TrialPayment())}
|
||||
element={<TrialPayment />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(routes.client.palmistryV2SaveOff())}
|
||||
element={<SaveOff />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(routes.client.palmistryV2SecretDiscount())}
|
||||
element={<SecretDiscount />}
|
||||
/>
|
||||
{/* <Route
|
||||
<Route
|
||||
path={removePrefix(routes.client.palmistryV2TrialPayment())}
|
||||
element={<TrialPayment />}
|
||||
/>
|
||||
<Route
|
||||
path={removePrefix(routes.client.palmistryV2SaveOff())}
|
||||
element={<SaveOff />}
|
||||
/>
|
||||
{/* <Route
|
||||
path={removePrefix(routes.client.palmistryV1Payment())}
|
||||
element={<Payment />}
|
||||
/> */}
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Route>
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Route>
|
||||
{/* </Route> */}
|
||||
</Routes>
|
||||
);
|
||||
|
||||
@ -196,6 +196,8 @@ const routes = {
|
||||
// MarketingLandingV1
|
||||
emailMarketingV1Landing: () => [emailMarketingV1Prefix, "marketing-landing"].join("/"),
|
||||
emailMarketingV1SpecialOffer: () => [emailMarketingV1Prefix, "special-offer"].join("/"),
|
||||
emailMarketingV1SaveOff: () => [emailMarketingV1Prefix, "save-off"].join("/"),
|
||||
emailMarketingV1SecretDiscount: () => [emailMarketingV1Prefix, "secret-discount"].join("/"),
|
||||
// ABDesignV1
|
||||
genderV1: () => [host, "v1", "gender"].join("/"),
|
||||
questionnaireV1: () => [host, "v1", "questionnaire"].join("/"),
|
||||
|
||||
@ -27,6 +27,7 @@ const initialState: TPaywalls = {
|
||||
"aura.placement.chat": null,
|
||||
"aura.placement.email.palmistry": null,
|
||||
"aura.placement.email.palmistry.discount": null,
|
||||
"aura.placement.email.compatibility.discount": null,
|
||||
isMustUpdate: {
|
||||
"aura.placement.v1.mike": true,
|
||||
"aura.placement.main": true,
|
||||
@ -38,6 +39,7 @@ const initialState: TPaywalls = {
|
||||
"aura.placement.chat": true,
|
||||
"aura.placement.email.palmistry": true,
|
||||
"aura.placement.email.palmistry.discount": true,
|
||||
"aura.placement.email.compatibility.discount": true,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user