@lmnto/h-mall-shared 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/app.css +457 -0
  2. package/bitbucket-pipelines.yml +223 -0
  3. package/package.json +43 -0
  4. package/public/gif/balloon.gif +0 -0
  5. package/public/gif/gift.gif +0 -0
  6. package/public/img/ai-dialog.png +0 -0
  7. package/public/img/ai-x1-banner.png +0 -0
  8. package/public/img/ai-x1.png +0 -0
  9. package/public/img/alert-Icon.png +0 -0
  10. package/public/img/aleta-payment-kyc-require.png +0 -0
  11. package/public/img/aleta-payment-pending.png +0 -0
  12. package/public/img/aleta-payment-rejected.png +0 -0
  13. package/public/img/aleta-payment-subscription-required.png +0 -0
  14. package/public/img/boost-banner-bg.png +0 -0
  15. package/public/img/boost-banner-right.png +0 -0
  16. package/public/img/boost-banner.png +0 -0
  17. package/public/img/boost-dialog-bg.png +0 -0
  18. package/public/img/boostPower.png +0 -0
  19. package/public/img/boostTooltip.png +0 -0
  20. package/public/img/brand-icon.svg +23 -0
  21. package/public/img/brand-text.svg +11 -0
  22. package/public/img/card-icon.png +0 -0
  23. package/public/img/cashback-Icon.png +0 -0
  24. package/public/img/crown.svg +9 -0
  25. package/public/img/empty-box.png +0 -0
  26. package/public/img/gen3-dialog.png +0 -0
  27. package/public/img/gen3-home-banner-bg.png +0 -0
  28. package/public/img/gen3-image.png +0 -0
  29. package/public/img/gift.png +0 -0
  30. package/public/img/img-placeholder-illustration.svg +6 -0
  31. package/public/img/logo.svg +31 -0
  32. package/public/img/offerBanner.png +0 -0
  33. package/public/img/pagenotfound.png +0 -0
  34. package/public/img/partial-pay-payment-successful.png +0 -0
  35. package/public/img/phoneShippingBG.png +0 -0
  36. package/public/img/placeholder-image.jpg +0 -0
  37. package/public/img/powerIcon.png +0 -0
  38. package/public/img/product-01.png +0 -0
  39. package/public/img/product-02.jpg +0 -0
  40. package/public/img/product-03.jpg +0 -0
  41. package/public/img/product-04.jpg +0 -0
  42. package/public/img/product-05.png +0 -0
  43. package/public/img/product-06.png +0 -0
  44. package/public/img/profile-avatar.svg +12 -0
  45. package/public/img/promotion-icon.png +0 -0
  46. package/public/img/session-expired.png +0 -0
  47. package/public/img/shipping-alert-icon.png +0 -0
  48. package/public/img/shippingIcon.png +0 -0
  49. package/public/img/smart-pay-banner-how.png +0 -0
  50. package/public/img/smart-pay-banner-own.png +0 -0
  51. package/public/img/smart-pay-chose.png +0 -0
  52. package/public/img/smart-pay-easy.png +0 -0
  53. package/public/img/smart-pay-payment.png +0 -0
  54. package/public/img/smart-plan-fire.gif +0 -0
  55. package/public/img/smart-plan-sparkle.gif +0 -0
  56. package/public/img/special.png +0 -0
  57. package/public/img/specialOfferBg.png +0 -0
  58. package/public/img/stop-shipping-banner.png +0 -0
  59. package/public/img/subscriptionBanner.png +0 -0
  60. package/public/img/subscriptionIcon.png +0 -0
  61. package/public/img/successIcon.png +0 -0
  62. package/public/img/wallet-icon.png +0 -0
  63. package/public/img/world-wide-shipping.png +0 -0
  64. package/public/next.svg +1 -0
  65. package/public/vercel.svg +1 -0
  66. package/shared/components/AddressFormComponent.tsx +215 -0
  67. package/shared/components/AddressShippingFormComponent.tsx +223 -0
  68. package/shared/components/AiPaymentDialog.tsx +95 -0
  69. package/shared/components/AppLoadingComponent.tsx +15 -0
  70. package/shared/components/AppLoadingComponentV2.tsx +18 -0
  71. package/shared/components/Button.tsx +48 -0
  72. package/shared/components/BuySubscriptionDialog.tsx +75 -0
  73. package/shared/components/CartDrawerComponent.tsx +324 -0
  74. package/shared/components/CashbackComponent.tsx +48 -0
  75. package/shared/components/CashbackMigrationComponent.tsx +41 -0
  76. package/shared/components/CommissionNonEligibleComponent.tsx +27 -0
  77. package/shared/components/CustomNextImage.tsx +50 -0
  78. package/shared/components/CustomNextLink.tsx +46 -0
  79. package/shared/components/EditAddressFormComponent.tsx +254 -0
  80. package/shared/components/EmptyCartMessageComponent.tsx +25 -0
  81. package/shared/components/EmptyCartMessageForCartPage.tsx +61 -0
  82. package/shared/components/EmptyLegacyOrderMessageForOrdersPage.tsx +52 -0
  83. package/shared/components/EmptyOrderMessageForOrdersPage.tsx +62 -0
  84. package/shared/components/EmptyOrderMessageForShippingOrdersPage.tsx +62 -0
  85. package/shared/components/Footer.tsx +71 -0
  86. package/shared/components/GreetingDialog.tsx +96 -0
  87. package/shared/components/Header.tsx +106 -0
  88. package/shared/components/HoldShippingDialog.tsx +165 -0
  89. package/shared/components/HomeBanners.tsx +298 -0
  90. package/shared/components/InactiveCartDialog.tsx +69 -0
  91. package/shared/components/InputCartQuantity.tsx +342 -0
  92. package/shared/components/Inputfield.tsx +110 -0
  93. package/shared/components/ListItemComponent.tsx +82 -0
  94. package/shared/components/LoadingIconComponent.tsx +284 -0
  95. package/shared/components/OrderNotFoundComponent.tsx +56 -0
  96. package/shared/components/PageHeader.tsx +42 -0
  97. package/shared/components/PhoneNumberCountries.tsx +133 -0
  98. package/shared/components/PhoneShippingDialog.tsx +77 -0
  99. package/shared/components/PickupDialog.tsx +92 -0
  100. package/shared/components/ProductNotFoundComponent.tsx +56 -0
  101. package/shared/components/ProductPriceComponent.tsx +32 -0
  102. package/shared/components/ProfileMenu.tsx +120 -0
  103. package/shared/components/RequestCreditNoteInvoiceDialog.tsx +214 -0
  104. package/shared/components/RequestInvoiceDialog.tsx +194 -0
  105. package/shared/components/SelectCities.tsx +133 -0
  106. package/shared/components/SelectComponent.tsx +334 -0
  107. package/shared/components/SelectCountries.tsx +179 -0
  108. package/shared/components/SelectPickUpStore.tsx +131 -0
  109. package/shared/components/SelectStates.tsx +144 -0
  110. package/shared/components/ServerTime.tsx +68 -0
  111. package/shared/components/SessionDialogComponent.tsx +60 -0
  112. package/shared/components/ShippingAddressFormDialogComponent.tsx +51 -0
  113. package/shared/components/Sidebar.tsx +281 -0
  114. package/shared/components/SpecialOfferDialog.tsx +193 -0
  115. package/shared/components/StatusComponents.tsx +25 -0
  116. package/shared/components/StepperComponent.tsx +79 -0
  117. package/shared/components/StepperSmartPayComponent.tsx +97 -0
  118. package/shared/components/SubscriptionPurchaseDialogComponent.tsx +81 -0
  119. package/shared/components/TableComponent.tsx +144 -0
  120. package/shared/components/TablePaginationComponent.tsx +151 -0
  121. package/shared/components/aleta/AletaManagerComponent.tsx +52 -0
  122. package/shared/components/aleta/KycPendingStatus.tsx +33 -0
  123. package/shared/components/aleta/KycRejectedStatus.tsx +53 -0
  124. package/shared/components/aleta/KycRequiredStatus.tsx +53 -0
  125. package/shared/components/aleta/SubscriptionRequiredStatus.tsx +45 -0
  126. package/shared/components/icons/AletaWalletIcon.tsx +21 -0
  127. package/shared/components/icons/BrandIcon.tsx +97 -0
  128. package/shared/components/icons/BrandText.tsx +47 -0
  129. package/shared/components/icons/CartIcon.tsx +21 -0
  130. package/shared/components/icons/DoneIcon.tsx +27 -0
  131. package/shared/components/icons/HomnifiIcon.tsx +23 -0
  132. package/shared/components/icons/InfoIcon.tsx +16 -0
  133. package/shared/components/icons/SmartPayProductIcon.tsx +31 -0
  134. package/shared/components/payments/DisclaimerAlertDialog.tsx +205 -0
  135. package/shared/components/payments/EmptyAssets.tsx +23 -0
  136. package/shared/components/payments/PaybyWalletItemComponent.tsx +116 -0
  137. package/shared/components/payments/PaymentAssetItemComponent.tsx +101 -0
  138. package/shared/components/payments/RequestProcessingDialog.tsx +123 -0
  139. package/shared/components/payments/WalletSectionManager.tsx +82 -0
  140. package/shared/components/payments/WalletSliderComponent.tsx +326 -0
  141. package/shared/components/payments/WalletTermsAndConditionComponent.tsx +88 -0
  142. package/shared/constants/address-shipping.schema.ts +56 -0
  143. package/shared/constants/address.schema.ts +56 -0
  144. package/shared/constants/app.ts +323 -0
  145. package/shared/constants/environment.ts +23 -0
  146. package/shared/constants/feature-flags.ts +15 -0
  147. package/shared/constants/query-keys.ts +106 -0
  148. package/shared/constants/routes.ts +41 -0
  149. package/shared/constants/socket.ts +15 -0
  150. package/shared/contexts/HomeProductsContext.tsx +51 -0
  151. package/shared/contexts/MainWrapperContext.tsx +289 -0
  152. package/shared/contexts/WebSocketContext.tsx +155 -0
  153. package/shared/hooks/useAddressForm.ts +169 -0
  154. package/shared/hooks/useAddresses.ts +28 -0
  155. package/shared/hooks/useAppForm.ts +39 -0
  156. package/shared/hooks/useCart.ts +207 -0
  157. package/shared/hooks/useCopy.ts +45 -0
  158. package/shared/hooks/useCountries.ts +27 -0
  159. package/shared/hooks/useCustomRouter.ts +51 -0
  160. package/shared/hooks/useDebounce.ts +21 -0
  161. package/shared/hooks/useInvoice.ts +41 -0
  162. package/shared/hooks/useOnlineUser.ts +38 -0
  163. package/shared/hooks/usePayments.ts +101 -0
  164. package/shared/hooks/useShippingAddressForm.ts +70 -0
  165. package/shared/hooks/useShippingAddresses.ts +33 -0
  166. package/shared/hooks/useStores.ts +29 -0
  167. package/shared/hooks/useUserActivity.ts +43 -0
  168. package/shared/i18n/client.ts +27 -0
  169. package/shared/i18n/locales/en.ts +383 -0
  170. package/shared/i18n/locales/it.ts +373 -0
  171. package/shared/i18n/server.ts +7 -0
  172. package/shared/services/addresses.service.ts +27 -0
  173. package/shared/services/api/core/ApiError.ts +29 -0
  174. package/shared/services/api/core/ApiRequestOptions.ts +25 -0
  175. package/shared/services/api/core/ApiResult.ts +11 -0
  176. package/shared/services/api/core/CancelablePromise.ts +130 -0
  177. package/shared/services/api/core/OpenAPI.ts +37 -0
  178. package/shared/services/api/core/request.ts +376 -0
  179. package/shared/services/api/index.ts +64 -0
  180. package/shared/services/api/models/AddAllProductToShippingCartDto.ts +7 -0
  181. package/shared/services/api/models/AddProductToCartDto.ts +8 -0
  182. package/shared/services/api/models/AddProductToShippingCartDto.ts +8 -0
  183. package/shared/services/api/models/AdminPlaceOrderDto.ts +16 -0
  184. package/shared/services/api/models/AssignPickupSerialNumberDto.ts +10 -0
  185. package/shared/services/api/models/CreateUserAddressDto.ts +19 -0
  186. package/shared/services/api/models/DeletCartProductsArrayDto.ts +7 -0
  187. package/shared/services/api/models/GenerateInvoiceDto.ts +7 -0
  188. package/shared/services/api/models/OrderConfirmationEmailDto.ts +8 -0
  189. package/shared/services/api/models/PlaceOrderDto.ts +9 -0
  190. package/shared/services/api/models/PlaceShipmentOrderDto.ts +8 -0
  191. package/shared/services/api/models/ProductBoostDto.ts +18 -0
  192. package/shared/services/api/models/RemoveAssignedSerialNumberDto.ts +9 -0
  193. package/shared/services/api/models/RemoveProductToCartDto.ts +8 -0
  194. package/shared/services/api/models/ShipmentItem.ts +8 -0
  195. package/shared/services/api/models/ShippingCartUpdateBillingAddressesDto.ts +7 -0
  196. package/shared/services/api/models/ShippingCartUpdateShippingMethodDto.ts +7 -0
  197. package/shared/services/api/models/UnfreezeWalletDto.ts +8 -0
  198. package/shared/services/api/models/UpdateCartBillingAddressesDto.ts +7 -0
  199. package/shared/services/api/models/UpdateCartPaymentMethodDto.ts +9 -0
  200. package/shared/services/api/models/UpdateCartShippingMethodDto.ts +7 -0
  201. package/shared/services/api/models/UpdateCartStoreDto.ts +7 -0
  202. package/shared/services/api/models/UpdateProductDto.ts +11 -0
  203. package/shared/services/api/models/UpdateShipmentItemStatusDto.ts +24 -0
  204. package/shared/services/api/models/UpdateShippingCartPaymentMethodDto.ts +9 -0
  205. package/shared/services/api/models/UpdateUserAddressDto.ts +19 -0
  206. package/shared/services/api/models/ValidateAuthCodeDto.ts +8 -0
  207. package/shared/services/api/models/WithdrawOrderWalletDto.ts +8 -0
  208. package/shared/services/api/services/AddressesService.ts +160 -0
  209. package/shared/services/api/services/AdminsDevicesService.ts +28 -0
  210. package/shared/services/api/services/AdminsOrdersService.ts +117 -0
  211. package/shared/services/api/services/AdminsPickupsService.ts +57 -0
  212. package/shared/services/api/services/AdminsProductsService.ts +86 -0
  213. package/shared/services/api/services/CartsService.ts +190 -0
  214. package/shared/services/api/services/CashBackQueueService.ts +39 -0
  215. package/shared/services/api/services/CategoriesService.ts +65 -0
  216. package/shared/services/api/services/DefaultService.ts +19 -0
  217. package/shared/services/api/services/FeaturesFlagsService.ts +20 -0
  218. package/shared/services/api/services/IossQueueService.ts +39 -0
  219. package/shared/services/api/services/OrderCallbackService.ts +67 -0
  220. package/shared/services/api/services/OrderEmailsService.ts +40 -0
  221. package/shared/services/api/services/OrderImportService.ts +228 -0
  222. package/shared/services/api/services/OrdersService.ts +183 -0
  223. package/shared/services/api/services/PaymentGatewayService.ts +19 -0
  224. package/shared/services/api/services/PaymentsService.ts +88 -0
  225. package/shared/services/api/services/ProductsService.ts +67 -0
  226. package/shared/services/api/services/ShipmentOrderCallbackService.ts +67 -0
  227. package/shared/services/api/services/ShipmentOrdersService.ts +88 -0
  228. package/shared/services/api/services/ShipmentsService.ts +30 -0
  229. package/shared/services/api/services/ShippingCartService.ts +247 -0
  230. package/shared/services/api/services/ShippingMethodsService.ts +20 -0
  231. package/shared/services/api/services/StoresService.ts +25 -0
  232. package/shared/services/api/services/SyncServiceQueueService.ts +39 -0
  233. package/shared/services/api/services/UpgradeOrdersService.ts +26 -0
  234. package/shared/services/api/services/UsersAuthService.ts +45 -0
  235. package/shared/services/api/services/UsersService.ts +53 -0
  236. package/shared/services/api/services/XeraQueueService.ts +39 -0
  237. package/shared/services/index.ts +0 -0
  238. package/shared/services/payment-method.service.ts +29 -0
  239. package/shared/stores/cartStore.ts +108 -0
  240. package/shared/stores/countriesStore.ts +62 -0
  241. package/shared/stores/formStore.ts +19 -0
  242. package/shared/stores/loadingStore.ts +12 -0
  243. package/shared/stores/userStore.ts +32 -0
  244. package/shared/types/SelectComponent.types.ts +31 -0
  245. package/shared/types/addressForm.ts +27 -0
  246. package/shared/types/cart.ts +208 -0
  247. package/shared/types/category.ts +6 -0
  248. package/shared/types/creditNote.ts +0 -0
  249. package/shared/types/feature-flags.ts +5 -0
  250. package/shared/types/icon.ts +5 -0
  251. package/shared/types/index.d.ts +84 -0
  252. package/shared/types/order.ts +332 -0
  253. package/shared/types/pagination.ts +5 -0
  254. package/shared/types/payments.ts +68 -0
  255. package/shared/types/product.ts +54 -0
  256. package/shared/types/shoppingCart.ts +219 -0
  257. package/shared/types/upgradeCart.ts +226 -0
  258. package/shared/types/user.ts +36 -0
  259. package/shared/utils/app.util.ts +261 -0
  260. package/shared/utils/notifications.util.ts +39 -0
  261. package/shared/utils/user-session.util.ts +171 -0
  262. package/tailwind.config.ts +234 -0
