From c0f1d224c7c9059dead104eecc18670d9c5cf829 Mon Sep 17 00:00:00 2001 From: sina_sajjadi Date: Thu, 9 Jan 2025 18:46:05 +0330 Subject: [PATCH] feat: implement subscription management features, including active subscriptions, plans, and history sections --- .env | 1 + public/image/Frame 1000005757.svg | 50 ++++ public/image/Frame 1000005757.webp | Bin 0 -> 7614 bytes public/image/payments/🦆 icon _PayPal_.svg | 8 + public/image/payments/🦆 icon _Stripe_.svg | 3 + public/locales/en/common.json | 2 +- src/data/client/api-endpoints.ts | 3 + src/data/subscription.ts | 6 + src/pages/subscriptions.tsx | 24 -- src/pages/subscriptions/active-section.tsx | 69 ++++++ src/pages/subscriptions/history-section.tsx | 93 ++++++++ src/pages/subscriptions/index.tsx | 32 +++ src/pages/subscriptions/modal.tsx | 69 ++++++ src/pages/subscriptions/plans-section.tsx | 236 +++++++++++++++++++ 14 files changed, 571 insertions(+), 25 deletions(-) create mode 100644 public/image/Frame 1000005757.svg create mode 100644 public/image/Frame 1000005757.webp create mode 100644 public/image/payments/🦆 icon _PayPal_.svg create mode 100644 public/image/payments/🦆 icon _Stripe_.svg delete mode 100644 src/pages/subscriptions.tsx create mode 100644 src/pages/subscriptions/active-section.tsx create mode 100644 src/pages/subscriptions/history-section.tsx create mode 100644 src/pages/subscriptions/index.tsx create mode 100644 src/pages/subscriptions/modal.tsx create mode 100644 src/pages/subscriptions/plans-section.tsx diff --git a/.env b/.env index 6eff044..9860ea1 100644 --- a/.env +++ b/.env @@ -5,3 +5,4 @@ NEXT_PUBLIC_ENABLE_MULTI_LANG=true NODE_ENV=production NEXT_PUBLIC_REST_API_ENDPOINT=https://mesbahi.nwhco.ir/api NEXT_PUBLIC_SITE_URL=https://mesbahi.nwhco.ir:3000 +NEXT_PUBLIC_AUTH_TOKEN_KEY=AUTH_CRED \ No newline at end of file diff --git a/public/image/Frame 1000005757.svg b/public/image/Frame 1000005757.svg new file mode 100644 index 0000000..b9f1636 --- /dev/null +++ b/public/image/Frame 1000005757.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/image/Frame 1000005757.webp b/public/image/Frame 1000005757.webp new file mode 100644 index 0000000000000000000000000000000000000000..915619b4798a09c6520c5b536fc0c8dba0d4c638 GIT binary patch literal 7614 zcmV;v9YNw!Nk&Gt9RL7VMM6+kP&il$0000G0000N0svM306|PpNSy!x00D3l+qPPr zu$Rv$g-HaXCWe|>O8Qip;TSm0jfe?AR80-ujz@vce_1h+sHE2(74FMwj8gOHu&V-4 zY91kWRUqwAVhQS+6LxyaBgMji73ObN_&KcLc;bKYzxZGLFa8(*i~q&{;zXAf+zTt* zCakc^utHfqa>Cg_juWZ?8jR3a0H}37=<0~g9J%0JR*pzP>w7=r?PW^ZKpW?6f-}Hayy{tT) z^__y)z>K~5Z-+#~lmgbuPUy^?-{^9t;{ww}R_n$@Y$zOndnZAHOiT@t{ zz4+7qC;orbzv2J1`B?p1^{?Cm_}}w??f=z(W&gbY|NC41tNt(gZ`d#Ie%yYG|1bGt z{ipxW;3x5KzbW|9Pkn{cWzLfa}x>uX=`wjW&&6 z3MzonWBLJpEClsfuolng2@xy473RF_K1yNvky0L-BvZuXNjP`+@&|Xo;!Q!>ID*Kf(>l3CT&kbM5Rhw zKS8YBH(psimrW#5Op0)62DwPyYJuCj&bH1~VAUswVG)U<=!3#xUy=-Bib|(*a zyQ#D*$WOJo0zVnd>*zDouSonCsZm{|aqdfT?}MG?fT*}J_uR*@oWzjRQ^}DKbNs@yNikmF0X}qG`(J-wxnqaznNOn zRYCI^=~{gBoVKW~YV9tjNMP<%^W6gI9x2Wr{T64LvbL5KfKi{-D*)s_)p4ZILo<3C zK6Bud2*FFvzBr5T*<-t4SL$LMI#HIZB6LY2F|=dm0ao3G6;Ja37em86IM%}J=-jpZ z>Jf=R#bK~Z@O3HH#b2@hjbgGccyyF*PvTkASG90MS%|Uj?{bR6Q_`ZG+U6sFEXypK z`RGlvw=tkR<02O3ccQ4isXuoHZ5^La+iND`RdMYW(K)=$&QQX(T zFS9ztcC*AA!0kGQAoWJ+JZI;jsTYCwM4ml(h*OqDn1NXh=K^BtEPWK9*Wr-Xsdt(o zYT!~a@k5!z9|_|Xc|uEf(q0fM**^S>FI<~=3$b2fKUIP+YV<9@F^b7F+CT&B=u&^S zH{Y>Rxva0d@)$;2vm#ZBnHEy7;*2=F&O46cK*55%Zc)%w^w}+FV09aW0+68pjEL&-fkU0A{`PMKtWmx)H2m@G}ZXDq_lK z&ef#II>@87??<+B zsv}t{b7bBcUoKar`S^_8YZlh`1`Z|C#1D zcivGVFW)-UgPZg~Vgz)M$lQ4}vaC+^pQ)Ae z00vPMBRa>mJED3cqKpk78KY_#&>^)#5=8@3N>|~ZoBrle<}+q;s-4u*eir(bro)*d z=-#yc104oG-?sP)!>Ws5hV7nsw#}9CAu<_yI zu^vFoi>=B1R%6|HQVSoYoZg`%NPK-(rubtkE1;x!7da);&A&joUo-~Um^c+_Gv10Q ze+YAT^J^O2BA1B9IYNG2-f@B(8vL0Tk#xb{Cb|~WJh`ieqDjQ`pPx(mO`LeHN6Xr{ z0}_S7@gDZX7v`DmH3H*(6h?;13U3X+B`~s5O(}ICc8-+%9E- z!@Q0HJBkyEtM6-j5|z2Pr$a1t7V+T$$Y7pG}u41=8& zvOafL1gusuUmm?+f6lfx%>Vy;p7!Zuz=8fW&^r$pUpYr}2ozbNzA&8WV#j(E`)h}- zR~=~@{ASP0*;0L`s$jW*H%1bbWkL^SkxdCTdJ_>zcbLqwwYN%=x&*b^CHJ=uCo#0! zbchkoYKzxo|1P+CIg>BsZcg#ryQ$6*%-s_>G4)B&M!elvsiMpT@(Icc5qGISbGwkb z|M-N@?WxL32GMGUtK@~<<5#5azB+W$mCa$Im?WH&M94R0rOv}#mnlC3Q$m8=)Ao&H zAR?b%xM(l^Wa(zaRhd z34MYFH}qX#i+YSlSk*&7on7tZJo`o!HY639sNrW33sc2t%)>-3QQu zlhvgcV<4afN!mT5w?iua=Qy`J|?$y39nb>A_cb0kF>&Us{Gva)@E%!uj zQaI-y$>o0V1y4kt`g81HrSYrO_YKY4xLhYtvt$+-%9dPoXT0fh@4HlxHi9x?)GZl| z!+11M+k!ef7b!ej%SnkxIj6e9i^ef2!r#zC`$-s{_XZRT)8 zq^!eRQzo4CrRNIeKF0X5Sg;s4DvwPeamGmD9?pvxK##GLU6h(WITphv56c5EPinB6 z4cNZRVSXfy-wRhwfhXnYrSJ7rC)Oncy0WDe6Da!&pvy8)tYrBW=}>Zpe5k5mIzjqGj#dgFJIC^ zzfY#B11B_giG97%65`+Q#R9eXj|+Y$le%iFO{3L~Riwt1Ov|S8ZN7&3{EN&7kr8aF zOJ0m5hFA)3giXK{9?Q&8nRr|BB=?_=*)%tJ$!bmDzXv+6X|+WHOThN5*r@qmNIMH> z-{vWkT>)x*5sf{C5eJIPOyGLr4tKU7+7V5IqK>mPGJf=fKuSrZC{vaw5Zd+IsaCX* zEo1qn_!4FES4gY7Fsy_9*&+44_# zyRxc;M6MD;NUH$KhlA@N{HH(}^-@)(vk_e!%q@U^@Jo*bctfY0W7Kl;U&U}HlygIB z9(Vk7;9s3|s8?nRnGGu^L-Cmb{KqkFiDHADhPz%a<-bt(v$1vi`qS7sNl?>^9qeAB zcd}7vzA_-zV?zi*>t>KOQx!$Zv8*AVo&faHbA9$K&^R5qqPL2ZPysK0L zl5LUWr)P3nDk7kglR#&+Q$csA?f>?dk&V2YEJqu-32b;8?`cD3J@+8(yOjl4O7K|} zDh^GwZyf~O1!h4&|5Ce3#i%<^*aM4R79@iqWZA}QJM!*(mgz+_vVrMHyD)jMt8MOd zL3TlZ{iWn%Zzjt0nma={CA?vUg#8HvB6qa7mu8-#&nN#3n=@cvt~zo6=ndrkkTi(Vjfm5s-61{Y$bB0Aam%G+;f3B<@Dx*h>RE!;B1mPoFY)zo9`8s+wFcj1T}R$I<*8H`kq8}w6~^H?@&*R zCbPeQ2_FO6zTQUJgTAaFQ1C{P#{Y%GF`D3ylHAX4wts>Gz5e^Irg3*Asu{$cqX zSC9mk&UL&QN-To{e75E=5Giw+9IQZ-55daW4jJuGCV6O7O}t4yyowD%KY#Fd7x@$& zENW3?TWXO>LEAol<0!JHRlB%HaD`MxvLgMfW6U?!(IfSYGt#z7 z0CJS5qNtH;>%WvvQR!|N8E7Yb1V=|^QMZX8q=mK_RKNaeC}7hEj13I_gHJHop-NBR zHI-yu5f7z1QIqL5j|!;W=)Be2zgoBd&kM>zpmbhVuDnh+Z|V8I_aLXaQ$0@#CUQw_ zkN`QLnLl2}O{h)9Ws=Se@!Ph^LEU2>ya<fK{PngMc(Z!^ zY2L~QZB^c~4DZL?cV1i*C#)U{!OQ0<$%E|U{&Scen78fpt_SktQ#l|b8w)aRTNpo0yQZ0 zSAN1j+G7NWA}cU1nzZ>6Ai2M)y-Q$3Ca(4@7>&SH_5~Z`YVZHKAIi;mvTreO5!-S^ zaAwZ_YRV@_*d`l`G)?>Tn4@RGDkLTcqc|$_48xC1g6AUpy8UP-9N>({4!me{|M@bL z-{Ne(teS<~d?9Ug@FW?0CQ7>~y1lH&EQEN@;swKP?NkcsxkDxh*2@*-MfXF!{Rsf~TGRX>U`{HyphG=Vy;>*5_S zxDwj*Z_if)a}yPR`v|L7l7trjzW;==*dk%mkclu!sc^Xbt;B`s|C*d|Fy2 zGMd0HbZh!-PN-ZrpY9B7m9Dw*kc{g3*j+2G(i{C+>QG$#Sj2ysIfy-QsZK0wj)UGY zgw<625Teek&?S7jozO4IcFHIf{kee$kamjWoV&U;(Vyr~y$+FdB@bN5gr zPI?zLrAdFpN`urzXj%~P76>1Mm|HSW6E{C-jNfj9?=Ijv+!A#uvj>)tHL(6#l#DK} zKP8Y;`2KqKZyCI3BM?!HUzunu$QJc;8FkNBtYs$HXv+>zVFUQ_V_(<4P%L-N8f7&C z5td$xSiaSar6&QIy8>WJ#3r~Ne^eJq>uIBQlJ^N%5B;X8D(yO8Y^uP@M%KQnk&)Va z>fwZG>dve_y+j5KRmj|?0Kc*2-c1I8%tanN{*?j-%KEW=`_=tA+0p+?0lh31v5>$0 z4MzHmZE)9p%P-#I`x~?V!c-0KNEbcv+*#kLQD_p=-T&$NWon|nn_Xc0@}3|EB2fnp<=Gmm3N$t-f71~?gk6r-|ojF zf1SAhBy1WJWup&M4>B1M0=gb*;|!c)cQ{0ZaCTqu<92*sxg~IVfL|g=Zq*2Wi zqVWyUM*vk(DPxfYR($UO*OG{`XD;YY_?q_V(ScD5A2UY4gBlQRWl&oM7pV0?d2Sm< z*0Y{6-l1V>FI6x)yHG*Bv*_*4_2q!pXZz#v5kIf&sSIa5bs1b5B1Y19D(wm*QVtR? zgs7+!tfECqtEMs;Fzjv-PyiYK#%bo_-x!M#X7az^{134iCldo$WyAfSK0z-*{@5*J z1E&+&rLQ5Lu|;vRiZv}8$|-i-rS_FK zt|;`Eb3O80qE70>wU&q|8a=o}o8G^gwc0nH}J&Y54=bnGjixw3KxlZyZDn;xVCNB}1XjpQi%7hxfgel9Lb z|Gt70gY!i~TPWR7l8k5^ovG*8YArwo9k&eZFnn&@w87Nap<<6Moa zf!UqOyt|fM|3T|8^uF+Lmj_L0!``wo1(F1rKV_;`s?tevtv|zfveL$-(W#32J$4x) zHYzpbY&-lbPL?(^YFkZexd>1dVhyTrJmmivXHY@Wf(x%47v`YfE<+2ef!>0CIL%>U zCl--sab>9J=zA-3dGM<9I1fTWu9FaY1^{T}d|0LgYwD1N%-wG?zvjzLvV@Cs*MIxI zKuUrKCGh9F%6p~7I-avE#sOn-_~Rqz6jFb(-IKJQ-{;!M2!m62=Cx@plT zbHlD4M!(f(IxkbZsk1xryP5t;)M&dIVgy^~^A!K6mK#Q)21Ii3#5AP>TMZ9`j7xc! zMgkk3KH|uB$mrk5Aw%CwBE684I1H{)R&5XqW<7-vCgIABK7bCrlMxknTag&z;)v3l>Sg@k-<*Ac#d#F? z6iruq^B8Eg9GXfg3y?acI$0btPsQvd=gcMVwJcO7LL}_0UX(rxss753pq=p=(<`kr zgi-}kf@LH0CIv&zz8*%4eN*aF!dUKO1aeHQ6K*;Nrz+B)TFJR-V~r+GN-1*;V*P~L zfn%5An2|As+n-A;j*sE8qDcFMY^(-H2@N9yjpqR7DMJG&mHKginz79%0)dj7x8RSn z_bKycRraIcF@o^$+M_mVvh4AfhK7Y~vpo5*QyiQ!+G4y0&EgVi!U{!a3qP-Uvd10W ze_3mG_Ofra?3EwX<_!icj?*TFJa4+J9I>vBy}j)nZc4-?R6^0 zUjUbD0MhGg>HH;ehd6LH3EGW|P}z9CE0*HRuTR!6Rr&HsK&z>s?@|J(wvWw>TC7%m z;@i}}p~a8AeGLdbwzgC8fuyo(??2GkVc&sy02<4QG0#qlkIv zfB5LrP;yK%Y0jZbJ=lRGc1vQVN>Hh1({5d2*R99MjX&lHhNrY!r$lz+;4gRpL}sn@ zVOdg^T8@Z+F-CBwso3*e)y?0Z?)fZq`*WFbJQ5-39I zq^RX@eM6*B(0dwz?Q1b>?_W(-8!h#XX-1R`+_<2-7yANvJG3K@!rKlGpIDgN5aYI@ z|Ms`G0Ld0t^R&zjAaEa+i=>mFWIOk6Ll(h87B)fO`OJ^MaCIzSOcNOOm6+oSgq^;d z>Yx8f=WmRoG!%)LgOfHJmE}ryN6?{ zyUkrn@&jf?T#YyUHG`Js5#MGaN_qL?i55qy_o~W>FshfS}t*P%@;e5z>qJ|@;G3qO7 zs%rE&#mC8ntbdZ_-9ew@bMdsEVit}p8g;!@!h|wN-znha%Fi@~&2L7N0xXICe=;nF z52zK>1$OiO#Y~jL^Ut$MvJN@uZ5U!1rkr_lc~5Lk*}AH3FvUWvtjKX(0=b}yAY~2I zS6t3?Q&ru04KyaB3{mnv%!(wb#z<2qz>GpA+=g2Wl2fVqh9p-~IBDb_7Sl8SI1aDMqIQ-z z`*T<&HELUAvh@sT8Dp#=VJ~B(8N9=ZHP8s*m23}PTDZlb?p9Zzv|$y^^2lt9XX85;JYNEnQcz9kQltw0z6MpJ(4?mhgS{==650QrIH$N&HU literal 0 HcmV?d00001 diff --git a/public/image/payments/🦆 icon _PayPal_.svg b/public/image/payments/🦆 icon _PayPal_.svg new file mode 100644 index 0000000..38ff5f9 --- /dev/null +++ b/public/image/payments/🦆 icon _PayPal_.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/image/payments/🦆 icon _Stripe_.svg b/public/image/payments/🦆 icon _Stripe_.svg new file mode 100644 index 0000000..507044d --- /dev/null +++ b/public/image/payments/🦆 icon _Stripe_.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 56d5c8d..8c515c9 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -558,6 +558,6 @@ "text-image-uploading-message": "Please wait! Image is uploading", "text-quote-title": "Quote", "text-transfer-shop-ownership-status": "Transfer shop ownership.", - + "subscription.title" : "You have a " , "text-pixel-dot": "pixel." } diff --git a/src/data/client/api-endpoints.ts b/src/data/client/api-endpoints.ts index c854d78..155325e 100644 --- a/src/data/client/api-endpoints.ts +++ b/src/data/client/api-endpoints.ts @@ -107,4 +107,7 @@ export const API_ENDPOINTS = { BECAME_SELLER: 'became-seller', TRANSFER_SHOP_OWNERSHIP: 'transfer-shop-ownership', OWNERSHIP_TRANSFER: 'ownership-transfer', + GET_ACTIVE_SUBSCRIPTION : "merchant-panel/subscriptions/active/", + GET_ALL_SUBSCRIPTIONS : "merchant-panel/subscriptions/list/", + GET_SUBSCRIPTIONS_HISTORY : "merchant-panel/subscriptions/history/list/" }; diff --git a/src/data/subscription.ts b/src/data/subscription.ts index 5c1f611..877b5a1 100644 --- a/src/data/subscription.ts +++ b/src/data/subscription.ts @@ -4,4 +4,10 @@ import { HttpClient } from "./client/http-client"; export const useGetActiveSubscription = () => { return useQuery([API_ENDPOINTS.GET_ACTIVE_SUBSCRIPTION], () => HttpClient.get(API_ENDPOINTS.GET_ACTIVE_SUBSCRIPTION)); +}; +export const useGetAllSubscriptions = () => { + return useQuery([API_ENDPOINTS.GET_ALL_SUBSCRIPTIONS], () => HttpClient.get(API_ENDPOINTS.GET_ALL_SUBSCRIPTIONS)); +}; +export const useGetSubscriptionsHistory = () => { + return useQuery([API_ENDPOINTS.GET_SUBSCRIPTIONS_HISTORY], () => HttpClient.get(API_ENDPOINTS.GET_SUBSCRIPTIONS_HISTORY)); }; \ No newline at end of file diff --git a/src/pages/subscriptions.tsx b/src/pages/subscriptions.tsx deleted file mode 100644 index c2a53a8..0000000 --- a/src/pages/subscriptions.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import Layout from '@/components/layouts/admin'; - -const Subscription: React.FC = () => { - const { t } = useTranslation(); - - return ( -
-

