From 1ccc5aa835bb7ed636b96bb3757766d6075b4f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BD=D0=B8=D1=81=20=D0=9A=D0=B0=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D0=B2?= Date: Sat, 10 Feb 2024 02:30:02 +0000 Subject: [PATCH] Preview/discount pages --- public/applepay.webp | Bin 0 -> 662 bytes public/credit-card.svg | 15 ++ public/ellipse.webp | Bin 0 -> 6342 bytes public/fire.png | Bin 0 -> 3251 bytes public/friends.webp | Bin 0 -> 6186 bytes public/paypal.webp | Bin 0 -> 1948 bytes public/present.png | Bin 0 -> 18734 bytes public/security.svg | 6 + src/components/App/index.tsx | 17 +- src/components/Modal/index.tsx | 14 +- src/components/Modal/styles.module.css | 2 +- .../PaymentPage/methods/Stripe/Modal.tsx | 69 ++++-- .../methods/Stripe/styles.module.css | 38 +++ .../StripePage/ApplePayButton/index.tsx | 6 +- .../pages/AdditionalDiscount/index.tsx | 40 +++ .../AdditionalDiscount/styles.module.css | 69 ++++++ .../components/PaymentMethodsChoice/index.tsx | 30 +++ .../PaymentMethodsChoice/styles.module.css | 23 ++ .../components/PayPalButton/index.tsx | 23 ++ .../components/PayPalButton/styles.module.css | 9 + .../components/PaymentModal/index.tsx | 232 ++++++++++++++++++ .../components/PaymentModal/styles.module.css | 25 ++ .../components/SecurityPayments/index.tsx | 10 + .../SecurityPayments/styles.module.css | 12 + src/components/pages/TrialPayment/index.tsx | 31 ++- .../PaymentDiscountTable/index.tsx | 43 ++++ .../PaymentDiscountTable/styles.module.css | 156 ++++++++++++ .../pages/TrialPaymentWithDiscount/index.tsx | 47 ++++ .../styles.module.css | 58 +++++ .../CreditCard/index.tsx | 12 + .../CreditCard/styles.module.css | 14 ++ .../PayPayOrApplePay/index.tsx | 10 + .../PayPayOrApplePay/styles.module.css | 12 + src/data/paymentMethods.tsx | 23 ++ src/index.css | 121 ++++++++- src/init.tsx | 10 +- src/routes.ts | 7 + src/store/index.ts | 9 +- src/store/payment.ts | 9 +- 39 files changed, 1158 insertions(+), 44 deletions(-) create mode 100644 public/applepay.webp create mode 100644 public/credit-card.svg create mode 100644 public/ellipse.webp create mode 100644 public/fire.png create mode 100644 public/friends.webp create mode 100644 public/paypal.webp create mode 100644 public/present.png create mode 100644 public/security.svg create mode 100644 src/components/PaymentPage/methods/Stripe/styles.module.css create mode 100644 src/components/pages/AdditionalDiscount/index.tsx create mode 100644 src/components/pages/AdditionalDiscount/styles.module.css create mode 100644 src/components/pages/TrialPayment/components/PaymentMethodsChoice/index.tsx create mode 100644 src/components/pages/TrialPayment/components/PaymentMethodsChoice/styles.module.css create mode 100644 src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/index.tsx create mode 100644 src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/styles.module.css create mode 100644 src/components/pages/TrialPayment/components/PaymentModal/index.tsx create mode 100644 src/components/pages/TrialPayment/components/PaymentModal/styles.module.css create mode 100644 src/components/pages/TrialPayment/components/SecurityPayments/index.tsx create mode 100644 src/components/pages/TrialPayment/components/SecurityPayments/styles.module.css create mode 100644 src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/index.tsx create mode 100644 src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/styles.module.css create mode 100644 src/components/pages/TrialPaymentWithDiscount/index.tsx create mode 100644 src/components/pages/TrialPaymentWithDiscount/styles.module.css create mode 100644 src/components/ui/PaymentMethodsButtons/CreditCard/index.tsx create mode 100644 src/components/ui/PaymentMethodsButtons/CreditCard/styles.module.css create mode 100644 src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/index.tsx create mode 100644 src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/styles.module.css create mode 100644 src/data/paymentMethods.tsx diff --git a/public/applepay.webp b/public/applepay.webp new file mode 100644 index 0000000000000000000000000000000000000000..366920722f291c1815cb7a9620c88444efbf22d1 GIT binary patch literal 662 zcmV;H0%`qHNk&GF0ssJ4MM6+kP&il$0000G0001A001Wd06|PpNH+oi00EFy+jiYn z8-f^IK`;bC2m~>>W>7P*87K@~27+J+2ZA814?QfW0T2-rpxa?DLErobSiad0V7j~K z(Q^xbYXL~NGB+*$r3{#V3iiL_fU@X0_CFIKFPf8my@bswUVp$lNiGJ-EsL9>{!!Vd zg;c$lOg&6d+47>MLK3;w4vrqn7~FImtf} z01hbv98Qvd100evU_Aqpt~psxq$)@kkkrmVkrV*_wHAON<{nS%046EtoP8$(ILQ@Y zZLNUmV=muZR-=x(8>)Yj4j{KOa7_EXclXe|0N^SQ;3b<=+%t#zOWaaAvsq6Ch*bnG zN#`8aC+wxR`zee!4j@-25G8Gsioa8G`#gfWOW|%K0L4iH@RGtL%-_hm?+ZXl#}?yj z9nb{?=>=rfWfH7UC4g1!FtP!Mlz`aSrfp9ZAgTKraX^s_@C`rBKLrqbX+#+a0r*w> zJt^rEwvK0L1-1i-9bcvt|Cj;AH-e2hAYV|_U#F~kY@<$VTr*rl$~4n}&Iw7+Ijhurp{F09H^qATR&`0I&l9 wodGIf04M-Hkw~CSC8Q%FA; + + + + + + + + + + + \ No newline at end of file diff --git a/public/ellipse.webp b/public/ellipse.webp new file mode 100644 index 0000000000000000000000000000000000000000..215b5549e925e45bc15e5af27aab02dd5f3ffbbd GIT binary patch literal 6342 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 literal 0 HcmV?d00001 diff --git a/public/fire.png b/public/fire.png new file mode 100644 index 0000000000000000000000000000000000000000..a4525ade8c4085a4245a0ee9f446902cafcc2734 GIT binary patch literal 3251 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 literal 0 HcmV?d00001 diff --git a/public/friends.webp b/public/friends.webp new file mode 100644 index 0000000000000000000000000000000000000000..aee1fba41392b4e9b333ad8ee224696348de23aa GIT binary patch literal 6186 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 IFPH!T0A<1CC;$Ke literal 0 HcmV?d00001 diff --git a/public/paypal.webp b/public/paypal.webp new file mode 100644 index 0000000000000000000000000000000000000000..d391eda24401fa547e2781f2eb0c5e427b9eaa14 GIT binary patch literal 1948 zcmV;N2V?kBNk&GL2LJ$9MM6+kP&il$0000G0001Q001EX06|PpNMHm200E#yZQC)o zdj2>wWs=&qZQHhO+qP}nw%e)Ar21=P(m37o9K7FrlVMd}!9Mv_ysa)oM;{8c3u1$@#V8B#l@ z=D1dsE`CoJdkRG8F{Ee=9T2Ux+PlKlv8NtgJsLx|L;qP27rJGFM^}OKdnhw<)Y1IYm3SkY&P3eC;5-tY_=`FDZh$YBK|Y+ z(gGwH_{1I+@pVU*au;kiTYq4b7^_u`vaV=Exdv$ZhUyQyNpe0KlVn6bh4l(a+AKw* zMO#t*w>S=?>MN?>945)_z!uS3tF4GG1|Z=BMLkrBrgMiFj3joF9Hr>u5S3AnvN!<0 z^OXHjDvZt@ViItZq@%LdbP=&|;>;Z(ls_%$zSGr4FwTh+BH%koZUFiS$Bc>7xBsC0 zoih0ky4n=JUr2^z)7m7oM;M1G14p%;^Inx5XeZt!1hAVVeg~ zPm*^Go%lg6TuzTwBzfBcIaa&|fXgH~8F8PG$^=&03DQ4E?X zDg9M5#BkveNRmM$`8t89N?8NC5u%7{oR|ij5@oc8D-!FZP*heV`6B8ItP;xlko`qi zZmmesL18sTcUT{Zj9MFn-O%y+El#-s2slPrSW(H@K5xZB@xfo~qqye_Al`1u%19AR z@-H8)%i=P$?+RWV+HqPSWoT-JC0J$80OekB*U;K8cxz;b2{))rhLR-t4BD^78ohxE z-DhzX*rw_MrRP;~1llbWx*-NSiGx}m(PE-r>USE^;;g%>x8k9<%J02cr04fYBU%Ik z)5R36T$JtgQ0TMDad3fH3Ux0LogeBf4$(~yx!*gvGE*D}wu+`&>4HqtypzlYtE14Dy3D*;5 z&Y8j+fMFO0lpb42(oF3PhJ@?!GUQC@3BWK60{{%eF!1**?+`ukuNm(dNuI^O=WLRU zD){e_J3`Dd{ykEz*|cfXlmz&j16EKtAPxfn01y!XodGIv03rZBkvy14rK2jLEwh}d zP!b7j0K+kO*}hEvN6p^B{iE>D7s*QN%NE|CKPCGBbB*%>`se!%?$!P?^;g_Spa=I4 z>b|m{>^+5j06%7ZH$9ENx_`~|s!sxd4{^2RLa4iXsQid|@;IE*y#7;1C@-j5(1+C7?$3Ou7_<*ls6TuP|ts++DlE~{YduVjV$zxZcR~^x9c7YDW zFm%uw(w&yD!3VXaQKvw8INc(4{(d2YoJ2Acp^^Sb48k3O^h|Bh)=#LVk9DT8C9+qn z9p|g3Wvp9MJpy&HRkiI~O?y@c7Dk_)2XpBgyKT{ZLx98|8)KRr-sdYMORvmf zmxTI*UgTN@U;g&8M^%*slmOcpfBkMu*t_WCO+R&nuVA>`=cBv$ZvwK{%Wr#;KkGaN ilmJHxl2EmX1FTQhm&B2m>r3KD+-ya`IE)iu00009j;YlE literal 0 HcmV?d00001 diff --git a/public/present.png b/public/present.png new file mode 100644 index 0000000000000000000000000000000000000000..73bc6e5d721b60fc9762a2bf57ba76a738e981c9 GIT binary patch literal 18734 zcmV({K+?a7P)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00007 zbV*G`2j&9^7BUeLwyQw^000JJOGiWi{{a60|De66lK=onGD$>1RCwC$y=RzZS9K=* zuDwsVsd86!Rd=hq6&b|Abd#e#3gkiq(JXh6Ks=D`_z4l7) zde_>7C-_=>$2$;Sf4!G{{Nuaj(PL*)rLtW$nl&ScIV?4jjrs{*m|vuG&R!aO_A}Sk zKKV&~#_N9Jar=B`1^~M2Uq3oof8<}zRNduEjKMqwV-HlfU2(_G>)){0Idw#zciyhk zzK>!C0o1FIOE2s0#rO7N`(+C~ksq2cLjcr#)0=LP2M@g2 zGdP!(o=|b(X=9*)STQwB;Gfc-x1T2`zDnnu^%Xk%tb2KVy;ptX8!=7a-YtLWJ->>n zU00XuM{hs3xAMRhy1jTU#~YV%(%NOTt{4%nAe_EkRCj))y5nh|z4m1{9(x49xrNVb zz5SiQS-VSk=;6Km=z%Bejg3o^MstTs;viv;65l(lrY7!`S6y{$_2n-=6n*JSy!)ju z{ay(ePpE*K?|m--oMry*@BTK_`dSf`p_M|S4V2Q*TEiH&Uzwqay2kg8@a)Vd>B0-% zjms{+4^gDfeC1C*W=*~S)=vcWgJ0aHT8mFp^#j-G&f?Xi+GhwPV}h6j5deZV21)}8 z;fC>N?v#_~{*yPp>l3?Q^|n(-|M9oYGhh3M=zZblGTna1<-C9YOL=MOIl9x`p_8PR zB#Km`kr<6cC8np7jZWy_i@kgQZ0C(PevIQNn!o&t?};WqVFGU7d^0N7T_@_VeeLUm zV@LlWsMM-P8N@0J_Z0mAd(P5NN#s8)DJ!SwpN_%oO{C?{^!0I|JYjg{$Ky>KcKSpLaHD9 zw}sxRyDvWS@z-8sdTUQd(zsCQ_OvIMgy%t!4Q-DFyjjSCiz(_3`?t zE4Lr--08110afXTPN9gY9=pPnY;_;OjPLMsE!W*}s?Ys{$!VE_zD{{@I301`E; zA35;G&PQ+J+E2auzd!Vm54D#5>GvmkOLsr1@z4jajn^N!o|491;%KW6N=czj2Nid`pE>Ze|eIffwAQJGThrsjEZne%o z@{j+x(A~d(dG*8z%xv5CgD2qR6aRvZlP7DkQGdQC#5jZm8wEB7YWdNmt%Cl~^FE3P zA}0f(3G=76W9Ra2cr3x5N$-gPk^U_MBm_t>gk%T?h!}zdq>%7@AKd9|PfjhJ^(!C# z@Vy^>{p)`)HJCsAVaS=8v&HJ#(?z)gW&>u1c7!`H(E~wnFYQ_LZHGUG9Vw{y-t%<}%f_zV35CtzkqLWJJR@>8VN3*`GSy{K=R4I8VqndWl# zcX?yVNaex=;YpN}Ak_C((d!t{nHn_CAO>I<>MjV9V8MNB&O{)S-W!qqo(Mu9FI>QCvGf3y2x2~Cd2!J*-R=`EVDVj#o^QJi(e1aF z`RLK7dZn;Lsn88u;aVYQ{&!%e;RqImXqf;=-$O->p>$Ub;pYR$`#qQrfu(&ab}ax? z|26RQl>2JPfD~c-u^rg9;s_TQNG&t@sWgcQBq$B8$iB#aZv`(QNg!!9w;uk`hrIg! z{Xcjn+C2Iwn0dQww=WdsGO;O|WFY&QD-S> z>`^c~HBiZZx8Q3Tfa&MSKx&p{EV2E>4s2UL!wQ$oAPxgLK7#rVm?$;V0;!;>f2ROJ z2mv7^dh2Ujx(_}OwjOw({R5}LynjD%;J_|HG!M@MV8B`Q%r@sW*}CVn*jd-ihh~1x z5t{G&@TwJP;lV#CQNAUBe{%rwkO%LO2mcWt-Xk8w0SP)GfDHnxfu6Lbn$qP85_jskwIJ+z~-m>X@^1$~aa)u|cF_ z=%|AUAyD#5pg>x|Xh3TXWfT&j5c>*AMIo9?z`G5`;~KUtZo_0W4R|&p&NkCvCsDx) zFj7PY(8j)hqLIW2qF5tV3b8Wisu-)?ChENo<`_w7a_SNPoU=a^oO|y3=il_p53|y} z@$K8cKTY)NJN^pMp+(QU=I5TQ@A%3KL>N>vn`xM>?@jYxlM2ohQPfN$*j;GuYq0>O ze?x!}m>L_$+{8Gf^k6hY8)FeNt)WsuBuXJ(NkAt%XdMEkx151-e+=xI6a=AxJLsz- z<--8ZjGx_YWUT*neRa-vlYkWD^V;eKy*t*LL3qN$|)1SUSx$7>y z=huJz`$ND-62^oF+V}Z)ho|20=9i7#e)q3EDJh>lan5;!ex7UkCW(-iqza%0SGDJc@mYF(5ZK^5hj@4V&H*5 z7UjqVQpij*Q6J8v{;o41w&ocG8(-X&&3Bi8P1-$RhTLBWKV;}s_+a6th;4%|!JX3i4UzD8R+-{Da z1tEMeD@Be5;}VH-50$0$3F<*I5R(PLjH~fy#E{bfn*>z4u5%J`RjjisjBb4266pr z#OqHIraLt`MhHcKa5)5|1XR)ogZh|r=2!dL&3%o_1THv@QiV-Fzzks+S{o>3;Cm8d z6JsD>01y@*W&=aTb*9{Z1L>oiC?r~=BxOG_%ko5Cr|wGEC1$Qw|?z2U*^ky_!&>EfSDOzyX7k= zSI6Y~1MeR{e%qg4qFcwVRgHt!A>P<4Iocvgc~S}p&jSmN9VgLRXjRgPgMdR0?delc~U z(m(&n>waVJ^MB+1cmMA5)N6kGQ{R07$F@)}?)u!{Pc`>{_MB$CdX4F={E%rcUMWpy zIuPtjDItUa!FO^-Kx%@3H%Kp>2$dY1xFF%a_K~svRx6vj2oT^gq!17uL1+UR1`wVE zjLCx#S^6T-H`O_9CIJnGPSG^?rZ}?}sat?CAY;IqLB;^efU}=vL89S)nm33q40;5t z2^$bbTB9Dv*f2dLl^E6S8k5@yW0Mk6deHjVbJh=jdf{vDedv9Y3m4sV-yOg6(8kjK z4N?JD25qsw8MsBKp-|y#sVWUMVYL23;T|lf9xY9G=vim5^O7o z9s`NiND>7h1q5X3QMEP2S>UAEepdDwX_~~w!00Tl7~tt9)m~=Csy4X4+d{L~L(CDp zT7rq~2IE@=L6X>2+53b~ldxSbcHdN9Olj_p$r#_{xKK-|~gz z(yO2O?F%?#LEQGqzYF#1zBAOu{a3dSJaRpvm8VG4-Qgjrd4eGV38J7cSVji-a_V2$ ziIuGHbWr%dsd6J@TV~6mYbTJ&1Z70}c?_e{l!geb2Y*AUaQi0H#m1b?&%|IR#LS2c z!^9drjv)lW^CYBH5MFAL1m!kv--Yx)E3JqbLSP8+Bz)gP$>Wl*S1yZA-g4o30V8(zr188^vX}ZqM27Z7i2%aPal2Gy`O1?lK$tPaAh*#Io@3jxU`1tC+ zkIm0t`O&Z6eAB)=Klj1-if8}$XaP^3c@0*+{`1e9KJ(tU%H7Q!`nW**kcZ|Hq1$CB z%`i;X<)i{&Vhaj@a|n}-jCimK2++c&6$34{+AA|HPOC2hY0XstYy=nvnW_pm7;{_! z0oG$`0~7c_H3Z@W9K}E{0=fc%2$BH724o1V?QKSEFkKF@ty+aF;M>_}Wd4?f>Bj_p ztUUd<(FR7Fl$&MEvw_`AiB@R)3azqs2m(%;?|i0!4cs%~`ch;*8KEQuzJM=65ZKpd z4y`2jF1 zP};?)?l)*20Gg|WUSvakn9TfAq2vZ|T3h8*vUU*+v}72VzHlKe<3dUSk#d-r2T`qo z0R%uStSRhgE(A$7n2MNDR|!tW5e{|Qs5K>S*f|R?m>v90s`ds9#SF!OVnAnK1u(yT zD<(^2aJz@N7b8hjzB-uMEMowtr2O3Mm0es%>cpV!fxWh z^LZjK*uWdS{8aFQ2tErK!=WV(Pak9eTgfVt5LCz4$V-mrFLHQ9z;|S1dGUC{pI^+16+!H(btlh~3Ef|JW zA1@{$KvLQyUuqa*(vl%TkmG&H`Y4$OWnE)%Jc@9z+rdMf77q71I3D$|k|^vAd|ZBT z24&v^d=G*d6er*~K^!IMbYnz`LXv1i+7>5{G}m#{gDcoGy9F0dP2sGuGHS6#62(wj z511io1ZhkPzN7>_!ztFS4U{ofzyNfLk1M0l;{3f9TUG8ZHTA&>&0ClE?P z=m`Yr%aa10wC1~Cij;tq9s&=(Qfn8)jrBdMbM!?^^@l#RYv&KWZv%Mn;X1=B0ZGEN zjwX_&uX4~mWia_9LTO$=?hvR=Gr|Q%a-X480HCCSqS*cq zDZrkzv>Q)AdIDMtNH*!>Nl@xj6*F2o!KpaH{%#u&wwu`B>0mjD5vTdCNzHd%X*_!P zC?=&qX=WNC@F5IC#R+;*f?lE##Ttpyh*g9*>f+e?8a}JRj4H|KR;AK!}ta2Lwc5 zDRFdwwQ+V3^0xqAK!H!ZHlO!N@8Y#QlnhIALYY|k&QXzse8c`BsHH^^k6S&kbqP&$B z1o#3d`2v+dpzKSO1A%g2wb_>fo=6KBE+t3+o&W-mPzr!@RiHdBAS)(ZUpe;lBP+N6 z%KneN{Xy@GZ@Un}tJMLS2%#Z;z;qewcLSZ{1``($s=Eee+uuqkKWfl8%xKktC;^lP zv>A9ULkvv}(*@{+hh$ZOCmB>O!3dzM6xOxEdb@?idJ~5mO&n>qaJxQ`jlP7iyN1Pcv;uuLG&YMV0JMzqSH)nTl4fn@;Y zFhrmfNGlKkqck2}T*8A(EBH{gf-@&4@ucZ#oI5d&-Q^1ALmxqA8B)KiGbGExI$!{a zQRwOfOMZ+VaRF=?e6gvV)!eEtQ41t$VJ2v+!Je?%n`W7ilHgk%L8U|}j|1gNKvWE( z*kE?fv z4aE%2+-E|BB!(zAnMn(TM9K3&Qd*N}44NR^55~b*VKyO5w;5&Mz?b%^lp%CtU@az` z(gw@MpbKC;46sX-Fl`iKrO;tuLu;(H+PI_D##c`+q7?YpTCL#xsYzTsIfaX=HEatc zf+X#P$f{uk7#KuJgoaV*Zd6b;fST-pLuKO%hhiqkYG5sp7z-uFLJ!q21v)%ywxzKC z))zn^8Ksa=t^(yrLO8)-&!E#bSU;?>vC3$*47xo7C5(_dl3qAI3xK<(PDvj@Pk{mv z0<0OW{Xln-F}{Z}e!hS=P6*DGnA~El**?VBIK|-O0%Csv???dI@WDnv3xl<84>y1D z%eZ5C85>cIh#AVdaTqXsB6tKC*Tm=IH|}e4==tzG+4uUk{FidH(#^1F436{|h*DAj zGh(CBGmHp!BMBn-M2L;Sdf;Q%*ci4mqtoqSEm2rg3ai>+-5Av41c#$K_OI3PS??IO zR?4_+Y7$SKn8Kd0g0c|SY^UbAlSFm{X%DrP32>`|c%qGXq5~6ZTOlJL@C3#}iOI5u z$%=>ZvP3nMDEn#TED3=G!T>0jf$|ukGzrKmpp3@an#TIFL36{P8!-}X17K|!Z$}0^O4aWZw?{v_Q4Hlqe%}? zV6YL0wT5oDQ8ETqPr_$L%!BkrXHho|m>pS^EqrNvx;c4 zjbx$&udGlD1*XeBrmH@t${uQEfwE61`vPSjD1|_&3REV6U>tzY=yWt1M-A$$2Cb$+ ztN@j=hx+iajm4DGNW4~k0|0jzNDAnG-y{+UGO$U1ybP?Yr)K*?f?tyGzj`jFk58iP zt)g9NBT@<27zksai6NxGo{0$rzJ~*y4p!m>ZLJYA`p}~lYFb!N!4D}VJSpKx0hvaP z9ta@;WdKzW#)(m5pa!4}LI9u##v!G!5%;jDG$1@oNe`vemrFtLDE&9t`j^xOYwHa> zSS{o1nF;Ken!rMmpwWx47R6ZUMp%hrtS1_6V-N#S+Ti|;I*xQYxM*ww&zP7-Sw~n_ z8fz(*&O~5gO|#z%5uOSlniY6+9gLr$FgxjCy6j;h6sVO1s-Zxo3{=K}(iFj~G8AiU ztQ%}(+S+3z%I5U!;_Q@>`Uz}UM?Gac=r#cKKr(hoZ$!g*3kkpr0izk6gFttUQHDhI z4jppaOAks(eG}}1Yi_n;!z}D#r%ubABi&EIpi&5`(vC)mN-iuI=6}o0{?+2Q7 zbbDP~R0?ohYsAb5va}|n>@Wl(D5&!Hm*rejOu85Y-W)HOB^88p@h+GDWwcu@cyR68R^YTG2(fKS_0HzLT!cM z8wSgPYXBH5C8hx=V-T&?5p6Wk^(EpUfC_wQPeKzx3lGW{NQ6Md37RCZ$_knYL0U%^ zu+KJ5gasnZ3xO#qQIQ@z&x0`%?fVlfp0kFoAER-K(P}cHm^6UBV(>nrAq=y+b6^yjLl78Vw}92$gyr<@v3sY@I_e zF^=-7WwcjU&}p|2Go#FmaUxruODQtOpig+qC;=`e?2+vh=t1CNrEzc6!clY(P8d9C zn~yWMjbUcOL+A<1=4XbXG=Ou2T&fVvZa~d5dM5}t;-Rt*hzyaVe)EHk+J)WlMj7b1 zisngnzJ#ITrg0P53v0|3)R*eNBpdOpTIAA#osLZHhUO%tKsjUn%5 zOjb2?>RaKI5~;ud%Lx;l?n`p>f{dP34aU>*#pA5TN@|0W<#5?QuNe&d@bt!o(1HW1h%SpX6M2@zr<^Z;~0P=ZkMd~69qOa~>4l>=z$811Nw zwo0sdH$adOda=_PLwRf@Pseq>BaEm`5tunqT4-pgbsl5;a$TZ zkk5cOU_-)=TtKhgfr?|q%@!)NTM%s7f?#|MrHL`Ljvhs8c?EHj*u^Ju_g=p{4~ z>@a8^@=-k&B4}wCwsAw|k<8HEwhI}`K~0*#meDCC5QIQj28AWC^pLPdQ}xhJBJ`9> zq0ND{%Fi9hcVxLnI4^R_r#q=R7lM$)Npj-YN$m->4ijuwBB(Y^LMAP-<~9Ts^*4^Myv%|+B^5DKa9^^15{u4A6Imf6@k6nJ|T>7?~Y&g7#up* zpVM0)hnJO-&${h4R1_g5XHra@31J1$XSUGW(!<*E8pcnIqa0ZXR!a#jJw#;C z(Ggm`4!SBqoU%Nb2>4byMCo*W5_zV;Fzqp8p!R1KfV@&XHg}Qs0AT7FzlGULf9~s# z9KZi}POjhin{(Vge_}!k5^4TRoZ~WQ+tN~EZICs5Klwol13r_bx5iNfv$hKEMOb8o zg%_{m;-3iNM*`}oM)?4vdz8_wGm?b*5imO}h^%i{5kV36Yo-ED`==O0>b4)*reOC*Im5TbSoD>D80%(tIPG>QEOv&xh%-@;ZoJ}fmXV} z=AZ;l1|W|dACPn21rtOAR$`*Ds@m|+Yhv~+f;UBwbA;dwfp8A+Lx405B#x&VyrCd`}*h`AmP zKdR7M1XiOK>QRKARym;m+Z%&GkaG!w?a7chN3k!5!>xe>&AgKmQ+KE>&;NA47X9L% z`!z6g0=)K%pLpl1KXK&I15a;u5H8Gln4Tg?={j-bM@O3Lb#pv!gXrE;C+7^%H6qU%TPf1#Ol+Hfr(|Xf1hbi^5 zl)bFbAuL>QyqDbB6h!^}+6vkd2RZFo@;tx~fS?3~H9{}}cvXfbgKo>9enQ*QT#M037?Ckh zY>-C`zCGc(!9J0jgE&~TBmIF{W^iF&WNtBvVziUmO0B%}Yp*7XhI*6Ez8vT_>a9u` znCh58XTo54Sz-TSjky^QTV@12pZXjuB8x|7Y(q+WkeG%dEvE;`xm98aCfKT;^`pAZ zSighOIZhZmk5FD9&2f(j6f z6M}JoN(M=6u(o1QU(sl8FgjgEtg;l#S_-!F?!F_lOc(;6a&C~ab)vlX_AzLukO2nb zgwg4->hZ!urgHYX#%Hej)Gxi~iV@9c;{Z&r4f2en7?V{Gm6E_(UE$Pvf=1KB{G7zt zIHmnT0EK|dEXxvbDvmVRWG|Dk+Q`HZ(vr#85aMHu)g?ydOv3ni0^TG+Y!fI=68w2a z=^&$h+@RM6bOIPN09tZD4cUz+Js|LbunYt>AQ%H=4Pc?sZ87R6H5w}#%@!j{0IgF5 z%XvG~rw7yHJK==fdalFH<4tf&vQ6;41n6`bt&WB%?fTcT-8cNk{N-=FXZg0@O>_c$l3P2m)BRa)|Rw!xa8ab9_7b z#&z&QGJ+C8E1>fbBRZu~+e4`C7LZkf+)b!XGlDswduU*`wXvH6gKimkJ|PSNzYK)q z1b@udTa(0~xvEiLF=%WsI-QhTmg`yLe$zG$LrRa&u$oaWw-743S(j2kH)3>pj94** zU{uQlF%~{Naq;WE(LV5jEse%0Yld4bMlUU-^N?e6eGPIXK#WXRzD?<- zvCL3vO9Mk;24)z&$nvg2ADE~Sd@mjFRtk7>dKR7f&R)tQa&KZ2PSAuzSm$U;U;*K;??d6Tno>LuG*gpTS)QNg9Ehj)2@8l@X~1yR1Ouz_CHjN2%GC2GC7L z{cDWgB4PXjLO3VDq3!H5Mq%Ta#>&YwXo}%X@+oXK#t10KwUOEM9W#JkALsZAWXp$1 z%;@&4V8M(aAXLT(o|pA$QLg$lxP4)SSB?Ofv5Bpl3)rj7f~AZ}mRg1f1Z^H_;mm}? z@GArpXqdLNqm9xoKop|ke zPVP^zykro?wwo|#ZJCEa`k}}^2P$kHSv@YoCPGgE?G1+NF-QQV(3)YH_T2a3R~O*u zuP*^0LnM0uOi-?!&IeD!&`iQ8wQbFJF#L;M1C|C=f&dB_2pFuhl{5pWf!yWc&3GYT zJt%H4JAa-lcIS5r!vC?*-&8Mq}MzbtNT&JJxl$in|%4pBFDi1&{O{1XVQr z!(MEU2q;$s!oade+}7DXx5UYp`=m{)1E$yO#Q@B~M;^@K>v;_>Prx5vFy#3iW1Rd9 zoU7&xB5Z;P0hEn1_bQuaXNIQ87N_a-Ys|{#b%T6xGvt+rRYzC?plUIqbw;<#NHh;| z6#5&6Mvz5E=p-`@{|52n8%(8FsX`bZ6DXGkeZT^P>3Jf@S98*;L9}nQ((#*#u)cI+ za{*Vk?8eNV>$G1QZy1xGib@Wo>DCa)XHeB0f57Qr5<3ck**F7Cs35zvj;n3=eID5v zUJ=eM^k7G453iMtp!3pYr~Px3Y34%3b(Am)*e?%fRP-GP-3wsbM1?cZRyfmvoG}+8 zjS7(=j+4t?2&mP&HaF3|u0j%xnaInz?q zPImK9C$PyINzPY89UjtYeJWEv=;LCFLnEzdzNSK(Pwn{@g`4~M)ExD<~faBH@Onrq#-03iRj-Y5LQO*1^2`k`25^KLryR?}zFoXuP zvdE$LXpvH9c~d`^4GZm`pD;4`%U39=0POc^rn#cY2vLowYx5>*O5Y;z(MKe+aX%up zf;kh+xfo_phA(6@+#lR(WH6q1lMAR!p0&A&E=}#k{4*{wG8}6%3o{rb$$8hsd2>xQ z1|%0sii3%=nf7O=thn>6?4U6?)?vUBq|IO>8f^^<`>SYx(poeX>hV0Bchj(L=6s)% zMDw?xx$F+q>-UkeWyKWoH@EqKH#w|suusUyuojnQFG6{2VRHe)o5ZXSW7umN7K#f@ zg3T6``;4-qzFc(2V>-{h5H%=Vg1E&r`kCY|0dbUH=)L0 zu<;y?;o{m-p0!atJf!%xTr8It;zW`R;pHdHQgP&be^(UFr0F+gkV#@48!?Hip7p}b z1w65B55|aChSessNZ1S>mWyLlofal4GvGTKxktxI@WNM+vWnSyjdZk-%3RJ6J~7RPL3#(-w7kB zcT@z(jkdGThsc__E3D1o?b86@Z!o+ra}lDW5vm2-&l&KDHRMgi&HZ7Wf(%Y=7<}&Y zT^zVqq1CYeZ{UZD1%P~A=*jYLrVG0ER znXy56#5(8iEN@8q>NYDF36qsba63o;d~HDo@K@4fT5wDq`_RzTzoai zHB3;hsN%B?30R_qVD2hVSm~ha^)eHUp#!Fz{lp9k04lljo+&quu%F~`et2odO)d)o zK|rIU(OETEIvk_bl$f3OFyV5-+#o8O;Y`lMo6{#ISv1c%AcKZDPheX@jABOAVMH5@ zQ;QnQE5P~b#?0T$2qL8`@Zn0!VB>nW{tX(`oQwO@?w7%RvOE0MmTYubnNErk=8%A= zc3p{Rc^_C#bQ!_GL=$gf0aJhC0aP)Aw1-C7JuUzhgE~$~!nTG=!t{iP+3kcDD;ztj z(cJJbyC6}nQXZaja@(xDI5LVJ1w|C|=X6PpA;b!ZB1Y6@n8eOLXsj}pnzn%7S8ifK?+WgHK+&E2-X$nHzN-keESR*LAq3%Ns7`L^1)|b_AComR;LpP(PQIu z#}w$HE(kzbhck_GnnUy>XWFlQ!;lDGYHp`L-opvwjw7aL*U;wC0?}DuCh})~5>KMbw@w|%no@uMJ zaAm%#T5O#vrg{T^5<=R>@a)(evY71Dq(G%=u(7Ie>QszI-NV*x616ePC*_&XVjhtW z4G)*evVufgZS8g$Mlpl{g3t;yJ57Zc{c@ccik^#BiUy~=pioMt5xpT~O*H76vGR~x zPjXUFfM6JAeRVAv%6dl$xYO(ax)+;7w>S`kd=oTchYngSQFV4AWqnF0w9w?a`q|*p zfb2k&Oh7^o=ZKkC_8 z&7KF8s{}ttyEPovq@4?i{vnh@xJ#Y}lA|1f^FkT&5S#{CLno&)ifB9!PPlM}!S;h$ zW8(F19%KQ4PU=A1h{2}IJTT3J!7K%wIU@yB+4>Wc1yBPtiicy8+{X9Gj%HxyJQkZi zs3!>HH6P`Y!N!Kd`f`F+(_n6^hskN%h=zjsbT4DFpb6;q43gL`PY_s*m0k+&GYx3A zDM}%P4e}@lb8eE8v16(o47`A*=68wDINfm!JgQt-#1}w@c=&YFkpeDHoe6*Dillks zpW9jF=4O5_AKOj*sgjzTxR4c$a2V`{NW}0-z$XFYqB9xp?9?^_PhiVTfJ#+kby?x) zQG;f~!~DEJNnmiA2L>n5MTw=q^tueK03jLWG9d^E(xW_V=q#aePG1uucdm$oW#`e4 zVu(~!cQ;712?T@uQ5KFy)15|T1^`HSI;d^ck4b|OOhL?CtPEtUuMkcRzkf1G(N>jg z;UAInV;a6yF5Dw?HZDoPIH{t0*a!88!PqHrwX#HMyFh(RVQo1^Yrn+&gom+7LPywP zsc{T+I+g;%%0X@U5z@QzQ^gIWkRD#u3Nqt}E z9_urbm&|!ihmh3Q;5;ng&Zy!-h05)d&eMT%%oU%92!^BZwBa4(HnUK|B2@HP1T4HU z08obgb!MTMIVfse?kTf_q5|~MtYKp^IO`x^k+VXOWCxH&#gdcq@9*I-{NOg!Tn z$Z)d7J(;lO-4?B7e%~@co=ufFhnc1*j5~jVhGPW@AhhFCa;R5E5^yb&D6%eS;G;v2 zAWJ?P9z#H%mP$izGkXE1&b2qCV+e^x7kTouKpGttaUS+2SH-;iIa8WKwAXs7}*Y+>?&gYDKU3DMVXa>0LQ zha5f_Sp=tNsDBC_4Gy?)=Q5it7(w3Q^5}sG{SDJUfklkyWzRvBH2m{T*nPQ@u4Jni z#qNC`E*p+2h4x!br5}$?&8ecV8EK-o?T{!<%%e21wQE4h&~fCPw@{Qt*_3g!z6s5N z-DGK?*(H+wV*#87-y!&n+(3w3zO-BPACC6jQ@yzutZ-iBxh>$PNi^cf*aFLlij}1i zIb#MdWTir2P)y)7Y8{Xo@^w-!5Rw=SlBH;DYP%Yr+V&XIBmjPO9$>V3ETW-&mWF*Q zTr^7}N7x<3Rbn2lu7v5KLOcShBji7bh(}VDDNoMmN4~|f+en}09=}m6LkbIH<`{D> z>XCyZhud7ZP^9R;h2*|~vz5v$6cs3d#bd~FDHKyLjuSPYrhXFvH&>1UKt~#i`E(Fz zF)ql!4mAv4^+|T_R8$8Y3rX}R;?RhE*@Y1Z#eN~z&*#rRVi$i#2A+(fXE;k(*v%wT zGhM8~Q$FVhWAt!^VhuOj4IZ3YFpo%;FdT3p&nYJ&#IcGW{>DB{oIFMi4g-MEW8uX- zA{rjz6=kCh%WOLxk`cP$@TH1tM6ea5ENB=xt%mZ%cgkV0dCjJgl#4>e4Mlb$Ve|=B z=>gTF0$I1{&V)9@*m;kxs&hC5V7B)EP$=Y)|XFAPt*DWFTag%J#xxVLhzA=70Lsm`7f3)k*X(aZ<#mL!xG|9R147o6)a^xZum;(%k@XASPd^;mqe{2HY zvhzYPWT$m^d0iXc4Dl{RwE+15Rk(k0Cyy$)6yZ9ukzhb%yZsFp%%4k=Acv4%z)g|2 zCZl#oUqg6!5D^oNf_eu)hoC~9N{-f_o(Rb_yv6(2fpG4mZ-seycNE|;$BnTU)S~fS z&oOHCYu`Lg0)Y1VB3ku&R~dgvC1e<<_i2N?E{&u|qz0}P&7n2vLj@$@K|ph1plB{u z~DM~O;4e<9(rr$6hl3Ha(CdlTOGhAjveuDq$qXMAFH#q@d|T}a*w2u)_n zsu*tWa{rL+*}NIWCX<*CvKs6j?>;1$qMyZZG2Ilg5gzJK8o575wJN!@EJglg@}bCl zsQaG^CsK|;_B<}k$Z|V6X`~YIh#*yZL9!Z+C>)&zZ^F*!|4%&mhF?4s?0UhCbvpkq zPc3`vt&Id))r<6L>oI__P_p?w!iRaSK~9^e`*Qsb|Xn!IUIKb z*z@H)48eg^G?A_!O_>@2(l$~a#1m{V)V%;V%H&Nerz6-9obUamV&l;$XqASju0tH+ zQLY)A)S$V)*9L?;%46R})99LIJDbxMIFvF-w6D88b86<|Z&Sbk@VsC6V_a~?yiUZ} z?Y!d!zuYcg^?Rq*$M>(V#z-RW4+aI>39Sf+9VHLt5&1O39z$aV%v{Lxom|P~R5N%8 z@XERym<#XYV7<(|sb!|2zI`6V7sda7+K$CdoI5!IsmG4Za2ORl$^ZyN60*)q02?9+#6Mk#H4m^SvS%~pS@(}t}M6j6c zLDWg25i2iIVroBXXMW0`-21-L)LD1E__ed&v`1IN5qJh4^2Mh z#<#}P*S~40HFwL(vQ}}Tt$?|RsX9&7j%sH)DS;7?05s>|Puj%C7298m0gRjTy1BF4 z>pTRMaPElCdn|MLMXJpUIqu=iajSEw0L zoF@3}B=0OtPfY_Fa7xZ1ur?&Z8y{pUA+hN^p8De**q=+&aUUb?%lZmwws&k6*s;s zy#Hqsec!+S5vgQ`{2-;vIy_-=j(Kv@gdi*W7p&k=0VzN5d>HZKJkryr!{iwCg}j-E z0Lq*?y1cB-`N|IAke#4`hVPN2ojS!D&`KkU6cSW7OfYwwH+IekCwE-^`SP}_4y*Pl z{fz6L`fbVNaTPECT=_n~Uxxp~B zDNmUS0RajA_K@Ysvnwz>4Je;Vao@btNN<|ecHQ%Nown3XCwTMdN$ouBwh8u7PpuW= zt~Rl*G{o5X|0b(vyuUJg#lP*o_D#o6-v2Rv_H&>09m(Ty7cc4x%!G% z{BG-`A6Zx5_{QsG6witv$U8CTsM<_{d=e=(UnmJtmj4a)RS);Hj>4)@?0qdB_&5Tm z<~-FX4@hhtNe|UYX|#|PG{>sCXi8hYe$!z??z!xX9wWB47a6 z`%mw~qnBQ`Qhw2k{#$QiV!yig-d{Ew8<%)NKqN(Oab0cmm_UYxd0C@su9W9$kLn!d zO$rAiPY~l#>hEj<;j$`u^CDr9sxrcyo45zBvhWq!ap50Vc0A=vvU6a)HT81LKTd)@ zP5J)uy*4Zt*+236*I%ygyYEImbm#^tMJPOfFs3MDOb}r;)_7>=3a8Yh3im`z3$hqeaG8Bckg@ui7$E8E50l1FTUFXo+QG?$3LE|yz`x3 zE#2@Vzl0|}>AxyJIIN=x?BbiL#(G2$#@2+HPVZMol?-tjjXez#;zYr`>5%YIoUD6xGFvcXJTz+)&(o6sQ)*pWEYtDYnYd&}PJKy^Idx1zBikdm8W^e_~iNhwUq#*88m_q(W`ifxdP6+%mH;ZZO&q6((Mp? zqYw{Reivh8r=S?osO{EGNB9(03e3#@#N_^PZuS$EJ?DO4`%7MO$MKtP>g~Ad%J2Hx zOFV(s+duX(yzxg~kWBvG?>%fzE`C+FTBPdrb0|qFmX>N^%Jo`=xeI_&Nem+80){*) z&n4I+-i-z#W;DAxk4z1nZ=Ro4zuqm`D`>Y=ec z=l;XgWtYC~tXIA2y(HzMm%RGb>g*T1;0alC!uQ}}X8Z*au7BIx#^Ss0{!#tlgKreg z=B2)renK7|n%%&8KW;ERD+hH`j68rl_aK6-4^|1Vp$$%~#xU9p<;9}{piy~mJ_#qZ zI|fU)2ADWjgOoHpAk-KbtwB;mvRXY<-F@cADrcSj!7Wd_`reCP`HJr9@+!8>ZFxf1 z+WTGzIEzFNopVle{x^Q(y{F##)_vX2fBxs=u%IoKIXL>bQQ2s!7+Grz9E+5l2?O7jc%mFDL^ zS~_FL$M*j6oA$HPz3orB0N?A&`(EEbrp*sr@T84BfBV<(J^Dv~atrRi=S4?4$1WJJ zH!hv(u3a)UL4H_~kfVYt9`5ZuO`MEk7k<85YA{`=fBZn2UKwU2iH1ouSS4U730d#&a_``@QITK5TFU;I0}9{S>)5afvi%WHme{QY~q z#=XB-jt{fB1=%sMxTr=AOfJzd? zTJqwG-|gJxi?hw_k#J`6E9D(KKQa4^XWV+(FaF|*gP;FApY`l#|9e}`e}4jY1F`?| zqYHTUYyRKq_~^gBTsH1|{Y0>O-o%6_N};fW(nP@|6CgF9qcq4`OQ4i?Olpy|=1wac zoHLA+Igum=Q39pq^g*iay6MDH^`6Rd+1oocd*A#GH$1%WXMU>w(s~2`o-gm;>y2e5 z?z$HqSv%_||MIU6e)xCpSvv8hH}#fJ-Y`9`DwUd-(`F}<$_acVOza}hJn*+xV>Ob* zAl6<_iK$1!*$Y2Xo4xqMJpGh!yyYi;Eb4o6B78qw-xqiJWo9)0>`kDsx{cQE{%NnZ z@8_n-W^%yV_P&Wq0*ApahW}*wUodUbV7=t)gNKDu>{@lGvPJb-e za`C_Hx$gD%AH4lN>ZQN<-|+o-(f8BM{=-|Ih6{e?uSzFB{kH49#=f`IaN?TjDe}u@ z*`FopF<@e#p$(32SOF94xx)RF#$i@!ZMja7^4lg{xZ9sN>qGwJ`5&L$`-;ObI(pV~ zo`&z|i@x7(_zR!@EGB>W*|hXmzkXJ7^2@KQ=_9Y6s&=+cjQj9?f=>X)K!a_N6{Q-S z9RZ&SRw<1{ffG|&H{sT=RkmLK0jll(^u!rg9((5Ve^d{8b@BfT_s#A&LzYjjd>Z_DiyM)^McnLyZ826_|m659Y09d_w^RN>-O6*e$JKh(1+e~aeVUDU#i6WU%quh*TzB* zO$LtF6C{a((Kd&TVm2{XS43^s7i4Ys2g3QMer4y6zIjRaV6NOw_(8mWAfD=z|KpV~ zW9N>GjeA}lHuwF<8L@T&0>~4s1c^!zE2(0x9jD69&-*i%{IfrG?rj&n`pxx~Z>;iF z7j3~0@EZy<{n9hkC>Tz7-p!SFo(>IUpc-l=nE`Rx* zOZWYKYx~vz85i!|f&UvWVqgn^l^+xX_5Qa!3DZ~q%53}K=YI%fwusvH+r4eieW-EQ zAGTliU%r6<+phl)@+H7wiuD@=0000bbVXQnWMOn=I%9HWVRU5xGB7eSEif}JFgR2) zGCDOdIxsgYFfckWFvm+(z5oCKC3HntbYx+4WjbwdWNBu305UK#GA%GOEigD#GBP?f dFgh?dD=;uRFfg_j?E(M*002ovPDHLkV1iU + + \ No newline at end of file diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 7c6d7c9..331b8dc 100755 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -91,8 +91,14 @@ import OnboardingPage from "../pages/Onboarding"; import TrialChoicePage from "../pages/TrialChoice"; import TrialPaymentPage from "../pages/TrialPayment"; import ReactGA from "react-ga4"; +import AdditionalDiscount from "../pages/AdditionalDiscount"; +import TrialPaymentWithDiscount from "../pages/TrialPaymentWithDiscount"; -ReactGA.initialize("G-00S3ECJGSJ"); +const isProduction = import.meta.env.MODE === "production"; + +if (isProduction) { + ReactGA.initialize("G-00S3ECJGSJ"); +} function App(): JSX.Element { const [isSpecialOfferOpen, setIsSpecialOfferOpen] = useState(false); @@ -104,6 +110,7 @@ function App(): JSX.Element { const { token, user } = useAuth(); useEffect(() => { + if (!isProduction) return; ReactGA.send({ hitType: "pageview", page: document.location.pathname + document.location.search, @@ -252,6 +259,14 @@ function App(): JSX.Element { element={} /> } /> + } + /> + } + /> } diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index ded1489..1eff20c 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from "react"; +import { ReactNode, useEffect } from "react"; import styles from "./styles.module.css"; interface ModalProps { @@ -16,8 +16,20 @@ function Modal({ }: ModalProps): JSX.Element { const handleClose = (event: React.MouseEvent) => { if (event.target !== event.currentTarget) return; + document.body.classList.remove("no-scroll"); onClose?.(); }; + + useEffect(() => { + if (open) { + document.body.classList.add("no-scroll"); + } + + return () => { + document.body.classList.remove("no-scroll"); + }; + }, [open]); + if (!open) return <>; return (
diff --git a/src/components/Modal/styles.module.css b/src/components/Modal/styles.module.css index ffdbec5..245e4c5 100644 --- a/src/components/Modal/styles.module.css +++ b/src/components/Modal/styles.module.css @@ -1,6 +1,6 @@ .modal { background: rgba(85,84,85,.8); - height: 100vh; + height: 100dvh; position: fixed; left: 0; top: 0; diff --git a/src/components/PaymentPage/methods/Stripe/Modal.tsx b/src/components/PaymentPage/methods/Stripe/Modal.tsx index f38259e..b37d61c 100644 --- a/src/components/PaymentPage/methods/Stripe/Modal.tsx +++ b/src/components/PaymentPage/methods/Stripe/Modal.tsx @@ -1,4 +1,5 @@ -import { SubscriptionReceipts, useApi } from "@/api"; +import styles from "./styles.module.css"; +import { useApi } from "@/api"; import Modal from "@/components/Modal"; import Loader from "@/components/Loader"; import { useEffect, useState } from "react"; @@ -8,13 +9,19 @@ import CheckoutForm from "./CheckoutForm"; import { useAuth } from "@/auth"; import { useSelector } from "react-redux"; import { selectors } from "@/store"; -import { PayPalReceiptPayload } from "@/api/resources/UserSubscriptionReceipts"; +import Title from "@/components/Title"; +import ApplePayButton from "@/components/StripePage/ApplePayButton"; +import SubPlanInformation from "@/components/SubPlanInformation"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans"; +import routes from "@/routes"; interface StripeModalProps { open: boolean; onClose: () => void; - onSuccess: (receipt: SubscriptionReceipts.SubscriptionReceipt) => void; - onError: (error: Error) => void; + // onSuccess: (receipt: SubscriptionReceipts.SubscriptionReceipt) => void; + // onError: (error: Error) => void; } export function StripeModal({ @@ -23,37 +30,55 @@ export function StripeModal({ }: // onSuccess, // onError, StripeModalProps): JSX.Element { + const { i18n } = useTranslation(); const api = useApi(); const { token } = useAuth(); + const locale = i18n.language; + const navigate = useNavigate(); const activeSubPlan = useSelector(selectors.selectActiveSubPlan); + const email = useSelector(selectors.selectUser).email; const [stripePromise, setStripePromise] = useState | null>(null); + const [subPlans, setSubPlans] = useState(null); const [clientSecret, setClientSecret] = useState(""); + const [subscriptionReceiptId, setSubscriptionReceiptId] = + useState(""); const [isLoading, setIsLoading] = useState(true); + if (!activeSubPlan) { + navigate(routes.client.trialChoice()); + } useEffect(() => { (async () => { const siteConfig = await api.getAppConfig({ bundleId: "auraweb" }); setStripePromise(loadStripe(siteConfig.data.stripe_public_key)); + const { sub_plans } = await api.getSubscriptionPlans({ locale }); + setSubPlans(sub_plans); + const isActiveSubPlan = sub_plans.find( + (subPlan) => subPlan.id === activeSubPlan?.id + ); + if (!activeSubPlan || !isActiveSubPlan) { + navigate(routes.client.priceList()); + } })(); - }, [api]); + }, [activeSubPlan, api, locale, navigate]); useEffect(() => { - if (!open) return; (async () => { const { subscription_receipt } = await api.createSubscriptionReceipt({ token, - way: "paypal", - itemInterval: "year", + way: "stripe", subscription_receipt: { - sub_plan_id: activeSubPlan?.id || "", + sub_plan_id: activeSubPlan?.id || "stripe.7", }, - } as PayPalReceiptPayload); + }); + const { id } = subscription_receipt; const { client_secret } = subscription_receipt.data; + setSubscriptionReceiptId(id); setClientSecret(client_secret); setIsLoading(false); })(); - }, [api, token, open]); + }, [api, token]); const handleClose = () => { onClose(); @@ -62,13 +87,29 @@ StripeModalProps): JSX.Element { return ( {isLoading ? ( -
+
) : null} - {stripePromise && clientSecret && ( + {!isLoading && ( + <> + + Choose payment method + +

{email}

+ + )} + {stripePromise && clientSecret && subscriptionReceiptId && ( - + + {activeSubPlan && ( + + )} + )} diff --git a/src/components/PaymentPage/methods/Stripe/styles.module.css b/src/components/PaymentPage/methods/Stripe/styles.module.css new file mode 100644 index 0000000..1a43dab --- /dev/null +++ b/src/components/PaymentPage/methods/Stripe/styles.module.css @@ -0,0 +1,38 @@ +.page { + /* position: relative; */ + position: static; + /* height: calc(100vh - 50px); + max-height: -webkit-fill-available; */ + display: flex; + justify-items: center; + justify-content: center; + gap: 16px; +} + +.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; +} diff --git a/src/components/StripePage/ApplePayButton/index.tsx b/src/components/StripePage/ApplePayButton/index.tsx index 0039355..ba3e47d 100644 --- a/src/components/StripePage/ApplePayButton/index.tsx +++ b/src/components/StripePage/ApplePayButton/index.tsx @@ -70,10 +70,10 @@ function ApplePayButton({ if (stripeError) { // Show error to your customer (e.g., insufficient funds) - return; + return e.complete("fail"); } - - navigate(`${routes.client.paymentResult()}/${subscriptionReceiptId}`); + navigate(`${routes.client.paymentResult()}/${subscriptionReceiptId}/`); + e.complete("success"); // Show a success message to your customer // There's a risk of the customer closing the window before callback // execution. Set up a webhook or plugin to listen for the diff --git a/src/components/pages/AdditionalDiscount/index.tsx b/src/components/pages/AdditionalDiscount/index.tsx new file mode 100644 index 0000000..e3b5c3b --- /dev/null +++ b/src/components/pages/AdditionalDiscount/index.tsx @@ -0,0 +1,40 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import MainButton from "@/components/MainButton"; +import { useNavigate } from "react-router-dom"; +import routes from "@/routes"; + +function AdditionalDiscount() { + const navigate = useNavigate(); + + const handleNext = () => { + navigate(routes.client.trialPaymentWithDiscount()); + }; + + return ( +
+ + Save 65% off! + + Friends +
+ Fire +

+ 65% off on your personalized plan +

+
+
+ Present +

7-day trial

+
+

+ $9 instead of $19 +

+ + Get secret discount! + +
+ ); +} + +export default AdditionalDiscount; diff --git a/src/components/pages/AdditionalDiscount/styles.module.css b/src/components/pages/AdditionalDiscount/styles.module.css new file mode 100644 index 0000000..3d8e28a --- /dev/null +++ b/src/components/pages/AdditionalDiscount/styles.module.css @@ -0,0 +1,69 @@ +.page { + position: relative; + height: fit-content; + min-height: 100dvh; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + background-position-y: top; + background-position-x: center; + background-size: contain; + background-repeat: no-repeat; + color: #fff; + padding-bottom: 64px; + background-image: url("/ellipse.webp"); + background-color: #fff0f0; + color: #333333; +} + +.title { + font-size: 32px; + line-height: 44px; + margin-bottom: 20px; +} + +.discount-point { + display: flex; + flex-direction: row; + align-items: center; + justify-content: start; + gap: 20px; + width: 100%; + max-width: 262px; + margin-top: 20px; +} + +.discount-point > img { + width: 48px; +} + +.discount-point-description { + font-weight: 700; + font-size: 18px; + line-height: 140%; + color: #000; +} + +.discount-description { + font-size: 16px; + margin-top: 20px; +} + +.discount-description > span { + color: #8e8cf0; + font-weight: 700; +} + +.button { + background: rgb(187, 107, 217); + height: 50px; + min-height: 0; + min-width: 0; + max-width: 300px; + box-shadow: rgba(0, 0, 0, 0.25) 0px 4px 4px 0px; + border-radius: 16px; + margin-top: 16px; + font-size: 16px; + font-weight: normal; +} diff --git a/src/components/pages/TrialPayment/components/PaymentMethodsChoice/index.tsx b/src/components/pages/TrialPayment/components/PaymentMethodsChoice/index.tsx new file mode 100644 index 0000000..950eea6 --- /dev/null +++ b/src/components/pages/TrialPayment/components/PaymentMethodsChoice/index.tsx @@ -0,0 +1,30 @@ +import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods"; +import styles from "./styles.module.css"; + +interface IPaymentMethodsChoiceProps { + selectedPaymentMethod: EPaymentMethod; + onSelectPaymentMethod: (method: EPaymentMethod) => void; +} + +function PaymentMethodsChoice({ + selectedPaymentMethod, + onSelectPaymentMethod, +}: IPaymentMethodsChoiceProps) { + return ( +
+ {paymentMethods.map((method, index) => ( +
onSelectPaymentMethod(method.id)} + key={index} + > + {method.component} +
+ ))} +
+ ); +} + +export default PaymentMethodsChoice; diff --git a/src/components/pages/TrialPayment/components/PaymentMethodsChoice/styles.module.css b/src/components/pages/TrialPayment/components/PaymentMethodsChoice/styles.module.css new file mode 100644 index 0000000..fb9e06c --- /dev/null +++ b/src/components/pages/TrialPayment/components/PaymentMethodsChoice/styles.module.css @@ -0,0 +1,23 @@ +.payment-methods { + width: 100%; + display: flex; + justify-content: space-between; +} + +.payment-method { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + border: 2px solid rgb(232, 232, 252); + border-radius: 12px; + max-width: 160px; + height: 68px; + padding-top: 8px; + padding-bottom: 8px; +} + +.payment-method.active { + border-radius: 12px; + border: 2px solid rgb(114, 112, 192); +} diff --git a/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/index.tsx b/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/index.tsx new file mode 100644 index 0000000..5fb253f --- /dev/null +++ b/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/index.tsx @@ -0,0 +1,23 @@ +import MainButton from "@/components/MainButton"; +import styles from "./styles.module.css"; +import Loader from "@/components/Loader"; + +interface IPayPalButton { + isLoading: boolean; + handlePayPalButton: () => void; +} + +function PayPalButton({ isLoading, handlePayPalButton }: IPayPalButton) { + return ( + + {!isLoading && PayPal Button} + {isLoading && } + + ); +} + +export default PayPalButton; diff --git a/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/styles.module.css b/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/styles.module.css new file mode 100644 index 0000000..b75606d --- /dev/null +++ b/src/components/pages/TrialPayment/components/PaymentModal/components/PayPalButton/styles.module.css @@ -0,0 +1,9 @@ +.pay-pal-button { + width: 100%; + height: 60px; + display: flex; + align-items: center; + justify-content: center; + background-color: #ffc43a; + border-radius: 7px; +} diff --git a/src/components/pages/TrialPayment/components/PaymentModal/index.tsx b/src/components/pages/TrialPayment/components/PaymentModal/index.tsx new file mode 100644 index 0000000..472a63b --- /dev/null +++ b/src/components/pages/TrialPayment/components/PaymentModal/index.tsx @@ -0,0 +1,232 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import PaymentMethodsChoice from "../PaymentMethodsChoice"; +import { useEffect, useState } from "react"; +import { EPaymentMethod } from "@/data/paymentMethods"; +import { Elements } from "@stripe/react-stripe-js"; +import ApplePayButton from "@/components/StripePage/ApplePayButton"; +import CheckoutForm from "@/components/PaymentPage/methods/Stripe/CheckoutForm"; +import { Stripe, loadStripe } from "@stripe/stripe-js"; +import { useDispatch, useSelector } from "react-redux"; +import { actions, selectors } from "@/store"; +import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans"; +import { useApi } from "@/api"; +import { useNavigate } from "react-router-dom"; +import routes from "@/routes"; +import { useTranslation } from "react-i18next"; +import { useAuth } from "@/auth"; +import Loader from "@/components/Loader"; +import { getPriceFromTrial } from "@/services/price"; +import SecurityPayments from "../SecurityPayments"; +import PayPalButton from "./components/PayPalButton"; + +function PaymentModal() { + const { i18n } = useTranslation(); + const locale = i18n.language; + const api = useApi(); + const { token } = useAuth(); + const navigate = useNavigate(); + const dispatch = useDispatch(); + const activeSubPlan = useSelector(selectors.selectActiveSubPlan); + const [payPalSubPlan, setPayPalSubPlan] = useState(); + const subscriptionReceiptFromStore = useSelector( + selectors.selectSubscriptionReceipt + ); + const [stripePromise, setStripePromise] = + useState | null>(null); + const [clientSecret, setClientSecret] = useState(""); + const [subscriptionReceiptId, setSubscriptionReceiptId] = + useState(""); + const [subPlans, setSubPlans] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [isLoadingPayPal, setIsLoadingPayPal] = useState(false); + const [isError, setIsError] = useState(false); + const [errors, setErrors] = useState(""); + + const [selectedPaymentMethod, setSelectedPaymentMethod] = useState( + EPaymentMethod.CREDIT_CARD + ); + + const onSelectPaymentMethod = (method: EPaymentMethod) => { + setSelectedPaymentMethod(method); + }; + + useEffect(() => { + (async () => { + const siteConfig = await api.getAppConfig({ bundleId: "auraweb" }); + setStripePromise(loadStripe(siteConfig.data.stripe_public_key)); + const { sub_plans } = await api.getSubscriptionPlans({ locale }); + setSubPlans(sub_plans); + const isActiveSubPlan = sub_plans.find( + (subPlan) => subPlan.id === activeSubPlan?.id + ); + if (!activeSubPlan || !isActiveSubPlan) { + navigate(routes.client.priceList()); + } + })(); + }, [activeSubPlan, api, locale, navigate]); + + useEffect(() => { + (async () => { + try { + const { subscription_receipt } = await api.createSubscriptionReceipt({ + token, + way: "stripe", + subscription_receipt: { + sub_plan_id: activeSubPlan?.id || "stripe.7", + }, + }); + + if (!subscription_receipt && !subscriptionReceiptFromStore) { + setIsError(true); + setIsLoading(false); + } else if (!subscription_receipt && subscriptionReceiptFromStore) { + const { id } = subscriptionReceiptFromStore; + const { client_secret } = subscriptionReceiptFromStore.data; + setSubscriptionReceiptId(id); + setClientSecret(client_secret); + setIsLoading(false); + } else if (subscription_receipt) { + dispatch( + actions.payment.update({ + subscriptionReceipt: subscription_receipt, + }) + ); + const { id } = subscription_receipt; + const { client_secret } = subscription_receipt.data; + setSubscriptionReceiptId(id); + setClientSecret(client_secret); + setIsLoading(false); + } + console.log(subscription_receipt); + } catch (error) { + if (subscriptionReceiptFromStore) { + console.log(1); + + const { id } = subscriptionReceiptFromStore; + const { client_secret } = subscriptionReceiptFromStore.data; + setSubscriptionReceiptId(id); + setClientSecret(client_secret); + setIsLoading(false); + return; + } else { + setIsError(true); + setIsLoading(false); + } + console.log(error); + } + })(); + }, [activeSubPlan?.id, api, dispatch, subscriptionReceiptFromStore, token]); + + useEffect(() => { + if (!subPlans) return; + const paypalPlan = subPlans + .filter((plan: ISubscriptionPlan) => plan.provider === "paypal") + .filter((plan: ISubscriptionPlan) => { + if (activeSubPlan?.trial && plan?.trial) return true; + if (!activeSubPlan?.trial && !plan?.trial) return true; + return false; + }) + .find((plan: ISubscriptionPlan) => { + if (activeSubPlan?.trial && plan?.trial) { + return plan?.trial?.price_cents === activeSubPlan?.trial?.price_cents; + } + if (!activeSubPlan?.trial && !plan?.trial) { + return plan?.name === activeSubPlan?.name; + } + return false; + }); + setPayPalSubPlan(paypalPlan); + }, [activeSubPlan?.name, activeSubPlan?.trial, subPlans]); + + const handlePayPalButton = async () => { + setIsLoadingPayPal(true); + const { + subscription_receipt: { data }, + } = await api.createSubscriptionReceipt({ + token, + way: "paypal", + subscription_receipt: { + sub_plan_id: payPalSubPlan?.id || "paypal.6", + }, + }); + if (!data?.links) { + return setErrors("Something went wrong. Please try again later."); + } + const link = data.links.find((link) => link.rel === "approve"); + if (!link) { + return setErrors("Something went wrong. Please try again later."); + } + setIsLoadingPayPal(false); + window.location.href = link.href; + }; + + if (isLoading) { + return ( +
+
+ +
+
+ ); + } + + if (isError) { + return ( +
+ + Something went wrong + +
+ ); + } + + return ( +
+ + Choose payment method + + + {activeSubPlan && ( +

+ You will be charged only{" "} + + ${getPriceFromTrial(activeSubPlan?.trial)} for your 3-day trial. + +

+ )} +
+ {stripePromise && clientSecret && subscriptionReceiptId && ( + + {selectedPaymentMethod === EPaymentMethod.PAYPAL_OR_APPLE_PAY && ( + <> + + {payPalSubPlan && ( + + )} + {!!errors.length &&

{errors}

} + + )} + + {selectedPaymentMethod === EPaymentMethod.CREDIT_CARD && ( + + )} +
+ )} +
+ +
+ ); +} + +export default PaymentModal; diff --git a/src/components/pages/TrialPayment/components/PaymentModal/styles.module.css b/src/components/pages/TrialPayment/components/PaymentModal/styles.module.css new file mode 100644 index 0000000..900e309 --- /dev/null +++ b/src/components/pages/TrialPayment/components/PaymentModal/styles.module.css @@ -0,0 +1,25 @@ +.payment-modal { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 250px; + gap: 25px; +} + +.title { + font-weight: 800; + font-size: 20px; + line-height: 20px; + text-align: center; + margin: 0; +} + +.sub-plan-description { + font-size: 12px; + text-align: center; +} + +.payment-method-container { + width: 100%; +} diff --git a/src/components/pages/TrialPayment/components/SecurityPayments/index.tsx b/src/components/pages/TrialPayment/components/SecurityPayments/index.tsx new file mode 100644 index 0000000..160a317 --- /dev/null +++ b/src/components/pages/TrialPayment/components/SecurityPayments/index.tsx @@ -0,0 +1,10 @@ +import styles from "./styles.module.css"; + +function SecurityPayments() { + return
+ Guaranteed security +

Guaranteed security payments

+
; +} + +export default SecurityPayments; diff --git a/src/components/pages/TrialPayment/components/SecurityPayments/styles.module.css b/src/components/pages/TrialPayment/components/SecurityPayments/styles.module.css new file mode 100644 index 0000000..77bdb21 --- /dev/null +++ b/src/components/pages/TrialPayment/components/SecurityPayments/styles.module.css @@ -0,0 +1,12 @@ +.container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 6px; +} + +.text { + font-size: 12px; + font-weight: 400; +} diff --git a/src/components/pages/TrialPayment/index.tsx b/src/components/pages/TrialPayment/index.tsx index 4ff0337..8a841bf 100755 --- a/src/components/pages/TrialPayment/index.tsx +++ b/src/components/pages/TrialPayment/index.tsx @@ -4,8 +4,8 @@ import PersonalInformation from "./components/PersonalInformation"; import styles from "./styles.module.css"; import Goal from "./components/Goal"; import PaymentTable from "./components/PaymentTable"; -import { useSelector } from "react-redux"; -import { selectors } from "@/store"; +import { useDispatch, useSelector } from "react-redux"; +import { actions, selectors } from "@/store"; import { Navigate, useNavigate, useParams } from "react-router-dom"; import routes from "@/routes"; import { getZodiacSignByDate } from "@/services/zodiac-sign"; @@ -19,10 +19,13 @@ import { useApi } from "@/api"; import { getClientLocale } from "@/locales"; import { Locale } from "@/components/PaymentTable"; import WithPartnerInformation from "./components/WithPartnerInformation"; +import Modal from "@/components/Modal"; +import PaymentModal from "./components/PaymentModal"; const locale = getClientLocale() as Locale; function TrialPaymentPage() { + const dispatch = useDispatch(); const api = useApi(); const navigate = useNavigate(); const birthdate = useSelector(selectors.selectBirthdate); @@ -42,6 +45,7 @@ function TrialPaymentPage() { const [activeSubPlan, setActiveSubPlan] = useState( activeSubPlanFromStore ); + const [isOpenPaymentModal, setIsOpenPaymentModal] = useState(false); const [marginTopTitle, setMarginTopTitle] = useState(360); const [singleOrWithPartner, setSingleOrWithPartner] = useState< "single" | "partner" @@ -81,9 +85,10 @@ function TrialPaymentPage() { ); if (targetSubPlan) { setActiveSubPlan(targetSubPlan); + dispatch(actions.payment.update({ activeSubPlan: targetSubPlan })); } } - }, [subPlan, subPlans]); + }, [dispatch, subPlan, subPlans]); useEffect(() => { if (["relationship", "married"].includes(flowChoice)) { @@ -103,13 +108,21 @@ function TrialPaymentPage() { return ; } - const handleNext = () => { - navigate(routes.client.paymentStripe()); + const handleDiscount = () => { + setIsOpenPaymentModal(false); + navigate(routes.client.additionalDiscount()); + }; + + const openStripeModal = () => { + setIsOpenPaymentModal(true); }; return (
-
+ + + +
{singleOrWithPartner === "partner" && ( - + - +
); } diff --git a/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/index.tsx b/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/index.tsx new file mode 100644 index 0000000..b62b2f7 --- /dev/null +++ b/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/index.tsx @@ -0,0 +1,43 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import { useSelector } from "react-redux"; +import { selectors } from "@/store"; +import { getPriceFromTrial } from "@/services/price"; + +function PaymentDiscountTable() { + const activeSub = useSelector(selectors.selectActiveSubPlan); + + return ( +
+ + You get a secret discount! + +

No pressure. Cancel anytime.

+
+
+ Present +

Secret discount applied!

+
+
+ -30% + -65% +
+
+
+

Your cost per 1 month after trial:

+
+ $19 + $9 +
+
+

You save $30

+
+
+

Total today:

+ {activeSub && ${getPriceFromTrial(activeSub.trial)}} +
+
+ ); +} + +export default PaymentDiscountTable; diff --git a/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/styles.module.css b/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/styles.module.css new file mode 100644 index 0000000..b41ff23 --- /dev/null +++ b/src/components/pages/TrialPaymentWithDiscount/PaymentDiscountTable/styles.module.css @@ -0,0 +1,156 @@ +.container { + padding: 16px 12px; + border-radius: 8px; + margin-top: 20px; + width: 100%; + background-color: #fbfbff; +} + +.title { + font-size: 18px; + line-height: 28px; + text-align: center; + color: #0f0f0f; +} + +.no-pressure { + font-weight: 600; + font-size: 14px; + line-height: 25px; + text-align: center; + margin-top: 4px; + color: #4f4f4f; +} + +.applied { + display: flex; + align-items: center; + justify-content: space-between; + background: linear-gradient( + 95.17deg, + rgb(207, 139, 243) -16.49%, + rgb(167, 112, 239) -15.14%, + rgb(253, 185, 155) 115.23% + ); + border-radius: 6px; + margin-top: 16px; + padding: 3px 9px; +} + +.present-image { + width: 20px; +} + +.applied .description { + font-size: 14px; + font-weight: 700; + line-height: 24px; + color: rgb(251, 251, 255); +} + +.applied > .side { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +} + +.discount { + position: relative; + font-weight: 400; + font-size: 14px; + color: rgb(51, 51, 51); + line-height: 19px; + margin-right: 10px; +} + +.discount::before { + position: absolute; + content: ""; + left: -2px; + top: 50%; + right: -2px; + border-top: 1px solid rgb(235, 87, 87); + border-right-color: rgb(235, 87, 87); + border-bottom-color: rgb(235, 87, 87); + border-left-color: rgb(235, 87, 87); + transform: rotate(8deg); +} + +.applied strong { + font-weight: 800; + font-size: 14px; + line-height: 19px; + color: rgb(15, 15, 15); +} + +.cost-container { + width: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + margin-top: 8px; +} + +.cost-container > p { + font-size: 14px; + line-height: 24px; + font-weight: 600; + color: rgb(79, 79, 79); +} + +.cost-container .discount { + color: rgb(130, 130, 130); +} + +.cost-container strong { + font-size: 14px; + font-weight: 600; + line-height: 19px; + color: rgb(51, 51, 51); +} + +.save { + font-size: 14px; + font-weight: 600; + line-height: 24px; + color: rgb(32, 31, 31); +} + +.line { + height: 1px; + background-color: rgb(153, 116, 246); + margin-top: 16px; + margin-bottom: 16px; + box-sizing: content-box; +} + +.total-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.total-container > p { + font-size: 16px; + font-weight: 600; + line-height: 24px; + color: rgb(15, 15, 15); +} + +.total-container strong { + font-size: 18px; + font-weight: 700; + line-height: 24px; + background: linear-gradient( + 165.54deg, + rgb(20, 19, 51) -33.39%, + rgb(32, 34, 97) 15.89%, + rgb(84, 60, 151) 55.84%, + rgb(105, 57, 162) 74.96% + ) + text; + -webkit-text-fill-color: transparent; +} diff --git a/src/components/pages/TrialPaymentWithDiscount/index.tsx b/src/components/pages/TrialPaymentWithDiscount/index.tsx new file mode 100644 index 0000000..972cdd9 --- /dev/null +++ b/src/components/pages/TrialPaymentWithDiscount/index.tsx @@ -0,0 +1,47 @@ +import Title from "@/components/Title"; +import styles from "./styles.module.css"; +import MainButton from "@/components/MainButton"; +import PaymentDiscountTable from "./PaymentDiscountTable"; +import Modal from "@/components/Modal"; +import PaymentModal from "../TrialPayment/components/PaymentModal"; +import { useState } from "react"; + +function TrialPaymentWithDiscount() { + const [isOpenPaymentModal, setIsOpenPaymentModal] = useState(false); + + const handleClose = () => { + setIsOpenPaymentModal(false); + }; + + return ( +
+ + + + Party popper + + You get a secret discount! + + + setIsOpenPaymentModal(true)} + > + Start your 3-day trial + +

+ By continuing you agree that if you don't cancel prior to the end of the + 3-days trial, you will automatically be charged $9 for the introductory + period of 30 days thereafter the standard rate of $9 every 30 days until + you cancel in settings. Learn more about cancellation and refund policy + in Subscription terms. +

+
+ ); +} + +export default TrialPaymentWithDiscount; diff --git a/src/components/pages/TrialPaymentWithDiscount/styles.module.css b/src/components/pages/TrialPaymentWithDiscount/styles.module.css new file mode 100644 index 0000000..4049a7a --- /dev/null +++ b/src/components/pages/TrialPaymentWithDiscount/styles.module.css @@ -0,0 +1,58 @@ +.page { + position: relative; + height: fit-content; + min-height: 100dvh; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + color: #fff; + padding-bottom: 64px; + background-color: #fff0f0; +} + +.party-popper { + width: 32px; +} + +.title { + margin-top: 11px; + font-size: 24px; + line-height: 33px; + text-align: center; + background: linear-gradient( + 165.54deg, + rgb(20, 19, 51) -33.39%, + rgb(32, 34, 97) 15.89%, + rgb(84, 60, 151) 55.84%, + rgb(105, 57, 162) 74.96% + ) + text; + -webkit-text-fill-color: transparent; + font-weight: 700; +} + +.button { + margin-top: 32px; + border-radius: 30px; + min-height: 0; + height: 50px; + font-size: 20px; + font-weight: normal; + line-height: 22px; + width: 100%; + min-width: 0; + max-width: 360px; + background: #27ae60; + color: #fbfbff; +} + +.policy { + color: rgb(51, 51, 51); + font-size: 13px; + font-weight: 400; + line-height: 20px; + margin-top: 28px; + padding-bottom: 10px; + max-width: 400px; +} diff --git a/src/components/ui/PaymentMethodsButtons/CreditCard/index.tsx b/src/components/ui/PaymentMethodsButtons/CreditCard/index.tsx new file mode 100644 index 0000000..83124c8 --- /dev/null +++ b/src/components/ui/PaymentMethodsButtons/CreditCard/index.tsx @@ -0,0 +1,12 @@ +import styles from "./styles.module.css" + +function CreditCard() { + return ( +
+ Credit card + Credit Card +
+ ) +} + +export default CreditCard \ No newline at end of file diff --git a/src/components/ui/PaymentMethodsButtons/CreditCard/styles.module.css b/src/components/ui/PaymentMethodsButtons/CreditCard/styles.module.css new file mode 100644 index 0000000..f272c57 --- /dev/null +++ b/src/components/ui/PaymentMethodsButtons/CreditCard/styles.module.css @@ -0,0 +1,14 @@ +.container { + width: 100%; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + gap: 10px; +} + +.text { + font-weight: 600; + line-height: 21px; + color: rgb(99, 99, 157); +} diff --git a/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/index.tsx b/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/index.tsx new file mode 100644 index 0000000..9b9b4b1 --- /dev/null +++ b/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/index.tsx @@ -0,0 +1,10 @@ +import styles from "./styles.module.css"; + +function PayPalOrApplePay() { + return
+ PayPal + ApplePay +
; +} + +export default PayPalOrApplePay; diff --git a/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/styles.module.css b/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/styles.module.css new file mode 100644 index 0000000..7d1360f --- /dev/null +++ b/src/components/ui/PaymentMethodsButtons/PayPayOrApplePay/styles.module.css @@ -0,0 +1,12 @@ +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + gap: 8px; +} + +.container > img { + height: 16px; +} \ No newline at end of file diff --git a/src/data/paymentMethods.tsx b/src/data/paymentMethods.tsx new file mode 100644 index 0000000..bafe93a --- /dev/null +++ b/src/data/paymentMethods.tsx @@ -0,0 +1,23 @@ +import CreditCard from "@/components/ui/PaymentMethodsButtons/CreditCard"; +import PayPalOrApplePay from "@/components/ui/PaymentMethodsButtons/PayPayOrApplePay"; + +export enum EPaymentMethod { + CREDIT_CARD = "card", + PAYPAL_OR_APPLE_PAY = "payPalOrApplePay", +} + +interface IPaymentMethod { + id: EPaymentMethod; + component: JSX.Element; +} + +export const paymentMethods: IPaymentMethod[] = [ + { + id: EPaymentMethod.PAYPAL_OR_APPLE_PAY, + component: , + }, + { + id: EPaymentMethod.CREDIT_CARD, + component: , + }, +]; diff --git a/src/index.css b/src/index.css index cec9a22..963cec1 100644 --- a/src/index.css +++ b/src/index.css @@ -22,7 +22,8 @@ h4 { font-weight: 400; } -button,h4 { +button, +h4 { font-size: 18px; } @@ -36,18 +37,109 @@ input { outline: none; } -a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video { +a, +abbr, +acronym, +address, +applet, +article, +aside, +audio, +b, +big, +blockquote, +body, +canvas, +caption, +center, +cite, +code, +dd, +del, +details, +dfn, +div, +dl, +dt, +em, +embed, +fieldset, +figcaption, +figure, +footer, +form, +h1, +h2, +h3, +h4, +h5, +h6, +header, +hgroup, +html, +i, +iframe, +img, +ins, +kbd, +label, +legend, +li, +mark, +menu, +nav, +object, +ol, +output, +p, +pre, +q, +ruby, +s, +samp, +section, +small, +span, +strike, +strong, +sub, +summary, +sup, +table, +tbody, +td, +tfoot, +th, +thead, +time, +tr, +tt, +u, +ul, +var, +video { border: 0; margin: 0; padding: 0; vertical-align: initial; } -article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section { +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { display: block; } -body,html { +body, +html { height: 100%; line-height: 1; } @@ -56,7 +148,8 @@ body,html { height: auto; } -ol,ul { +ol, +ul { list-style: none; } @@ -65,8 +158,8 @@ a { text-decoration: none; } -div[class^=divider] { - font-family: SF Pro Text Regular,sans-serif +div[class^="divider"] { + font-family: SF Pro Text Regular, sans-serif; } * { @@ -93,7 +186,12 @@ div[class^=divider] { height: 100%; } -a,button,div,input,select,textarea { +a, +button, +div, +input, +select, +textarea { -webkit-tap-highlight-color: transparent; } @@ -134,4 +232,9 @@ a,button,div,input,select,textarea { left: 0; top: 0; width: 100%; -} \ No newline at end of file +} + +.no-scroll { + position: fixed; + overflow: hidden; +} diff --git a/src/init.tsx b/src/init.tsx index ab725b2..c8573fb 100755 --- a/src/init.tsx +++ b/src/init.tsx @@ -37,13 +37,14 @@ const init = async () => { .init(options); window.Chargebee.init(config.chargebee); + const isProduction = import.meta.env.MODE === "production"; + // SCRIPTS TO HEAD const yandexMetric = () => { const script = document.createElement("script"); script.setAttribute("src", "/metrics/yandex.js"); document.head.appendChild(script); }; - yandexMetric(); const smartLook = () => { if (!config.smartlook_manage) return; @@ -51,7 +52,10 @@ const init = async () => { script.setAttribute("src", "/metrics/smartlook.js"); document.head.appendChild(script); }; - smartLook(); + if (isProduction) { + yandexMetric(); + smartLook(); + } // const googleManager = () => { // const script = document.createElement("script"); @@ -59,7 +63,7 @@ const init = async () => { // document.head.appendChild(script); // }; // googleManager(); - + return ( diff --git a/src/routes.ts b/src/routes.ts index b443700..7b67cfb 100755 --- a/src/routes.ts +++ b/src/routes.ts @@ -74,6 +74,9 @@ const routes = { onboarding: () => [host, "onboarding"].join("/"), trialChoice: () => [host, "trial-choice"].join("/"), trialPayment: () => [host, "trial-payment"].join("/"), + additionalDiscount: () => [host, "additional-discount"].join("/"), + trialPaymentWithDiscount: () => + [host, "trial-payment-with-discount"].join("/"), notFound: () => [host, "404"].join("/"), }, server: { @@ -221,6 +224,8 @@ export const withoutFooterRoutes = [ routes.client.onboarding(), routes.client.trialChoice(), routes.client.trialPayment(), + routes.client.additionalDiscount(), + routes.client.trialPaymentWithDiscount(), ]; export const withoutFooterPartOfRoutes = [routes.client.questionnaire()]; @@ -281,6 +286,8 @@ export const withoutHeaderRoutes = [ routes.client.satisfiedResult(), routes.client.onboarding(), routes.client.trialPayment(), + routes.client.additionalDiscount(), + routes.client.trialPaymentWithDiscount(), ]; export const hasNoHeader = (path: string) => { return !withoutHeaderRoutes.includes(`/${path.split("/")[1]}`); diff --git a/src/store/index.ts b/src/store/index.ts index e06ad61..68cdcd6 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -4,7 +4,10 @@ import { createAction, } from "@reduxjs/toolkit"; import token, { actions as tokenActions, selectToken } from "./token"; -import questionnaire, { actions as questionnaireActions, selectQuestionnaire } from "./questionnaire"; +import questionnaire, { + actions as questionnaireActions, + selectQuestionnaire, +} from "./questionnaire"; import user, { actions as userActions, selectUser } from "./user"; import form, { actions as formActions, @@ -27,6 +30,7 @@ import payment, { actions as paymentActions, selectActiveSubPlan, selectIsDiscount, + selectSubscriptionReceipt, } from "./payment"; import subscriptionPlans, { actions as subscriptionPlasActions, @@ -85,6 +89,7 @@ export const selectors = { selectUserCallbacksPrevStat, selectHome, selectIsDiscount, + selectSubscriptionReceipt, selectOnboarding, selectOnboardingHome, selectOnboardingCompatibility, @@ -108,7 +113,7 @@ export const reducer = combineReducers({ userCallbacks, siteConfig, onboardingConfig, - questionnaire + questionnaire, }); export type RootState = ReturnType; diff --git a/src/store/payment.ts b/src/store/payment.ts index 908ad06..2f7631e 100644 --- a/src/store/payment.ts +++ b/src/store/payment.ts @@ -1,4 +1,5 @@ import { ISubscriptionPlan } from "@/api/resources/SubscriptionPlans"; +import { SubscriptionReceipt } from "@/api/resources/UserSubscriptionReceipts"; import { createSlice, createSelector } from "@reduxjs/toolkit"; import type { PayloadAction } from "@reduxjs/toolkit"; @@ -6,12 +7,14 @@ interface IPayment { selectedPrice: number | null; isDiscount: boolean; activeSubPlan: ISubscriptionPlan | null; + subscriptionReceipt: SubscriptionReceipt | null; } const initialState: IPayment = { selectedPrice: null, isDiscount: false, - activeSubPlan: null + activeSubPlan: null, + subscriptionReceipt: null, }; const paymentSlice = createSlice({ @@ -38,4 +41,8 @@ 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 default paymentSlice.reducer;