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 0000000..915619b Binary files /dev/null and b/public/image/Frame 1000005757.webp differ 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;