{t('subscription.title')}

-

{t('subscription.description')}

-
- ); -}; - -export default Subscription; - -Subscription.Layout = Layout; - -// export const getStaticProps = async ({ locale }: any) => ({ -// props: { -// ...(await serverSideTranslations(locale, ['table', 'common', 'form'])), -// }, -// }); diff --git a/src/pages/subscriptions/active-section.tsx b/src/pages/subscriptions/active-section.tsx new file mode 100644 index 0000000..56195d9 --- /dev/null +++ b/src/pages/subscriptions/active-section.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { useGetActiveSubscription } from '@/data/subscription'; +import { FaStar } from 'react-icons/fa'; +import Image from 'next/image'; +import background from '../../../public/image/Frame 1000005757.webp'; + +const ActiveSubscriptionSection: React.FC = () => { + const { data: activeData, isLoading: isActiveLoading } = + useGetActiveSubscription(); + + const formatDate = (dateString: string): string => { + if (!dateString) return ''; + + const date = new Date(dateString); + if (isNaN(date.getTime())) return dateString; // Return original string if invalid + + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }); + }; + + if (isActiveLoading) { + return
Loading...
; + } + + return ( +
+
+
+ +

{activeData?.subscription?.title}

+
+
+ subscription type +
+
+
+
+
+

Start Your Plan

+

{formatDate(activeData?.start_date)}

+
+
+

Expiration Date

+

{formatDate(activeData?.end_date)}

+
+
+

Reserve Subscription

+
+
+

Remaining Days

+

{activeData?.days_left}

+
+
+
+
+ ); +}; + +export default ActiveSubscriptionSection; diff --git a/src/pages/subscriptions/history-section.tsx b/src/pages/subscriptions/history-section.tsx new file mode 100644 index 0000000..7e8f9e1 --- /dev/null +++ b/src/pages/subscriptions/history-section.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { + useGetActiveSubscription, + useGetAllSubscriptions, + useGetSubscriptionsHistory, +} from '@/data/subscription'; + +const HistorySection: React.FC = () => { + const { data: historyData, isLoading: isHistoryLoading } = + useGetSubscriptionsHistory(); + const formatDate = (dateString: string): string => { + if (!dateString) return ''; + + const date = new Date(dateString); + if (isNaN(date.getTime())) return dateString; // Return original string if invalid + + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }); + }; + + // Helper function to format price strings + const formatPrice = (priceString: string): string => { + if (!priceString) return ''; + + const priceNumber = parseFloat(priceString); + if (isNaN(priceNumber)) return priceString; // Return original string if invalid + + return priceNumber.toFixed(0); // Removes decimals + }; + if (isHistoryLoading) { + return

Loading...

; + } +console.log(historyData); + + + return ( +
+

+ History Plans +

+ {historyData?.results.length > 0 ? ( +
+ + + + + + + + + + + + {historyData?.results.map((subscription) => ( + + + + + + + ))} + +
+ Subscription Type + + Costs + + Start Date + + End Date + + Tracking Number +
+ {subscription.subscription.title} + + {formatPrice(subscription.subscription.price)}$ + + {formatDate(subscription.start_date)} + + {formatDate(subscription.end_date)} +
+
+ ) : ( +

No historical subscriptions found.

+ )} +
+ ); +}; + +export default HistorySection; diff --git a/src/pages/subscriptions/index.tsx b/src/pages/subscriptions/index.tsx new file mode 100644 index 0000000..3623284 --- /dev/null +++ b/src/pages/subscriptions/index.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import Layout from '@/components/layouts/admin'; +import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +import { GetStaticProps } from 'next'; +import ActiveSubscriptionSection from './active-section'; +import PlansSection from './plans-section'; +import HistorySection from './history-section'; + +export const getStaticProps: GetStaticProps = async ({ locale }) => ({ + props: { + ...(await serverSideTranslations(locale!, ['common', 'form'])), + }, +}); + +const Subscription: React.FC = () => { + return ( +
+ {/* Active Subscription Section */} + + + {/* Plans Section */} + + + {/* History Plans Section */} + +
+ ); +}; + +export default Subscription; + +Subscription.Layout = Layout; diff --git a/src/pages/subscriptions/modal.tsx b/src/pages/subscriptions/modal.tsx new file mode 100644 index 0000000..97e08b9 --- /dev/null +++ b/src/pages/subscriptions/modal.tsx @@ -0,0 +1,69 @@ +// components/ui/Modal.tsx + +import React, { useEffect } from 'react'; +import { FaTimes } from 'react-icons/fa'; + +interface ModalProps { + display: boolean; + setDisplay: (display: boolean) => void; + children: React.ReactNode; +} + +const Modal: React.FC = ({ display, setDisplay, children }) => { + // Close the modal when the Escape key is pressed + useEffect(() => { + const handleEscape = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + setDisplay(false); + } + }; + if (display) { + document.addEventListener('keydown', handleEscape); + } else { + document.removeEventListener('keydown', handleEscape); + } + return () => { + document.removeEventListener('keydown', handleEscape); + }; + }, [display, setDisplay]); + + // Prevent scrolling when modal is open + useEffect(() => { + if (display) { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = 'auto'; + } + return () => { + document.body.style.overflow = 'auto'; + }; + }, [display]); + + if (!display) return null; + + return ( +
setDisplay(false)} // Close when clicking on the backdrop + > +
e.stopPropagation()} // Prevent closing when clicking inside the modal + > + {/* Close Button */} + + + {/* Modal Content */} +
{children}
+
+
+ ); +}; + +export default Modal; diff --git a/src/pages/subscriptions/plans-section.tsx b/src/pages/subscriptions/plans-section.tsx new file mode 100644 index 0000000..0366d56 --- /dev/null +++ b/src/pages/subscriptions/plans-section.tsx @@ -0,0 +1,236 @@ +import React from 'react'; +import { useGetAllSubscriptions } from '@/data/subscription'; +import { RiHeadphoneLine } from 'react-icons/ri'; +import { FaRegCheckCircle } from 'react-icons/fa'; +import Button from '@/components/ui/button'; +import { useState } from 'react'; +import Modal from '@/components/ui/modal/modal'; +import { IoMdClose } from 'react-icons/io'; +import payPal from '../../../public/image/payments/🦆 icon _PayPal_.svg'; +import Stripe from '../../../public/image/payments/🦆 icon _Stripe_.svg'; +import Image from 'next/image'; +const PlansSection: React.FC = () => { + const [display, setDisplay] = useState(false); + const [selected, setSelected] = useState({}); + const { data: allSubscriptions, isLoading: isAllLoading } = + useGetAllSubscriptions(); + + const formatPrice = (priceString: string): string => { + if (!priceString) return ''; + + const priceNumber = parseFloat(priceString); + if (isNaN(priceNumber)) return priceString; // Return original string if invalid + + return priceNumber.toFixed(0); // Removes decimals + }; + + const formatTitle = (text: string) => { + if (!text) { + return; + } + const splitedText = text.split(''); + const number = splitedText[0]; + const duration = () => { + const char = splitedText[1]; + if (char === 'D') { + return 'Day'; + } + if (char === 'M') { + return 'Month'; + } + if (char === 'Y') { + return 'Year'; + } + return ''; + }; + return ( +
+ {number}  + + {duration()} + +
+ ); + }; + + if (isAllLoading) { + return
Loading...
; + } + + return ( +
+
+
+

Plans

+

+ Change your current workspace plan +

+
+
+

Support

+ +
+
+
+ {allSubscriptions?.results.map((item) => ( +
+
+

{formatTitle(item.duration)}

+

User Per Month

+
+
+
+ + {formatPrice(item.final_price)}$  + + User Per Month +
+ {!!formatPrice(item.discount_percentage) && ( +
+

+ Save {item.discount_percentage}% +

+
+ )} +
+ +
+ ))} +
+
+

+ Features of All Subscriptions +

+
+
+
+ +
+

+ Customizable Storefront: Merchants can personalize their + storefront with custom branding, logos, and banners to create a + unique shopping experience. +

+
+
+
+ +
+

+ Advanced Analytics Dashboard: Access detailed insights into sales, + traffic, and customer behavior to optimize listings and marketing + strategies. +

+
+
+
+ +
+

+ Bulk Listing Management: Easily upload and manage multiple + gemstone listings at once with bulk editing features. +

+
+
+
+ +
+

+ Real-Time Inventory Tracking: Merchants can track stock levels in + real-time, ensuring they never over-sell or run out of inventory. +

+
+
+
+ { + setSelected({}); + }} + > +
+
+

+ One-year subscription +

+ +
+

+ Customers like the functionality and ease of setup of the product. + They mention it works great, is easy to mount, and hook up through + your home internet. +

+
+
+

{formatTitle(selected.duration)}

+

User Per Month

+
+
+
+ + {formatPrice(selected.final_price)}$  + + User Per Month +
+ {!!formatPrice(selected.discount_percentage) && ( +
+

+ Save {selected.discount_percentage}% +

+
+ )} +
+
+
+

+ Features Subscription +

+
+
+
+ +
+

Joining Live Streams

+
{' '} +
+
+ +
+

Joining Live Streams

+
{' '} +
+
+ +
+

Creating a Custom Profile

+
+
+
+
+ + +
+ +
+
+
+ ); +}; + +export default PlansSection;