@mohasinac/appkit 2.7.26 → 2.7.30

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 (363) hide show
  1. package/dist/_internal/client/features/classified/ClassifiedDetailView.js +2 -2
  2. package/dist/_internal/client/features/digital-code/CodeRevealPanel.js +2 -2
  3. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
  4. package/dist/_internal/server/features/checkout/actions.js +42 -35
  5. package/dist/_internal/server/features/classified/adapters.d.ts +21 -0
  6. package/dist/_internal/server/features/classified/adapters.js +24 -0
  7. package/dist/_internal/server/features/classified/index.d.ts +3 -0
  8. package/dist/_internal/server/features/classified/index.js +3 -0
  9. package/dist/_internal/server/features/classified/metadata.d.ts +8 -0
  10. package/dist/_internal/server/features/classified/metadata.js +43 -0
  11. package/dist/_internal/server/features/classified/og.d.ts +30 -0
  12. package/dist/_internal/server/features/classified/og.js +76 -0
  13. package/dist/_internal/server/features/digital-code/adapters.d.ts +21 -0
  14. package/dist/_internal/server/features/digital-code/adapters.js +29 -0
  15. package/dist/_internal/server/features/digital-code/index.d.ts +3 -0
  16. package/dist/_internal/server/features/digital-code/index.js +3 -0
  17. package/dist/_internal/server/features/digital-code/metadata.d.ts +8 -0
  18. package/dist/_internal/server/features/digital-code/metadata.js +36 -0
  19. package/dist/_internal/server/features/digital-code/og.d.ts +27 -0
  20. package/dist/_internal/server/features/digital-code/og.js +77 -0
  21. package/dist/_internal/server/features/live/adapters.d.ts +21 -0
  22. package/dist/_internal/server/features/live/adapters.js +34 -0
  23. package/dist/_internal/server/features/live/index.d.ts +3 -0
  24. package/dist/_internal/server/features/live/index.js +3 -0
  25. package/dist/_internal/server/features/live/metadata.d.ts +8 -0
  26. package/dist/_internal/server/features/live/metadata.js +39 -0
  27. package/dist/_internal/server/features/live/og.d.ts +27 -0
  28. package/dist/_internal/server/features/live/og.js +72 -0
  29. package/dist/_internal/server/features/promotions/actions.js +4 -3
  30. package/dist/_internal/server/jobs/core/onOrderCreate.js +19 -16
  31. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +25 -22
  32. package/dist/_internal/server/jobs/core/onProductWrite.js +48 -58
  33. package/dist/_internal/shared/actions/action-registry.js +310 -8
  34. package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
  35. package/dist/_internal/shared/features/brands/schema.d.ts +5 -5
  36. package/dist/_internal/shared/features/cart/schema.d.ts +18 -18
  37. package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
  38. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +38 -38
  39. package/dist/_internal/shared/features/events/schema.d.ts +8 -8
  40. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  41. package/dist/_internal/shared/features/products/schema.d.ts +42 -42
  42. package/dist/_internal/shared/features/promotions/schema.d.ts +4 -4
  43. package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
  44. package/dist/_internal/shared/media/limits.js +4 -3
  45. package/dist/client.d.ts +2 -0
  46. package/dist/client.js +1 -0
  47. package/dist/configs/next.js +4 -0
  48. package/dist/constants/table-keys.d.ts +6 -0
  49. package/dist/constants/table-keys.js +9 -0
  50. package/dist/core/hooks/useSyncManager.js +9 -8
  51. package/dist/features/about/components/FeesView.js +10 -7
  52. package/dist/features/about/components/HelpPageView.js +10 -1
  53. package/dist/features/about/components/HowAuctionsWorkView.js +10 -1
  54. package/dist/features/about/components/HowCheckoutWorksView.js +10 -1
  55. package/dist/features/about/components/HowOffersWorkView.js +10 -1
  56. package/dist/features/about/components/HowOrdersWorkView.js +10 -1
  57. package/dist/features/about/components/HowPayoutsWorkView.js +10 -1
  58. package/dist/features/about/components/HowPreOrdersWorkView.js +10 -1
  59. package/dist/features/about/components/HowReviewsWorkView.js +10 -1
  60. package/dist/features/about/components/PublicProfileView.js +44 -14
  61. package/dist/features/about/components/SecurityPrivacyView.js +7 -1
  62. package/dist/features/account/components/AddressesIndexListing.js +2 -2
  63. package/dist/features/account/components/NotificationBell.js +27 -16
  64. package/dist/features/account/components/NotificationPreferencesPanel.d.ts +9 -0
  65. package/dist/features/account/components/NotificationPreferencesPanel.js +91 -0
  66. package/dist/features/account/components/UserSidebar.js +4 -4
  67. package/dist/features/account/components/UserSupportView.js +16 -6
  68. package/dist/features/account/components/index.d.ts +2 -0
  69. package/dist/features/account/components/index.js +1 -0
  70. package/dist/features/account/schemas/index.d.ts +4 -4
  71. package/dist/features/account/types/index.d.ts +34 -0
  72. package/dist/features/admin/actions/admin-actions.js +4 -3
  73. package/dist/features/admin/actions/notification-actions.d.ts +24 -22
  74. package/dist/features/admin/actions/notification-actions.js +122 -22
  75. package/dist/features/admin/components/AdminAdEditorView.js +29 -15
  76. package/dist/features/admin/components/AdminAdsView.js +22 -28
  77. package/dist/features/admin/components/AdminAllEventEntriesView.js +6 -1
  78. package/dist/features/admin/components/AdminBidsView.js +10 -3
  79. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  80. package/dist/features/admin/components/AdminBlogView.js +8 -3
  81. package/dist/features/admin/components/AdminBrandsView.js +7 -2
  82. package/dist/features/admin/components/AdminBundleEditorView.js +5 -2
  83. package/dist/features/admin/components/AdminCarouselEditorView.js +6 -3
  84. package/dist/features/admin/components/AdminCarouselView.js +13 -7
  85. package/dist/features/admin/components/AdminCategoriesView.js +8 -3
  86. package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
  87. package/dist/features/admin/components/AdminContactView.js +6 -1
  88. package/dist/features/admin/components/AdminCouponEditorView.js +8 -2
  89. package/dist/features/admin/components/AdminCouponsView.js +6 -1
  90. package/dist/features/admin/components/AdminEmployeeEditorView.js +12 -9
  91. package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
  92. package/dist/features/admin/components/AdminFaqsView.js +7 -2
  93. package/dist/features/admin/components/AdminFeaturesView.js +4 -3
  94. package/dist/features/admin/components/AdminMediaView.js +9 -8
  95. package/dist/features/admin/components/AdminNavigationView.js +10 -17
  96. package/dist/features/admin/components/AdminNewsletterView.js +6 -1
  97. package/dist/features/admin/components/AdminNotificationsView.js +11 -3
  98. package/dist/features/admin/components/AdminOrdersView.js +6 -1
  99. package/dist/features/admin/components/AdminPayoutsView.js +10 -3
  100. package/dist/features/admin/components/AdminPrizeDrawsView.js +10 -3
  101. package/dist/features/admin/components/AdminProductsView.js +18 -12
  102. package/dist/features/admin/components/AdminReviewsView.js +33 -24
  103. package/dist/features/admin/components/AdminScammerEditorView.js +3 -2
  104. package/dist/features/admin/components/AdminScammersView.js +28 -26
  105. package/dist/features/admin/components/AdminSectionsView.js +112 -99
  106. package/dist/features/admin/components/AdminSessionsView.js +10 -3
  107. package/dist/features/admin/components/AdminSidebar.js +4 -4
  108. package/dist/features/admin/components/AdminSiteSettingsView.js +45 -4
  109. package/dist/features/admin/components/AdminStoresView.js +6 -1
  110. package/dist/features/admin/components/AdminSublistingCategoriesView.js +0 -1
  111. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  112. package/dist/features/admin/components/AdminSupportTicketsView.js +32 -34
  113. package/dist/features/admin/components/AdminTeamView.js +6 -1
  114. package/dist/features/admin/components/AdminUserEditorView.js +8 -15
  115. package/dist/features/admin/components/AdminUsersView.js +6 -1
  116. package/dist/features/admin/components/AdminViewCards.js +4 -3
  117. package/dist/features/admin/components/DashboardStats.js +2 -2
  118. package/dist/features/admin/components/DataTable.js +2 -2
  119. package/dist/features/admin/components/sections/adminSectionsTypes.js +4 -3
  120. package/dist/features/admin/repository/chat.repository.js +8 -8
  121. package/dist/features/admin/repository/site-settings.repository.d.ts +1 -0
  122. package/dist/features/admin/repository/site-settings.repository.js +11 -9
  123. package/dist/features/admin/schemas/firestore.d.ts +38 -0
  124. package/dist/features/admin/schemas/firestore.js +15 -0
  125. package/dist/features/auctions/components/AuctionDetailPageView.js +13 -19
  126. package/dist/features/auctions/components/MarketplaceAuctionCard.js +18 -9
  127. package/dist/features/auctions/components/MarketplaceAuctionGrid.js +3 -2
  128. package/dist/features/auctions/components/index.d.ts +0 -1
  129. package/dist/features/auctions/components/index.js +0 -1
  130. package/dist/features/auctions/schemas/index.d.ts +14 -14
  131. package/dist/features/auth/actions/realtime-token-actions.js +19 -4
  132. package/dist/features/auth/components/RegisterForm.js +8 -4
  133. package/dist/features/auth/consent-otp.d.ts +2 -0
  134. package/dist/features/auth/consent-otp.js +8 -0
  135. package/dist/features/auth/schemas/firestore.d.ts +2 -0
  136. package/dist/features/auth/schemas/firestore.js +18 -25
  137. package/dist/features/auth/schemas/index.d.ts +2 -2
  138. package/dist/features/before-after/api/[id]/route.js +8 -6
  139. package/dist/features/before-after/schemas/index.d.ts +2 -2
  140. package/dist/features/blog/actions/blog-actions.d.ts +3 -3
  141. package/dist/features/blog/components/BlogIndexListing.js +21 -2
  142. package/dist/features/blog/components/BlogPostForm.js +11 -5
  143. package/dist/features/blog/components/BlogPostView.js +10 -2
  144. package/dist/features/blog/schemas/index.d.ts +18 -18
  145. package/dist/features/cart/components/CartDrawer.js +3 -2
  146. package/dist/features/cart/repository/cart.repository.js +4 -3
  147. package/dist/features/cart/utils/pending-ops.d.ts +1 -1
  148. package/dist/features/categories/api/[id]/route.js +4 -3
  149. package/dist/features/categories/api/route.js +4 -3
  150. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  151. package/dist/features/categories/components/BrandDetailTabs.js +2 -2
  152. package/dist/features/categories/components/BundleCollage.d.ts +8 -0
  153. package/dist/features/categories/components/BundleCollage.js +50 -0
  154. package/dist/features/categories/components/BundleDetailView.js +2 -10
  155. package/dist/features/categories/components/BundleItemsPicker.js +20 -13
  156. package/dist/features/categories/components/CategoriesIndexListing.js +28 -9
  157. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  158. package/dist/features/categories/components/CategoryDetailTabs.js +2 -2
  159. package/dist/features/categories/components/CategoryProductsListing.js +52 -13
  160. package/dist/features/categories/components/index.d.ts +2 -0
  161. package/dist/features/categories/components/index.js +1 -0
  162. package/dist/features/categories/schemas/index.d.ts +6 -6
  163. package/dist/features/checkout/actions/checkout-actions.js +4 -3
  164. package/dist/features/checkout/hooks/usePaymentCheckout.js +31 -22
  165. package/dist/features/classified/components/ClassifiedFilters.d.ts +8 -0
  166. package/dist/features/classified/components/ClassifiedFilters.js +19 -0
  167. package/dist/features/classified/components/ClassifiedIndexListing.d.ts +4 -0
  168. package/dist/features/classified/components/ClassifiedIndexListing.js +110 -0
  169. package/dist/features/classified/components/ClassifiedListView.d.ts +6 -0
  170. package/dist/features/classified/components/ClassifiedListView.js +43 -0
  171. package/dist/features/classified/components/index.d.ts +6 -0
  172. package/dist/features/classified/components/index.js +3 -0
  173. package/dist/features/collections/api/[slug]/route.js +16 -12
  174. package/dist/features/collections/schemas/index.d.ts +5 -5
  175. package/dist/features/consultation/api/[id]/route.js +12 -9
  176. package/dist/features/consultation/components/ConsultationForm.js +3 -2
  177. package/dist/features/consultation/schemas/index.d.ts +6 -6
  178. package/dist/features/contact/email.js +77 -116
  179. package/dist/features/copilot/components/AdminCopilotView.js +12 -2
  180. package/dist/features/corporate/api/[id]/route.js +12 -9
  181. package/dist/features/corporate/components/CorporateInquiryForm.js +2 -1
  182. package/dist/features/digital-codes/components/DigitalCodeFilters.d.ts +8 -0
  183. package/dist/features/digital-codes/components/DigitalCodeFilters.js +16 -0
  184. package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +4 -0
  185. package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +107 -0
  186. package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +6 -0
  187. package/dist/features/digital-codes/components/DigitalCodesListView.js +37 -0
  188. package/dist/features/digital-codes/components/index.d.ts +6 -0
  189. package/dist/features/digital-codes/components/index.js +3 -0
  190. package/dist/features/events/actions/event-actions.js +4 -3
  191. package/dist/features/events/components/AdminEventEditorView.js +3 -2
  192. package/dist/features/events/components/AdminEventsView.js +4 -2
  193. package/dist/features/events/components/EventPollWidget.js +5 -5
  194. package/dist/features/events/components/EventsIndexListing.js +2 -2
  195. package/dist/features/events/schemas/index.d.ts +20 -20
  196. package/dist/features/faq/actions/faq-actions.d.ts +8 -8
  197. package/dist/features/faq/components/ContactCTA.js +3 -1
  198. package/dist/features/faq/components/FAQAccordion.js +1 -1
  199. package/dist/features/faq/schemas/index.d.ts +4 -4
  200. package/dist/features/filters/AsyncFacetSection.d.ts +18 -0
  201. package/dist/features/filters/AsyncFacetSection.js +65 -0
  202. package/dist/features/forms/Checkbox.js +2 -2
  203. package/dist/features/forms/Input.js +2 -2
  204. package/dist/features/forms/Radio.js +5 -5
  205. package/dist/features/forms/Select.js +2 -2
  206. package/dist/features/forms/Slider.js +2 -2
  207. package/dist/features/forms/Textarea.js +2 -2
  208. package/dist/features/homepage/api/[id]/route.js +4 -3
  209. package/dist/features/homepage/api/sections/[id]/route.js +12 -9
  210. package/dist/features/homepage/components/AdSlot.js +2 -1
  211. package/dist/features/homepage/components/AdvertisementBanner.js +5 -3
  212. package/dist/features/homepage/components/BrandsSection.js +2 -2
  213. package/dist/features/homepage/components/CTABannerSection.js +2 -2
  214. package/dist/features/homepage/components/CharacterHotspot.d.ts +2 -2
  215. package/dist/features/homepage/components/CharacterHotspot.js +119 -157
  216. package/dist/features/homepage/components/CharacterHotspotForm.js +25 -20
  217. package/dist/features/homepage/components/CustomCardsSection.js +8 -6
  218. package/dist/features/homepage/components/FeaturedProductsSection.js +2 -2
  219. package/dist/features/homepage/components/GoogleReviewsSection.js +3 -3
  220. package/dist/features/homepage/components/HeroBanner.js +5 -4
  221. package/dist/features/homepage/components/HeroCarousel.js +13 -26
  222. package/dist/features/homepage/components/HowItWorksSection.js +4 -2
  223. package/dist/features/homepage/components/SocialFeedSection.js +2 -2
  224. package/dist/features/homepage/components/SocialPostCard.js +3 -2
  225. package/dist/features/homepage/lib/social-feed-fetcher.js +6 -3
  226. package/dist/features/layout/AppLayoutShell.d.ts +8 -2
  227. package/dist/features/layout/AppLayoutShell.js +55 -84
  228. package/dist/features/layout/BottomActions.js +43 -48
  229. package/dist/features/layout/NavbarWithSettings.d.ts +6 -0
  230. package/dist/features/layout/NavbarWithSettings.js +11 -0
  231. package/dist/features/live/components/LiveItemFilters.d.ts +8 -0
  232. package/dist/features/live/components/LiveItemFilters.js +23 -0
  233. package/dist/features/live/components/LiveItemsIndexListing.d.ts +4 -0
  234. package/dist/features/live/components/LiveItemsIndexListing.js +108 -0
  235. package/dist/features/live/components/LiveItemsListView.d.ts +6 -0
  236. package/dist/features/live/components/LiveItemsListView.js +43 -0
  237. package/dist/features/live/components/index.d.ts +6 -0
  238. package/dist/features/live/components/index.js +3 -0
  239. package/dist/features/media/MediaLightbox.js +8 -14
  240. package/dist/features/media/types/index.d.ts +2 -2
  241. package/dist/features/media/upload/CameraCapture.js +2 -2
  242. package/dist/features/media/upload/ImageUpload.js +4 -2
  243. package/dist/features/media/upload/MediaUploadField.js +24 -8
  244. package/dist/features/media/upload/MediaUploadList.js +1 -1
  245. package/dist/features/orders/actions/order-actions.js +4 -3
  246. package/dist/features/orders/components/MarketplaceOrderCard.js +2 -1
  247. package/dist/features/orders/components/OrderFilters.js +12 -9
  248. package/dist/features/orders/schemas/index.d.ts +6 -6
  249. package/dist/features/pre-orders/api/[id]/route.js +12 -9
  250. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +5 -4
  251. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +27 -20
  252. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +17 -52
  253. package/dist/features/pre-orders/components/PreorderCard.d.ts +0 -9
  254. package/dist/features/pre-orders/components/PreorderCard.js +2 -13
  255. package/dist/features/pre-orders/components/index.d.ts +1 -1
  256. package/dist/features/pre-orders/components/index.js +1 -1
  257. package/dist/features/pre-orders/schemas/index.d.ts +10 -10
  258. package/dist/features/products/api/[id]/route.js +9 -7
  259. package/dist/features/products/api/route.js +1 -1
  260. package/dist/features/products/columns/productTableColumns.js +2 -2
  261. package/dist/features/products/components/AuctionsIndexListing.js +15 -51
  262. package/dist/features/products/components/BidHistory.js +2 -2
  263. package/dist/features/products/components/CompareOverlay.js +3 -2
  264. package/dist/features/products/components/MakeOfferButton.js +20 -14
  265. package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -1
  266. package/dist/features/products/components/PrizeDrawCollage.d.ts +1 -15
  267. package/dist/features/products/components/PrizeDrawCollage.js +54 -14
  268. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
  269. package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
  270. package/dist/features/products/components/PrizeDrawItemsEditor.js +9 -7
  271. package/dist/features/products/components/PrizeDrawsIndexListing.js +18 -51
  272. package/dist/features/products/components/PrizeRevealModal.js +24 -14
  273. package/dist/features/products/components/ProductDetailPageView.js +2 -1
  274. package/dist/features/products/components/ProductFilters.js +8 -6
  275. package/dist/features/products/components/ProductForm.js +12 -10
  276. package/dist/features/products/components/ProductGalleryClient.js +2 -1
  277. package/dist/features/products/components/ProductGrid.js +11 -14
  278. package/dist/features/products/components/ProductsIndexListing.js +16 -52
  279. package/dist/features/products/components/ProductsIndexPageView.js +2 -2
  280. package/dist/features/products/schemas/index.d.ts +56 -56
  281. package/dist/features/products/schemas/product-features.validators.d.ts +14 -14
  282. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  283. package/dist/features/promotions/schemas/index.d.ts +6 -6
  284. package/dist/features/reviews/actions/review-actions.js +6 -5
  285. package/dist/features/reviews/api/[id]/route.js +4 -3
  286. package/dist/features/reviews/components/ReviewDetailPageView.js +2 -2
  287. package/dist/features/reviews/components/ReviewDetailShell.js +6 -3
  288. package/dist/features/reviews/components/ReviewsIndexListing.js +2 -2
  289. package/dist/features/reviews/schemas/index.d.ts +6 -6
  290. package/dist/features/scams/components/ScamProfileView.js +19 -10
  291. package/dist/features/scams/components/ScamRegistryView.js +2 -1
  292. package/dist/features/search/components/Search.d.ts +1 -1
  293. package/dist/features/search/components/Search.js +56 -49
  294. package/dist/features/search/schemas/index.d.ts +5 -5
  295. package/dist/features/seller/actions/offer-actions.js +11 -9
  296. package/dist/features/seller/components/SellerAddressesView.js +6 -4
  297. package/dist/features/seller/components/SellerBidsView.js +2 -2
  298. package/dist/features/seller/components/SellerCouponsView.js +1 -1
  299. package/dist/features/seller/components/SellerOffersPanel.js +6 -1
  300. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  301. package/dist/features/seller/components/SellerPayoutSettingsView.js +3 -3
  302. package/dist/features/seller/components/SellerPreOrdersView.js +5 -4
  303. package/dist/features/seller/components/SellerPrizeDrawsView.js +5 -4
  304. package/dist/features/seller/components/SellerProductShell.d.ts +19 -1
  305. package/dist/features/seller/components/SellerProductShell.js +75 -9
  306. package/dist/features/seller/components/SellerProductsView.js +10 -11
  307. package/dist/features/seller/components/SellerShippingView.js +3 -3
  308. package/dist/features/seller/components/SellerSidebar.js +10 -7
  309. package/dist/features/seller/components/SellerStorefrontView.js +3 -3
  310. package/dist/features/seller/schemas/index.d.ts +4 -4
  311. package/dist/features/shell/FormShell.js +2 -1
  312. package/dist/features/shell/QuickFormDrawer.js +4 -3
  313. package/dist/features/shell/StepForm.js +4 -3
  314. package/dist/features/stores/actions/store-query-actions.js +4 -3
  315. package/dist/features/stores/components/StoreAboutView.js +2 -1
  316. package/dist/features/stores/components/StoreAuctionsListing.js +43 -64
  317. package/dist/features/stores/components/StoreDetailLayoutView.js +2 -2
  318. package/dist/features/stores/components/StorePreOrdersListing.js +33 -57
  319. package/dist/features/stores/components/StoreProductsListing.js +16 -49
  320. package/dist/features/stores/components/StoresIndexListing.js +2 -2
  321. package/dist/features/stores/schemas/index.d.ts +2 -2
  322. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +8 -7
  323. package/dist/features/wishlist/hooks/useGuestWishlist.d.ts +5 -5
  324. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +6 -6
  325. package/dist/features/wishlist/utils/guest-wishlist.d.ts +5 -5
  326. package/dist/index.d.ts +21 -3
  327. package/dist/index.js +1737 -1725
  328. package/dist/monitoring/server-logger.js +5 -9
  329. package/dist/next/api/apiHandler.js +56 -37
  330. package/dist/next/components/GlobalError.js +2 -1
  331. package/dist/next/routing/route-map.d.ts +27 -0
  332. package/dist/next/routing/route-map.js +10 -0
  333. package/dist/react/ErrorBoundary.js +2 -1
  334. package/dist/react/contexts/SessionContext.js +38 -28
  335. package/dist/react/hooks/useCamera.d.ts +1 -1
  336. package/dist/react/hooks/useCamera.js +8 -7
  337. package/dist/seed/site-settings-seed-data.js +6 -0
  338. package/dist/server-entry.d.ts +3 -0
  339. package/dist/server-entry.js +3 -0
  340. package/dist/server.d.ts +12 -0
  341. package/dist/server.js +15 -0
  342. package/dist/tailwind-utilities.css +1 -1
  343. package/dist/ui/DataTable.js +28 -7
  344. package/dist/ui/components/Button.d.ts +8 -1
  345. package/dist/ui/components/Button.js +47 -8
  346. package/dist/ui/components/CollageLightbox.d.ts +18 -0
  347. package/dist/ui/components/CollageLightbox.js +41 -0
  348. package/dist/ui/components/Dropdown.js +7 -6
  349. package/dist/ui/components/ImageLightbox.d.ts +7 -1
  350. package/dist/ui/components/ImageLightbox.js +15 -5
  351. package/dist/ui/components/PageLoader.js +4 -3
  352. package/dist/ui/components/PaginatedMultiSelect.d.ts +28 -0
  353. package/dist/ui/components/PaginatedMultiSelect.js +141 -0
  354. package/dist/ui/components/PaginatedMultiSelect.style.css +272 -0
  355. package/dist/ui/components/Semantic.js +1 -1
  356. package/dist/ui/components/SideDrawer.js +17 -15
  357. package/dist/ui/components/index.style.css +1 -0
  358. package/dist/ui/index.d.ts +2 -0
  359. package/dist/ui/index.js +1 -0
  360. package/dist/utils/number.formatter.js +5 -13
  361. package/dist/utils/string.formatter.js +21 -18
  362. package/dist/validation/schemas.d.ts +2 -2
  363. package/package.json +2 -2
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import Link from "next/link";
3
3
  import { categoriesRepository, productRepository, } from "../../../repositories";
