diff --git a/public/locales/v1/en/male_en.json b/public/locales/v1/en/male_en.json index eb6c54c..e56bf47 100644 --- a/public/locales/v1/en/male_en.json +++ b/public/locales/v1/en/male_en.json @@ -683,6 +683,12 @@ "description": "By continuing you agree that if you don't cancel prior to the end of the -days trial, you will automatically be charged for the introductory period of 14 days thereafter the standard rate of every 14 days until you cancel in settings. Learn more about cancellation and refund policy in Subscription terms." }, "/trial-choice": { + "text_variant_1": "AURA is the only accurate app with reliable astrological predictions, verified by professionals and guaranteed to provide accurate astrological forecasts.

AURA has already helped millions of people find happiness and discover the whole truth about their relationships.

An astrological forecast that will completely change your life is almost ready! Before we provide it to you, we would like to offer you the opportunity to choose the amount you consider reasonable to try AURA for days and which you think is fair for the changes that will happen to you:

- You will discover all the most intimate secrets that the stars have prepared for you and solve relationship issues within just one month;
- You will once and for all put the finishing touches on unresolved issues and forget about problems that have been haunting you for years (if not decades);
- You will save hundreds of dollars on fake and unprofessional astrological predictions and fortune tellers;
- You will receive not only a personal astrological forecast but also personalized daily horoscopes, learn who and how is draining your energy, and get other personalized readings.

", + "text_variant_1_span": "A -day trial period costs us $5, but please choose the amount that suits you best:", + "text_variant_2": "AURA is the only app you can trust for accurate astrological insights, crafted and verified by seasoned professionals, ensuring you receive predictions that are both reliable and transformative.

AURA has already transformed the lives of millions, bringing clarity, joy, and a deeper understanding of their relationships.

Your life-changing astrological forecast is almost ready! But before we reveal the secrets that will change your life, we want to give you the freedom to choose how much you feel is fair to try AURA for days. This is your chance to decide what the transformation is worth to you:

- Uncover the deepest, most intimate secrets the stars have in store for you, and watch your relationship issues resolve in just one month;
- Finally, put an end to those lingering issues that have been troubling you for years, maybe even decades;
- Save hundreds of dollars by avoiding unreliable astrologers and fake fortune tellers;
- Receive not just a personal astrological forecast but also personalized daily horoscopes, learn who's draining your energy, and get exclusive, tailored readings.

", + "text_variant_2_span": "While a -day trial costs us $5, we want you to choose the amount you believe is right for you:", + "text_variant_3": "Discover AURA—the only app that delivers truly accurate astrological forecasts, with predictions you can trust, all verified by top industry professionals. Your path to clarity and happiness starts here.

Millions have already found happiness and uncovered the truth about their relationships with AURA. Now, it’s your turn.

Your life-changing astrological forecast is almost ready! Before we share this powerful insight with you, we’re giving you the chance to set your own price to experience AURA for days. You decide what feels right for the life-changing revelations you’ll receive:

- Reveal the deepest secrets the universe has in store for you and resolve your relationship dilemmas within a month;
- Finally close the chapter on long-standing issues that have plagued you for years, perhaps even decades;
- Avoid wasting hundreds of dollars on untrustworthy, fake astrologers;
- Gain access to your personal astrological forecast, receive daily personalized horoscopes, learn who’s been draining your energy, and benefit from other insightful readings.

", + "text_variant_3_span": "Our -day trial typically costs us $5, but you get to choose the amount that feels right for you:", "button": "Choose an amount that you think is reasonable." }, "skip_trial": "Skip Trial", diff --git a/public/old-backend/auraweb.json b/public/old-backend/auraweb.json new file mode 100644 index 0000000..d498b41 --- /dev/null +++ b/public/old-backend/auraweb.json @@ -0,0 +1 @@ +{"data":{"subscription_popup":true,"locale":"en","alerts":{},"version":"5.0","apple_music_api":{"jwt":"eyJhbGciOiJFUzI1NiIsImtpZCI6IlRSUDZXTUtERFYifQ.eyJpc3MiOiJLVEFMQTg4WkNSIiwiaWF0IjoxNzM2OTUyODM3LCJleHAiOjE3MzcwODI0Mzd9.0XjbuMOZ_7EcejBb5WEQxWH0tAkd1VqbbFJnYDDaKgpoLBd2fM7nYYHEOugKnmfWaJJt-OguJpmKI6opG1ilPg"},"chargebee":{"site":"kefirapp-test","publishableKey":"test_VtWSamZEfP175nqGZhkD0uvoouHieElv"},"first_open_subscription_popup":true,"runs_before_subscription_popup":0,"stripe_public_key":"pk_live_51Ndqf4IlX4lgwUxrdyEW5BKH30OLEYemyVj3XFqi3RNx3K149o0jNQEswuIutBXNQ4CeqJuODh6OMT9I3r1fq3VT00ncnJjWov","smartlook_manage":false,"appirater_alerts":[],"active_iaps":[{"bundle_id":"auraweb.yearlymembership","active":true,"subscription_type":"yearly"},{"bundle_id":"auraweb.weeklymembership","active":true,"subscription_type":"trial"},{"bundle_id":"auraweb.weekmembership","active":true,"subscription_type":"trial"},{"bundle_id":"auraweb.monthlymembership","active":true,"subscription_type":"monthly"}]}} \ No newline at end of file diff --git a/public/old-backend/elements.json b/public/old-backend/elements.json new file mode 100644 index 0000000..6d29005 --- /dev/null +++ b/public/old-backend/elements.json @@ -0,0 +1 @@ +{"data":{"variant":"a","groups":[]}} \ No newline at end of file diff --git a/public/old-backend/t.json b/public/old-backend/t.json new file mode 100644 index 0000000..962e88e --- /dev/null +++ b/public/old-backend/t.json @@ -0,0 +1 @@ +{"translations":[{"key":"chat.inapppush.title","value":"{name} Astrologer"},{"key":"chat.inapppush.message","value":"New message"},{"key":"au.my_horoscope.loading4","value":"Today's Forecast Compilation"},{"key":"au.my_horoscope.loading3","value":"Prediction Crafting"},{"key":"au.my_horoscope.loading2","value":"Love Energy Analysis"},{"key":"au.my_horoscope.loading1","value":"Astrological Data Analysis"},{"key":"au.my_horoscope.loading_title","value":"In-Depth Personal Analysis and Daily Horoscope"},{"key":"aura.chakras.onboarding","value":"Chat with a professional Advisor and get answers to your pressing questions"},{"key":"aura.stat_fields.feelings","value":"LOVE PARTNER'S FEELINGS"},{"key":"aura.stat_fields.happiness","value":"LOVE RELATIONSHIP PROSPECTS"},{"key":"aura.3.loading","value":"Love Ex Analysis"},{"key":"aura.2.loading","value":"Love Partner's feelings Analysis"},{"key":"aura.1.loading","value":"Love Relationship Prospects Analysis"},{"key":"aura.stat_fields.energy_loss","value":"SELF-LOVE"},{"key":"aura.stat_fields.energy_loss.negative","value":"SELF-LOVE"},{"key":"aura.stat_fields.self_control","value":"LOVE EX"},{"key":"au.ar.no","value":"Do not use AR"},{"key":"au.ar.yes","value":"Continue"},{"key":"aura_subscrption.text_end_proplan2","value":"And then $59.99 forever. No commitment. Cancel anytime."},{"key":"aura_subscrption.text_end","value":"✔️Secured with iTunes.3-Day Free Trial. And then $59.99 per year. Cancel anytime."},{"key":"auweb.button.next","value":"TRY NOW"},{"key":"au.awareness_purchase.text_under_button","value":"$99.99 one time purchase. No commitment."},{"key":"au.awareness_purchase.text_above_button","value":"One-time purchase $99.99"},{"key":"au.awareness_purchase.button","value":"Start Awareness"},{"key":"au.awareness_purchase.text1","value":"You have used one awareness for today. Activate unlimited awarenesses for $99.99, valid forever, right now!"},{"key":"au.awareness_purchase.title","value":"Awareness PRO Plan"},{"key":"au.question.text2","value":"Are you adventurous person?"},{"key":"au.question.text1","value":"Do you enjoy time spent alone?"},{"key":"au.compatibility_purchase.text_under_button","value":"$99.99 one time purchase.No commitment."},{"key":"au.compatibility_purchase.text_above_button","value":"One-time purchase $99.99"},{"key":"au.compatibility_purchase.button","value":"Start Compatibility"},{"key":"au.compatibility_purchase.text1","value":"You have used one compatibility per day. Activate an unlimited number of compatibilities for $99.99 forever, right now!"},{"key":"au.compatibility_purchase.title","value":"Unlimited Compatibility Access"},{"key":"auweb.pay.information","value":"You'll pay %@ today for your 3-day trial, and then nineteen dollars every two weeks after your trial ends."},{"key":"au.ball.first_answer","value":"It's better for you not to know about this right now."},{"key":"au.one_time_purchase.unlock_time","value":"Get it all now for $39.99"},{"key":"aura_subscrption.trial.billed","value":"then $7.99 \r\nper week"},{"key":"au.meditations.button_awareness","value":"Awareness"},{"key":"au.meditations.button_breath","value":"Breath"},{"key":"au.meditations.text1","value":"Our unique practices will help you restore your energy balance, improve your well-being, and learn more about yourself. Let's begin!"},{"key":"au.meditations.title","value":"Meditations"},{"key":"au.crystal_ball.text_under_button","value":"$19.99 one time purchase.No commitment."},{"key":"au.crystal_ball.text_above_button","value":"One-time purchase $19.99"},{"key":"au.crystal_ball.button","value":"Start Crystal Ball"},{"key":"au.crystal_ball.text1","value":"The prediction ball is ready for your use. Activate unlimited answers from the ball for just $19.99 forever, right now!"},{"key":"au.crystal_ball.title","value":"Crystal Ball"},{"key":"aura.warming_up.body","value":"The analysis of your Aura revealed a negative energy influence on you and a partial positive one. Such a combination is very rare! Which one would you like to start with?"},{"key":"aura.warming_up.button","value":"GOOD BAD"},{"key":"subscription_hint.array","value":"And then %@ per three month. No commitment. Cancel anytime."},{"key":"subscription_hint.yearly","value":"And then %@ per year. Cancel anytime."},{"key":"subscription_hint.monthly","value":"And then %@ per month. No commitment. Cancel anytime."},{"key":"aura.4.loading","value":"Crafting Restorative Practices"},{"key":"aura.1.clap","value":"You're doing great"},{"key":"aura.2.clap","value":"There's just a little left"},{"key":"au.2_week.web","value":"2-Week Plan\r\n$1.46 per day"},{"key":"aura_subscrption.monthly.try","value":"A MONTH FOR"},{"key":"aura_subscrption.monthly.price","value":"$19.99"},{"key":"aura_subscrption.monthly.billed","value":"billed every\r\n month"},{"key":"aura_subscrption.quarterly.try","value":"3 MONTHS FOR"},{"key":"aura_subscrption.quarterly.billed","value":"billed every\r\n3 months"},{"key":"aura_subscrption.quarterly.price","value":"$39.99"},{"key":"aura_subscrption.trial.try","value":"TRY 3 DAYS FOR"},{"key":"aura_subscrption.trial.price","value":"FREE"},{"key":"aura_subscrption.hot_deal.auto_renewable","value":"auto-renewable"},{"key":"aura_subscrption.hot_deal","value":"HOT DEAL"},{"key":"zodiac_signs.metas.mode_element","value":"Mode + Element"},{"key":"aura.money_compatibility.button","value":"low MONEY energy. Determine who drains your energy"},{"key":"aura.sexual_compatibility.button","value":"Discover who's draining your SEXUAL energy"},{"key":"aura.friendship_compatibility.button","value":"Find out who drains your FRIENDSHIP energy"},{"key":"aura.name_person.body","value":"Write the name of the person with whom you want to check compatibility."},{"key":"awareness.title","value":"Focus on your chakras, repeat the following:"},{"key":"aura.choose_age.body","value":"Select what exactly you want to know."},{"key":"forecast.review","value":"Based on your aura you are in a playful mood today. You should be able to infect others with that spirit easily. Make games out of work and chores and try not to take anything too seriously."},{"key":"aura.breath_compatibility.button","value":"Discover who is causing your financial energy loss."},{"key":"breathe.title","value":"Stop and breathe to help you relax and focus on what really matters."},{"key":"breathe.subtitle","value":"Breathing practice will help improve your aura. Breath in the positive energy, breathe out the negative..."},{"key":"aura.compatibility_breath.button","value":"Use all the power of your Aura"},{"key":"awareness.subtitle","value":"…"},{"key":"subscription_hint.trial","value":"And then %@ per week. No commitment. Cancel anytime."},{"key":"aura_subscrption.yearly.try","value":"YEARLY FOR"},{"key":"au.web_onbording.start","value":"Our system offers you to undergo practices to improve energies where you have a leakage. Click to begin."},{"key":"aura.aura.onboarding","value":"Our system offers you to undergo practices to improve energies where you have a leakage. Click to begin."},{"key":"aura.breathing.onboarding","value":"Relaxation Through Breathing - allow yourself a minute of doing nothing and fill yourself with pure energy. A unique breathing practice with augmented reality."},{"key":"aura.compatibility.onboarding","value":"Your Compatibility with close people. Find out who drains your energy, and who fills you with the energy of love."},{"key":"aura.begin_breathe.button","value":"BEGIN"},{"key":"aura.personal_aura.button","value":"Receive an In-Depth Analysis and Today's Horoscope"},{"key":"aura_subscrption.text_end_proplan","value":" Unlimited access to Check Compatibility."},{"key":"aura.moons.onboarding","value":"Here you will learn everything about your Zodiac sign and download wallpapers with your sign. IMPORTANT - new wallpapers appear every day"},{"key":"aura.breath_in.text","value":"Breath_in"},{"key":"aura.breath_out.text","value":"Breath_out"},{"key":"aura.breath_relax.text","value":"Breath & Relax"},{"key":"aura.warmin_good.button","value":"GOOD"},{"key":"aura.attention.title","value":"ATTENTION"},{"key":"aura.name_1.review","value":"Samantha Green"},{"key":"aura.name_2.review","value":"James Wilson"},{"key":"au.web_onbording.name","value":"Write the name of the person with whom you want to check Сompatibility."},{"key":"au.web_onbording.date","value":"Choose his birthdate."},{"key":"au.web_onbording.category","value":"Write the name of the person with whom you want to check Сompatibility."},{"key":"au.web_onbording.moon","value":"Here you will learn everything about your Zodiac sign and download wallpapers with your sign. IMPORTANT - new wallpapers appear every day."},{"key":"aura.review_2.content","value":"I don't know why, but I always had bad experiences in relationships and couldn't find the one who would understand and love me. So, I took a special extended test and immediately figured everything out. It turns out that the birth date of the chosen one and number coincidences are very important in relationships. Now I consider this in all areas, and it helps me a lot."},{"key":"aura.review_1.content","value":"As for me, money just doesn't stick with me at all. It turned out that it wasn't because I was bad or irresponsible, but it was because of my partner. We delved deeply into this issue thanks to Aura, and what do you think? Everything changed the very next day. Now we are happy and wealthy."},{"key":"au.web_onbording.breathing","value":"Relaxation Through Breathing - allow yourself a minute of doing nothing and fill yourself with pure energy. A unique breathing practice with augmented reality."},{"key":"au.web_onbording.compatibility","value":"Your Compatibility with close people. Find out who drains your energy, and who fills you with the energy of love."},{"key":"aura_subscrption.yearly.price","value":"$59.99"},{"key":"aura.web.price_selection","value":"Money shouldn't get in the way of finding something for your well-being"},{"key":"aura_subscrption.yearly.billed","value":"billed yearly"},{"key":"aura.web.email_title","value":"We will email you a copy for easy access."},{"key":"au.friends.window","value":"Your two Friends are already using the full power of Aura"},{"key":"au.more_llc.button","value":"More About "},{"key":"au.name.my_name","value":"My Name"},{"key":"aura.warmin_bad.button","value":"BAD"},{"key":"au.try_for.button","value":"$1 - Try Now"},{"key":"au.get50.only","value":"GET Lower Price Today Only"},{"key":"au.name.person","value":"The Person’s Name"},{"key":"au.onboarding.my_name","value":"Your Name for Compatibility"},{"key":"au.free_trial_web.week","value":"Use 7-Days Trial"},{"key":"aura.ten_breath.button","value":"Increase up to 10%. Practice for the Energy of Money"},{"key":"au.moons.button","value":"Find out about Friends or Family"},{"key":"au.web.pay_good_title","value":"Your payment was successful"},{"key":"auweb.pay_good.text1","value":"Thank You!"},{"key":"auweb.pay_good.title","value":"Your payment was successful"},{"key":"auweb.pay_good.button","value":"Next"},{"key":"auweb.pay_bad.text1","value":"1. You entered the correct data during checkout\r\n\r\n2. You have enough funds available\r\n\r\n3. Your card can be used for international payment"},{"key":"auweb.pay_bad.text2","value":"If you're still having trouble, try a different payment method."},{"key":"auweb.pay_bad.button","value":"Update details"},{"key":"auweb.pay_bad.title","value":"If you're still having trouble, try a different payment method."},{"key":"aura_subscrption.title_moons","value":"To continue"},{"key":"aura.choose_birthdate.body","value":"Choose the birthdate of the person with whom you want to check compatibility."},{"key":"au.anxiety.category","value":"Anxiety"},{"key":"au.calm.category","value":"Calm"},{"key":"au.focus.category","value":"Focus"},{"key":"au.relationships.category","value":"Relationships"},{"key":"au.sleep.category","value":"Sleep"},{"key":"au.birds.category","value":"Birds"},{"key":"au.bonfire.category","value":"Bonfire"},{"key":"au.cat.category","value":"Cat"},{"key":"au.metropolis.category","value":"Metropolis"},{"key":"au.rain.category","value":"Rain"},{"key":"au.waterfall.category","value":"Waterfall"},{"key":"au.sea.category","value":"Sea"},{"key":"aura_subscrption.text_compatibility","value":" Discover everything about this person without limitations."},{"key":"aura_subscrption.text_horoscope","value":" Your extended Analysis and personal Horoscope for today are ready."},{"key":"aura_subscrption.text_moons","value":" You've read halfway about yourself."},{"key":"aura_subscrption.title_horoscope","value":"Personal Analysis"},{"key":"aura_subscrption.title_compatibility","value":"To continue"},{"key":"au.magic.title","value":"Welcome to the Magic Prediction Ball!"},{"key":"au.magic.text1","value":"Ask a question that can be answered with a \"yes\" or \"no\", and I'll try to predict your future. When you're ready, pose your question and I'll reveal what the ball predicts."},{"key":"auweb.agree.text1","value":"By proceeding, you agree that if you do not cancel your subscription before the end of the 7-day trial period, you will be automatically charged sixty nine US dollars ninty nine cents annual until you cancel the subscription in the settings."},{"key":"au.besties.title","value":"Preparing data on"},{"key":"au.besties.loading1","value":" Besties' Horoscope 1"},{"key":"au.besties.loading2","value":" Besties' Horoscope 2"},{"key":"au.besties.loading3","value":" Besties' Horoscope 3"},{"key":"au.besties.loading4","value":" Besties' Horoscope 4"},{"key":"au.besties.result","value":"Today's Horoscope with"},{"key":"au.prediction_moons.loading_title","value":"Preparing data"},{"key":"au.prediction_moons.loading1","value":"Moons losding 1"},{"key":"au.prediction_moons.loading2","value":"Moons losding 2"},{"key":"au.prediction_moons.loading3","value":"Moons losding 3"},{"key":"au.prediction_moons.loading4","value":"Moons losding 4"},{"key":"au.prediction_moons.result_title","value":"Forecast based on your moons"},{"key":"au.name_horoscope.loading_title","value":"Preparing Name"},{"key":"au.name_horoscope.loading1","value":"Name's Horoscope 1"},{"key":"au.name_horoscope.loading2","value":"Name's Horoscope 2"},{"key":"au.name_horoscope.loading3","value":"Name's Horoscope 3"},{"key":"au.name_horoscope.loading4","value":"Name's Horoscope 4"},{"key":"au.thermal_compatibility.loading_title","value":"Preparing Thermal"},{"key":"au.thermal_compatibility.loading1","value":"Thermal Compatibility 1"},{"key":"au.thermal_compatibility.loading2","value":"Thermal Compatibility 2"},{"key":"au.thermal_compatibility.loading3","value":"Thermal Compatibility 3"},{"key":"au.thermal_compatibility.loading4","value":"Thermal Compatibility 4"},{"key":"au.moonse_phase.loading_title","value":"Preparing Moon Phase"},{"key":"au.moonse_phase.loading1","value":"Moon Phase 1"},{"key":"au.moonse_phase.loading2","value":"Moon Phase 2"},{"key":"au.moonse_phase.loading3","value":"Moon Phase 3"},{"key":"au.moonse_phase.loading4","value":"Moon Phase 4"},{"key":"au.moonse_phase.result_title","value":"Impact on Your Personal Energy Today"},{"key":"au.energy_vampirism.loading_title","value":"Preparing data Vampirism"},{"key":"au.energy_vampirism.loading4","value":"Vampirism 4"},{"key":"au.energy_vampirism.loading3","value":"Vampirism 3"},{"key":"au.energy_vampirism.loading2","value":"Vampirism 2"},{"key":"au.energy_vampirism.loading1","value":"Vampirism 1"},{"key":"au.energy_vampirism.result_title","value":" Energy Vampirism Today"},{"key":"au.thermal_compatibility.result_title","value":"Thermal Aura Compatibility for Today with "},{"key":"au.moonse_phase.preview","value":"Discover the Impact on Your Personal Energy"},{"key":"au.energy_vampirism.preview","value":"Find out if you're an energy vampire today or not?"}],"meta":{"locale":"en"}} \ No newline at end of file diff --git a/public/v1/email-marketing/messages.png b/public/v1/email-marketing/messages.png deleted file mode 100644 index fd2e590..0000000 Binary files a/public/v1/email-marketing/messages.png and /dev/null differ diff --git a/public/v1/email-marketing/messages.svg b/public/v1/email-marketing/messages.svg new file mode 100644 index 0000000..10e1bc1 --- /dev/null +++ b/public/v1/email-marketing/messages.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/v1/email-marketing/payments.png b/public/v1/email-marketing/payments.png new file mode 100644 index 0000000..6c60729 Binary files /dev/null and b/public/v1/email-marketing/payments.png differ diff --git a/public/v1/email-marketing/smartphone.png b/public/v1/email-marketing/smartphone.png deleted file mode 100644 index c2d2981..0000000 Binary files a/public/v1/email-marketing/smartphone.png and /dev/null differ diff --git a/public/v1/email-marketing/smartphone.svg b/public/v1/email-marketing/smartphone.svg new file mode 100644 index 0000000..5c24c44 --- /dev/null +++ b/public/v1/email-marketing/smartphone.svg @@ -0,0 +1,706 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/api/api.ts b/src/api/api.ts index 6cf58ac..cc20704 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -2,15 +2,12 @@ import { createMethod } from './utils' import { User, Auras, - Element, Elements, AuthTokens, Apps, Assets, AssetCategories, DailyForecasts, - SubscriptionItems, - SubscriptionCheckout, SubscriptionStatus, AICompatCategories, AICompats, @@ -18,9 +15,6 @@ import { UserCallbacks, Translations, Zodiacs, - GoogleAuth, - SubscriptionPlans, - AppleAuth, AIRequestsV2, Assistants, OpenAI, @@ -41,12 +35,10 @@ import { } from './resources' const api = { - auth: createMethod(AuthTokens.createRequest), - appleAuth: createMethod(AppleAuth.createRequest), - googleAuth: createMethod(GoogleAuth.createRequest), + // auth: createMethod(AuthTokens.createRequest), getRealToken: createMethod(AuthTokens.createGetRealTokenRequest), getAppConfig: createMethod(Apps.createRequest), - getElement: createMethod(Element.createRequest), + // getElement: createMethod(Element.createRequest), getElements: createMethod(Elements.createRequest), getUser: createMethod(User.createGetRequest), updateUser: createMethod(User.createPatchRequest), @@ -54,9 +46,8 @@ const api = { getAssetCategories: createMethod(AssetCategories.createRequest), getDailyForecasts: createMethod(DailyForecasts.createRequest), getAuras: createMethod(Auras.createRequest), - getSubscriptionItems: createMethod(SubscriptionItems.createRequest), - getSubscriptionPlans: createMethod(SubscriptionPlans.createRequest), - getSubscriptionCheckout: createMethod(SubscriptionCheckout.createRequest), + // getSubscriptionPlans: createMethod(SubscriptionPlans.createRequest), + // getSubscriptionCheckout: createMethod(SubscriptionCheckout.createRequest), getSubscriptionStatus: createMethod(SubscriptionStatus.createRequest), // new get subscription status getSubscriptionStatusNew: createMethod(SubscriptionStatus.createRequestNew), @@ -101,6 +92,7 @@ const api = { // Session createSession: createMethod(Session.createRequest), updateSession: createMethod(Session.updateRequest), + getLocaleTranslations: createMethod(Session.getLocaleRequest), // Chats getChatsCategories: createMethod(ChatsCategories.getRequest), getChatMessages: createMethod(ChatMessages.getRequest), diff --git a/src/api/resources/AppleAuth.ts b/src/api/resources/AppleAuth.ts deleted file mode 100644 index da00711..0000000 --- a/src/api/resources/AppleAuth.ts +++ /dev/null @@ -1,13 +0,0 @@ -import routes from "@/routes"; - -export interface Payload { - origin: string; -} - -export type Response = unknown; - -export const createRequest = ({ origin }: Payload): Request => { - - const url = new URL(routes.server.appleAuth(origin)); - return new Request(url, { method: "POST" }); -}; diff --git a/src/api/resources/AuthTokens.ts b/src/api/resources/AuthTokens.ts index 6e3c976..e9d83e4 100644 --- a/src/api/resources/AuthTokens.ts +++ b/src/api/resources/AuthTokens.ts @@ -1,7 +1,7 @@ import routes from "@/routes"; import { AuthToken } from "../types"; import { User } from "./User"; -import { getAuthHeaders, getBaseHeaders } from "../utils"; +import { getAuthHeaders } from "../utils"; export interface PayloadRegisterByEmail { email: string; @@ -13,7 +13,7 @@ export interface PayloadAuthWithJWT { jwt: string; } -export type Payload = PayloadRegisterByEmail | PayloadAuthWithJWT; +// export type Payload = PayloadRegisterByEmail | PayloadAuthWithJWT; export interface Response { auth: { @@ -36,11 +36,11 @@ export interface JwtPayload { iss: string; } -export const createRequest = (payload: Payload): Request => { - const url = new URL(routes.server.token()); - const body = JSON.stringify({ auth: { ...payload } }); - return new Request(url, { method: "POST", headers: getBaseHeaders(), body }); -}; +// export const createRequest = (payload: Payload): Request => { +// const url = new URL(routes.server.token()); +// const body = JSON.stringify({ auth: { ...payload } }); +// return new Request(url, { method: "POST", headers: getBaseHeaders(), body }); +// }; export interface PayloadGetRealToken { token: string; diff --git a/src/api/resources/Element.ts b/src/api/resources/Element.ts deleted file mode 100644 index 0de9cb8..0000000 --- a/src/api/resources/Element.ts +++ /dev/null @@ -1,31 +0,0 @@ -import routes from '@/routes' -import { getBaseHeaders } from '../utils' - -export interface Payload { - locale: string - type: string -} - -export interface Response { - data: { - variant: string - element: Element - } -} - -export interface Element { - type: string - href: string - title: string - url_slug: string - body: string -} - -export const createRequest = ({ locale, type }: Payload): Request => { - const url = new URL(routes.server.element(type)) - const query = new URLSearchParams({ locale }) - - url.search = query.toString() - - return new Request(url, { method: 'GET', headers: getBaseHeaders() }) -} diff --git a/src/api/resources/GoogleAuth.ts b/src/api/resources/GoogleAuth.ts deleted file mode 100644 index 4f66cee..0000000 --- a/src/api/resources/GoogleAuth.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface Payload { - requestUrl: string; -} - -export interface Response { - access_token: string; -} - -export const createRequest = ({ requestUrl }: Payload): Request => { - const url = new URL(requestUrl); - return new Request(url, { - method: "GET", - }); -}; diff --git a/src/api/resources/Paywall.ts b/src/api/resources/Paywall.ts index 4c303f4..16a1cfe 100644 --- a/src/api/resources/Paywall.ts +++ b/src/api/resources/Paywall.ts @@ -17,7 +17,9 @@ export enum EPlacementKeys { "aura.placement.secret.discount" = "aura.placement.secret.discount", "aura.placement.palmistry.main" = "aura.placement.palmistry.main", "aura.placement.palmistry.redesign" = "aura.placement.palmistry.redesign", - "aura.placement.chat" = "aura.placement.chat" + "aura.placement.chat" = "aura.placement.chat", + "aura.placement.email.palmistry" = "aura.placement.email.palmistry", + "aura.placement.email.palmistry.discount" = "aura.placement.email.palmistry.discount" } export interface ResponseGetSuccess { diff --git a/src/api/resources/Session.ts b/src/api/resources/Session.ts index 4a2d479..85b945b 100644 --- a/src/api/resources/Session.ts +++ b/src/api/resources/Session.ts @@ -2,6 +2,7 @@ import routes from "@/routes"; import { getBaseHeaders } from "../utils"; import { IUTM } from "@/store/utm"; import { ICreateAuthorizeUser } from "./User"; +import { ELocalesPlacement } from "@/locales"; export interface PayloadCreate { feature: string, // Type: string @@ -78,6 +79,15 @@ export interface ResponseUpdate { message: "Session updated" | string } +export interface ResponseGetLocale { + male: Record + female: Record + fallback: { + male: Record, + female: Record + } +} + export const createRequest = (data: PayloadCreate) => { const url = new URL(routes.server.createSession()); const body = JSON.stringify(data); @@ -97,3 +107,18 @@ export const updateRequest = ({ data, sessionId }: PayloadUpdate) => { headers: getBaseHeaders() }); }; + +export interface PayloadGetLocale { + funnel: ELocalesPlacement, + locale: string +} + +export const getLocaleRequest = (data: PayloadGetLocale) => { + const url = new URL(routes.server.getLocale()); + const body = JSON.stringify(data); + return new Request(url, { + method: "POST", + body, + headers: getBaseHeaders() + }); +}; \ No newline at end of file diff --git a/src/api/resources/SubscriptionPlans.ts b/src/api/resources/SubscriptionPlans.ts deleted file mode 100644 index 3e91f12..0000000 --- a/src/api/resources/SubscriptionPlans.ts +++ /dev/null @@ -1,43 +0,0 @@ -import routes from "@/routes"; - -export interface Payload { - locale: string; -} - -export interface Response { - sub_plans: ISubscriptionPlan[]; -} - -export interface ISubscriptionPlan { - id: string; - name: string; - desc: string; - provider: "stripe" | "paypal"; - interval: "week" | "month" | "year"; - price_cents: number; - trial: ITrial | null; -} - -export interface ITrial { - is_paid: boolean; - is_free: boolean; - days: number; - price_cents: number; -} - -export interface AssetMetadata { - size: number; - width: number; - height: number; - filename: string; - mime_type: string; -} - -export const createRequest = ({ locale }: Payload): Request => { - const url = new URL(routes.server.subscriptionPlans()); - const query = new URLSearchParams({ locale }); - - url.search = query.toString(); - - return new Request(url, { method: "GET" }); -}; diff --git a/src/api/resources/UserSubscriptionCheckout.ts b/src/api/resources/UserSubscriptionCheckout.ts deleted file mode 100644 index d375f90..0000000 --- a/src/api/resources/UserSubscriptionCheckout.ts +++ /dev/null @@ -1,35 +0,0 @@ -import routes from "@/routes" -import { AuthPayload } from "../types" -import { getAuthHeaders } from "../utils" - -export interface Payload extends AuthPayload { - embed?: boolean - locale: string - itemPriceId: string -} - -export interface Response { - hosted_page: string -} - -export interface HostedPage { - id: string - url: string - embed: boolean - type: string - object: string - state: string - resource_version: number - created_at: number - updated_at: number - expires_at: number -} - -export const createRequest = ({ locale, token, itemPriceId, embed = false }: Payload): Request => { - const url = new URL(routes.server.subscriptionCheckout()) - const query = new URLSearchParams({ locale, item_price_id: itemPriceId, embed: embed.toString() }) - - url.search = query.toString() - - return new Request(url, { method: 'GET', headers: getAuthHeaders(token) }) -} diff --git a/src/api/resources/UserSubscriptionItemPrices.ts b/src/api/resources/UserSubscriptionItemPrices.ts deleted file mode 100644 index b8f0194..0000000 --- a/src/api/resources/UserSubscriptionItemPrices.ts +++ /dev/null @@ -1,40 +0,0 @@ -import routes from "@/routes" -import { AuthPayload } from "../types" -import { getAuthHeaders } from "../utils" - -export interface Payload extends AuthPayload { - locale: string -} - -export interface Response { - item_prices: ItemPrice[] -} - -export interface ItemPrice { - currency_code: string - external_name: string - free_quantity: number - id: string - is_taxable: boolean - item_id: string - item_type: string - name: string - object: string - period: number - period_unit: string - price: number - pricing_model: string - resource_version: number - status: string - created_at: number - updated_at: number -} - -export const createRequest = ({ locale, token }: Payload): Request => { - const url = new URL(routes.server.subscriptionItems()) - const query = new URLSearchParams({ locale }) - - url.search = query.toString() - - return new Request(url, { method: 'GET', headers: getAuthHeaders(token) }) -} diff --git a/src/api/resources/UserSubscriptionReceipts.ts b/src/api/resources/UserSubscriptionReceipts.ts deleted file mode 100644 index 1bddd7b..0000000 --- a/src/api/resources/UserSubscriptionReceipts.ts +++ /dev/null @@ -1,104 +0,0 @@ -import routes from "@/routes"; -import { AuthPayload } from "../types"; -import { getAuthHeaders } from "../utils"; - -export interface GetPayload extends AuthPayload { - id: string; -} - -export interface AppleReceiptPayload extends AuthPayload { - receiptData: string; - autorenewable?: boolean; - sandbox?: boolean; -} - -export interface StripeReceiptPayload extends AuthPayload { - way: "stripe"; - subscription_receipt: { - sub_plan_id: string; - }; -} - -export type Payload = - | AppleReceiptPayload - | StripeReceiptPayload - -export interface Response { - subscription_receipt: SubscriptionReceipt; -} - -export interface SubscriptionReceipt { - id: string; - user_id: number; - status: number; - expires_at: null | string; - requested_at: string; - created_at: string; - data: { - input: { - subscription_items: [ - { - item_price_id: string; - } - ]; - payment_intent: { - gw_token: string; - gateway_account_id: string; - }; - }; - client_secret: string; - app_bundle_id: string; - autorenewable: boolean; - error: string; - stripe_status?: string; - checkout_url?: string; - checkout_session?: unknown; - }; -} - -function createRequest({ - token, - receiptData, - autorenewable, - sandbox, -}: AppleReceiptPayload): Request; -function createRequest({ token }: StripeReceiptPayload): Request; -function createRequest(payload: Payload): Request; -function createRequest(payload: Payload): Request { - const url = new URL(routes.server.subscriptionReceipts()); - const data = getDataPayload(payload); - const body = JSON.stringify(data); - return new Request(url, { - method: "POST", - headers: getAuthHeaders(payload.token), - body, - }); -} - -function getDataPayload(payload: Payload) { - if ("receiptData" in payload) { - return { - way: "apple", - subscription_receipt: { - receipt_data: payload.receiptData, - autorenewable: payload.autorenewable, - sandbox: payload.sandbox, - }, - }; - } - if ("way" in payload && payload.way === "stripe") { - return { - way: "stripe", - subscription_receipt: { - sub_plan_id: payload.subscription_receipt.sub_plan_id, - }, - }; - } -} - -function createGetRequest({ id, token }: GetPayload): Request { - const url = new URL(routes.server.subscriptionReceipt(id)); - return new Request(url, { method: "GET", headers: getAuthHeaders(token) }); -} - -export { createRequest, createGetRequest }; diff --git a/src/api/resources/index.ts b/src/api/resources/index.ts index 988bd4c..0588b21 100644 --- a/src/api/resources/index.ts +++ b/src/api/resources/index.ts @@ -4,11 +4,8 @@ export * as Apps from "./Apps"; export * as User from "./User"; export * as DailyForecasts from "./UserDailyForecasts"; export * as Auras from "./Auras"; -export * as Element from "./Element"; export * as Elements from "./Elements"; export * as AuthTokens from "./AuthTokens"; -export * as SubscriptionItems from "./UserSubscriptionItemPrices"; -export * as SubscriptionCheckout from "./UserSubscriptionCheckout"; export * as SubscriptionStatus from "./UserSubscriptionStatus"; export * as AICompatCategories from "./AICompatCategories"; export * as AICompats from "./AICompats"; @@ -16,9 +13,6 @@ export * as AIRequests from "./AIRequests"; export * as UserCallbacks from "./UserCallbacks"; export * as Translations from "./Translations"; export * as Zodiacs from "./Zodiacs"; -export * as GoogleAuth from "./GoogleAuth"; -export * as SubscriptionPlans from "./SubscriptionPlans"; -export * as AppleAuth from "./AppleAuth"; export * as AIRequestsV2 from "./AIRequestsV2"; export * as Assistants from "./Assistants"; export * as OpenAI from "./OpenAI"; diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 8d4bbd6..d576797 100755 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -34,10 +34,8 @@ import BirthdayPage from "../BirthdayPage"; import BirthtimePage from "../BirthtimePage"; import CreateProfilePage from "../CreateProfilePage"; import EmailEnterPage from "../EmailEnterPage"; -import SubscriptionPage from "../SubscriptionPage"; import PaymentPage from "../PaymentPage"; import WallpaperPage from "../WallpaperPage"; -import StaticPage from "../StaticPage"; import NotFoundPage from "../NotFoundPage"; import Header from "../Header"; import Navbar from "../Navbar"; @@ -45,7 +43,6 @@ import Footer from "../Footer"; import "./styles.css"; import DidYouKnowPage from "../DidYouKnowPage"; import FreePeriodInfoPage from "../FreePeriodInfoPage"; -import AttentionPage from "../AttentionPage"; import FeedbackPage from "../FeedbackPage"; import CompatibilityPage from "../Compatibility"; import BreathPage from "../BreathPage"; @@ -147,13 +144,11 @@ if (isProduction) { function App(): JSX.Element { const location = useLocation(); - const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState(false); const [leoApng, setLeoApng] = useState(Error); // const [ // padLockApng, // setPadLockApng, // ] = useState(Error); - const navigate = useNavigate(); const api = useApi(); const dispatch = useDispatch(); const { token, user, signUp, logout } = useAuth(); @@ -228,11 +223,6 @@ function App(): JSX.Element { }); }, []); - const closeSpecialOfferAttention = () => { - setIsSpecialOfferOpen(false); - navigate(routes.client.auth()); - }; - const assetsData = useCallback(async () => { const { assets } = await api.getAssets({ category: String("au"), @@ -345,7 +335,7 @@ function App(): JSX.Element { element={} /> } + element={} > } /> {/* Email - Pay - Email */} @@ -852,15 +842,6 @@ function App(): JSX.Element { path={routes.client.freePeriodInfo()} element={} /> - - } - /> } /> } /> - } /> + {/* } /> */} } /> - }> + {/* }> } > } /> - + */} }> }> >; -} - -function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element { +function Layout(): JSX.Element { const location = useLocation(); const navigate = useNavigate(); const dispatch = useDispatch(); @@ -988,7 +965,6 @@ function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element { const showHeader = hasNoHeader(location.pathname); const isRouteFullDataModal = hasFullDataModal(location.pathname); const [isMenuOpen, setIsMenuOpen] = useState(false); - const changeIsSpecialOfferOpen = () => setIsSpecialOfferOpen(true); const homeConfig = useSelector(selectors.selectHome); const showNavbarFooter = homeConfig.isShowNavbar; const mainRef = useRef(null); @@ -1081,7 +1057,6 @@ function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element { {showHeader ? (
setIsMenuOpen(true)} - clickCross={changeIsSpecialOfferOpen} /> ) : null} {isRouteFullDataModal && ( diff --git a/src/components/AttentionPage/index.tsx b/src/components/AttentionPage/index.tsx deleted file mode 100755 index 8dd384b..0000000 --- a/src/components/AttentionPage/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { useNavigate } from "react-router-dom"; -import { useTranslations } from "@/hooks/translations"; -import Title from "../Title"; -import routes from "@/routes"; -import styles from "./styles.module.css"; -import SpecialWelcomeOffer from "../SpecialWelcomeOffer"; -import MainButton from "../MainButton"; - -interface AttentionPageProps { - isOpenModal: boolean; - onCloseSpecialOffer?: () => void; -} - -function AttentionPage({ - isOpenModal, - onCloseSpecialOffer, -}: AttentionPageProps): JSX.Element { - const { translate } = useTranslations(); - const navigate = useNavigate(); - const handleNext = () => navigate(routes.client.priceList()); - - return ( -
- - stop - {translate("aura.attention.title")} -

{translate("aura.warming_up.body")}

-
- - {translate("aura.warmin_good.button")} - - - {translate("aura.warmin_bad.button")} - -
-
- ); -} - -export default AttentionPage; diff --git a/src/components/AttentionPage/styles.module.css b/src/components/AttentionPage/styles.module.css deleted file mode 100644 index 912da47..0000000 --- a/src/components/AttentionPage/styles.module.css +++ /dev/null @@ -1,33 +0,0 @@ -.page { - position: relative; - flex: auto; - height: calc(100vh - 50px); - max-height: -webkit-fill-available; - justify-content: center; - gap: 16px; -} - -.icon { - width: 96px; - height: 96px; -} - -.text { - text-align: center; - line-height: 1.2; - font-weight: 700; -} - -.buttons-container { - width: 100%; - margin: 64px auto 0; - display: flex; - flex-direction: column; - align-items: center; - gap: 13px; -} - -.button { - white-space: pre; - cursor: pointer; -} diff --git a/src/components/AuthPage/AppleAuthButton/index.tsx b/src/components/AuthPage/AppleAuthButton/index.tsx deleted file mode 100644 index 1a20e5d..0000000 --- a/src/components/AuthPage/AppleAuthButton/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import MainButton from "@/components/MainButton"; -import styles from "./styles.module.css"; - -interface IAppleAuthButtonProps { - onClick: () => void; -} - -function AppleAuthButton({ onClick }: IAppleAuthButtonProps): JSX.Element { - return ( - - Apple - {"Sign in with Apple"} - - ); -} - -export default AppleAuthButton; diff --git a/src/components/AuthPage/AppleAuthButton/styles.module.css b/src/components/AuthPage/AppleAuthButton/styles.module.css deleted file mode 100644 index ab68165..0000000 --- a/src/components/AuthPage/AppleAuthButton/styles.module.css +++ /dev/null @@ -1,11 +0,0 @@ -.button { - width: 100%; - background-color: transparent; - color: #000; - font-size: 19px; - font-weight: 600; - border: solid #000 2px; - flex-direction: row; - justify-content: flex-start; - gap: 20px; -} diff --git a/src/components/AuthPage/GoogleAuthButton/index.tsx b/src/components/AuthPage/GoogleAuthButton/index.tsx deleted file mode 100644 index d5ab7f4..0000000 --- a/src/components/AuthPage/GoogleAuthButton/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import MainButton from "@/components/MainButton"; -import styles from "./styles.module.css"; - -interface IGoogleAuthButtonProps { - onClick: () => void; -} - -function AppleAuthButton({ onClick }: IGoogleAuthButtonProps): JSX.Element { - return ( - - Google - {"Sign in with Google"} - - ); -} - -export default AppleAuthButton; diff --git a/src/components/AuthPage/GoogleAuthButton/styles.module.css b/src/components/AuthPage/GoogleAuthButton/styles.module.css deleted file mode 100644 index 6cb39f3..0000000 --- a/src/components/AuthPage/GoogleAuthButton/styles.module.css +++ /dev/null @@ -1,11 +0,0 @@ -.button { - width: 100%; - background-color: transparent; - color: #000; - font-size: 19px; - font-weight: 600; - border: solid #4285f4 2px; - flex-direction: row; - justify-content: flex-start; - gap: 20px; -} diff --git a/src/components/AuthPage/index.tsx b/src/components/AuthPage/index.tsx deleted file mode 100644 index 49eefa2..0000000 --- a/src/components/AuthPage/index.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import Policy from "../Policy"; -import { useTranslations } from "@/hooks/translations"; -import styles from "./styles.module.css"; -import AppleAuthButton from "./AppleAuthButton"; -import routes from "@/routes"; -import Title from "../Title"; -import { APNG } from "apng-js"; -import Player from "apng-js/types/library/player"; -import { useEffect, useRef } from "react"; -import GoogleAuthButton from "./GoogleAuthButton"; - -let apngPlayer: Player | null = null; - -interface AuthPageProps { - padLockApng: Error | APNG; -} - -function AuthPage({ padLockApng }: AuthPageProps): JSX.Element { - const { translate } = useTranslations(); - const padLockCanvasRef = useRef(null); - - useEffect(() => { - let padLockTimeOut: NodeJS.Timeout; - async function getApngPlayer() { - const context = padLockCanvasRef.current?.getContext("2d"); - if (context && !(padLockApng instanceof Error)) { - context.canvas.height = padLockApng.height; - context.canvas.width = padLockApng.width; - const _apngPlayer = await padLockApng.getPlayer(context); - apngPlayer = _apngPlayer; - if (apngPlayer) { - apngPlayer.play(); - padLockTimeOut = setTimeout(() => { - if (apngPlayer) { - apngPlayer.pause(); - } - }, 900); - } - } - } - getApngPlayer(); - return () => { - clearTimeout(padLockTimeOut); - }; - }, [padLockApng]); - - const handleAppleAuth = async () => { - window.location.href = routes.server.appleAuth( - encodeURI(`${window.location.origin}/auth/result/`) - ); - }; - - const handleGoogleAuth = async () => { - window.location.href = routes.server.googleAuth( - encodeURI(`${window.location.origin}/auth/result/`) - ); - }; - - return ( -
- - Sign in to save your energy analysis, horoscope, and predictions. - - -

{translate("we_dont_share")}

-
- - -
- - {translate("_continue_agree", { - eulaLink: ( - - {translate("eula")} - - ), - privacyLink: ( - - {translate("privacy_policy")} - - ), - })} - -
- ); -} - -export default AuthPage; diff --git a/src/components/AuthPage/styles.module.css b/src/components/AuthPage/styles.module.css deleted file mode 100644 index 649f7c0..0000000 --- a/src/components/AuthPage/styles.module.css +++ /dev/null @@ -1,41 +0,0 @@ -.page { - position: relative; - /* height: calc(100vh - 103px); - max-height: -webkit-fill-available; */ - flex: auto; - justify-content: flex-start; - display: flex; - grid-template-rows: 1fr 96px; - justify-items: center; -} - -.disclaimer { - font-size: 19px; - font-weight: 500; - text-align: center; - margin-top: 24px; - line-height: 150%; -} - -.title { - font-weight: 700; - margin: 32px 0 0; -} - -.pad-lock { - width: 76px; - margin-top: 48px; -} - -.buttons-container { - width: 100%; - display: flex; - flex-direction: column; - gap: 20px; - max-width: 320px; - margin-top: 42px; -} - -.policy { - margin-top: 32px; -} \ No newline at end of file diff --git a/src/components/EmailMarketing/v1/components/AdviceFromAstrologer/index.tsx b/src/components/EmailMarketing/v1/components/AdviceFromAstrologer/index.tsx index 2b1814d..56f4a55 100644 --- a/src/components/EmailMarketing/v1/components/AdviceFromAstrologer/index.tsx +++ b/src/components/EmailMarketing/v1/components/AdviceFromAstrologer/index.tsx @@ -6,7 +6,7 @@ function AdviceFromAstrologer() { return (
- messages + messages
) } diff --git a/src/components/EmailMarketing/v1/components/CustomerCounter/styles.module.scss b/src/components/EmailMarketing/v1/components/CustomerCounter/styles.module.scss index d2f4882..d0a3bca 100644 --- a/src/components/EmailMarketing/v1/components/CustomerCounter/styles.module.scss +++ b/src/components/EmailMarketing/v1/components/CustomerCounter/styles.module.scss @@ -10,6 +10,8 @@ margin-top: 26px; box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.46), inset 6px 6px 82px 0px rgba(0, 0, 0, 0.25); + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } .circularText { @@ -17,20 +19,34 @@ width: 100%; height: 100%; animation: rotate 20s linear infinite; + -webkit-transform: translateZ(0); + -webkit-perspective: 1000; + -webkit-backface-visibility: hidden; svg { width: 100%; height: 100%; + -webkit-transform: translateZ(0); } text { fill: white; font-size: 8px; font-weight: 600; + -webkit-font-smoothing: antialiased; &:nth-of-type(2) { - transform: rotate(180deg); - transform-origin: center; + -webkit-transform: rotate(180deg) translateZ(0); + -moz-transform: rotate(180deg) translateZ(0); + -ms-transform: rotate(180deg) translateZ(0); + -o-transform: rotate(180deg) translateZ(0); + transform: rotate(180deg) translateZ(0); + + -webkit-transform-origin: center center; + -moz-transform-origin: center center; + -ms-transform-origin: center center; + -o-transform-origin: center center; + transform-origin: center center; } } } @@ -45,10 +61,22 @@ @keyframes rotate { from { - transform: rotate(0deg); + -webkit-transform: rotate(0deg) translateZ(0); + transform: rotate(0deg) translateZ(0); } to { - transform: rotate(360deg); + -webkit-transform: rotate(360deg) translateZ(0); + transform: rotate(360deg) translateZ(0); + } +} + +@-webkit-keyframes rotate { + from { + -webkit-transform: rotate(0deg) translateZ(0); + } + + to { + -webkit-transform: rotate(360deg) translateZ(0); } } \ No newline at end of file diff --git a/src/components/EmailMarketing/v1/components/FindingPartner/index.tsx b/src/components/EmailMarketing/v1/components/FindingPartner/index.tsx index 1bb8071..7a816a7 100644 --- a/src/components/EmailMarketing/v1/components/FindingPartner/index.tsx +++ b/src/components/EmailMarketing/v1/components/FindingPartner/index.tsx @@ -6,7 +6,7 @@ function FindingPartner() { return (
- smartphone + smartphone
) } diff --git a/src/components/EmailMarketing/v1/components/Payments/index.tsx b/src/components/EmailMarketing/v1/components/Payments/index.tsx index b76fef2..82a9093 100644 --- a/src/components/EmailMarketing/v1/components/Payments/index.tsx +++ b/src/components/EmailMarketing/v1/components/Payments/index.tsx @@ -4,7 +4,7 @@ import styles from "./styles.module.scss"; function Payments() { return (
- payments + payments
) } diff --git a/src/components/EmailMarketing/v1/pages/MarketingLanding/index.tsx b/src/components/EmailMarketing/v1/pages/MarketingLanding/index.tsx index 1d1b6bc..0e11329 100644 --- a/src/components/EmailMarketing/v1/pages/MarketingLanding/index.tsx +++ b/src/components/EmailMarketing/v1/pages/MarketingLanding/index.tsx @@ -20,6 +20,7 @@ import { useNavigate } from "react-router-dom"; import routes from "@/routes"; import { usePaywall } from "@/hooks/paywall/usePaywall"; import { EPlacementKeys } from "@/api/resources/Paywall"; +import BlurComponent from "@/components/BlurComponent"; const features = [ { @@ -171,9 +172,13 @@ function MarketingLanding() { - +
+ + + +
) } diff --git a/src/components/EmailMarketing/v1/pages/MarketingLanding/styles.module.scss b/src/components/EmailMarketing/v1/pages/MarketingLanding/styles.module.scss index 54a0b69..902bcbc 100644 --- a/src/components/EmailMarketing/v1/pages/MarketingLanding/styles.module.scss +++ b/src/components/EmailMarketing/v1/pages/MarketingLanding/styles.module.scss @@ -6,7 +6,7 @@ width: 100%; max-width: 460px; margin: 0 auto; - padding-bottom: 64px; + padding-bottom: 140px; overflow-x: hidden; } @@ -67,11 +67,6 @@ padding-left: 34px; } -.buttonContinue { - max-width: 300px; - margin-top: 30px; -} - .relative-container { position: relative; width: 100%; @@ -152,4 +147,23 @@ .backgroundElement14 { bottom: -50px; right: -30px; +} + +.buttonContainer { + width: 100%; + display: flex; + justify-content: center; + position: fixed; + bottom: 0; + pointer-events: none; + z-index: 9999; + + .buttonContinue { + position: relative; + z-index: 1000; + max-width: 300px; + margin-top: 48px; + margin-bottom: 64px; + pointer-events: all; + } } \ No newline at end of file diff --git a/src/components/EmailMarketing/v1/pages/SpecialOffer/index.tsx b/src/components/EmailMarketing/v1/pages/SpecialOffer/index.tsx index 6e66666..7c6115d 100644 --- a/src/components/EmailMarketing/v1/pages/SpecialOffer/index.tsx +++ b/src/components/EmailMarketing/v1/pages/SpecialOffer/index.tsx @@ -8,15 +8,21 @@ import { actions, selectors } from "@/store"; import { usePaywall } from "@/hooks/paywall/usePaywall"; import { EPlacementKeys } from "@/api/resources/Paywall"; import Modal from "@/components/Modal"; -import PaymentModal from "@/components/PaymentModal"; +import PaymentForm from "@/components/Payment/nmi/PaymentForm"; +import routes from "@/routes"; +import { useNavigate } from "react-router-dom"; +import BlurComponent from "@/components/BlurComponent"; + +const placementKey = EPlacementKeys["aura.placement.email.marketing"]; function SpecialOffer() { const dispatch = useDispatch(); + const navigate = useNavigate(); const [isOpenPaymentModal, setIsOpenPaymentModal] = useState(false); const activeProduct = useSelector(selectors.selectActiveProduct); const { products, getText } = usePaywall({ - placementKey: EPlacementKeys["aura.placement.email.marketing"], + placementKey, }); const trialPrice = ((products[0]?.trialPrice || 0) / 100).toFixed(2) || 0; @@ -27,7 +33,7 @@ function SpecialOffer() { dispatch(actions.payment.update({ activeProduct: products[0] })); }, [dispatch, products]); - const openStripeModal = () => { + const openPaymentModal = () => { setIsOpenPaymentModal(true); }; @@ -35,6 +41,14 @@ function SpecialOffer() { setIsOpenPaymentModal(false); }; + const onPaymentError = () => { + return navigate(routes.client.paymentFail()) + } + + const onPaymentSuccess = () => { + return navigate(routes.client.paymentSuccess()) + } + return ( <> {products[0] && ( @@ -44,8 +58,10 @@ function SpecialOffer() { onClose={handleCloseModal} type="hidden" > - )} @@ -67,12 +83,16 @@ function SpecialOffer() { trialDuration={trialDuration} saveText={getText("text.save") as string} /> -

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

+
+ + + +
diff --git a/src/components/EmailMarketing/v1/pages/SpecialOffer/styles.module.scss b/src/components/EmailMarketing/v1/pages/SpecialOffer/styles.module.scss index dadfa6e..1c151e5 100644 --- a/src/components/EmailMarketing/v1/pages/SpecialOffer/styles.module.scss +++ b/src/components/EmailMarketing/v1/pages/SpecialOffer/styles.module.scss @@ -25,7 +25,7 @@ border-radius: 30px 30px 0 0; min-height: calc(100dvh - 39px - 26px * 1.25 - 29px); margin-top: 29px; - padding: 53px 18px 46px; + padding: 53px 18px 160px; color: #000; display: flex; flex-direction: column; @@ -51,7 +51,7 @@ line-height: 125%; font-weight: 300; margin-bottom: 0; - margin-top: 61px; + margin-top: 36px; text-align: center; } @@ -59,4 +59,23 @@ margin-top: 59px; max-width: 307px; } +} + +.buttonContainer { + width: 100%; + display: flex; + justify-content: center; + position: fixed; + bottom: 0; + pointer-events: none; + z-index: 1000; + + .button { + position: relative; + z-index: 1000; + max-width: 300px; + margin-top: 24px; + margin-bottom: 64px; + pointer-events: all; + } } \ No newline at end of file diff --git a/src/components/PalmistryV1/components/PaymentModalV1/PaymentForm/index.tsx b/src/components/PalmistryV1/components/PaymentModalV1/PaymentForm/index.tsx index 1de6562..31daf17 100644 --- a/src/components/PalmistryV1/components/PaymentModalV1/PaymentForm/index.tsx +++ b/src/components/PalmistryV1/components/PaymentModalV1/PaymentForm/index.tsx @@ -1,43 +1,43 @@ import { useTranslations } from '@/hooks/translations'; import { addCurrency, ELocalesPlacement } from '@/locales'; import styles from "./styles.module.scss"; -import { LegacyRef, useEffect, useRef } from 'react'; +import { LegacyRef, useRef, useState } from 'react'; import { useSelector } from 'react-redux'; import { selectors } from '@/store'; import { EPlacementKeys, IPaywallProduct } from '@/api/resources/Paywall'; -import Loader from '@/components/Loader'; import cn from "classnames"; import { getFormattedPrice } from '@/utils/price.utils'; import SecurityPayments from '@/components/pages/TrialPayment/components/SecurityPayments'; -import { usePayment } from '@/hooks/payment/nmi/usePayment'; import Title from '@/components/Title'; import metricService, { EGoals, EMetrics } from '@/services/metric/metricService'; import { useNavigate } from 'react-router-dom'; import routes from '@/routes'; import CreditCardIcon from '@/components/PaymentModalNew/PaymentCardModal/CreditCardIcon'; +import Modal from '@/components/Modal'; +import NMIPaymentForm from '@/components/Payment/nmi/PaymentForm'; interface IPaymentFormProps { - placementKey: EPlacementKeys; activeProduct: IPaywallProduct; } function PaymentForm({ activeProduct, - placementKey, }: IPaymentFormProps) { const navigate = useNavigate(); const { translate } = useTranslations(ELocalesPlacement.PalmistryV1); const ref = useRef(); const currency = useSelector(selectors.selectCurrency); + const [isPaymentSuccess, setIsPaymentSuccess] = useState(false); + const [isPaymentError, setIsPaymentError] = useState(false); + const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false); - const { isLoading, error, isPaymentSuccess, showCreditCardForm } = usePayment({ - placementKey, - activeProduct - }); + const onPaymentError = () => { + setIsPaymentError(true); + } - useEffect(() => { - if (!isPaymentSuccess) return; + const onPaymentSuccess = () => { + setIsPaymentSuccess(true); metricService.reachGoal(EGoals.PAYMENT_SUCCESS); metricService.reachGoal(EGoals.PAYMENT_SUCCESS_PALMISTRY, [ EMetrics.YANDEX, @@ -48,13 +48,12 @@ function PaymentForm({ value: ((activeProduct.trialPrice || 100) / 100).toFixed(2), }); } - const redirectTimeout = setTimeout(() => { + setTimeout(() => { navigate(routes.client.skipTrial()); }, 1500); - return () => clearTimeout(redirectTimeout); - }, [isPaymentSuccess]) + } - if (error?.length) { + if (isPaymentError) { return (
} @@ -97,15 +96,13 @@ function PaymentForm({
} className={cn( - styles.paymentModalContainer, - isLoading && styles.paymentModalContainerLoading + styles.paymentModalContainer )} > - {isLoading && ( -
- -
- )} + + + +
{translate( @@ -119,29 +116,25 @@ function PaymentForm({ ELocalesPlacement.PalmistryV1 )}
- {!isLoading && ( - <> -
- -
Credit / Debit Card
-
- {/* +
setIsPaymentModalOpen(true)} + > + +
Credit / Debit Card
+
+ {/* */} -
- -

- {translate( - "payment_modal.address", - undefined, - ELocalesPlacement.V1 - )} -

-
- - )} +
+ +

+ {translate( + "payment_modal.address", + undefined, + ELocalesPlacement.V1 + )} +

+
); } diff --git a/src/components/PalmistryV1/components/PaymentModalV1/PaymentForm/styles.module.scss b/src/components/PalmistryV1/components/PaymentModalV1/PaymentForm/styles.module.scss index 158132a..7967d39 100644 --- a/src/components/PalmistryV1/components/PaymentModalV1/PaymentForm/styles.module.scss +++ b/src/components/PalmistryV1/components/PaymentModalV1/PaymentForm/styles.module.scss @@ -86,4 +86,9 @@ text-align: center; color: #121620; } +} + +.modal-content { + overflow-x: hidden; + } \ No newline at end of file diff --git a/src/components/PalmistryV1/components/PaymentModalV1/index.tsx b/src/components/PalmistryV1/components/PaymentModalV1/index.tsx index 0babae9..1e07d48 100644 --- a/src/components/PalmistryV1/components/PaymentModalV1/index.tsx +++ b/src/components/PalmistryV1/components/PaymentModalV1/index.tsx @@ -3,7 +3,6 @@ import { HTMLAttributes } from 'react'; import { useSelector } from 'react-redux'; import styles from "./styles.module.scss"; import PaymentForm from './PaymentForm'; -import { EPlacementKeys } from '@/api/resources/Paywall'; function PaymentModalV1(props: HTMLAttributes) { const activeProductFromStore = useSelector(selectors.selectActiveProduct); @@ -21,9 +20,6 @@ function PaymentModalV1(props: HTMLAttributes) { >
diff --git a/src/components/PalmistryV2/components/PaymentTable/index.tsx b/src/components/PalmistryV2/components/PaymentTable/index.tsx index f3394c9..01986da 100644 --- a/src/components/PalmistryV2/components/PaymentTable/index.tsx +++ b/src/components/PalmistryV2/components/PaymentTable/index.tsx @@ -34,12 +34,12 @@ function PaymentTable({ }; const getPrice = useCallback( - (product: IPaywallProduct) => { - if ((product.trialPrice || 0) % 100 === 0) { - return addCurrency((product.trialPrice || 0) / 100, currency); + (price: number) => { + if (price % 100 === 0) { + return addCurrency(price / 100, currency); } return addCurrency( - ((product.trialPrice || 0) / 100).toFixed(2), + (price / 100).toFixed(2), currency ); }, @@ -78,13 +78,13 @@ function PaymentTable({ {/* {translate("/trial-payment.payment_table.title", { price: {getPrice(product)}, })} */} - Personalized plan for {getPrice(product)} + Personalized plan for {getPrice(product.trialPrice || 0)}

{translate("/trial-payment.payment_table.total_today")}

- {getPrice(product)} + {getPrice(product.trialPrice || 0)}

@@ -109,9 +109,9 @@ function PaymentTable({ )} */}
- {addCurrency(Number(getText("full.price")) / 100, currency)} + {addCurrency(Number(getText("full.price")), currency)} - {getPrice(product)} + {getPrice(product.price)}
@@ -129,7 +129,7 @@ function PaymentTable({ ), trialDuration: product.trialDuration, price: addCurrency(product.price / 100, currency), - trialPrice: getPrice(product), + trialPrice: getPrice(product.trialPrice || 0), })}

diff --git a/src/components/PalmistryV2/components/SecretDiscountTable/index.tsx b/src/components/PalmistryV2/components/SecretDiscountTable/index.tsx index 4842c2b..8487059 100644 --- a/src/components/PalmistryV2/components/SecretDiscountTable/index.tsx +++ b/src/components/PalmistryV2/components/SecretDiscountTable/index.tsx @@ -1,8 +1,34 @@ 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"; + +const placementKey = EPlacementKeys["aura.placement.email.palmistry.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.PalmistryV1, + }); + + const activeProduct = products[0]; + const price = activeProduct?.price || 0; + const trialPrice = activeProduct?.trialPrice || 0; + const trialDuration = activeProduct?.trialDuration || 7; + return (
@@ -14,21 +40,21 @@ function SecretDiscountTable() { <Title className={styles.title} variant="h4"> Secret discount applied! - -30% - -50% + {getText("old.discount")} + {getText("new.discount")}
-

Your cost per 14 days after trial:

- $19 - $19 +

Your cost per {trialDuration} days after trial:

+ {addCurrency(Number(getText("old.price")), currency)} + {getPrice(price, currency)}
-

You save $30

+

You save {addCurrency(Number(getText("save")), currency)}


Total today

- $9 + {getPrice(trialPrice, currency)}
) diff --git a/src/components/PalmistryV2/pages/SaveOff/index.tsx b/src/components/PalmistryV2/pages/SaveOff/index.tsx index 78ac058..8fa9fa0 100644 --- a/src/components/PalmistryV2/pages/SaveOff/index.tsx +++ b/src/components/PalmistryV2/pages/SaveOff/index.tsx @@ -10,9 +10,11 @@ import { usePaywall } from "@/hooks/paywall/usePaywall"; import { EPlacementKeys } from "@/api/resources/Paywall"; import { ELocalesPlacement } from "@/locales"; +const placementKey = EPlacementKeys["aura.placement.email.palmistry.discount"] + function SaveOff() { - const { products } = usePaywall({ - placementKey: EPlacementKeys["aura.placement.email.marketing"], + const { products, getText } = usePaywall({ + placementKey, localesPlacement: ELocalesPlacement.PalmistryV1, }); const activeProduct = products[0] @@ -31,16 +33,16 @@ function SaveOff() { gift - SAVE 70% OFF! + SAVE {getText("discount")}% OFF!

- ${price} instead of $65 + ${price} instead of ${getText("full.price")}

fire {trialDuration}-day trial

- gift 70% off on your personalized plan + gift {getText("discount")}% off on your personalized plan

-

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

+ +
+

+ 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. +

+ +
) } diff --git a/src/components/PalmistryV2/pages/SecretDiscount/styles.module.scss b/src/components/PalmistryV2/pages/SecretDiscount/styles.module.scss index efd840a..c38b907 100644 --- a/src/components/PalmistryV2/pages/SecretDiscount/styles.module.scss +++ b/src/components/PalmistryV2/pages/SecretDiscount/styles.module.scss @@ -5,13 +5,6 @@ z-index: -1; } -.blob4 { - position: absolute; - bottom: 0; - left: 0; - z-index: -1; -} - .title { padding: 15px 0; background-color: #F096C4; @@ -33,16 +26,26 @@ margin-top: 30px; } -.policy { - width: 100%; - position: absolute; - left: 50%; - transform: translateX(-50%); - bottom: 0; - margin-bottom: 34px; - padding: 0 14px; - font-size: 13px; - font-weight: 400; - line-height: 130%; - color: #fff; +.policy-container { + position: relative; + width: calc(100% + 52px); + height: fit-content; + bottom: -58px; + + &>.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; + } } \ No newline at end of file diff --git a/src/components/PalmistryV2/pages/TrialPayment/index.tsx b/src/components/PalmistryV2/pages/TrialPayment/index.tsx index 72f0c15..28bc7dd 100644 --- a/src/components/PalmistryV2/pages/TrialPayment/index.tsx +++ b/src/components/PalmistryV2/pages/TrialPayment/index.tsx @@ -8,26 +8,32 @@ import { ELocalesPlacement } from "@/locales"; import { images } from "../../data"; import DiscountExpires from "../../components/DiscountExpires"; import PaymentTable from "../../components/PaymentTable"; -import { useSelector } from "react-redux"; -import { selectors } from "@/store"; +import { useDispatch, useSelector } from "react-redux"; +import { actions, selectors } from "@/store"; import { EPlacementKeys } from "@/api/resources/Paywall"; import { usePaywall } from "@/hooks/paywall/usePaywall"; import MoneyBackGuarantee from "../../components/MoneyBackGuarantee"; import PalmsSayAbout from "../../components/PalmsSayAbout"; -import { useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import { getZodiacSignByDate } from "@/services/zodiac-sign"; import WithPartnerInformation from "../../components/WithPartnerInformation"; import PersonalInformation from "../../components/PersonalInformation"; import Reviews from "../../components/Reviews"; import Address from "../../components/Address"; +import Modal from "@/components/Modal"; +import PaymentForm from "@/components/Payment/nmi/PaymentForm"; + +const placementKey = EPlacementKeys["aura.placement.email.palmistry"]; function TrialPayment() { + const dispatch = useDispatch(); // const { translate } = useTranslations(ELocalesPlacement.PalmistryV1); const { products } = usePaywall({ - placementKey: EPlacementKeys["aura.placement.email.marketing"], + placementKey, localesPlacement: ELocalesPlacement.PalmistryV1, }); const activeProduct = products[0] + const trialDuration = activeProduct?.trialDuration || 7; const birthdate = useSelector(selectors.selectBirthdate); @@ -43,6 +49,28 @@ function TrialPayment() { const partnerZodiacSign = getZodiacSignByDate(partnerBirthdate); const navigate = useNavigate(); + const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false); + + const onPaymentSuccess = () => { + return navigate(routes.client.paymentSuccess()) + } + + const onModalClosed = () => { + setIsPaymentModalOpen(false); + return handleDiscount() + } + + const handleDiscount = () => { + navigate(routes.client.palmistryV2SaveOff()); + }; + + const onPaymentError = () => { + return navigate(routes.client.paymentFail()) + } + + const openPaymentModal = () => { + setIsPaymentModalOpen(true); + }; const singleOrWithPartner = useMemo(() => { if (["relationship", "married"].includes(flowChoice)) { @@ -51,14 +79,25 @@ function TrialPayment() { return "single"; }, [flowChoice]); - const handleNext = () => { - navigate(routes.client.palmistryV2SaveOff()); - }; + useEffect(() => { + if (!activeProduct) return; + dispatch(actions.payment.update({ + activeProduct + })) + }, [activeProduct]) if (!activeProduct) return null; + return ( <> + + +
@@ -71,7 +110,7 @@ function TrialPayment() { </div> <div className={styles.discount}> <DiscountExpires /> - <Button className={styles.button} onClick={handleNext}> + <Button className={styles.button} onClick={openPaymentModal}> GET {trialDuration}-day trial </Button> </div> @@ -82,8 +121,8 @@ function TrialPayment() { <PaymentTable product={activeProduct} gender={gender} - placementKey={EPlacementKeys["aura.placement.email.marketing"]} - buttonClick={handleNext} + placementKey={placementKey} + buttonClick={openPaymentModal} /> <MoneyBackGuarantee /> <Title className={styles["title-hands"]}> diff --git a/src/components/PalmistryV2/pages/TrialPayment/styles.module.scss b/src/components/PalmistryV2/pages/TrialPayment/styles.module.scss index a714d85..2be6fd6 100644 --- a/src/components/PalmistryV2/pages/TrialPayment/styles.module.scss +++ b/src/components/PalmistryV2/pages/TrialPayment/styles.module.scss @@ -33,7 +33,7 @@ .discount { position: sticky; - top: 0; + top: -1px; width: calc(100% + 48px); padding: 9px 22px; border: solid 1px #fff; @@ -42,8 +42,9 @@ flex-direction: row; align-items: center; justify-content: space-between; - background-color: #dae7ff; - z-index: 9999; + background-color: transparent; + backdrop-filter: blur(5px); + z-index: 1000; &>.button { max-width: 176px; diff --git a/src/components/Payment/nmi/CheckoutForm/index.tsx b/src/components/Payment/nmi/CheckoutForm/index.tsx new file mode 100644 index 0000000..65abd17 --- /dev/null +++ b/src/components/Payment/nmi/CheckoutForm/index.tsx @@ -0,0 +1,134 @@ +import MainButton from "@/components/MainButton"; +import { useEffect } from "react"; +import styles from "./styles.module.scss"; +import { usePayment } from "@/hooks/payment/nmi/usePayment"; +import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall"; + +export type TConfirmType = "payment" | "setup"; + +interface ICheckoutFormProps { + subscriptionReceiptId?: string; + returnUrl?: string; + confirmType?: TConfirmType; + isHide?: boolean; + placementKey: EPlacementKeys; + activeProduct: IPaywallProduct; + onSuccess?: () => void; + onError?: (error?: string) => void; + onModalClosed?: () => void; +} + +export default function CheckoutForm({ + placementKey, + activeProduct, + onError, + onSuccess, + onModalClosed, + isHide = false, +}: ICheckoutFormProps) { + + const { + isLoading, + error, + isPaymentSuccess, + submitInlineForm, + formValidation, + isFormValid, + isModalClosed + } = usePayment({ + placementKey, + activeProduct, + paymentFormType: "inline" + }); + + useEffect(() => { + if (error && onError) { + console.log(error); + + onError(error); + } + }, [error, onError]); + + useEffect(() => { + if (isModalClosed && onModalClosed) { + onModalClosed(); + } + }, [isModalClosed, onModalClosed]); + + useEffect(() => { + if (isPaymentSuccess && onSuccess) { + onSuccess(); + } + }, [isPaymentSuccess, onSuccess]); + + const handleSubmit = (e: React.FormEvent<HTMLFormElement> | React.MouseEvent<HTMLButtonElement>) => { + e.preventDefault(); + if (!isFormValid) { + return; + } + submitInlineForm(); + }; + + return ( + <form + className={`${styles.form} ${isHide ? styles.hide : ""}`} + id="payment-form" + onSubmit={handleSubmit} + > + <div className={styles.formRow}> + <label htmlFor="card-number">Card Number</label> + <div + id="card-number" + className={`${styles.fieldContainer} ${formValidation.ccnumber.message ? styles.invalid : '' + } ${formValidation.ccnumber.isValid ? styles.valid : ''}`} + /> + {formValidation.ccnumber.message && ( + <div className={styles.errorMessage}> + {formValidation.ccnumber.message} + </div> + )} + </div> + + <div className={styles.formGroup}> + <div className={styles.formRow}> + <label htmlFor="card-expiry">Expiration Date</label> + <div + id="card-expiry" + className={`${styles.fieldContainer} ${formValidation.ccexp.message ? styles.invalid : '' + } ${formValidation.ccexp.isValid ? styles.valid : ''}`} + /> + {formValidation.ccexp.message && ( + <div className={styles.errorMessage}> + {formValidation.ccexp.message} + </div> + )} + </div> + + <div className={styles.formRow}> + <label htmlFor="card-cvv">CVV</label> + <div + id="card-cvv" + className={`${styles.fieldContainer} ${formValidation.cvv.message ? styles.invalid : '' + } ${formValidation.cvv.isValid ? styles.valid : ''}`} + /> + {formValidation.cvv.message && ( + <div className={styles.errorMessage}> + {formValidation.cvv.message} + </div> + )} + </div> + </div> + + <MainButton + color="blue" + disabled={isLoading || !isFormValid} + id="submit" + className={styles.button} + onClick={handleSubmit} + > + <img src="/lock.svg" alt="Secure" /> + <span>{isLoading ? "Processing..." : "Pay Now"}</span> + </MainButton> + </form> + ); +} diff --git a/src/components/Payment/nmi/CheckoutForm/styles.module.scss b/src/components/Payment/nmi/CheckoutForm/styles.module.scss new file mode 100644 index 0000000..8e227a3 --- /dev/null +++ b/src/components/Payment/nmi/CheckoutForm/styles.module.scss @@ -0,0 +1,134 @@ +.page { + /* position: relative; */ + position: static; + /* height: calc(100vh - 50px); + max-height: -webkit-fill-available; */ + display: flex; + justify-items: center; + justify-content: center; + gap: 16px; +} + +.hide { + height: 0; + visibility: hidden; +} + +.payment-loader { + display: flex; + justify-content: center; + align-items: center; +} + +.cross { + position: absolute; + top: -36px; + right: 28px; + width: 22px; + height: 22px; + cursor: pointer; + z-index: 9; +} + +.title { + font-size: 27px; + font-weight: 700; + margin: 0; +} + +.email { + font-size: 17px; + font-weight: 500; + margin: 0; +} + +.button { + min-height: 0; + height: 50px; + text-transform: uppercase; + background-color: #4caf50; + border-radius: 25px; + font-size: 16px; + gap: 8px; + opacity: 1; + transition: opacity 0.2s ease; + margin-top: 8px; + + &:disabled { + opacity: 0.7; + cursor: not-allowed; + } +} + +.button>img { + height: 18px; + margin-top: -5px; +} + +.form { + width: 100%; + max-width: 500px; + margin: 0 auto; + padding: 0; +} + +.hide { + display: none; +} + +.formRow { + margin-bottom: 16px; + + + label { + display: block; + margin-bottom: 0.5rem; + font-weight: 500; + text-align: left; + padding-left: 12px; + } +} + +.formGroup { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; +} + +.input { + width: 100%; + padding: 12px; + border: 1px solid #e2e8f0; + border-radius: 6px; + font-size: 16px; + transition: border-color 0.2s; + + &:focus { + outline: none; + border-color: #3b82f6; + } +} + +.fieldContainer { + border: 1px solid #e2e8f0; + border-radius: 6px; + overflow: hidden; + transition: border-color 0.2s; + padding: 8px; + min-height: 40px; + + &:focus-within { + border-color: #3b82f6; + } + + &.invalid { + border-color: #dc3545; + } +} + +.errorMessage { + color: #dc3545; + font-size: 12px; + margin: 4px 0; + min-height: 20px; +} \ No newline at end of file diff --git a/src/components/Payment/nmi/PaymentForm/index.tsx b/src/components/Payment/nmi/PaymentForm/index.tsx new file mode 100644 index 0000000..be3f68a --- /dev/null +++ b/src/components/Payment/nmi/PaymentForm/index.tsx @@ -0,0 +1,97 @@ +import { useTranslations } from "@/hooks/translations"; +import styles from "./styles.module.scss"; +import { addCurrency, ELocalesPlacement } from "@/locales"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import Loader from "@/components/Loader"; +import Title from "@/components/Title"; +import PaymentMethodsChoice from "@/components/pages/ABDesign/v1/pages/TrialPayment/components/PaymentMethodsChoice"; +import { paymentMethods } from "@/data/paymentMethods"; +import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall"; +import SecurityPayments from "@/components/pages/ABDesign/v1/pages/TrialPayment/components/SecurityPayments"; +import CheckoutForm from "../CheckoutForm"; + +const paymentMethodsButtons = paymentMethods(null); + +const getPrice = (product: IPaywallProduct | null) => { + if (!product) { + return 0; + } + return (product.trialPrice === 100 ? 99 : product.trialPrice || 0) / 100; +} + +interface IPaymentFormProps { + className?: string; + placementKey: EPlacementKeys; + onPaymentError?: () => void; + onPaymentSuccess?: () => void; + onModalClosed?: () => void; +} + +function PaymentForm({ className, placementKey, onPaymentError, onPaymentSuccess, onModalClosed }: IPaymentFormProps) { + const { translate } = useTranslations(ELocalesPlacement.V1); + const currency = useSelector(selectors.selectCurrency); + const activeProduct = useSelector(selectors.selectActiveProduct); + + const isLoading = false; + + + return ( + <> + {isLoading && ( + <div className={styles["payment-modal"]}> + <div className={styles["payment-loader"]}> + <Loader /> + </div> + </div> + )} + <div + className={`${styles["payment-modal"]} ${isLoading ? styles.hide : ""} ${className}`} + > + <Title variant="h3" className={styles.title}> + {translate("payment_modal.title")} + + { }} + /> + {activeProduct && ( +
+

+ {translate("payment_modal.description", { + priceForDays: ( + + {translate("payment_modal.price_for_days", { + trialPrice: addCurrency( + getPrice(activeProduct), + currency + ), + trialDuration: activeProduct?.trialDuration, + })} + + ), + emailReminder: ( + {translate("payment_modal.email_reminder")} + ), + })} +

+
+ )} +
+ {!!activeProduct && } +
+ +

{translate("payment_modal.address")}

+
+ + ) +} + +export default PaymentForm \ No newline at end of file diff --git a/src/components/Payment/nmi/PaymentForm/styles.module.scss b/src/components/Payment/nmi/PaymentForm/styles.module.scss new file mode 100644 index 0000000..0488cc8 --- /dev/null +++ b/src/components/Payment/nmi/PaymentForm/styles.module.scss @@ -0,0 +1,55 @@ +.payment-modal { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 250px; + gap: 25px; + color: #2f2e37; +} + +.payment-modal.hide { + min-height: 0; + height: 0; + opacity: 0; +} + +.title { + font-weight: 700; + font-size: 20px; + line-height: 20px; + text-align: center; + margin: 0; +} + +.sub-plan-description { + font-size: 12px; + text-align: center; + line-height: 150%; + white-space: pre-wrap; +} + +.payment-method-container { + width: 100%; + display: flex; + flex-direction: column; + gap: 24px; +} + +.address { + margin-bottom: 24px; + text-transform: uppercase; +} + +.payment-method { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; +} + +.address { + color: gray; + font-size: 10px; +} \ No newline at end of file diff --git a/src/components/PriceListPage/index.tsx b/src/components/PriceListPage/index.tsx index 2672e7a..8567792 100755 --- a/src/components/PriceListPage/index.tsx +++ b/src/components/PriceListPage/index.tsx @@ -1,5 +1,5 @@ -import { useNavigate } from "react-router-dom"; -import routes from "@/routes"; +// import { useNavigate } from "react-router-dom"; +// import routes from "@/routes"; import styles from "./styles.module.css"; import UserHeader from "../UserHeader"; import { useDispatch, useSelector } from "react-redux"; @@ -16,7 +16,7 @@ import { getRandomArbitrary } from "@/services/random-value"; function PriceListPage(): JSX.Element { const { translate } = useTranslations(); - const navigate = useNavigate(); + // const navigate = useNavigate(); const dispatch = useDispatch(); const homeConfig = useSelector(selectors.selectHome); const selectedPrice = useSelector(selectors.selectSelectedPrice); @@ -41,7 +41,7 @@ function PriceListPage(): JSX.Element { }) ); setTimeout(() => { - navigate(routes.client.subscription()); + // navigate(routes.client.subscription()); }, 1000); }; diff --git a/src/components/SpecialWelcomeOffer/index.tsx b/src/components/SpecialWelcomeOffer/index.tsx deleted file mode 100644 index bf6bfdf..0000000 --- a/src/components/SpecialWelcomeOffer/index.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useNavigate } from "react-router-dom"; -import { useTranslations } from "@/hooks/translations"; -import Title from "../Title"; -import routes from "@/routes"; -import styles from "./styles.module.css"; -import ModalTop from "../ModalTop"; -import Header from "../Header"; -import { useCallback } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { actions, selectors } from "@/store"; -import MainButton from "../MainButton"; - -interface ModalTopProps { - open: boolean; - onClose?: () => void; -} - -function SpecialWelcomeOffer({ open, onClose }: ModalTopProps): JSX.Element { - const { translate } = useTranslations(); - const navigate = useNavigate(); - const dispatch = useDispatch(); - const selectedPrice = useSelector(selectors.selectSelectedPrice); - const halfPrice = (Math.round(selectedPrice || 0) / 2).toFixed(2); - const updateIsDiscount = useCallback((isDiscount: boolean) => { - dispatch( - actions.payment.update({ - isDiscount - }) - ); - }, [dispatch]); - const handleNext = () => { - updateIsDiscount(true); - navigate(routes.client.paymentMethod()); - }; - - const handleMoreAbout = () => { - window.location.href = "https://witapps.us/en/aura"; - }; - - return ( - <> - {open ? ( - -
-
- {/* {translate('special_welcome_offer')} */} - Your friends - - {translate("au.friends.window")} - - - {translate("au.get50.only")} - -
- {Number(halfPrice) > 0 && - <> - ${selectedPrice}{" "} - – - {/* ${halfPrice} */} - $1 - - - } - {!Number(halfPrice) && {translate('au.free_trial_web.7_14')}} -
- - {/* $ {halfPrice} – */} - {translate("au.try_for.button")} - - - Leo - {translate("au.more_llc.button")} - -
- - ) : null} - - ); -} - -export default SpecialWelcomeOffer; diff --git a/src/components/SpecialWelcomeOffer/styles.module.css b/src/components/SpecialWelcomeOffer/styles.module.css deleted file mode 100644 index c52c4e2..0000000 --- a/src/components/SpecialWelcomeOffer/styles.module.css +++ /dev/null @@ -1,73 +0,0 @@ -.content { - padding: 0 24px 64px 24px; - display: flex; - flex-direction: column; - align-items: center; - gap: 12px; - padding-top: 28px; -} - -.welcome-offer { - color: #717171; - font-weight: 500; -} - -.discount-container { - display: flex; - flex-direction: row; - gap: 16px; - font-size: 32px; - font-weight: 600; - margin-bottom: 32px; -} - -.red-price { - color: red; - text-decoration: line-through; -} - -.button-green { - background-color: #18d136; - font-weight: 600; - font-size: 21px; - /* color: #fff; - border-radius: 26px; - width: 100%; - max-width: 300px; - padding: 12px 0; - border: none; - font-size: 14px; - margin-top: 16px; */ -} - -.button-black { - font-weight: 600; - font-size: 21px; - position: relative; -} - -.your-friends { - width: 80%; - font-weight: 600; - margin-bottom: 8px; -} - -.get-50-only { - margin-bottom: 0; - font-weight: 700; - color: #ff003d; -} - -.button-icon { - position: absolute; - width: 48px; - top: 50%; - left: 13px; - transform: translateY(-50%); - -} - -.free-trial { - font-size: 22px; - font-weight: 600; -} diff --git a/src/components/StaticPage/index.tsx b/src/components/StaticPage/index.tsx deleted file mode 100644 index 45984ef..0000000 --- a/src/components/StaticPage/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useParams } from "react-router"; -import { useTranslations } from "@/hooks/translations"; -import { useApi, useApiCall, Element } from "@/api"; -import { useCallback } from "react"; -import parse from "html-react-parser"; -import Loader from "../Loader"; -import NotFoundPage from "../NotFoundPage"; -import "./styles.css"; - -function StaticPage(): JSX.Element { - const { i18n } = useTranslations(); - const { typeId } = useParams(); - const api = useApi(); - const locale = i18n.language; - const loadData = useCallback(() => { - const type = typeId || ""; - return api - .getElement({ type, locale }) - .then((resp: Element.Response) => resp.data.element); - }, [api, typeId, locale]); - const { data, isPending, error } = useApiCall(loadData); - const content = data ? parse(data.body) : null; - - return ( -
- {isPending ? ( - - ) : ( -
{content}
- )} - {error && } -
- ); -} - -export default StaticPage; diff --git a/src/components/StaticPage/styles.css b/src/components/StaticPage/styles.css deleted file mode 100644 index 4b5d9b9..0000000 --- a/src/components/StaticPage/styles.css +++ /dev/null @@ -1,18 +0,0 @@ -.page-static { - line-height: 1.3; -} - -.page-static p { - margin-bottom: 5px; -} - -.page-static h1, -.page-static h2, -.page-static h3 { - margin-top: 10px; - margin-bottom: 10px; -} - -.page-static__content { - width: 100%; -} diff --git a/src/components/SubscriptionPage/index.tsx b/src/components/SubscriptionPage/index.tsx deleted file mode 100644 index 7756677..0000000 --- a/src/components/SubscriptionPage/index.tsx +++ /dev/null @@ -1,272 +0,0 @@ -import { useDispatch, useSelector } from "react-redux"; -import { useTranslations } from "@/hooks/translations"; -import { useNavigate, useParams } from "react-router-dom"; -import { actions, selectors } from "@/store"; -import MainButton from "../MainButton"; -import Policy from "../Policy"; -import PaymentTable, { Currency, Locale } from "../PaymentTable"; -import CallToAction from "../CallToAction"; -import routes from "@/routes"; -import styles from "./styles.module.css"; -// import Header from "../Header"; -// import SpecialWelcomeOffer from "../SpecialWelcomeOffer"; -import { useEffect, useState } from "react"; -import { ApiError, extractErrorMessage, useApi } from "@/api"; -import { useAuth } from "@/auth"; -import { getClientTimezone, language } from "@/locales"; -import Loader from "../Loader"; -import Title from "../Title"; -import ErrorText from "../ErrorText"; -import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall"; -import { usePaywall } from "@/hooks/paywall/usePaywall"; - -const currency = Currency.USD; -const locale = language as Locale; - -const getPrice = (product: IPaywallProduct | null) => { - if (!product?.trialPrice) { - return 0; - } - return (product.trialPrice === 100 ? 99 : product.trialPrice || 0) / 100; -}; - -function SubscriptionPage(): JSX.Element { - const api = useApi(); - const timezone = getClientTimezone(); - const { signUp } = useAuth(); - const { translate } = useTranslations(); - const navigate = useNavigate(); - const dispatch = useDispatch(); - // const [isOpenModal, setIsOpenModal] = useState(false); - const [email, setEmail] = useState(""); - const [emailError, setEmailError] = useState( - "Email is invalid" - ); - const [name, setName] = useState(""); - const [nameError, setNameError] = useState("Name is invalid"); - const [isSubmit, setIsSubmit] = useState(false); - const [isAuth, setIsAuth] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [apiError, setApiError] = useState(null); - const [error, setError] = useState(false); - const { subPlan } = useParams(); - const birthday = useSelector(selectors.selectBirthday); - console.log(nameError); - - const { products } = usePaywall({ - placementKey: EPlacementKeys["aura.placement.main"], - }); - const activeProductFromStore = useSelector(selectors.selectActiveProduct); - const [activeProduct, setActiveProduct] = useState( - activeProductFromStore - ); - - useEffect(() => { - if (subPlan) { - const targetProduct = products.find( - (product) => - String( - product?.trialPrice - ? Math.floor((product?.trialPrice + 1) / 100) - : product.key.replace(".", "") - ) === subPlan - ); - if (targetProduct) { - setActiveProduct(targetProduct); - } - } - }, [products, subPlan]); - - const paymentItems = [ - { - title: activeProduct?.name || "Per 7-Day Trial For", - price: getPrice(activeProduct), - description: activeProduct?.description?.length - ? activeProduct.description - : translate("au.2week_plan.web"), - }, - ]; - - const authorization = async () => { - try { - setIsLoading(true); - const auth = await api.auth({ email, timezone, locale }); - const { - auth: { token, user }, - } = auth; - signUp(token, user); - const payload = { - user: { profile_attributes: { birthday } }, - token, - }; - const updatedUser = await api.updateUser(payload).catch((error) => { - console.log("Error: ", error); - }); - if (updatedUser?.user) { - dispatch(actions.user.update(updatedUser.user)); - } - if (name) { - dispatch( - actions.user.update({ - username: name, - }) - ); - } - dispatch(actions.status.update("registred")); - dispatch( - actions.payment.update({ - activeProduct, - }) - ); - setIsLoading(false); - setIsAuth(true); - setTimeout(() => { - navigate(routes.client.paymentMethod()); - }, 1000); - } catch (error) { - console.error(error); - if (error instanceof ApiError) { - setApiError(error as ApiError); - } else { - setError(true); - } - setIsLoading(false); - } - }; - - const handleClick = async () => { - setIsSubmit(true); - - if ( - !isValidEmail(email) - // || !isValidName(name) - ) { - return; - } - await authorization(); - }; - // const handleCross = () => setIsOpenModal(true); - const policyLink = ( - - {translate("subscription_policy")} - - ); - - const isValidEmail = (email: string) => { - return /\S+@\S+\.\S+/.test(email); - }; - - const isValidName = (name: string) => { - return !!(name.length > 0 && name.length < 30); - }; - - const handleChangeEmail = (event: React.ChangeEvent) => { - const email = event.target.value; - if (!isValidEmail(email)) { - setEmailError("Email is invalid"); - } else { - setEmailError(null); - } - setEmail(email); - }; - - const handleChangeName = (event: React.ChangeEvent) => { - const name = event.target.value; - if (!isValidName(name)) { - setNameError("Name is invalid"); - } else { - setNameError(null); - } - setName(name); - }; - - return ( - <> - {/* */} - {/*
*/} - {/* */} -
- - {/* */} - -
-
- - {/* {isSubmit && !!nameError && ( - - {nameError} - - )} */} -
-
- - {isSubmit && !!emailError && ( - - {emailError} - - )} -
- {isLoading && isSubmit && isValidEmail(email) && ( - // isValidName(name) && - - )} - {(error || apiError) && ( - - Something went wrong - - )} - {apiError && ( - - )} - {!apiError && !error && !isLoading && isAuth && ( - Success Icon - )} -
-
- - Start ${getPrice(activeProduct || null)} - -
- - <> - {translate("auweb.agree.text1")} - {translate("subscription_text", { policyLink })} - - -
- - ); -} - -export default SubscriptionPage; diff --git a/src/components/SubscriptionPage/styles.module.css b/src/components/SubscriptionPage/styles.module.css deleted file mode 100644 index d25d4da..0000000 --- a/src/components/SubscriptionPage/styles.module.css +++ /dev/null @@ -1,59 +0,0 @@ -.page { - padding-bottom: 32px !important; -} - -.subscription-action { - position: fixed; - bottom: 0; - left: 0; - right: 0; - display: flex; - justify-content: center; - background-color: transparent; - padding: 15px; -} - -.cross { - left: 28px; -} - -.inputs-container { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 16px; - margin-top: 16px; -} - -.inputs-container__input { - width: 100%; - border: solid #000 2px; - border-radius: 20px; - font-size: 17px; - font-weight: 400; - line-height: 20px; - padding: 16px 25px; -} - -.inputs-container__input-container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 8px; - width: 100%; -} - -.inputs-container__input-error { - border-color: red; -} - -.inputs-container__label-error { - font-size: 12px; - font-weight: 400; - color: red; - width: 100%; - padding-left: 25px; -} diff --git a/src/components/pages/ABDesign/v1/pages/TrialChoice/index.tsx b/src/components/pages/ABDesign/v1/pages/TrialChoice/index.tsx index 3cf2cf0..f0950da 100644 --- a/src/components/pages/ABDesign/v1/pages/TrialChoice/index.tsx +++ b/src/components/pages/ABDesign/v1/pages/TrialChoice/index.tsx @@ -26,119 +26,9 @@ import { useTranslations } from "@/hooks/translations"; import { addCurrency, ELocalesPlacement, - getDefaultLocaleByLanguage, - language, } from "@/locales"; import DiscountExpires from "../TrialPayment/components/DiscountExpires"; -const textVariants = [ - <> - AURA is the only accurate app with reliable astrological predictions, - verified by professionals and guaranteed to provide accurate astrological - forecasts. -
-
- AURA has already helped millions of people find happiness and discover the - whole truth about their relationships. -
-
- An astrological forecast that will completely change your life is almost - ready! Before we provide it to you, we would like to offer you the - opportunity to choose the amount you consider reasonable to try AURA for 7 - days and which you think is fair for the changes that will happen to you: -
-
- - You will discover all the most intimate secrets that the stars have - prepared for you and solve relationship issues within just one month; -
- - You will once and for all put the finishing touches on unresolved issues - and forget about problems that have been haunting you for years (if not - decades); -
- - You will save hundreds of dollars on fake and unprofessional astrological - predictions and fortune tellers; -
- - You will receive not only a personal astrological forecast but also - personalized daily horoscopes, learn who and how is draining your energy, - and get other personalized readings. -
-
- - A 7-day trial period costs us $5, but please choose the amount that suits - you best: - - , - <> - AURA is the only app you can trust for accurate astrological insights, - crafted and verified by seasoned professionals, ensuring you receive - predictions that are both reliable and transformative. -
-
- AURA has already transformed the lives of millions, bringing clarity, joy, - and a deeper understanding of their relationships. -
-
- Your life-changing astrological forecast is almost ready! But before we - reveal the secrets that will change your life, we want to give you the - freedom to choose how much you feel is fair to try AURA for 7 days. This is - your chance to decide what the transformation is worth to you: -
-
- - Uncover the deepest, most intimate secrets the stars have in store for - you, and watch your relationship issues resolve in just one month; -
- - Finally, put an end to those lingering issues that have been troubling you - for years, maybe even decades; -
- - Save hundreds of dollars by avoiding unreliable astrologers and fake - fortune tellers; -
- - Receive not just a personal astrological forecast but also personalized - daily horoscopes, learn who's draining your energy, and get exclusive, - tailored readings. -
-
- - While a 7-day trial costs us $5, we want you to choose the amount you - believe is right for you: - - , - <> - Discover AURA—the only app that delivers truly accurate astrological - forecasts, with predictions you can trust, all verified by top industry - professionals. Your path to clarity and happiness starts here. -
-
- Millions have already found happiness and uncovered the truth about their - relationships with AURA. Now, it’s your turn. -
-
- Your life-changing astrological forecast is almost ready! Before we share - this powerful insight with you, we’re giving you the chance to set your own - price to experience AURA for 7 days. You decide what feels right for the - life-changing revelations you’ll receive: -
-
- - Reveal the deepest secrets the universe has in store for you and resolve - your relationship dilemmas within a month; -
- - Finally close the chapter on long-standing issues that have plagued you - for years, perhaps even decades; -
- - Avoid wasting hundreds of dollars on untrustworthy, fake astrologers; -
- - Gain access to your personal astrological forecast, receive daily - personalized horoscopes, learn who’s been draining your energy, and benefit - from other insightful readings. -
-
- - Our 7-day trial typically costs us $5, but you get to choose the amount - that feels right for you: - - , -]; - enum EDisplayOptionButton { "alwaysVisible" = "alwaysVisible", "visibleIfChosen" = "visibleIfChosen", @@ -168,7 +58,38 @@ function TrialChoicePage() { const { flags } = useMetricABFlags(); const isShowTimer = flags?.showTimerTrial?.[0] === "show"; - const textVariant = Number(flags?.text?.[0]) || 0; + + const textVariants = [ + translate("/trial-choice.text_variant_1", { + br: "\n", + trialDuration: activeProduct?.trialDuration?.toString() || "7", + span: + {translate("/trial-choice.text_variant_1_span", { + trialDuration: activeProduct?.trialDuration?.toString() || "7", + })} + , + }), + translate("/trial-choice.text_variant_2", { + br: "\n", + trialDuration: activeProduct?.trialDuration?.toString() || "7", + span: + {translate("/trial-choice.text_variant_2_span", { + trialDuration: activeProduct?.trialDuration?.toString() || "7", + })} + , + }), + translate("/trial-choice.text_variant_3", { + br: "\n", + trialDuration: activeProduct?.trialDuration?.toString() || "7", + span: + {translate("/trial-choice.text_variant_3_span", { + trialDuration: activeProduct?.trialDuration?.toString() || "7", + })} + , + }), + ]; + + const textVariant = Number(flags?.text?.[0]) || 2; const getPrice = useCallback( (product: IPaywallProduct) => { @@ -276,7 +197,7 @@ function TrialChoicePage() { }} /> )} - {(!textVariant || getDefaultLocaleByLanguage(language) !== "en") && ( + {(!textVariant && ( <>

- )} - - {!!textVariant && getDefaultLocaleByLanguage(language) === "en" && ( + ))} + {!!textVariant && (

{textVariants[textVariant - 1]} @@ -357,8 +278,8 @@ function TrialChoicePage() { style={ arrowLeft ? { - left: arrowLeft, - } + left: arrowLeft, + } : {} } /> @@ -402,9 +323,8 @@ function TrialChoicePage() { isActiveBlur={true} > {getText("text.button.1", { diff --git a/src/components/pages/ABDesign/v1/pages/TrialPayment/index.tsx b/src/components/pages/ABDesign/v1/pages/TrialPayment/index.tsx index 5bc9290..45eb5a2 100644 --- a/src/components/pages/ABDesign/v1/pages/TrialPayment/index.tsx +++ b/src/components/pages/ABDesign/v1/pages/TrialPayment/index.tsx @@ -28,8 +28,8 @@ import metricService, { } from "@/services/metric/metricService"; import { useTranslations } from "@/hooks/translations"; import { ELocalesPlacement } from "@/locales"; -import { usePayment } from "@/hooks/payment/nmi/usePayment"; -import Loader, { LoaderColor } from "@/components/Loader"; +import Modal from "@/components/Modal"; +import PaymentForm from "@/components/Payment/nmi/PaymentForm"; const placementKey = EPlacementKeys["aura.placement.redesign.main"] @@ -60,28 +60,20 @@ function TrialPaymentPage() { >("single"); const { subPlan } = useParams(); - const { isLoading, isModalClosed, error, isPaymentSuccess, showCreditCardForm } = usePayment({ - placementKey, - activeProduct: activeProduct! - }); + const [isPaymentModalOpen, setIsPaymentModalOpen] = useState(false); - useEffect(() => { - if (isPaymentSuccess) { - return navigate(routes.client.paymentSuccess()) - } - }, [isPaymentSuccess]) + const onPaymentSuccess = () => { + return navigate(routes.client.paymentSuccess()) + } - useEffect(() => { - if (isModalClosed && !isPaymentSuccess && !isLoading) { - return handleDiscount() - } - }, [isModalClosed, isPaymentSuccess, isLoading]) + const onModalClosed = () => { + setIsPaymentModalOpen(false); + return handleDiscount() + } - useEffect(() => { - if (error?.length && error !== "Product not found") { - return navigate(routes.client.paymentFail()) - } - }, [error]) + const onPaymentError = () => { + return navigate(routes.client.paymentFail()) + } useEffect(() => { metricService.reachGoal(EGoals.AURA_TRIAL_PAYMENT_PAGE_VISIT, [ @@ -137,7 +129,7 @@ function TrialPaymentPage() { const openPaymentModal = () => { metricService.reachGoal(EGoals.AURA_PAYMENT_METHODS_OPENED); - showCreditCardForm() + setIsPaymentModalOpen(true); }; return ( @@ -148,11 +140,13 @@ function TrialPaymentPage() { backgroundColor: gender === "male" ? "#C1E5FF" : "#F7EBFF", }} > - {isLoading && -

- -
- } + + +
} - const { isLoading, error, isPaymentSuccess, showCreditCardForm } = usePayment({ - placementKey: EPlacementKeys["aura.placement.secret.discount"], - activeProduct: activeProduct! - }); + const onPaymentSuccess = () => { + return navigate(routes.client.paymentSuccess()) + } + const onModalClosed = () => { + setIsPaymentModalOpen(false); + } - - useEffect(() => { - if (isPaymentSuccess) { - return navigate(routes.client.paymentSuccess()) - } - }, [isPaymentSuccess]) - - useEffect(() => { - if (error?.length && error !== "Product not found") { - return navigate(routes.client.paymentFail()) - } - }, [error]) - + const onPaymentError = () => { + return navigate(routes.client.paymentFail()) + } const openPaymentModal = () => { - showCreditCardForm() + setIsPaymentModalOpen(true); }; return ( - {isLoading && -
- -
- } + + +
diff --git a/src/components/palmistry/discount-screen/discount-screen.tsx b/src/components/palmistry/discount-screen/discount-screen.tsx index 2279e97..d7af0cb 100644 --- a/src/components/palmistry/discount-screen/discount-screen.tsx +++ b/src/components/palmistry/discount-screen/discount-screen.tsx @@ -43,18 +43,6 @@ export default function DiscountScreen() { navigate(routes.client.palmistryPremiumBundle()); }; - // React.useEffect(() => { - // (async () => { - // const { sub_plans } = await api.getSubscriptionPlans({ locale }); - // const plan = sub_plans.find((plan) => plan.id === "stripe.40"); - // - // if (!plan?.price_cents) return; - // - // setPrice((plan?.price_cents / 100).toFixed(2)); - // })(); - // // eslint-disable-next-line react-hooks/exhaustive-deps - // }, []); - React.useEffect(() => { (async () => { const products = await api.getSinglePaymentProducts({ token }); diff --git a/src/hooks/payment/nmi/usePayment.ts b/src/hooks/payment/nmi/usePayment.ts index 775b8ba..a71dbe9 100644 --- a/src/hooks/payment/nmi/usePayment.ts +++ b/src/hooks/payment/nmi/usePayment.ts @@ -10,9 +10,28 @@ import { useSelector } from "react-redux"; interface IUsePaymentProps { placementKey: EPlacementKeys; activeProduct: IPaywallProduct; + paymentFormType?: "lightbox" | "inline"; + cardNumberRef?: React.RefObject; + cardExpiryRef?: React.RefObject; + cardCvvRef?: React.RefObject; } -export const usePayment = ({ placementKey, activeProduct }: IUsePaymentProps) => { +interface IFieldValidation { + isValid: boolean; + message: string; +} + +interface IFormValidation { + ccnumber: IFieldValidation; + ccexp: IFieldValidation; + cvv: IFieldValidation; +} + +export const usePayment = ({ + placementKey, + activeProduct, + paymentFormType = "lightbox", +}: IUsePaymentProps) => { const api = useApi(); const token = useSelector(selectors.selectToken); const [isSubmitting, setIsSubmitting] = useState(false); @@ -22,6 +41,15 @@ export const usePayment = ({ placementKey, activeProduct }: IUsePaymentProps) => const formPrice = String((activeProduct?.trialPrice || 99) / 100); const [isOpenModal, setIsOpenModal] = useState(false); const [isModalClosed, setIsModalClosed] = useState(false); + const [formValidation, setFormValidation] = useState({ + ccnumber: { isValid: false, message: '' }, + ccexp: { isValid: false, message: '' }, + cvv: { isValid: false, message: '' } + }); + + const isFormValid = useMemo(() => { + return Object.values(formValidation).every(field => field.isValid); + }, [formValidation]); const updatePaymentModalState = () => { setIsOpenModal(false); @@ -39,41 +67,53 @@ export const usePayment = ({ placementKey, activeProduct }: IUsePaymentProps) => }); useEffect(() => { + if (!activeProduct || !products.length) return; const product = products.find((product) => product._id === activeProduct._id); if (!product) { setError("Product not found"); } else { setError(null); } - }, [products]); + }, [products, activeProduct]); useEffect(() => { if (!activeProduct) return; - window.CollectJS.configure({ - variant: 'lightbox', + + const config: any = { + variant: paymentFormType, callback: (token: any) => { finishSubmit(token); }, + paymentSelector: '#customPayButton', primaryColor: '#066fde', theme: "material", + price: formPrice, fields: { ccnumber: { placeholder: '1234 1234 1234 1234', - selector: '#ccnumber' + selector: '#card-number' }, ccexp: { placeholder: 'MM/YY', - selector: '#ccexp' + selector: '#card-expiry' }, cvv: { placeholder: 'CVC', - selector: '#cvv' + selector: '#card-cvv' } }, - price: formPrice, - // country: "US", - // currency: "USD", - }); + validationCallback: (field: string, status: boolean, message: string) => { + setFormValidation(prev => ({ + ...prev, + [field]: { + isValid: status, + message: status ? '' : message + } + })); + }, + }; + + window.CollectJS?.configure(config); }, [placementId, paywallId, activeProduct]); const finishSubmit = async (response: any) => { @@ -106,6 +146,24 @@ export const usePayment = ({ placementKey, activeProduct }: IUsePaymentProps) => setIsOpenModal(true) }; + const submitInlineForm = () => { + try { + setIsSubmitting(true); + console.log("submitInlineForm"); + + window.CollectJS.startPaymentRequest(); + } catch (error: any) { + console.error('Payment form error:', error); + setError(error?.message); + setIsSubmitting(false); + } + }; + + const closeModal = () => { + setIsOpenModal(false); + setIsModalClosed(true); + } + return useMemo(() => ({ isLoading, paymentResponse, @@ -113,7 +171,11 @@ export const usePayment = ({ placementKey, activeProduct }: IUsePaymentProps) => isPaymentSuccess, isOpenModal, isModalClosed, - showCreditCardForm + showCreditCardForm, + submitInlineForm, + closeModal, + formValidation, + isFormValid }), [ isLoading, paymentResponse, @@ -121,6 +183,10 @@ export const usePayment = ({ placementKey, activeProduct }: IUsePaymentProps) => isPaymentSuccess, isOpenModal, isModalClosed, - showCreditCardForm + showCreditCardForm, + submitInlineForm, + closeModal, + formValidation, + isFormValid ]) } \ No newline at end of file diff --git a/src/hooks/paywall/defaultPaywalls.ts b/src/hooks/paywall/defaultPaywalls.ts index 8144db0..813f398 100644 --- a/src/hooks/paywall/defaultPaywalls.ts +++ b/src/hooks/paywall/defaultPaywalls.ts @@ -692,4 +692,75 @@ export const defaultPaywalls: { [key in EPlacementKeys]: IPaywall } = { } ] }, + "aura.placement.email.palmistry": { + "_id": "678be264aaa17756a1e517de", + "key": "aura.paywall.email.palmistry.main", + "name": "Email Palmistry", + "properties": [ + { + "key": "full.price", + "value": "45", + "_id": "678be264aaa17756a1e517df" + } + ], + "products": [ + { + "_id": "65ff043dfc0fcfc4be550035", + "key": "compatibility.pdf.trial.0", + "productId": "prod_PnStTEBzrPLgvL", + "name": "Сompatibility AURA | Trial $0.99", + "priceId": "price_1PpFiwIlX4lgwUxruq9bpp0j", + "type": "subscription", + "description": "Description", + "discountPrice": null, + "discountPriceId": null, + "isDiscount": false, + "isFreeTrial": false, + "isTrial": true, + "price": 1900, + "trialDuration": 7, + "trialPrice": 100, + "trialPriceId": "price_1PpFoNIlX4lgwUxrP4l0lbE5", + "currency": "usd" + } + ] + }, + "aura.placement.email.palmistry.discount": { + "_id": "678be2b9aaa17756a1e51faa", + "key": "aura.paywall.email.palmistry.discount", + "name": "Email Palmistry Discount", + "properties": [ + { + "key": "full.price", + "value": "45", + "_id": "678be264aaa17756a1e517df" + }, + { + "key": "discount", + "value": "70", + "_id": "" + } + ], + "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" + } + ] + } } \ No newline at end of file diff --git a/src/hooks/translations/index.tsx b/src/hooks/translations/index.tsx index 01c564d..ee6d2d2 100644 --- a/src/hooks/translations/index.tsx +++ b/src/hooks/translations/index.tsx @@ -16,7 +16,7 @@ export const useTranslations = ( const [searchParams] = useSearchParams(); const { t, i18n } = useTranslation(); const gender = - useSelector(selectors.selectQuestionnaire)?.gender || "default"; + useSelector(selectors.selectQuestionnaire)?.gender || "male"; const { flags } = useMetricABFlags(); const esFlag = flags?.esFlag?.[0]; diff --git a/src/init.tsx b/src/init.tsx index cff22b2..a077d66 100755 --- a/src/init.tsx +++ b/src/init.tsx @@ -12,11 +12,9 @@ import { buildResources, fallbackLng, getDefaultLocaleByLanguage, - getTranslationJSON, - ELocalesPlacement, + getTranslationsJSON, language, setLanguage, - TTranslationPlacements, } from "./locales"; import App from "./components/App"; import metricService from "./services/metric/metricService"; @@ -39,16 +37,10 @@ const init = async () => { api.getAppConfig({ bundleId: "auraweb" }), ]); - const localePlacements = Object.values(ELocalesPlacement); - - const translationPlacements: TTranslationPlacements = {}; - - for (const placement of localePlacements) { - const translationsPlacement = await getTranslationJSON(placement, language); - translationPlacements[placement] = translationsPlacement; - } - - const resources = buildResources(translationsResponse, translationPlacements); + console.time('translations-loading'); + const translationsPlacement = await getTranslationsJSON(language); + console.timeEnd('translations-loading'); + const resources = buildResources(translationsResponse, translationsPlacement); const legal = buildLegal(elementsResponse); const config = configResponse.data; diff --git a/src/locales/index.ts b/src/locales/index.ts index 8efc681..1654092 100644 --- a/src/locales/index.ts +++ b/src/locales/index.ts @@ -70,58 +70,49 @@ export enum ELocalesPlacement { PalmistryV0 = "palmistry-v0", PalmistryV01 = "palmistry-v0_1", PalmistryV1 = "palmistry-v1", + PalmistryV11 = "palmistry-v1_1", Chats = "chats" } interface ITranslationJSON { male: { [key: string]: string } female: { [key: string]: string } - default: { [key: string]: string } - fallback: { male: { [key: string]: string }; female: { [key: string]: string }, default: { [key: string]: string } } + fallback: { male: { [key: string]: string }; female: { [key: string]: string } } } export type TTranslationPlacements = Partial< Record > -export const getTranslationJSON = async (placement: ELocalesPlacement | undefined, language: string): Promise => { - const protocol = window.location.protocol; - const host = window.location.host; +export const getTranslationsJSON = async (language: string): Promise => { + const api = createApi(); let defaultLanguage = getDefaultLocaleByLanguage(language).toLowerCase(); if (defaultLanguage === "pt") { defaultLanguage = "pt-pt" } - const localePlacement = placement || ELocalesPlacement.V1 - let result; - try { - const [ - resultMale, - resultFemale, - resultMaleFallback, - resultFemaleFallback, - ] = await Promise.all([ - (await fetch(`${protocol}//${host}/locales/${localePlacement}/${defaultLanguage}/male_${defaultLanguage}.json`)).json(), - (await fetch(`${protocol}//${host}/locales/${localePlacement}/${defaultLanguage}/female_${defaultLanguage}.json`)).json(), - (await fetch(`${protocol}//${host}/locales/${localePlacement}/en/male_en.json`)).json(), - (await fetch(`${protocol}//${host}/locales/${localePlacement}/en/female_en.json`)).json() - ]); - result = { - male: resultMale, female: resultFemale, default: resultMale, fallback: { - male: resultMaleFallback, female: resultFemaleFallback, default: resultMaleFallback - } - } + 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 result = responses.reduce((merged, current, index) => ({ + ...merged, + [placements[index]]: current + }), {}); + + return result; } catch (error) { - if (language !== fallbackLng) { - result = await getTranslationJSON(localePlacement, fallbackLng) - } - result = { - male: {}, female: {}, default: {}, fallback: { - male: {}, female: {}, default: {} - } - } + console.error('Translation loading error:', error); + return {}; } - return result; } export const buildResources = (resp: Translations.Response, translationJSON: TTranslationPlacements) => { diff --git a/src/routerComponents/Palmistry/v2/LayoutPalmistryV2/index.tsx b/src/routerComponents/Palmistry/v2/LayoutPalmistryV2/index.tsx index 1a74f16..527296e 100644 --- a/src/routerComponents/Palmistry/v2/LayoutPalmistryV2/index.tsx +++ b/src/routerComponents/Palmistry/v2/LayoutPalmistryV2/index.tsx @@ -2,11 +2,10 @@ import Header from "@/components/pages/ABDesign/v1/components/Header"; import styles from "./styles.module.scss"; import { useSchemeColorByElement } from "@/hooks/useSchemeColorByElement"; import { useEffect, useMemo, useRef } from "react"; -import { Outlet, useLocation, useSearchParams } from "react-router-dom"; +import { Outlet, useLocation } from "react-router-dom"; import routes from "@/routes"; -import PaymentModal from "@/components/PalmistryV1/components/PaymentModal"; -import { useDispatch, useSelector } from "react-redux"; -import { actions, selectors } from "@/store"; +import { useDispatch } from "react-redux"; +import { actions } from "@/store"; const isBackButtonVisibleRoutes = [ routes.client.palmistryV1Birthdate(), @@ -40,8 +39,6 @@ const headerClassNames = { function LayoutPalmistryV2() { const dispatch = useDispatch(); - const token = useSelector(selectors.selectToken); - const activeProductFromStore = useSelector(selectors.selectActiveProduct); const location = useLocation(); const mainRef = useRef(null); @@ -56,12 +53,6 @@ function LayoutPalmistryV2() { useSchemeColorByElement(mainRef.current, "section.page, .page, section", [ location, ]); - const isShowPaymentModal = useSelector( - selectors.selectPalmistryIsShowPaymentModalV1 - ); - const [searchParams] = useSearchParams(); - const subscriptionStatus = - searchParams.get("redirect_status") === "succeeded" ? "subscribed" : "lead"; const getIsBackButtonVisible = () => { for (const route of isBackButtonVisibleRoutes) { @@ -87,19 +78,6 @@ function LayoutPalmistryV2() { {/* }> */}
- {!!token.length && !!activeProductFromStore && ( - // [ - // routes.client.palmistryV1Payment(), - // routes.client.palmistryV1TrialPayment(), - // ].includes(location.pathname) && - - )}
{/*
*/} diff --git a/src/routerComponents/Palmistry/v2/LayoutPalmistryV2/styles.module.scss b/src/routerComponents/Palmistry/v2/LayoutPalmistryV2/styles.module.scss index 1e96061..2b66641 100644 --- a/src/routerComponents/Palmistry/v2/LayoutPalmistryV2/styles.module.scss +++ b/src/routerComponents/Palmistry/v2/LayoutPalmistryV2/styles.module.scss @@ -5,11 +5,15 @@ min-height: 100dvh; max-width: 560px; margin: 0 auto; - overflow-x: hidden; + // overflow-x: hidden; } .header { padding: 8px 0 30px; + + & > button { + margin-left: -12px; + } } .header-title { diff --git a/src/routes.ts b/src/routes.ts index c315bf6..2580c35 100755 --- a/src/routes.ts +++ b/src/routes.ts @@ -7,17 +7,20 @@ const host = ""; export const apiHost = environments.AURA_API_HOST; const dApiHost = environments.AURA_DAPI_HOST; const dApiPrefix = environments.AURA_DAPI_PREFIX; -const siteHost = environments.AURA_SITE_HOST; +// const siteHost = environments.AURA_SITE_HOST; const prefix = environments.AURA_PREFIX; const openAIHost = environments.AURA_OPEN_AI_HOST; const openAiPrefix = environments.AURA_OPEN_AI_PREFIX; export const palmistryV1Prefix = [host, "v1", "palmistry"].join("/") export const palmistryV2Prefix = [host, "v2", "palmistry"].join("/") +export const palmistryEmailMarketingV2Prefix = [palmistryV2Prefix, "email-marketing"].join("/") export const emailMarketingV1Prefix = [host, "v1", "email-marketing"].join("/") export const chatsPrefix = [host, "chats"].join("/") +export const oldBackendPrefix = [`${window.location.protocol}/`, window.location.host, "old-backend"].join("/") + const routes = { client: { root: () => [host, ""].join("/"), @@ -55,7 +58,7 @@ const routes = { emailEnter: () => [host, "email"].join("/"), authResult: () => [host, "auth", "result"].join("/"), auth: () => [host, "auth"].join("/"), - subscription: () => [host, "subscription"].join("/"), + // subscription: () => [host, "subscription"].join("/"), createProfile: () => [host, "profile", "create"].join("/"), attention: () => [host, "attention"].join("/"), feedback: () => [host, "feedback"].join("/"), @@ -64,7 +67,7 @@ const routes = { paymentSuccess: () => [host, "payment", "success"].join("/"), paymentFail: () => [host, "payment", "fail"].join("/"), wallpaper: () => [host, "wallpaper"].join("/"), - static: () => [host, "static", ":typeId"].join("/"), + // static: () => [host, "static", ":typeId"].join("/"), legal: (type: string) => [host, "static", type].join("/"), compatibility: () => [host, "compatibility"].join("/"), compatibilityResult: () => [host, "compatibility", "result"].join("/"), @@ -187,9 +190,9 @@ const routes = { palmistryV1Payment: () => [palmistryV1Prefix, "payment"].join("/"), palmistryOnboardingV1: () => [palmistryV1Prefix, "onboarding"].join("/"), // PalmistryV2 - palmistryV2TrialPayment: () => [palmistryV2Prefix, "trial-payment"].join("/"), - palmistryV2SaveOff: () => [palmistryV2Prefix, "save-off"].join("/"), - palmistryV2SecretDiscount: () => [palmistryV2Prefix, "secret-discount"].join("/"), + palmistryV2TrialPayment: () => [palmistryEmailMarketingV2Prefix, "trial-payment"].join("/"), + palmistryV2SaveOff: () => [palmistryEmailMarketingV2Prefix, "save-off"].join("/"), + palmistryV2SecretDiscount: () => [palmistryEmailMarketingV2Prefix, "secret-discount"].join("/"), // MarketingLandingV1 emailMarketingV1Landing: () => [emailMarketingV1Prefix, "marketing-landing"].join("/"), emailMarketingV1SpecialOffer: () => [emailMarketingV1Prefix, "special-offer"].join("/"), @@ -299,19 +302,15 @@ const routes = { }, server: { userLocale: () => ["https://ipapi.co", "json"].join("/"), - appleAuth: (origin: string) => - [apiHost, "auth", "apple", `gate?origin=${origin}`].join("/"), - googleAuth: (origin: string) => - [apiHost, "auth", "google", `gate?origin=${origin}`].join("/"), user: () => [apiHost, prefix, "user.json"].join("/"), - token: () => [apiHost, prefix, "auth", "token.json"].join("/"), - elements: () => [apiHost, prefix, "elements.json"].join("/"), + // token: () => [apiHost, prefix, "auth", "token.json"].join("/"), + elements: () => [oldBackendPrefix, "elements.json"].join("/"), zodiacs: (zodiac: string) => [apiHost, prefix, "zodiacs", `${zodiac}.json`].join("/"), - element: (type: string) => - [apiHost, prefix, "elements", `${type}.json`].join("/"), + // element: (type: string) => + // [apiHost, prefix, "elements", `${type}.json`].join("/"), apps: (bundleId: string) => - [apiHost, prefix, "apps", `${bundleId}.json`].join("/"), + [oldBackendPrefix, `${bundleId}.json`].join("/"), assets: (category: string) => [apiHost, prefix, "assets", "categories", `${category}.json`].join("/"), assetCategories: () => @@ -319,15 +318,13 @@ const routes = { dailyForecasts: () => [apiHost, prefix, "user", "daily_forecast.json"].join("/"), auras: () => [apiHost, prefix, "user", "aura.json"].join("/"), - paymentIntents: () => - [apiHost, prefix, "user", "payment_intents.json"].join("/"), - subscriptionItems: () => - [apiHost, prefix, "user", "subscription", "item_prices.json"].join("/"), - subscriptionPlans: () => [apiHost, prefix, "sub_plans.json"].join("/"), - subscriptionCheckout: () => - [apiHost, prefix, "user", "subscription", "checkout", "new.json"].join( - "/" - ), + // subscriptionItems: () => + // [apiHost, prefix, "user", "subscription", "item_prices.json"].join("/"), + // subscriptionPlans: () => [apiHost, prefix, "sub_plans.json"].join("/"), + // subscriptionCheckout: () => + // [apiHost, prefix, "user", "subscription", "checkout", "new.json"].join( + // "/" + // ), subscriptionStatus: () => [apiHost, prefix, "user", "subscription_receipts", "status.json"].join( "/" @@ -336,10 +333,10 @@ const routes = { [dApiHost, "users", "subscription", "status"].join("/"), subscriptionReceipts: () => [apiHost, prefix, "user", "subscription_receipts.json"].join("/"), - subscriptionReceipt: (id: string) => - [apiHost, prefix, "user", "subscription_receipts", `${id}.json`].join( - "/" - ), + // subscriptionReceipt: (id: string) => + // [apiHost, prefix, "user", "subscription_receipts", `${id}.json`].join( + // "/" + // ), compatCategories: () => [apiHost, prefix, "ai", "compat_categories.json"].join("/"), compat: () => [apiHost, prefix, "ai", "compats.json"].join("/"), @@ -347,7 +344,8 @@ const routes = { [apiHost, prefix, "user", "callbacks.json"].join("/"), getUserCallbacks: (id: string) => [apiHost, prefix, "user", "callbacks", `${id}.json`].join("/"), - getTranslations: () => [siteHost, "api/v2", "t.json"].join("/"), + getTranslations: () => [oldBackendPrefix, "t.json"].join("/"), + // getTranslations: () => [siteHost, "api/v2", "t.json"].join("/"), aiRequestsV2: (promptKey: string) => [apiHost, "api/v2", "ai", "prompts", promptKey, "requests.json"].join( "/" @@ -395,6 +393,7 @@ const routes = { // Session createSession: () => [dApiHost, dApiPrefix, "session"].join("/"), updateSession: (id: string) => [dApiHost, dApiPrefix, "session", id].join("/"), + getLocale: () => [dApiHost, dApiPrefix, "session", "locale"].join("/"), // Chats getChatsCategories: () => [dApiHost, "chats", "categories"].join("/"), @@ -422,7 +421,7 @@ const routes = { export const entrypoints = [ routes.client.root(), routes.client.birthday(), - routes.client.subscription(), + // routes.client.subscription(), routes.client.wallpaper(), routes.client.didYouKnow(), routes.client.attention(), @@ -451,7 +450,7 @@ export const hasNoNavigation = (path: string) => !hasNavigation(path); export const withCrossButtonRoutes = [ // routes.client.attention(), - routes.client.subscription(), + // routes.client.subscription(), routes.client.paymentMethod(), ]; /** @@ -562,7 +561,7 @@ export const withoutHeaderRoutes = [ routes.client.palmistryDiscount(), routes.client.palmistryPremiumBundle(), routes.client.compatibility(), - routes.client.subscription(), + // routes.client.subscription(), routes.client.paymentMethod(), routes.client.paymentResult(), routes.client.paymentSuccess(), diff --git a/src/services/price/index.ts b/src/services/price/index.ts index 01cb9f0..50a4ba3 100755 --- a/src/services/price/index.ts +++ b/src/services/price/index.ts @@ -1,4 +1,9 @@ -import { ITrial } from "@/api/resources/SubscriptionPlans"; +interface ITrial { + is_paid: boolean; + is_free: boolean; + days: number; + price_cents: number; +} export const roundToWhole = (value: string | number): number => { value = Number(value); diff --git a/src/store/index.ts b/src/store/index.ts index f091192..2300bdc 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -39,14 +39,7 @@ import onboardingConfig, { import payment, { actions as paymentActions, selectActiveProduct, - selectIsDiscount, - selectStripeButton, - selectSubscriptionReceipt, } from "./payment"; -import subscriptionPlans, { - actions as subscriptionPlasActions, - selectPlanById, -} from "./subscriptionPlan"; import status, { actions as userStatusActions, selectStatus } from "./status"; import compatibility, { actions as compatibilityActions, @@ -100,7 +93,6 @@ export const actions = { user: userActions, form: formActions, status: userStatusActions, - subscriptionPlan: subscriptionPlasActions, aura: auraActions, paywalls: paywallsActions, siteConfig: siteConfigActions, @@ -126,7 +118,6 @@ export const selectors = { selectUTM, selectUser, selectStatus, - selectPlanById, selectAuraCoordinates, selectRightUser, selectSelfName, @@ -136,8 +127,6 @@ export const selectors = { selectUserCallbacksDescription, selectUserCallbacksPrevStat, selectHome, - selectIsDiscount, - selectSubscriptionReceipt, selectOnboarding, selectOnboardingHome, selectOnboardingCompatibility, @@ -160,7 +149,6 @@ export const selectors = { selectPaywalls, selectPaywallsIsMustUpdate, selectPrivacyPolicy, - selectStripeButton, selectPersonalVideo, selectCurrency, selectPalmistryFromRedesign, @@ -183,7 +171,6 @@ export const reducer = combineReducers({ user, form, status, - subscriptionPlans, aura, payment, compatibility, diff --git a/src/store/payment.ts b/src/store/payment.ts index 50d4d06..8e2bffb 100644 --- a/src/store/payment.ts +++ b/src/store/payment.ts @@ -1,5 +1,4 @@ import { IPaywallProduct } from "@/api/resources/Paywall"; -import { SubscriptionReceipt } from "@/api/resources/UserSubscriptionReceipts"; import { TCanMakePaymentResult } from "@/hooks/payment/useCanUseStripeButton"; import { createSlice, createSelector } from "@reduxjs/toolkit"; import type { PayloadAction } from "@reduxjs/toolkit"; @@ -12,21 +11,12 @@ interface IStripeButton { interface IPayment { selectedPrice: number | null; - isDiscount: boolean; - subscriptionReceipt: SubscriptionReceipt | null; activeProduct: IPaywallProduct | null; - stripeButton: IStripeButton; } const initialState: IPayment = { selectedPrice: null, - isDiscount: false, - subscriptionReceipt: null, activeProduct: null, - stripeButton: { - paymentRequest: null, - availableMethods: null, - } }; const paymentSlice = createSlice({ @@ -52,16 +42,4 @@ export const selectActiveProduct = createSelector( (state: { payment: IPayment }) => state.payment.activeProduct, (payment) => payment ); -export const selectIsDiscount = createSelector( - (state: { payment: IPayment }) => state.payment.isDiscount, - (payment) => payment -); -export const selectSubscriptionReceipt = createSelector( - (state: { payment: IPayment }) => state.payment.subscriptionReceipt, - (payment) => payment -); -export const selectStripeButton = createSelector( - (state: { payment: IPayment }) => state.payment.stripeButton, - (payment) => payment -); export default paymentSlice.reducer; diff --git a/src/store/paywalls.ts b/src/store/paywalls.ts index 65542b4..2692ab7 100644 --- a/src/store/paywalls.ts +++ b/src/store/paywalls.ts @@ -25,6 +25,8 @@ const initialState: TPaywalls = { "aura.placement.palmistry.main": null, "aura.placement.palmistry.redesign": null, "aura.placement.chat": null, + "aura.placement.email.palmistry": null, + "aura.placement.email.palmistry.discount": null, isMustUpdate: { "aura.placement.v1.mike": true, "aura.placement.main": true, @@ -34,6 +36,8 @@ const initialState: TPaywalls = { "aura.placement.palmistry.main": true, "aura.placement.palmistry.redesign": true, "aura.placement.chat": true, + "aura.placement.email.palmistry": true, + "aura.placement.email.palmistry.discount": true, }, } diff --git a/src/store/subscriptionPlan.ts b/src/store/subscriptionPlan.ts deleted file mode 100644 index 64e8bf4..0000000 --- a/src/store/subscriptionPlan.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - createSlice, createEntityAdapter, createSelector, EntityState -} from '@reduxjs/toolkit' -import { SubscriptionItems } from '../api' - -type SubscriptionPlan = SubscriptionItems.ItemPrice - -const subscriptionPlanAdapter = createEntityAdapter({ - selectId: (plan) => plan.id, - sortComparer: (a, b) => a.created_at - b.created_at, -}) - -const initialState = subscriptionPlanAdapter.getInitialState() - -const subscriptionPlanSlice = createSlice({ - name: 'subscriptionPlans', - initialState, - reducers: { - setAll: subscriptionPlanAdapter.setAll, - }, - extraReducers: (builder) => builder.addCase('reset', () => initialState) -}) - -export const { actions } = subscriptionPlanSlice -const { selectById } = subscriptionPlanAdapter.getSelectors() -export const selectPlanById = (id: string) => createSelector( - (state: { subscriptionPlans: EntityState }) => state.subscriptionPlans, - (state) => selectById(state, id) -) -export default subscriptionPlanSlice.reducer