@@ -0,0 +1,41 @@
1
+ import { Environments } from "./environment";
2
+
3
+ export const ROUTES = {
4
+ auth: {
5
+ login: "/auth/login",
6
+ },
7
+ aleta: {
8
+ aletaWithCode: (code: string) => `/aleta?code=${code}`,
9
+ aletaDashboard: `/aleta/dashboard`,
10
+ aletaStatus: `/aleta/kyc-status`,
11
+ aletaFee: `/aleta/kyc-fee`,
12
+ },
13
+ dashboard: {
14
+ products: "/products",
15
+
16
+ // Normal cart
17
+ cart: "/cart",
18
+ checkout: "/shipping-method",
19
+ payment: "/payment",
20
+
21
+ profile: "/profile",
22
+ orders: "/orders",
23
+ ordersDetails: "/order-details",
24
+ wiki: "https://docs.hmall.shop",
25
+ termsAndConditions:
26
+ " https://docs.hmall.shop/legal-docs/terms-and-conditions",
27
+ riskDisclosure: "https://docs.hmall.shop/legal-docs/risk-disclosure-policy",
28
+ privacyPolicy:
29
+ "https://docs.hmall.shop/legal-docs/privacy-and-cookie-policy",
30
+ support: "https://support.hmall.shop/support/home",
31
+ nodeLinkProfile: Environments.NODE_LINK.profileUrl || "",
32
+ nodeLinkDeposit: Environments.NODE_LINK.depositUrl || "",
33
+ },
34
+ // shipping: {
35
+ // orders: "/shipping-orders",
36
+ // methods: "/shipping-method",
37
+ // // payments: "/initiate-delivery/payment-method",
38
+ // // review: "/initiate-delivery/shipping-review",
39
+ // orderDetails: "/shipping-order",
40
+ // },
41
+ };
@@ -0,0 +1,15 @@
1
+ import { ROUTES } from "./routes";
2
+
3
+ export const AuthorizedRoutes = [ROUTES.dashboard.products];
4
+
5
+ export enum SocketMessageType {
6
+ ORDER_PAYMENT_URL = "order-payment-url",
7
+ SHIPPING_ORDER_PAYMENT_URL = "shipping-order-payment-url",
8
+ ORDER_PAYMENT_COMPLETED = "order-payment-completed",
9
+ SHIPMENT_PAYMENT_COMPLETED = "shipping-payment-completed",
10
+ }
11
+
12
+ export enum SocketEventType {
13
+ ORDER_PAYMENT_PROCESS = "order-payment-process",
14
+ SHIPMENT_PAYMENT_PROCESS = "shipment-payment-process",
15
+ }
@@ -0,0 +1,51 @@
1
+ "use client";
2
+
3
+ import React, { createContext, useCallback, useContext, useState } from "react";
4
+
5
+ import { useUserStore } from "../stores/userStore";
6
+
7
+ const HomeProductsWrapperContext = createContext<any>({});
8
+
9
+ export type HomeProductsWrapperProps = {
10
+ children: React.ReactNode;
11
+ isOfferBannerClosed: boolean;
12
+ closeBanner: () => Promise<void>;
13
+ };
14
+
15
+ export function HomeProductsWrapper({
16
+ isOfferBannerClosed,
17
+ closeBanner,
18
+ children,
19
+ }: HomeProductsWrapperProps) {
20
+ //
21
+ const { user } = useUserStore();
22
+
23
+ const [isBannerClosed, setIsBannerClosed] =
24
+ useState<boolean>(isOfferBannerClosed);
25
+
26
+ const proceedCloseBanner = useCallback(() => {
27
+ setIsBannerClosed(true);
28
+ closeBanner();
29
+ }, [closeBanner, setIsBannerClosed]);
30
+
31
+ const MainData: HomeProductsWrapperContextDto = {
32
+ proceedCloseBanner,
33
+ isBannerClosed,
34
+ isSubscriptionActive: user?.activeMembership === true,
35
+ };
36
+
37
+ return (
38
+ <HomeProductsWrapperContext.Provider value={MainData}>
39
+ {children}
40
+ </HomeProductsWrapperContext.Provider>
41
+ );
42
+ }
43
+
44
+ export const useHomeProductsContext = (): HomeProductsWrapperContextDto =>
45
+ useContext(HomeProductsWrapperContext);
46
+
47
+ export type HomeProductsWrapperContextDto = {
48
+ proceedCloseBanner: () => void;
49
+ isBannerClosed: boolean;
50
+ isSubscriptionActive: boolean;
51
+ };
@@ -0,0 +1,289 @@
1
+ "use client";
2
+
3
+ import { usePathname } from "next/navigation";
4
+ import {
5
+ createContext,
6
+ useCallback,
7
+ useContext,
8
+ useEffect,
9
+ useLayoutEffect,
10
+ useRef,
11
+ useState,
12
+ } from "react";
13
+
14
+ import CartDrawerComponent from "../components/CartDrawerComponent";
15
+ import { Footer } from "../components/Footer";
16
+ import { Header } from "../components/Header";
17
+ import { InactiveCartDialog } from "../components/InactiveCartDialog";
18
+ import { SessionDialogComponent } from "../components/SessionDialogComponent";
19
+ import Sidebar from "../components/Sidebar";
20
+ import { CART_STATUS } from "../constants/app";
21
+ import { FeatureCodes } from "../constants/feature-flags";
22
+ import { ROUTES } from "../constants/routes";
23
+ import { useAddressForm } from "../hooks/useAddressForm";
24
+ import { useCart } from "../hooks/useCart";
25
+ import { useCountries } from "../hooks/useCountries";
26
+ import useCustomRouter from "../hooks/useCustomRouter";
27
+ import useDebounce from "../hooks/useDebounce";
28
+ import useOnlineUser from "../hooks/useOnlineUser";
29
+ import { useShippingAddressForm } from "../hooks/useShippingAddressForm";
30
+ import useUserActivity from "../hooks/useUserActivity";
31
+ import { useCartStore } from "../stores/cartStore";
32
+ import { useLoadingStore } from "../stores/loadingStore";
33
+ import { useUserStore } from "../stores/userStore";
34
+ import { IFeatureFlags } from "../types/feature-flags";
35
+ import { deleteToken, getTokenExpiredTime } from "../utils/user-session.util";
36
+ // import { PhoneShippingDialog } from '../components/PhoneShippingDialog';
37
+
38
+ const MainWrapperContext = createContext<any>({});
39
+
40
+ export type MainWrapperProps = {
41
+ children: React.ReactNode;
42
+ featureFlags: { [key: string]: IFeatureFlags };
43
+ handleRefreshToken: () => Promise<any>;
44
+ eligibleForPayableClaim?: boolean | null;
45
+ };
46
+
47
+ export function MainWrapper({
48
+ children,
49
+ featureFlags,
50
+ handleRefreshToken,
51
+ eligibleForPayableClaim,
52
+ }: MainWrapperProps) {
53
+ //
54
+ let tokenTimeInterval: NodeJS.Timeout | undefined = undefined;
55
+ const isRefreshingRef = useRef(false); // Guard against concurrent refresh attempts
56
+ const [isSidebarOpen, setIsSidebarOpen] = useState(false);
57
+ const [isCartDrawerOpen, setIsCartDrawerOpen] = useState(false);
58
+ const [isPageEnabled, setIsPageEnabled] = useState<boolean>(false);
59
+ const { onlineUser } = useOnlineUser();
60
+ const { user, setUser } = useUserStore();
61
+ const {} = useCart();
62
+ const {} = useCountries();
63
+ const {} = useAddressForm();
64
+ const {} = useShippingAddressForm();
65
+ const { cart, cartQty, loading: isCartLoading } = useCartStore();
66
+ const { isUserActiveOnBrowser } = useUserActivity();
67
+ const [isSessionExpired, setIsSessionExpired] = useState<boolean>(false);
68
+ const { setLoading: setAppLoading } = useLoadingStore();
69
+ const pathname = usePathname();
70
+ const { push } = useCustomRouter();
71
+ const [openInvalidDialog, setOpenInvalidDialog] = useState(false);
72
+
73
+ useEffect(() => {
74
+ setIsPageEnabled(true);
75
+ }, [pathname, featureFlags, push, setIsPageEnabled]);
76
+
77
+ const validateSessionTokenExpiry = useDebounce(async () => {
78
+ if (isSessionExpired) return;
79
+ tokenTimeInterval && clearInterval(tokenTimeInterval);
80
+
81
+ const expiryTime = await getTokenExpiredTime(); // get the remaining token time from server
82
+
83
+ // Guard: If no valid expiry time, don't start the interval loop
84
+ if (!expiryTime || expiryTime <= 0) {
85
+ console.warn("No valid token expiry time, skipping session check");
86
+ return;
87
+ }
88
+
89
+ const now = Date.now() / 1000;
90
+ // Guard: If token is already expired, handle it once instead of looping
91
+ if (expiryTime - now <= 0) {
92
+ setIsSessionExpired(true);
93
+ await proceedLogout(false);
94
+ return;
95
+ }
96
+
97
+ tokenTimeInterval = setInterval(async () => {
98
+ const currentTime = Date.now() / 1000;
99
+ const remainingSeconds = expiryTime - currentTime;
100
+
101
+ if (remainingSeconds <= 30) {
102
+ clearInterval(tokenTimeInterval);
103
+ tokenTimeInterval = undefined;
104
+
105
+ try {
106
+ const isRefreshed = await proceedRefreshToken();
107
+
108
+ if (isRefreshed) {
109
+ validateSessionTokenExpiry();
110
+ return;
111
+ }
112
+ } catch (error) {
113
+ console.error("Token refresh failed:", error);
114
+ }
115
+
116
+ setIsSessionExpired(true);
117
+ await proceedLogout(false); //disconnect if the token is already expired
118
+ return;
119
+ }
120
+ }, 1000);
121
+ }, 1000);
122
+
123
+ // const [, setStartSessionCheck] = useState(validateSessionTokenExpiry);
124
+
125
+ const toggleSidebar = useCallback(() => {
126
+ setIsSidebarOpen((prevState) => !prevState);
127
+ }, [setIsSidebarOpen]);
128
+
129
+ const closeSidebar = useCallback(() => {
130
+ setIsSidebarOpen(false);
131
+ }, [setIsSidebarOpen]);
132
+
133
+ // Toggle cart drawer functions
134
+ const openCartDrawer = useCallback(() => {
135
+ if (cart.cartState === CART_STATUS.shopping) {
136
+ setIsCartDrawerOpen(true);
137
+ } else if (cart.cartState === CART_STATUS.checkout) {
138
+ push(ROUTES.dashboard.checkout);
139
+ } else if (cart.cartState === CART_STATUS.payment) {
140
+ push(ROUTES.dashboard.payment);
141
+ }
142
+
143
+ // eslint-disable-next-line react-hooks/exhaustive-deps
144
+ }, [cart]);
145
+
146
+ const closeCartDrawer = useCallback(() => setIsCartDrawerOpen(false), []);
147
+
148
+ const proceedLogout = useCallback(async (autoRedirect: boolean = true) => {
149
+ if (autoRedirect) await deleteToken();
150
+ window.sessionStorage.clear();
151
+ if (autoRedirect) window.open(ROUTES.auth.login, "_self");
152
+ }, []);
153
+
154
+ useLayoutEffect(() => {
155
+ if (onlineUser) {
156
+ setUser(onlineUser);
157
+ }
158
+ }, [onlineUser, setUser]);
159
+
160
+ useEffect(() => {
161
+ return () => {
162
+ setTimeout(() => {
163
+ setAppLoading(false);
164
+ }, 2000);
165
+ };
166
+ }, [pathname, setAppLoading]);
167
+
168
+ useEffect(() => {
169
+ if (isSidebarOpen || isCartDrawerOpen) {
170
+ document.body.classList.add("no-scroll");
171
+ } else {
172
+ document.body.classList.remove("no-scroll");
173
+ }
174
+
175
+ // Cleanup when the component unmounts
176
+ return () => {
177
+ document.body.classList.remove("no-scroll");
178
+ };
179
+ }, [isSidebarOpen, isCartDrawerOpen]);
180
+
181
+ const proceedRefreshToken = useCallback(async () => {
182
+ if (!isUserActiveOnBrowser) return false;
183
+ if (isRefreshingRef.current) return false; // Prevent concurrent refresh attempts
184
+
185
+ isRefreshingRef.current = true;
186
+ try {
187
+ const refreshedUser = await handleRefreshToken();
188
+ if (refreshedUser) {
189
+ setUser(refreshedUser);
190
+ return true;
191
+ }
192
+ return false;
193
+ } catch (error) {
194
+ console.error("Token refresh error:", error);
195
+ return false;
196
+ } finally {
197
+ isRefreshingRef.current = false;
198
+ }
199
+ }, [isUserActiveOnBrowser, setUser, handleRefreshToken]);
200
+
201
+ useEffect(() => {
202
+ validateSessionTokenExpiry();
203
+ // eslint-disable-next-line react-hooks/exhaustive-deps
204
+ }, []);
205
+
206
+ const triggerInvalidDialog = useCallback(() => {
207
+ setOpenInvalidDialog(true);
208
+ }, []);
209
+
210
+ useEffect(() => {
211
+ if (pathname.startsWith("/aleta") && featureFlags) {
212
+ if (
213
+ !featureFlags?.[FeatureCodes.ALETA_PAYMENT] ||
214
+ featureFlags?.[FeatureCodes.ALETA_PAYMENT].isActive !== true
215
+ ) {
216
+ push(ROUTES.dashboard.products);
217
+ }
218
+ }
219
+ }, [pathname, featureFlags, push]);
220
+
221
+ const MainData: MainWrapperContextDto = {
222
+ proceedLogout,
223
+ isSessionExpired,
224
+ featureFlags,
225
+ triggerInvalidDialog,
226
+ eligibleForPayableClaim,
227
+ };
228
+
229
+ return (
230
+ <MainWrapperContext.Provider value={MainData}>
231
+ <InactiveCartDialog
232
+ open={openInvalidDialog}
233
+ handleOpen={setOpenInvalidDialog}
234
+ />
235
+
236
+ <div className={`flex min-h-screen bg-offWhite`}>
237
+ <div
238
+ className={`sidebar
239
+ bg-white border-r-slate-50
240
+ mobileSidebar ${isSidebarOpen ? "open" : ""}
241
+ `}
242
+ >
243
+ <Sidebar
244
+ handleLogout={() => proceedLogout(true)}
245
+ bidNumber={user?.bid}
246
+ onClose={closeSidebar}
247
+ cartProductsCount={cartQty}
248
+ />
249
+ </div>
250
+ <div className="flex-1 overflow-hidden flex flex-col w-auto">
251
+ <Header
252
+ user={user}
253
+ logout={() => proceedLogout(true)}
254
+ onMenuClick={toggleSidebar}
255
+ openCartDrawer={openCartDrawer}
256
+ cartProductsCount={cartQty}
257
+ />
258
+ <div className="grow container mx-auto p-2 md:p-3 pb-44 md:pb-32 lg:pb-32">
259
+ {isPageEnabled && children}
260
+ </div>
261
+ <Footer />
262
+ <CartDrawerComponent
263
+ open={isCartDrawerOpen}
264
+ closeDrawer={closeCartDrawer}
265
+ cartData={cart}
266
+ isCartLoading={isCartLoading}
267
+ />
268
+ {/* <PhoneShippingDialog /> */}
269
+ </div>
270
+ </div>
271
+
272
+ <SessionDialogComponent
273
+ isExpired={isSessionExpired}
274
+ callBackFn={() => proceedLogout()}
275
+ />
276
+ </MainWrapperContext.Provider>
277
+ );
278
+ }
279
+
280
+ export const useMainContext = (): MainWrapperContextDto =>
281
+ useContext(MainWrapperContext);
282
+
283
+ export type MainWrapperContextDto = {
284
+ proceedLogout: (autoRedirect?: boolean) => void;
285
+ isSessionExpired: boolean;
286
+ featureFlags: { [key: string]: IFeatureFlags };
287
+ triggerInvalidDialog: () => void;
288
+ eligibleForPayableClaim: boolean;
289
+ };
@@ -0,0 +1,155 @@
1
+ "use client";
2
+
3
+ import React, {
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useRef,
8
+ useState,
9
+ } from "react";
10
+ import io, { Socket } from "socket.io-client";
11
+
12
+ import { SHIPPING_ORDER_ID_KEY } from "../constants/app";
13
+ import { Environments } from "../constants/environment";
14
+ import { SocketMessageType } from "../constants/socket";
15
+ import useDebounce from "../hooks/useDebounce";
16
+ import { getToken } from "../utils/user-session.util";
17
+
18
+ const WebSocketWrapperContext = createContext<any>({});
19
+
20
+ export type WebSocketWrapperProps = {
21
+ children: React.ReactNode;
22
+ };
23
+
24
+ export function WebSocketWrapper({ children }: WebSocketWrapperProps) {
25
+ //
26
+ let socket = useRef<Socket>(null);
27
+ const [isSocketConnected, setIsSocketConnected] = useState<boolean>(false);
28
+ const [paymentProcessFailed, setPaymentProcessFailed] =
29
+ useState<boolean>(false);
30
+ const [paymentProcessResponse, setPaymentProcessResponse] =
31
+ useState<any>(undefined);
32
+ const [isPaymentCompleted, setIsPaymentCompleted] = useState<boolean>(false);
33
+
34
+ const disconnectSocket = useCallback(() => {
35
+ if (socket.current) {
36
+ socket.current.close();
37
+ }
38
+ }, []);
39
+
40
+ const subscribeEventMessage = useCallback(() => {
41
+ if (!socket.current) return;
42
+
43
+ socket.current.on(SocketMessageType.ORDER_PAYMENT_URL, function (data) {
44
+ if (!data.status) {
45
+ setPaymentProcessFailed(true);
46
+ }
47
+
48
+ setPaymentProcessResponse(data);
49
+ });
50
+ socket.current.on(
51
+ SocketMessageType.SHIPPING_ORDER_PAYMENT_URL,
52
+ function (data) {
53
+ if (!data.status) {
54
+ setPaymentProcessFailed(true);
55
+ }
56
+
57
+ setPaymentProcessResponse(data);
58
+ },
59
+ );
60
+
61
+ socket.current.on(
62
+ SocketMessageType.ORDER_PAYMENT_COMPLETED,
63
+ function (data) {
64
+ if (data) {
65
+ setIsPaymentCompleted(true);
66
+ }
67
+ },
68
+ );
69
+
70
+ socket.current.on(
71
+ SocketMessageType.SHIPMENT_PAYMENT_COMPLETED,
72
+ async function (data) {
73
+ if (data) {
74
+ await setIsPaymentCompleted(true);
75
+ await localStorage?.setItem(SHIPPING_ORDER_ID_KEY, "");
76
+ }
77
+ },
78
+ );
79
+ }, [
80
+ setPaymentProcessFailed,
81
+ setPaymentProcessResponse,
82
+ setIsPaymentCompleted,
83
+ ]);
84
+
85
+ const handleConnect = useDebounce(async () => {
86
+ //TODO validate authorized page
87
+
88
+ if (socket.current) return;
89
+
90
+ socket.current = io(`${Environments.SOCKET.hostname}`, {
91
+ extraHeaders: {
92
+ Authorization: `Bearer ${await getToken()}`,
93
+ },
94
+ });
95
+
96
+ socket.current.on("connect", () => {
97
+ console.log("socket", "connected");
98
+ setIsSocketConnected(true);
99
+
100
+ subscribeEventMessage();
101
+ });
102
+
103
+ socket.current.on("exception", function (data) {
104
+ console.log("event", data);
105
+ setIsSocketConnected(false);
106
+ });
107
+
108
+ socket.current.on("disconnect", function () {
109
+ console.log("Disconnected");
110
+ setIsSocketConnected(false);
111
+ });
112
+ }, 50);
113
+
114
+ const sendEventMessage = useCallback(
115
+ (event: string, msg: string | number) => {
116
+ if (!socket.current) return;
117
+
118
+ setPaymentProcessFailed(false);
119
+
120
+ socket.current.emit(event, msg, (data: any) => {
121
+ console.log("orderInfo", data);
122
+ });
123
+ },
124
+ [setPaymentProcessFailed],
125
+ );
126
+
127
+ const MainData: WebSocketWrapperContextDto = {
128
+ isSocketConnected,
129
+ paymentProcessFailed,
130
+ paymentProcessResponse,
131
+ isPaymentCompleted,
132
+ initConnection: handleConnect,
133
+ sendEventMessage,
134
+ disconnectSocket,
135
+ };
136
+
137
+ return (
138
+ <WebSocketWrapperContext.Provider value={MainData}>
139
+ {children}
140
+ </WebSocketWrapperContext.Provider>
141
+ );
142
+ }
143
+
144
+ export const useWebSocketContext = (): WebSocketWrapperContextDto =>
145
+ useContext(WebSocketWrapperContext);
146
+
147
+ export type WebSocketWrapperContextDto = {
148
+ isSocketConnected: boolean;
149
+ paymentProcessFailed: boolean;
150
+ paymentProcessResponse: any;
151
+ isPaymentCompleted: boolean;
152
+ initConnection: (...args: any) => void;
153
+ sendEventMessage: (event: string, msg: string | number) => void;
154
+ disconnectSocket: () => void;
155
+ };