4
4
  import { ROUTES } from "../../../next";
5
- import { Container, Main, Section } from "../../../ui";
5
+ import { Container, Heading, Main, Section, Text } from "../../../ui";
6
6
  import { CategoryDetailTabs } from "./CategoryDetailTabs";
7
7
  export async function CategoryDetailPageView({ slug }) {
8
8
  const category = await categoriesRepository
@@ -68,7 +68,7 @@ export async function CategoryDetailPageView({ slug }) {
68
68
  const totalCount = productCount + auctionCount + preOrderCount + prizeDrawCount + bundleCount;
69
69
  const coverImage = category?.display?.coverImage;
70
70
  const hasCover = Boolean(coverImage);
71
- return (_jsxs(Main, { children: [_jsxs("section", { className: `relative overflow-hidden ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover", style: { backgroundImage: `url(${coverImage})` } }), _jsx("div", { className: "absolute inset-0 bg-black/55" })] })), _jsxs("div", { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs("nav", { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Home" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORIES), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Categories" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx("span", { className: hasCover ? "text-white font-medium" : "text-zinc-900 dark:text-zinc-100 font-medium", children: category?.name ?? slug })] }), _jsx("h1", { className: `text-3xl md:text-4xl font-bold mb-2 ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: category?.name ?? slug }), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx("p", { className: `text-base max-w-2xl mb-4 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: category.description })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [productCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), auctionCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] }))] })] })] }), childCategories.length > 0 && (_jsx("section", { className: "border-b border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 py-3", children: _jsx("div", { className: "flex gap-2.5 overflow-x-auto pb-1", style: { scrollbarWidth: "none" }, children: childCategories.map((child) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(child.slug)), className: "flex-shrink-0 flex items-center gap-1.5 rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 px-4 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:border-primary hover:text-primary transition-colors whitespace-nowrap", children: [child.display?.icon && (_jsx("span", { className: "text-base leading-none", children: child.display.icon })), child.name, (child.metrics?.productCount ?? 0) > 0 && (_jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-500", children: (child.metrics?.productCount ?? 0).toLocaleString() }))] }, child.id))) }) }) })), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: _jsx(CategoryDetailTabs, { categorySlug: slug, categoryId: category?.id, initialProductsData: productsResult ?? undefined, initialBundles: bundlesResult ?? [], counts: {
71
+ return (_jsxs(Main, { children: [_jsxs(Section, { className: `relative overflow-hidden ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover", style: { backgroundImage: `url(${coverImage})` } }), _jsx("div", { className: "absolute inset-0 bg-black/55" })] })), _jsxs("div", { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs("nav", { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Home" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORIES), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Categories" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx("span", { className: hasCover ? "text-white font-medium" : "text-zinc-900 dark:text-zinc-100 font-medium", children: category?.name ?? slug })] }), _jsx(Heading, { level: 1, className: `text-3xl md:text-4xl font-bold mb-2 ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: category?.name ?? slug }), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx(Text, { className: `text-base max-w-2xl mb-4 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: category.description })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [productCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), auctionCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] }))] })] })] }), childCategories.length > 0 && (_jsx(Section, { className: "border-b border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 py-3", children: _jsx("div", { className: "flex gap-2.5 overflow-x-auto pb-1", style: { scrollbarWidth: "none" }, children: childCategories.map((child) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(child.slug)), className: "flex-shrink-0 flex items-center gap-1.5 rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 px-4 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:border-primary hover:text-primary transition-colors whitespace-nowrap", children: [child.display?.icon && (_jsx("span", { className: "text-base leading-none", children: child.display.icon })), child.name, (child.metrics?.productCount ?? 0) > 0 && (_jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-500", children: (child.metrics?.productCount ?? 0).toLocaleString() }))] }, child.id))) }) }) })), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: _jsx(CategoryDetailTabs, { categorySlug: slug, categoryId: category?.id, initialProductsData: productsResult ?? undefined, initialBundles: bundlesResult ?? [], counts: {
72
72
  products: productCount,
73
73
  auctions: auctionCount,
74
74
  preOrders: preOrderCount,
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
4
  import { CategoryProductsListing } from "./CategoryProductsListing";
5
5
  import { AuctionsIndexListing } from "../../products/components/AuctionsIndexListing";
@@ -30,7 +30,7 @@ export function CategoryDetailTabs({ categorySlug, categoryId, initialProductsDa
30
30
  return undefined;
31
31
  }
32
32
  };
33
- return (_jsxs("div", { children: [_jsx("div", { className: "flex border-b border-zinc-200 dark:border-slate-700 mb-6 overflow-x-auto", children: CATEGORY_PAGE_TABS.map((t) => (_jsx("button", { type: "button", onClick: () => setActiveTab(t.id), className: `px-5 py-2.5 text-sm font-medium whitespace-nowrap transition-colors -mb-px border-b-2 ${activeTab === t.id
33
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex border-b border-zinc-200 dark:border-slate-700 mb-6 overflow-x-auto", children: CATEGORY_PAGE_TABS.map((t) => (_jsx("button", { type: "button", onClick: () => setActiveTab(t.id), className: `px-5 py-2.5 text-sm font-medium whitespace-nowrap transition-colors -mb-px border-b-2 ${activeTab === t.id
34
34
  ? "border-primary text-primary"
35
35
  : "border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"}`, children: tabLabel(t.label, countFor(t.id)) }, t.id))) }), activeTab === "products" && (_jsx(CategoryProductsListing, { categorySlug: categorySlug, categoryId: categoryId, initialData: initialProductsData })), activeTab === "auctions" && (_jsx(AuctionsIndexListing, { categorySlug: categorySlug })), activeTab === "pre-orders" && (_jsx(PreOrdersIndexListing, { categorySlug: categorySlug })), activeTab === "prize-draws" && (_jsx(PrizeDrawsIndexListing, { categorySlug: categorySlug })), activeTab === "bundles" && (_jsx(CategoryBundlesListing, { initialBundles: initialBundles }))] }));
36
36
  }
@@ -1,19 +1,27 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback } from "react";
4
- import { Search, SlidersHorizontal, LayoutGrid, List, X } from "lucide-react";
5
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
+ import { usePendingTable } from "../../../react/hooks/usePendingTable";
6
6
  import { useProducts } from "../../products/hooks/useProducts";
7
- import { Pagination, SortDropdown, useToast, LoginRequiredModal } from "../../../ui";
7
+ import { Pagination, useToast, ListingToolbar, FilterDrawer, LoginRequiredModal, } from "../../../ui";
8
8
  import { useAuthGate } from "../../../react/hooks/useAuthGate";
9
9
  import { ACTION_ID } from "../../products/constants/action-defs";
10
10
  import { ROUTES } from "../../../next";
11
11
  import { ProductGrid } from "../../products/components/ProductGrid";
12
- import { ProductFilters } from "../../products/components/ProductFilters";
13
- import { PRODUCT_PUBLIC_SORT_OPTIONS } from "../../products/components/ProductFilters";
12
+ import { ProductFilters, PRODUCT_PUBLIC_SORT_OPTIONS } from "../../products/components/ProductFilters";
14
13
  import { useGuestCart } from "../../cart/hooks/useGuestCart";
15
14
  import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
16
15
  import { pushCartOp, pushWishlistOp } from "../../cart/utils/pending-ops";
16
+ const FILTER_KEYS = ["condition", "brand", "minPrice", "maxPrice"];
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ function renderCategoryProductGrid(props) {
19
+ const { isLoading, products, view, getProductHref, onWishlistToggle, wishlistedIds, onAddToCart } = props;
20
+ if (isLoading) {
21
+ return (_jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-6", children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" })] })] }, i))) }));
22
+ }
23
+ return (_jsx(ProductGrid, { products: products, getProductHref: getProductHref, view: view, emptyLabel: "No products found in this category.", onWishlistToggle: onWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: onAddToCart }));
24
+ }
17
25
  export function CategoryProductsListing({ categorySlug, categoryId, brandName, initialData, }) {
18
26
  const table = useUrlTable({ defaults: { pageSize: "24", sort: "-createdAt" } });
19
27
  const { showToast } = useToast();
@@ -24,6 +32,7 @@ export function CategoryProductsListing({ categorySlug, categoryId, brandName, i
24
32
  const localCart = useGuestCart();
25
33
  const localWishlist = useGuestWishlist();
26
34
  const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "product").map((i) => i.itemId));
35
+ const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onFilterReset } = usePendingTable(table, FILTER_KEYS);
27
36
  const params = {
28
37
  q: table.get("q") || undefined,
29
38
  category: categoryId || undefined,
@@ -36,11 +45,32 @@ export function CategoryProductsListing({ categorySlug, categoryId, brandName, i
36
45
  perPage: table.getNumber("pageSize", 24),
37
46
  listingType: "standard",
38
47
  };
39
- const { products, total, totalPages, page, isLoading } = useProducts(params, { initialData });
48
+ const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
40
49
  const commitSearch = useCallback(() => {
41
50
  table.set("q", searchInput.trim());
42
51
  table.setPage(1);
43
52
  }, [searchInput, table]);
53
+ const openFilters = useCallback(() => {
54
+ onFilterReset();
55
+ setFilterOpen(true);
56
+ }, [onFilterReset]);
57
+ const applyFilters = useCallback(() => {
58
+ onFilterApply();
59
+ setFilterOpen(false);
60
+ }, [onFilterApply]);
61
+ const handleViewToggle = (next) => {
62
+ setView(next);
63
+ table.set("view", next);
64
+ };
65
+ const resetAll = useCallback(() => {
66
+ table.setMany({ q: "", sort: "" });
67
+ onFilterClear();
68
+ setSearchInput("");
69
+ }, [table, onFilterClear]);
70
+ const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
71
+ const hasActiveState = !!table.get("q") ||
72
+ table.get("sort") !== "-createdAt" ||
73
+ activeFilterCount > 0;
44
74
  const handleWishlistToggle = useCallback((productId) => {
45
75
  const isWishlisted = wishlistedIds.has(productId);
46
76
  requireAuth(isWishlisted ? ACTION_ID.REMOVE_FROM_WISHLIST : ACTION_ID.ADD_TO_WISHLIST, () => {
@@ -62,13 +92,22 @@ export function CategoryProductsListing({ categorySlug, categoryId, brandName, i
62
92
  productImage: product.mainImage,
63
93
  price: product.price,
64
94
  });
65
- pushCartOp({ op: "add", productId: product.id, quantity: 1, productTitle: product.title, productImage: product.mainImage, price: product.price });
95
+ pushCartOp({
96
+ op: "add",
97
+ productId: product.id,
98
+ quantity: 1,
99
+ productTitle: product.title,
100
+ productImage: product.mainImage,
101
+ price: product.price,
102
+ });
66
103
  showToast("Added to cart", "success");
67
104
  }, [localCart, showToast]);
68
- const handleViewToggle = (next) => {
69
- setView(next);
70
- table.set("view", next);
71
- };
72
- const closeFilters = () => setFilterOpen(false);
73
- return (_jsxs("div", { className: "min-h-[200px]", children: [_jsx("div", { className: "sticky top-[var(--header-height,0px)] z-20 border-b border-zinc-200 dark:border-slate-700 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm py-2.5 px-4", children: _jsxs("div", { className: "flex items-center gap-2.5 max-w-full", children: [_jsxs("button", { type: "button", onClick: () => setFilterOpen(true), className: "flex shrink-0 items-center gap-2 rounded-lg border border-zinc-300 dark:border-slate-600 px-3.5 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:bg-zinc-50 dark:hover:bg-slate-800 transition-colors", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsx("span", { className: "hidden sm:inline", children: "Filters" })] }), _jsxs("div", { className: "flex flex-1 items-center overflow-hidden rounded-lg border border-zinc-300 dark:border-slate-600 bg-white dark:bg-slate-900", children: [_jsx("input", { type: "text", value: searchInput, onChange: (e) => setSearchInput(e.target.value), onKeyDown: (e) => e.key === "Enter" && commitSearch(), placeholder: `Search in ${categorySlug.replace(/-/g, " ")}...`, className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none" }), _jsx("button", { type: "button", onClick: commitSearch, className: "flex shrink-0 items-center justify-center px-3 py-2 text-zinc-400 hover:text-primary dark:hover:text-primary-400 transition-colors", "aria-label": "Search", children: _jsx(Search, { className: "h-4 w-4" }) })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5 text-sm text-zinc-500 dark:text-zinc-400", children: [_jsx("span", { className: "hidden md:inline whitespace-nowrap", children: "Sort by" }), _jsx(SortDropdown, { value: table.get("sort") || "-createdAt", onChange: (v) => { table.set("sort", v); }, options: PRODUCT_PUBLIC_SORT_OPTIONS })] }), _jsxs("div", { className: "flex shrink-0 items-center rounded-lg border border-zinc-300 dark:border-slate-600 overflow-hidden", children: [_jsx("button", { type: "button", onClick: () => handleViewToggle("card"), "aria-label": "Grid view", className: `p-2 transition-colors ${view === "card" ? "bg-primary text-white" : "text-zinc-500 hover:bg-zinc-50 dark:hover:bg-slate-800 dark:text-zinc-400"}`, children: _jsx(LayoutGrid, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => handleViewToggle("list"), "aria-label": "List view", className: `p-2 transition-colors ${view === "list" ? "bg-primary text-white" : "text-zinc-500 hover:bg-zinc-50 dark:hover:bg-slate-800 dark:text-zinc-400"}`, children: _jsx(List, { className: "h-4 w-4" }) })] })] }) }), _jsxs("div", { className: "py-6", children: [isLoading ? (_jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-6", children: Array.from({ length: 8 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: view, emptyLabel: "No products found in this category.", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart })), totalPages > 1 && (_jsx("div", { className: "mt-8 flex justify-center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }))] }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: closeFilters }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsxs("span", { className: "flex items-center gap-2 text-base font-semibold text-zinc-900 dark:text-zinc-100", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), "Filters"] }), _jsx("button", { type: "button", onClick: closeFilters, "aria-label": "Close filters", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(ProductFilters, { table: table, currencyPrefix: "\u20B9" }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsx("button", { type: "button", onClick: closeFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors", children: "Apply filters" }) })] })] })), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
105
+ return (_jsxs("div", { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: `Search in ${categorySlug.replace(/-/g, " ")}...`, onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "-createdAt", sortOptions: PRODUCT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => {
106
+ table.set("sort", v);
107
+ }, view: view === "card" ? "grid" : "list", onViewChange: (v) => handleViewToggle(v === "grid" ? "card" : "list"), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: renderCategoryProductGrid({
108
+ isLoading, products: products, view,
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)),
111
+ onWishlistToggle: handleWishlistToggle, wishlistedIds, onAddToCart: handleAddToCart,
112
+ }) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9" }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
74
113
  }
@@ -30,6 +30,8 @@ export { BundleBuyNowCta } from "./BundleBuyNowCta";
30
30
  export type { BundleBuyNowCtaProps } from "./BundleBuyNowCta";
31
31
  export { BundleDynamicRuleEditor } from "./BundleDynamicRuleEditor";
32
32
  export type { BundleDynamicRuleEditorProps } from "./BundleDynamicRuleEditor";
33
+ export { BundleCollage } from "./BundleCollage";
34
+ export type { BundleCollageProps } from "./BundleCollage";
33
35
  export { BundleItemsPicker, defaultBundleItemsFetch, } from "./BundleItemsPicker";
34
36
  export type { BundleItemsPickerProps, BundleItemSearchResult, } from "./BundleItemsPicker";
35
37
  export { CategoryBundlesListing } from "./CategoryBundlesListing";
@@ -17,6 +17,7 @@ export { CategoryDetailTabs } from "./CategoryDetailTabs";
17
17
  export { BundleBuyNowCta } from "./BundleBuyNowCta";
18
18
  // S-SBUNI-5 2026-05-13 — admin dynamic-rule editor (paired with BundleItemsPicker).
19
19
  export { BundleDynamicRuleEditor } from "./BundleDynamicRuleEditor";
20
+ export { BundleCollage } from "./BundleCollage";
20
21
  // S-SBUNI-4 2026-05-13 — multi-select product picker for bundle editor.
21
22
  export { BundleItemsPicker, defaultBundleItemsFetch, } from "./BundleItemsPicker";
22
23
  export { CategoryBundlesListing } from "./CategoryBundlesListing";
@@ -115,13 +115,14 @@ export declare const categoryItemSchema: z.ZodObject<{
115
115
  updatedAt: z.ZodOptional<z.ZodString>;
116
116
  }, "strip", z.ZodTypeAny, {
117
117
  id: string;
118
- name: string;
119
118
  slug: string;
119
+ name: string;
120
120
  tier: number;
121
121
  createdAt?: string | undefined;
122
122
  updatedAt?: string | undefined;
123
123
  path?: string | undefined;
124
124
  type?: "category" | "brand" | "collection" | "concern" | undefined;
125
+ description?: string | undefined;
125
126
  display?: {
126
127
  icon?: string | undefined;
127
128
  showInMenu?: boolean | undefined;
@@ -129,7 +130,6 @@ export declare const categoryItemSchema: z.ZodObject<{
129
130
  } | undefined;
130
131
  order?: number | undefined;
131
132
  isFeatured?: boolean | undefined;
132
- description?: string | undefined;
133
133
  seo?: {
134
134
  title?: string | undefined;
135
135
  description?: string | undefined;
@@ -148,13 +148,14 @@ export declare const categoryItemSchema: z.ZodObject<{
148
148
  } | undefined;
149
149
  }, {
150
150
  id: string;
151
- name: string;
152
151
  slug: string;
152
+ name: string;
153
153
  tier: number;
154
154
  createdAt?: string | undefined;
155
155
  updatedAt?: string | undefined;
156
156
  path?: string | undefined;
157
157
  type?: "category" | "brand" | "collection" | "concern" | undefined;
158
+ description?: string | undefined;
158
159
  display?: {
159
160
  icon?: string | undefined;
160
161
  showInMenu?: boolean | undefined;
@@ -162,7 +163,6 @@ export declare const categoryItemSchema: z.ZodObject<{
162
163
  } | undefined;
163
164
  order?: number | undefined;
164
165
  isFeatured?: boolean | undefined;
165
- description?: string | undefined;
166
166
  seo?: {
167
167
  title?: string | undefined;
168
168
  description?: string | undefined;
@@ -192,16 +192,16 @@ export declare const categoryListParamsSchema: z.ZodObject<{
192
192
  perPage?: number | undefined;
193
193
  type?: "category" | "brand" | "collection" | "concern" | undefined;
194
194
  q?: string | undefined;
195
- page?: number | undefined;
196
195
  featured?: boolean | undefined;
196
+ page?: number | undefined;
197
197
  tier?: number | undefined;
198
198
  parentId?: string | undefined;
199
199
  }, {
200
200
  perPage?: number | undefined;
201
201
  type?: "category" | "brand" | "collection" | "concern" | undefined;
202
202
  q?: string | undefined;
203
- page?: number | undefined;
204
203
  featured?: boolean | undefined;
204
+ page?: number | undefined;
205
205
  tier?: number | undefined;
206
206
  parentId?: string | undefined;
207
207
  }>;
@@ -11,6 +11,7 @@ import { serverLogger } from "../../../monitoring";
11
11
  import { sendEmail } from "../../contact/email";
12
12
  import { CONSENT_OTP_EXPIRY_MS, CONSENT_OTP_MAX_ATTEMPTS, hashOtp, maskEmail, generateOtpCode, buildConsentOtpEmail, enforceConsentOtpRateLimit, saveConsentOtp, readConsentOtp, patchConsentOtp, } from "../../auth/consent-otp";
13
13
  import { addressesRepository } from "../../addresses/repository/addresses.repository";
14
+ const ERR_ADDRESS_NOT_FOUND = "Address not found.";
14
15
  async function findUserAddress(userId, addressId) {
15
16
  const address = await addressesRepository.findById(addressId);
16
17
  if (!address || address.ownerType !== "user" || address.ownerId !== userId) {
@@ -24,7 +25,7 @@ import { resolveDate } from "../../../utils";
24
25
  export async function sendCheckoutConsentOtp(userId, userEmail, addressId) {
25
26
  const address = await findUserAddress(userId, addressId);
26
27
  if (!address)
27
- throw new ValidationError("Address not found.");
28
+ throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
28
29
  await enforceConsentOtpRateLimit(userId);
29
30
  const code = generateOtpCode();
30
31
  const codeHash = hashOtp(code);
@@ -78,7 +79,7 @@ export async function grantCheckoutConsentViaSms(userId, userPhone, addressId) {
78
79
  throw new ValidationError("No phone number registered on your account.");
79
80
  const address = await findUserAddress(userId, addressId);
80
81
  if (!address)
81
- throw new ValidationError("Address not found.");
82
+ throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
82
83
  const normalizePhone = (s) => s.replace(/[^0-9]/g, "").slice(-10);
83
84
  if (normalizePhone(address.phone) !== normalizePhone(userPhone)) {
84
85
  throw new ValidationError("Phone number does not match the shipping address. Please use email verification for this address.");
@@ -107,7 +108,7 @@ export async function grantCheckoutConsentViaSms(userId, userPhone, addressId) {
107
108
  export async function grantAdminCheckoutBypass(userId, addressId, bypassingAdminUid) {
108
109
  const address = await findUserAddress(userId, addressId);
109
110
  if (!address)
110
- throw new ValidationError("Address not found.");
111
+ throw new ValidationError(ERR_ADDRESS_NOT_FOUND);
111
112
  await saveConsentOtp(userId, addressId, {
112
113
  codeHash: "",
113
114
  expiresAt: new Date(Date.now() + CONSENT_OTP_EXPIRY_MS),
@@ -3,6 +3,29 @@ import { useCallback, useRef, useState } from "react";
3
3
  import { getClientPaymentGateway } from "../../../contracts/client-payment-gateway";
4
4
  import { useCheckout as useCheckoutApi } from "../hooks/useCheckoutApi";
5
5
  import { usePaymentEvent, } from "../../payments/hooks/usePaymentEvent";
6
+ // --- Helpers ---------------------------------------------------------------
7
+ async function openGatewayStep(adapter, orderResponse, params) {
8
+ try {
9
+ await adapter.loadScript();
10
+ const response = await adapter.openPayment({
11
+ gatewayOrderId: orderResponse.gatewayOrderId,
12
+ publicKey: orderResponse.keyId,
13
+ amount: orderResponse.amount,
14
+ currency: orderResponse.currency,
15
+ merchantName: params.merchantName,
16
+ logoUrl: params.logoUrl,
17
+ prefill: params.prefill,
18
+ theme: params.themeColor ? { color: params.themeColor } : undefined,
19
+ });
20
+ return { response, cancelled: false, error: null };
21
+ }
22
+ catch (err) {
23
+ if (err instanceof Error && err.message.toLowerCase().includes("cancel")) {
24
+ return { response: null, cancelled: true, error: null };
25
+ }
26
+ return { response: null, cancelled: false, error: err instanceof Error ? err : new Error("Payment gateway error") };
27
+ }
28
+ }
6
29
  // --- Hook ------------------------------------------------------------------
7
30
  const INITIAL_STATE = {
8
31
  step: "idle",
@@ -101,31 +124,17 @@ export function usePaymentCheckout(options) {
101
124
  return null;
102
125
  // Step 2: Open gateway modal
103
126
  setState((s) => ({ ...s, step: "awaiting_payment" }));
104
- let gatewayResponse;
105
- try {
106
- const adapter = getAdapter();
107
- await adapter.loadScript();
108
- gatewayResponse = await adapter.openPayment({
109
- gatewayOrderId: orderResponse.gatewayOrderId,
110
- publicKey: orderResponse.keyId,
111
- amount: orderResponse.amount,
112
- currency: orderResponse.currency,
113
- merchantName,
114
- logoUrl,
115
- prefill: params.prefill,
116
- theme: themeColor ? { color: themeColor } : undefined,
117
- });
127
+ const gatewayResult = await openGatewayStep(getAdapter(), orderResponse, { prefill: params.prefill, merchantName, logoUrl, themeColor });
128
+ if (gatewayResult.cancelled) {
129
+ setState((s) => ({ ...s, step: "cancelled", error: null }));
130
+ onCancel?.();
131
+ return null;
118
132
  }
119
- catch (err) {
120
- if (err instanceof Error &&
121
- err.message.toLowerCase().includes("cancel")) {
122
- setState((s) => ({ ...s, step: "cancelled", error: null }));
123
- onCancel?.();
124
- return null;
125
- }
126
- setError(err instanceof Error ? err : new Error("Payment gateway error"));
133
+ if (gatewayResult.error || !gatewayResult.response) {
134
+ setError(gatewayResult.error ?? new Error("Payment gateway error"));
127
135
  return null;
128
136
  }
137
+ const gatewayResponse = gatewayResult.response;
129
138
  if (abortRef.current)
130
139
  return null;
131
140
  // Step 3: Verify payment on server
@@ -0,0 +1,8 @@
1
+ import type { FacetOption } from "../../filters/FilterFacetSection";
2
+ import type { UrlTable } from "../../filters/FilterPanel";
3
+ export interface ClassifiedFiltersProps {
4
+ table: UrlTable;
5
+ categoryOptions?: FacetOption[];
6
+ currencyPrefix?: string;
7
+ }
8
+ export declare function ClassifiedFilters({ table, categoryOptions, currencyPrefix, }: ClassifiedFiltersProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,19 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { FilterFacetSection } from "../../filters/FilterFacetSection";
4
+ import { RangeFilter } from "../../filters/RangeFilter";
5
+ import { Div } from "../../../ui";
6
+ const NEGOTIABLE_OPTIONS = [
7
+ { value: "true", label: "Negotiable only" },
8
+ ];
9
+ const SHIPPING_OPTIONS = [
10
+ { value: "true", label: "Accepts shipping" },
11
+ ];
12
+ export function ClassifiedFilters({ table, categoryOptions = [], currencyPrefix = "₹", }) {
13
+ const selectedCategories = table.get("category")
14
+ ? table.get("category").split("|").filter(Boolean)
15
+ : [];
16
+ const selectedNegotiable = table.get("negotiable") ? [table.get("negotiable")] : [];
17
+ const selectedShipping = table.get("acceptsShipping") ? [table.get("acceptsShipping")] : [];
18
+ return (_jsxs(Div, { children: [categoryOptions.length > 0 && (_jsx(FilterFacetSection, { title: "Category", options: categoryOptions, selected: selectedCategories, onChange: (vals) => table.set("category", vals.join("|")), searchable: true, defaultCollapsed: categoryOptions.length > 6 })), _jsxs("div", { className: "border-t border-[var(--appkit-color-border)] pt-4 mt-4", children: [_jsx("label", { className: "block text-sm font-medium text-[var(--appkit-color-text)] mb-2", children: "City / Area" }), _jsx("input", { type: "text", value: table.get("city") || "", onChange: (e) => table.set("city", e.target.value), placeholder: "e.g. Mumbai", className: "w-full rounded-lg border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-3 py-2 text-sm text-[var(--appkit-color-text)] placeholder-[var(--appkit-color-text-faint)] focus:outline-none focus:ring-2 focus:ring-[var(--appkit-color-primary)]" })] }), _jsx(FilterFacetSection, { title: "Negotiable", options: NEGOTIABLE_OPTIONS, selected: selectedNegotiable, onChange: (vals) => table.set("negotiable", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(FilterFacetSection, { title: "Shipping", options: SHIPPING_OPTIONS, selected: selectedShipping, onChange: (vals) => table.set("acceptsShipping", vals[0] ?? ""), searchable: false, selectionMode: "single", defaultCollapsed: false }), _jsx(RangeFilter, { title: "Asking Price", minValue: table.get("minPrice"), maxValue: table.get("maxPrice"), onMinChange: (v) => table.set("minPrice", v), onMaxChange: (v) => table.set("maxPrice", v), prefix: currencyPrefix, showSlider: true, minBound: 0, maxBound: 500000, step: 100, minPlaceholder: "Min price", maxPlaceholder: "Max price", defaultCollapsed: false })] }));
19
+ }
@@ -0,0 +1,4 @@
1
+ export interface ClassifiedIndexListingProps {
2
+ initialData?: any;
3
+ }
4
+ export declare function ClassifiedIndexListing({ initialData }: ClassifiedIndexListingProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,110 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useCallback } from "react";
4
+ import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
+ import { useProducts } from "../../products/hooks/useProducts";
6
+ import { BulkActionBar, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Text } from "../../../ui";
7
+ import { usePendingTable } from "../../../react/hooks/usePendingTable";
8
+ import { useAuthGate } from "../../../react/hooks/useAuthGate";
9
+ import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
10
+ import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
11
+ import { pushWishlistOp } from "../../cart/utils/pending-ops";
12
+ import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
13
+ import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
14
+ import { sortBy } from "../../../constants/sort";
15
+ import { PRODUCT_FIELDS } from "../../../constants/field-names";
16
+ import { ROUTES } from "../../../next";
17
+ import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
18
+ import { ClassifiedFilters } from "./ClassifiedFilters";
19
+ import { ACTION_ID } from "../../products/constants/action-defs";
20
+ import { useToast } from "../../../ui";
21
+ const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
22
+ const SORT_OPTIONS = [
23
+ { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
24
+ { value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
25
+ { value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Price: Low to High" },
26
+ { value: sortBy(PRODUCT_FIELDS.PRICE), label: "Price: High to Low" },
27
+ ];
28
+ const FILTER_KEYS = [
29
+ TABLE_KEYS.CATEGORY,
30
+ TABLE_KEYS.CITY,
31
+ TABLE_KEYS.MIN_PRICE,
32
+ TABLE_KEYS.MAX_PRICE,
33
+ TABLE_KEYS.NEGOTIABLE,
34
+ TABLE_KEYS.ACCEPTS_SHIPPING,
35
+ ];
36
+ export function ClassifiedIndexListing({ initialData }) {
37
+ const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
38
+ const { showToast } = useToast();
39
+ const { requireAuth, modalOpen, modalMessage, closeModal } = useAuthGate();
40
+ const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
41
+ const [filterOpen, setFilterOpen] = useState(false);
42
+ const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
43
+ const localWishlist = useGuestWishlist();
44
+ const { categories } = useCategoryTree();
45
+ const categoryOptions = categoriesToFacetOptions(categories);
46
+ const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "classified").map((i) => i.itemId));
47
+ const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onFilterReset } = usePendingTable(table, FILTER_KEYS);
48
+ const openFilters = useCallback(() => {
49
+ onFilterReset();
50
+ setFilterOpen(true);
51
+ }, [onFilterReset]);
52
+ const applyFilters = useCallback(() => {
53
+ onFilterApply();
54
+ setFilterOpen(false);
55
+ }, [onFilterApply]);
56
+ const resetAll = useCallback(() => {
57
+ table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" });
58
+ onFilterClear();
59
+ setSearchInput("");
60
+ }, [table, onFilterClear]);
61
+ const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
62
+ table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
63
+ filterActiveCount > 0;
64
+ const params = {
65
+ q: table.get(TABLE_KEYS.QUERY) || undefined,
66
+ category: table.get(TABLE_KEYS.CATEGORY) || undefined,
67
+ minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
68
+ maxPrice: table.get(TABLE_KEYS.MAX_PRICE) ? Number(table.get(TABLE_KEYS.MAX_PRICE)) : undefined,
69
+ sort: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT,
70
+ page: table.getNumber(TABLE_KEYS.PAGE, 1),
71
+ perPage: table.getNumber(TABLE_KEYS.PAGE_SIZE, 24),
72
+ listingType: "classified",
73
+ };
74
+ const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
75
+ const commitSearch = useCallback(() => {
76
+ table.set(TABLE_KEYS.QUERY, searchInput.trim());
77
+ }, [searchInput, table]);
78
+ const selection = useBulkSelection({ items: products, keyExtractor: (p) => p.id });
79
+ const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
80
+ const wishlistActions = {
81
+ addToWishlist: (productId) => {
82
+ requireAuth(ACTION_ID.ADD_TO_WISHLIST, () => {
83
+ localWishlist.add(productId, "classified");
84
+ pushWishlistOp({ op: "add", itemId: productId, type: "classified" });
85
+ showToast("Added to wishlist", "success");
86
+ });
87
+ return Promise.resolve();
88
+ },
89
+ removeFromWishlist: (productId) => {
90
+ requireAuth(ACTION_ID.REMOVE_FROM_WISHLIST, () => {
91
+ localWishlist.remove(productId, "classified");
92
+ pushWishlistOp({ op: "remove", itemId: productId, type: "classified" });
93
+ showToast("Removed from wishlist", "info");
94
+ });
95
+ return Promise.resolve();
96
+ },
97
+ isWishlisted: (productId) => wishlistedIds.has(productId),
98
+ };
99
+ function handleToggleWishlist(id) {
100
+ if (wishlistActions.isWishlisted(id)) {
101
+ wishlistActions.removeFromWishlist(id);
102
+ }
103
+ else {
104
+ wishlistActions.addToWishlist(id);
105
+ }
106
+ }
107
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search classified listings\u2026", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: (e) => { if (e.key === "Enter")
108
+ commitSearch(); }, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: (v) => { if (v !== "table")
109
+ setView(v); }, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: products.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-square bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-3 space-y-2", children: [_jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/3" })] })] }, i))) })) : products.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No classified listings found." })) : (_jsx("div", { className: view === "list" ? "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800" : gridClass, children: products.map((product) => (_jsx(InteractiveProductCard, { product: product, href: String(ROUTES.PUBLIC.CLASSIFIED_DETAIL(product.id)), variant: view === "list" ? "list" : "grid", isWishlisted: wishlistActions.isWishlisted(product.id), onToggleWishlist: handleToggleWishlist, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ClassifiedFilters, { table: pendingTable, categoryOptions: categoryOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
110
+ }
@@ -0,0 +1,6 @@
1
+ type SearchParams = Record<string, string | string[]>;
2
+ export interface ClassifiedListViewProps {
3
+ searchParams?: SearchParams;
4
+ }
5
+ export declare function ClassifiedListView({ searchParams }: ClassifiedListViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
6
+ export {};
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { productRepository } from "../../../repositories";
3
+ import { Container, Heading, Main, Section } from "../../../ui";
4
+ import { AdSlot } from "../../homepage/components/AdSlot";
5
+ import { parseListingSearchParams } from "../../../utils/listing-params";
6
+ import { ClassifiedIndexListing } from "./ClassifiedIndexListing";
7
+ const DEFAULT_PAGE = 1;
8
+ const DEFAULT_PAGE_SIZE = 24;
9
+ const DEFAULT_SORT = "-createdAt";
10
+ function sp(params, key) {
11
+ const v = params[key];
12
+ return Array.isArray(v) ? v[0] ?? "" : v ?? "";
13
+ }
14
+ function buildClassifiedFilters(params) {
15
+ const parts = ["status==published", "listingType==classified"];
16
+ const minPrice = sp(params, "minPrice");
17
+ const maxPrice = sp(params, "maxPrice");
18
+ if (minPrice)
19
+ parts.push(`price>=${minPrice}`);
20
+ if (maxPrice)
21
+ parts.push(`price<=${maxPrice}`);
22
+ const city = sp(params, "city");
23
+ if (city)
24
+ parts.push(`classifiedCity==${city}`);
25
+ const negotiable = sp(params, "negotiable");
26
+ if (negotiable === "true")
27
+ parts.push(`classifiedNegotiable==true`);
28
+ const acceptsShipping = sp(params, "acceptsShipping");
29
+ if (acceptsShipping === "true")
30
+ parts.push(`classifiedAcceptsShipping==true`);
31
+ return parts.join(",");
32
+ }
33
+ export async function ClassifiedListView({ searchParams = {} }) {
34
+ const std = parseListingSearchParams(searchParams);
35
+ const sort = std.sorts ?? DEFAULT_SORT;
36
+ const page = std.page ?? DEFAULT_PAGE;
37
+ const pageSize = std.pageSize ?? DEFAULT_PAGE_SIZE;
38
+ const filters = buildClassifiedFilters(searchParams);
39
+ const result = await productRepository
40
+ .list({ filters, sorts: sort, page, pageSize })
41
+ .catch(() => null);
42
+ return (_jsx(Main, { children: _jsx(Section, { className: "py-10", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8 text-3xl font-semibold text-zinc-900", children: "Classifieds" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(ClassifiedIndexListing, { initialData: result ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
43
+ }
@@ -0,0 +1,6 @@
1
+ export { ClassifiedListView } from "./ClassifiedListView";
2
+ export type { ClassifiedListViewProps } from "./ClassifiedListView";
3
+ export { ClassifiedIndexListing } from "./ClassifiedIndexListing";
4
+ export type { ClassifiedIndexListingProps } from "./ClassifiedIndexListing";
5
+ export { ClassifiedFilters } from "./ClassifiedFilters";
6
+ export type { ClassifiedFiltersProps } from "./ClassifiedFilters";
@@ -0,0 +1,3 @@
1
+ export { ClassifiedListView } from "./ClassifiedListView";
2
+ export { ClassifiedIndexListing } from "./ClassifiedIndexListing";
3
+ export { ClassifiedFilters } from "./ClassifiedFilters";