@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
@@ -1,10 +1,11 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useCallback, useMemo } from "react";
4
- import { Columns, Heart, ShoppingCart, SlidersHorizontal, X } from "lucide-react";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useCallback } from "react";
4
+ import { Columns, Heart, ShoppingCart } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useProducts } from "../../products/hooks/useProducts";
7
- import { Pagination, useToast, BulkActionBar, ListingToolbar, LoginRequiredModal } from "../../../ui";
7
+ import { BulkActionBar, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Text, useToast } from "../../../ui";
8
+ import { usePendingTable } from "../../../react/hooks/usePendingTable";
8
9
  import { useAuthGate } from "../../../react/hooks/useAuthGate";
9
10
  import { ACTION_ID, ACTION_META, COMPARE_MAX_ITEMS } from "../../products/constants/action-defs";
10
11
  import { CompareOverlay } from "../../products/components/CompareOverlay";
@@ -45,60 +46,24 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
45
46
  const categoryOptions = categoriesToFacetOptions(categories);
46
47
  const { brandOptions } = useBrands();
47
48
  const wishlistedIds = new Set(localWishlist.items.filter((i) => i.type === "preorder").map((i) => i.itemId));
48
- // Pending filter state buffered until "Apply Filters" clicked
49
- const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
50
- const pendingTable = useMemo(() => ({
51
- get: (key) => pendingFilters[key] ?? "",
52
- getNumber: (key, fallback = 0) => {
53
- const v = pendingFilters[key];
54
- if (!v)
55
- return fallback;
56
- const n = Number(v);
57
- return isNaN(n) ? fallback : n;
58
- },
59
- set: (key, value) => setPendingFilters((p) => ({ ...p, [key]: value })),
60
- setMany: (updates) => setPendingFilters((p) => ({ ...p, ...updates })),
61
- clear: (keys) => {
62
- const ks = keys ?? FILTER_KEYS;
63
- setPendingFilters((p) => ({
64
- ...p,
65
- ...Object.fromEntries(ks.map((k) => [k, ""])),
66
- }));
67
- },
68
- setPage: (_) => { },
69
- setPageSize: (_) => { },
70
- setSort: (_) => { },
71
- buildSieveParams: () => "",
72
- buildSearchParams: () => "",
73
- params: new URLSearchParams(),
74
- }), [pendingFilters]);
49
+ const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onFilterReset } = usePendingTable(table, FILTER_KEYS);
75
50
  const openFilters = useCallback(() => {
76
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
51
+ onFilterReset();
77
52
  setFilterOpen(true);
78
- }, [table]);
53
+ }, [onFilterReset]);
79
54
  const applyFilters = useCallback(() => {
80
- const updates = { page: "1" };
81
- for (const k of FILTER_KEYS)
82
- updates[k] = pendingFilters[k] ?? "";
83
- table.setMany(updates);
55
+ onFilterApply();
84
56
  setFilterOpen(false);
85
- }, [pendingFilters, table]);
86
- const clearFilters = useCallback(() => {
87
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
88
- }, []);
57
+ }, [onFilterApply]);
89
58
  const resetAll = useCallback(() => {
90
- const updates = { [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.SHOW_CLOSED]: "" };
91
- for (const k of FILTER_KEYS)
92
- updates[k] = "";
93
- table.setMany(updates);
59
+ table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.SHOW_CLOSED]: "" });
60
+ onFilterClear();
94
61
  setSearchInput("");
95
- }, [table]);
96
- const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
97
- const pendingFilterCount = FILTER_KEYS.filter((k) => !!pendingFilters[k]).length;
62
+ }, [table, onFilterClear]);
98
63
  const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
99
64
  table.get(TABLE_KEYS.SHOW_CLOSED) === "true" ||
100
65
  table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
101
- activeFilterCount > 0;
66
+ filterActiveCount > 0;
102
67
  const params = {
103
68
  q: table.get(TABLE_KEYS.QUERY) || undefined,
104
69
  category: table.get(TABLE_KEYS.CATEGORY) || undefined,
@@ -162,7 +127,7 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
162
127
  }, [localCart, showToast]);
163
128
  const selection = useBulkSelection({ items: preOrders, keyExtractor: (p) => p.id });
164
129
  const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-4";
165
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: preOrders.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show closed" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showClosed, onClick: () => table.set(TABLE_KEYS.SHOW_CLOSED, showClosed ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showClosed ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showClosed ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
130
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PREORDER_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: preOrders.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show closed" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showClosed, onClick: () => table.set(TABLE_KEYS.SHOW_CLOSED, showClosed ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showClosed ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showClosed ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
166
131
  {
167
132
  id: ACTION_ID.ADD_TO_CART,
168
133
  label: ACTION_META[ACTION_ID.ADD_TO_CART].label,
@@ -206,8 +171,8 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
206
171
  setCompareIds(ids);
207
172
  },
208
173
  },
209
- ] }), 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-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" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No pre-orders found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) : (_jsx("div", { className: gridClass, children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "preorder", onClose: () => {
174
+ ] }), 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-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" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx(Text, { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No pre-orders found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) : (_jsx("div", { className: gridClass, children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "preorder", onClose: () => {
210
175
  setCompareIds([]);
211
176
  selection.clearSelection();
212
- }, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _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"] }), _jsxs("div", { className: "flex items-center gap-2", children: [pendingFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "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(PreOrderFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", pendingFilterCount > 0 ? ` (${pendingFilterCount})` : ""] }) })] })] })), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
177
+ }, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(PreOrderFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
213
178
  }
@@ -1,15 +1,6 @@
1
- import type { PreorderItem } from "../types";
2
1
  interface PreorderBadgeProps {
3
2
  shipDate?: string;
4
3
  className?: string;
5
4
  }
6
5
  export declare function PreorderBadge({ shipDate, className }: PreorderBadgeProps): import("react/jsx-runtime").JSX.Element;
7
- interface PreorderCardProps {
8
- item: PreorderItem;
9
- href: string;
10
- selectable?: boolean;
11
- isSelected?: boolean;
12
- onSelect?: (id: string, selected: boolean) => void;
13
- }
14
- export declare function PreorderCard({ item, href, selectable, isSelected, onSelect }: PreorderCardProps): import("react/jsx-runtime").JSX.Element;
15
6
  export {};
@@ -1,10 +1,6 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import Link from "next/link";
3
- import { BaseListingCard, Div, Heading, Span, Text } from "../../../ui";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Span } from "../../../ui";
4
3
  import { getPreorderStatus } from "../types";
5
- import { formatCurrency } from "../../../utils/number.formatter";
6
- import { getDefaultLocale } from "../../../core/baseline-resolver";
7
- import { useLongPress } from "../../../react/hooks/useLongPress";
8
4
  const STATUS_LABELS = {
9
5
  available: "Pre-order now",
10
6
  shipping_soon: "Shipping soon",
@@ -19,10 +15,3 @@ export function PreorderBadge({ shipDate, className }) {
19
15
  const status = getPreorderStatus(shipDate);
20
16
  return (_jsx(Span, { className: `inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${STATUS_COLORS[status]} ${className ?? ""}`, children: STATUS_LABELS[status] }));
21
17
  }
22
- export function PreorderCard({ item, href, selectable = false, isSelected = false, onSelect }) {
23
- const longPress = useLongPress(() => onSelect?.(item.id, !isSelected));
24
- return (_jsxs(Div, { className: `group relative block overflow-hidden rounded-xl border bg-white dark:bg-slate-900 transition-shadow hover:shadow-md ${isSelected ? "border-primary outline outline-2 outline-primary" : "border-gray-200 dark:border-slate-700"}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(item.id, !isSelected); }, label: isSelected ? "Deselect pre-order" : "Select pre-order", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsxs(Link, { href: href, className: "block", children: [item.images[0] ? (_jsx(Div, { role: "img", "aria-label": item.name, className: "aspect-square w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105", style: { backgroundImage: `url(${item.images[0]})` } })) : (_jsx(Div, { className: "aspect-square w-full bg-gray-100" })), _jsxs(Div, { className: "p-4", children: [_jsx(PreorderBadge, { shipDate: item.preorderShipDate }), _jsx(Heading, { level: 3, className: "mt-2 font-semibold text-gray-900 dark:text-zinc-100 text-base", children: item.name }), _jsx(Text, { className: "mt-1 text-sm text-gray-500 dark:text-zinc-400", children: item.brand }), _jsxs(Div, { className: "mt-3 flex items-baseline gap-2", children: [_jsx(Span, { className: "text-lg font-bold text-gray-900 dark:text-zinc-100", children: formatCurrency(item.salePrice) }), item.regularPrice > item.salePrice && (_jsx(Span, { className: "text-sm text-gray-400 dark:text-zinc-500 line-through", children: formatCurrency(item.regularPrice) }))] }), item.preorderShipDate && (_jsxs(Text, { className: "mt-1 text-xs text-gray-400 dark:text-zinc-500", children: ["Ships:", " ", new Date(item.preorderShipDate).toLocaleDateString(getDefaultLocale(), {
25
- month: "short",
26
- year: "numeric",
27
- })] }))] })] })] }));
28
- }
@@ -1,4 +1,4 @@
1
- export { PreorderBadge, PreorderCard } from "./PreorderCard";
1
+ export { PreorderBadge } from "./PreorderCard";
2
2
  export { PreOrderFilters } from "./PreOrderFilters";
3
3
  export type { PreOrderFiltersProps } from "./PreOrderFilters";
4
4
  export { MarketplacePreorderCard, type MarketplacePreorderCardData, type MarketplacePreorderCardLabels, type MarketplacePreorderCardProps, } from "./MarketplacePreorderCard";
@@ -1,4 +1,4 @@
1
- export { PreorderBadge, PreorderCard } from "./PreorderCard";
1
+ export { PreorderBadge } from "./PreorderCard";
2
2
  export { PreOrderFilters } from "./PreOrderFilters";
3
3
  export { MarketplacePreorderCard, } from "./MarketplacePreorderCard";
4
4
  export { PreOrdersListView } from "./PreOrdersListView";
@@ -24,14 +24,14 @@ export declare const preorderItemSchema: z.ZodObject<{
24
24
  }, "strip", z.ZodTypeAny, {
25
25
  type: "video" | "image" | "file";
26
26
  url: string;
27
- source?: "external" | "upload" | "youtube" | undefined;
27
+ source?: "upload" | "youtube" | "external" | undefined;
28
28
  alt?: string | undefined;
29
29
  thumbnailUrl?: string | undefined;
30
30
  youtubeId?: string | undefined;
31
31
  }, {
32
32
  type: "video" | "image" | "file";
33
33
  url: string;
34
- source?: "external" | "upload" | "youtube" | undefined;
34
+ source?: "upload" | "youtube" | "external" | undefined;
35
35
  alt?: string | undefined;
36
36
  thumbnailUrl?: string | undefined;
37
37
  youtubeId?: string | undefined;
@@ -46,44 +46,44 @@ export declare const preorderItemSchema: z.ZodObject<{
46
46
  createdAt: z.ZodOptional<z.ZodString>;
47
47
  }, "strip", z.ZodTypeAny, {
48
48
  id: string;
49
+ slug: string;
49
50
  name: string;
50
51
  active: boolean;
51
52
  isFeatured: boolean;
52
- slug: string;
53
53
  salePrice: number;
54
54
  regularPrice: number;
55
55
  createdAt?: string | undefined;
56
+ brand?: string | undefined;
57
+ images?: string[] | undefined;
56
58
  media?: {
57
59
  type: "video" | "image" | "file";
58
60
  url: string;
59
- source?: "external" | "upload" | "youtube" | undefined;
61
+ source?: "upload" | "youtube" | "external" | undefined;
60
62
  alt?: string | undefined;
61
63
  thumbnailUrl?: string | undefined;
62
64
  youtubeId?: string | undefined;
63
65
  }[] | undefined;
64
- brand?: string | undefined;
65
- images?: string[] | undefined;
66
66
  franchise?: string | undefined;
67
67
  preorderShipDate?: string | undefined;
68
68
  }, {
69
69
  id: string;
70
- name: string;
71
70
  slug: string;
71
+ name: string;
72
72
  salePrice: number;
73
73
  regularPrice: number;
74
74
  createdAt?: string | undefined;
75
+ brand?: string | undefined;
76
+ images?: string[] | undefined;
75
77
  media?: {
76
78
  type: "video" | "image" | "file";
77
79
  url: string;
78
- source?: "external" | "upload" | "youtube" | undefined;
80
+ source?: "upload" | "youtube" | "external" | undefined;
79
81
  alt?: string | undefined;
80
82
  thumbnailUrl?: string | undefined;
81
83
  youtubeId?: string | undefined;
82
84
  }[] | undefined;
83
85
  active?: boolean | undefined;
84
- brand?: string | undefined;
85
86
  isFeatured?: boolean | undefined;
86
- images?: string[] | undefined;
87
87
  franchise?: string | undefined;
88
88
  preorderShipDate?: string | undefined;
89
89
  }>;
@@ -18,6 +18,8 @@ import { storeRepository } from "../../../stores/repository/store.repository";
18
18
  // SB-UNI-V — bundlesRepository deleted; bundle stock-sync moves to the
19
19
  // `onProductStockChange` Firebase Function (see functions/src/bundle-stock-sync.ts).
20
20
  import { sanitizeProductForPublic } from "../../utils/sanitize";
21
+ const ERR_DB_NOT_REGISTERED = "Database provider not registered";
22
+ const ERR_PRODUCT_NOT_FOUND = "Product not found";
21
23
  const UNAVAILABLE_PRODUCT_STATUSES = new Set([
22
24
  "sold",
23
25
  "out_of_stock",
@@ -53,7 +55,7 @@ const productUpdateSchema = z
53
55
  featured: z.boolean().optional(),
54
56
  isPromoted: z.boolean().optional(),
55
57
  listingType: z
56
- .enum(["standard", "auction", "pre-order", "prize-draw", "bundle"])
58
+ .enum(["standard", "auction", "pre-order", "prize-draw", "bundle", "classified", "digital-code", "live"])
57
59
  .optional(),
58
60
  media: z.array(mediaFieldSchema).optional(),
59
61
  })
@@ -64,7 +66,7 @@ export async function GET(_request, context) {
64
66
  const { id } = await context.params;
65
67
  const repo = getRepo();
66
68
  if (!repo) {
67
- return NextResponse.json({ success: false, error: "Database provider not registered" }, { status: 503 });
69
+ return NextResponse.json({ success: false, error: ERR_DB_NOT_REGISTERED }, { status: 503 });
68
70
  }
69
71
  // Support both Firestore document ID and URL slug
70
72
  let item = await repo.findById(id);
@@ -77,7 +79,7 @@ export async function GET(_request, context) {
77
79
  item = slugResult.data[0] ?? null;
78
80
  }
79
81
  if (!item) {
80
- return NextResponse.json({ success: false, error: "Product not found" }, { status: 404 });
82
+ return NextResponse.json({ success: false, error: ERR_PRODUCT_NOT_FOUND }, { status: 404 });
81
83
  }
82
84
  const sanitized = sanitizeProductForPublic(item);
83
85
  return NextResponse.json({ success: true, data: sanitized });
@@ -96,11 +98,11 @@ export const PATCH = createRouteHandler({
96
98
  const { id } = params;
97
99
  const repo = getRepo();
98
100
  if (!repo) {
99
- return NextResponse.json({ success: false, error: "Database provider not registered" }, { status: 503 });
101
+ return NextResponse.json({ success: false, error: ERR_DB_NOT_REGISTERED }, { status: 503 });
100
102
  }
101
103
  const product = await repo.findById(id);
102
104
  if (!product) {
103
- return NextResponse.json({ success: false, error: "Product not found" }, { status: 404 });
105
+ return NextResponse.json({ success: false, error: ERR_PRODUCT_NOT_FOUND }, { status: 404 });
104
106
  }
105
107
  const ownerStore = product.storeId ? await storeRepository.findByOwnerId(user?.uid ?? "") : null;
106
108
  const isOwner = !!ownerStore && ownerStore.id === product.storeId;
@@ -150,11 +152,11 @@ export const DELETE = createRouteHandler({
150
152
  const { id } = params;
151
153
  const repo = getRepo();
152
154
  if (!repo) {
153
- return NextResponse.json({ success: false, error: "Database provider not registered" }, { status: 503 });
155
+ return NextResponse.json({ success: false, error: ERR_DB_NOT_REGISTERED }, { status: 503 });
154
156
  }
155
157
  const product = await repo.findById(id);
156
158
  if (!product) {
157
- return NextResponse.json({ success: false, error: "Product not found" }, { status: 404 });
159
+ return NextResponse.json({ success: false, error: ERR_PRODUCT_NOT_FOUND }, { status: 404 });
158
160
  }
159
161
  const ownerStore = product.storeId ? await storeRepository.findByOwnerId(user?.uid ?? "") : null;
160
162
  const isOwner = !!ownerStore && ownerStore.id === product.storeId;
@@ -48,7 +48,7 @@ const productMutateSchema = z
48
48
  sellerEmail: z.string().email().optional(),
49
49
  slug: z.string().optional(),
50
50
  listingType: z
51
- .enum(["standard", "auction", "pre-order", "prize-draw", "bundle"])
51
+ .enum(["standard", "auction", "pre-order", "prize-draw", "bundle", "classified", "digital-code", "live"])
52
52
  .optional(),
53
53
  media: z.array(mediaFieldSchema).optional(),
54
54
  })
@@ -76,10 +76,10 @@ export function getProductTableColumns({ labels, onEdit, onDelete, currencyCode,
76
76
  },
77
77
  },
78
78
  ];
79
- const actions = (product) => (_jsxs(Div, { className: "flex gap-2", children: [_jsx(Button, { type: "button", onClick: (e) => {
79
+ const actions = (product) => (_jsxs(Div, { className: "flex gap-2", children: [_jsx(Button, { variant: "ghost", type: "button", onClick: (e) => {
80
80
  e.stopPropagation();
81
81
  onEdit(product);
82
- }, className: "text-primary hover:text-primary/80", children: labels.edit }), _jsx(Button, { type: "button", onClick: (e) => {
82
+ }, className: "text-primary hover:text-primary/80", children: labels.edit }), _jsx(Button, { variant: "ghost", type: "button", onClick: (e) => {
83
83
  e.stopPropagation();
84
84
  onDelete(product);
85
85
  }, className: "text-red-600 hover:text-red-800 dark:text-red-400", children: labels.delete })] }));
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useCallback, useMemo } from "react";
4
- import { SlidersHorizontal, X } from "lucide-react";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useCallback } from "react";
5
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
5
  import { useProducts } from "../hooks/useProducts";
7
- import { Pagination, useToast, ListingToolbar, BulkActionBar, LoginRequiredModal } from "../../../ui";
6
+ import { Pagination, useToast, ListingToolbar, BulkActionBar, LoginRequiredModal, FilterDrawer } from "../../../ui";
7
+ import { usePendingTable } from "../../../react/hooks/usePendingTable";
8
8
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
9
9
  import { MarketplaceAuctionGrid } from "../../auctions/components/MarketplaceAuctionGrid";
10
10
  import { AuctionFilters } from "../../auctions/components/AuctionFilters";
@@ -40,60 +40,24 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
40
40
  const { categories } = useCategoryTree();
41
41
  const categoryOptions = categoriesToFacetOptions(categories);
42
42
  const { brandOptions } = useBrands();
43
- // Pending filter state buffered until "Apply Filters" clicked
44
- const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
45
- const pendingTable = useMemo(() => ({
46
- get: (key) => pendingFilters[key] ?? "",
47
- getNumber: (key, fallback = 0) => {
48
- const v = pendingFilters[key];
49
- if (!v)
50
- return fallback;
51
- const n = Number(v);
52
- return isNaN(n) ? fallback : n;
53
- },
54
- set: (key, value) => setPendingFilters((p) => ({ ...p, [key]: value })),
55
- setMany: (updates) => setPendingFilters((p) => ({ ...p, ...updates })),
56
- clear: (keys) => {
57
- const ks = keys ?? FILTER_KEYS;
58
- setPendingFilters((p) => ({
59
- ...p,
60
- ...Object.fromEntries(ks.map((k) => [k, ""])),
61
- }));
62
- },
63
- setPage: (_) => { },
64
- setPageSize: (_) => { },
65
- setSort: (_) => { },
66
- buildSieveParams: () => "",
67
- buildSearchParams: () => "",
68
- params: new URLSearchParams(),
69
- }), [pendingFilters]);
43
+ const { pendingTable, filterActiveCount, onFilterApply, onFilterClear, onFilterReset } = usePendingTable(table, FILTER_KEYS);
70
44
  const openFilters = useCallback(() => {
71
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
45
+ onFilterReset();
72
46
  setFilterOpen(true);
73
- }, [table]);
47
+ }, [onFilterReset]);
74
48
  const applyFilters = useCallback(() => {
75
- const updates = { page: "1" };
76
- for (const k of FILTER_KEYS)
77
- updates[k] = pendingFilters[k] ?? "";
78
- table.setMany(updates);
49
+ onFilterApply();
79
50
  setFilterOpen(false);
80
- }, [pendingFilters, table]);
81
- const clearFilters = useCallback(() => {
82
- setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
83
- }, []);
51
+ }, [onFilterApply]);
84
52
  const resetAll = useCallback(() => {
85
- const updates = { [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.SHOW_ENDED]: "" };
86
- for (const k of FILTER_KEYS)
87
- updates[k] = "";
88
- table.setMany(updates);
53
+ table.setMany({ [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "", [TABLE_KEYS.SHOW_ENDED]: "" });
54
+ onFilterClear();
89
55
  setSearchInput("");
90
- }, [table]);
91
- const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
92
- const pendingFilterCount = FILTER_KEYS.filter((k) => !!pendingFilters[k]).length;
56
+ }, [table, onFilterClear]);
93
57
  const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
94
58
  table.get(TABLE_KEYS.SHOW_ENDED) === "true" ||
95
59
  table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
96
- activeFilterCount > 0;
60
+ filterActiveCount > 0;
97
61
  const params = {
98
62
  q: table.get(TABLE_KEYS.QUERY) || undefined,
99
63
  category: table.get(TABLE_KEYS.CATEGORY) || undefined,
@@ -148,7 +112,7 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
148
112
  isWishlisted: (productId) => wishlistedIds.has(productId),
149
113
  };
150
114
  const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-3 gap-4";
151
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: auctions.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show ended" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showEnded, onClick: () => table.set(TABLE_KEYS.SHOW_ENDED, showEnded ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showEnded ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showEnded ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
115
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: AUCTION_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState, bulkMode: selection.isSelecting, bulkSelectedCount: selection.selectedCount, bulkTotalCount: auctions.length, onBulkSelectAll: selection.toggleAll, onBulkClear: selection.clearSelection, extra: _jsxs("label", { className: "flex items-center gap-1.5 cursor-pointer select-none shrink-0", children: [_jsx("span", { className: "hidden sm:inline text-xs text-zinc-600 dark:text-zinc-300 whitespace-nowrap", children: "Show ended" }), _jsx("button", { type: "button", role: "switch", "aria-checked": showEnded, onClick: () => table.set(TABLE_KEYS.SHOW_ENDED, showEnded ? "" : "true"), className: `relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1 ${showEnded ? "bg-primary" : "bg-zinc-300 dark:bg-slate-600"}`, children: _jsx("span", { className: `inline-block h-3.5 w-3.5 transform rounded-full bg-white shadow-sm transition-transform duration-200 ${showEnded ? "translate-x-[19px]" : "translate-x-[3px]"}` }) })] }) }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
152
116
  {
153
117
  id: ACTION_ID.WATCH_AUCTION,
154
118
  label: "Add to Watchlist",
@@ -169,5 +133,5 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
169
133
  selection.clearSelection();
170
134
  },
171
135
  },
172
- ] }), 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: 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-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-4" : gridClass, wishlistActions: wishlistActions })) }), filterOpen && (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _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"] }), _jsxs("div", { className: "flex items-center gap-2", children: [pendingFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "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(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", pendingFilterCount > 0 ? ` (${pendingFilterCount})` : ""] }) })] })] })), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
136
+ ] }), 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: 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-4 bg-zinc-200 dark:bg-slate-700 rounded w-1/2" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-4" : gridClass, wishlistActions: wishlistActions })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
173
137
  }
@@ -1,6 +1,6 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
- import { Span, Text } from "../../../ui";
3
+ import { Heading, Span, Text } from "../../../ui";
4
4
  import { formatCurrency } from "../../../utils/number.formatter";
5
5
  function formatBidDate(raw) {
6
6
  if (!raw)
@@ -30,5 +30,5 @@ export function BidHistory({ isLoading = false, isEmpty = false, renderList, ren
30
30
  }
31
31
  if (renderList)
32
32
  return _jsx("div", { className: className, "data-section": "bidhistory-div-420", children: renderList() });
33
- return (_jsxs("div", { className: className, "data-section": "bidhistory-div-421", children: [labels.title && (_jsx("h3", { className: "mb-3 text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: labels.title })), _jsx("div", { className: "space-y-2", children: bids.map((bid, i) => renderBid ? (_jsx(React.Fragment, { children: renderBid(bid, i) }, bid.id)) : (_jsxs("div", { className: "flex items-center justify-between rounded-lg border border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-3 text-sm", "data-section": "bidhistory-div-422", children: [_jsx(Span, { className: "font-bold text-primary-600 dark:text-primary-400", children: currency ? formatCurrency(bid.amount, currency) : bid.amount.toLocaleString() }), _jsx(Span, { className: "text-xs text-zinc-400 dark:text-zinc-500", children: formatBidDate(bid.placedAt) })] }, bid.id))) })] }));
33
+ return (_jsxs("div", { className: className, "data-section": "bidhistory-div-421", children: [labels.title && (_jsx(Heading, { level: 3, className: "mb-3 text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400", children: labels.title })), _jsx("div", { className: "space-y-2", children: bids.map((bid, i) => renderBid ? (_jsx(React.Fragment, { children: renderBid(bid, i) }, bid.id)) : (_jsxs("div", { className: "flex items-center justify-between rounded-lg border border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900 p-3 text-sm", "data-section": "bidhistory-div-422", children: [_jsx(Span, { className: "font-bold text-primary-600 dark:text-primary-400", children: currency ? formatCurrency(bid.amount, currency) : bid.amount.toLocaleString() }), _jsx(Span, { className: "text-xs text-zinc-400 dark:text-zinc-500", children: formatBidDate(bid.placedAt) })] }, bid.id))) })] }));
34
34
  }
@@ -17,6 +17,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
17
17
  * `GET /api/products?ids=<csv>` (BK3 listByIds endpoint).
18
18
  */
19
19
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
20
+ const REL_NOOPENER = "noopener noreferrer";
20
21
  import Link from "next/link";
21
22
  import { X } from "lucide-react";
22
23
  import { Button, Div, Heading, Row, Stack, Text } from "../../../ui";
@@ -67,9 +68,9 @@ function FieldRow({ label, children }) {
67
68
  function CompareColumn({ item, productType, labels, onRemove, onClose }) {
68
69
  const href = detailHref(item, productType);
69
70
  const img = item.mainImage ?? item.images?.[0];
70
- return (_jsxs(Stack, { gap: "md", className: COLUMN_CARD_CLASS, children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", children: [_jsx(Link, { href: href, target: "_blank", rel: "noopener noreferrer", className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 hover:underline line-clamp-2", children: item.title ?? item.id }), onRemove && (_jsx("button", { type: "button", onClick: () => onRemove(item.id), "aria-label": `${labels.remove} ${item.title ?? item.id}`, className: "shrink-0 rounded-full p-1 text-zinc-400 hover:bg-zinc-100 hover:text-rose-500 dark:hover:bg-zinc-800", children: _jsx(X, { className: "h-4 w-4" }) }))] }), _jsx(FieldRow, { label: labels.field.image, children: _jsx(Link, { href: href, target: "_blank", rel: "noopener noreferrer", children: _jsx(Div, { className: "relative aspect-square overflow-hidden rounded-lg bg-zinc-100 dark:bg-zinc-800", children: img ? (
71
+ return (_jsxs(Stack, { gap: "md", className: COLUMN_CARD_CLASS, children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", children: [_jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, className: "text-sm font-semibold text-zinc-900 dark:text-zinc-100 hover:underline line-clamp-2", children: item.title ?? item.id }), onRemove && (_jsx("button", { type: "button", onClick: () => onRemove(item.id), "aria-label": `${labels.remove} ${item.title ?? item.id}`, className: "shrink-0 rounded-full p-1 text-zinc-400 hover:bg-zinc-100 hover:text-rose-500 dark:hover:bg-zinc-800", children: _jsx(X, { className: "h-4 w-4" }) }))] }), _jsx(FieldRow, { label: labels.field.image, children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, children: _jsx(Div, { className: "relative aspect-square overflow-hidden rounded-lg bg-zinc-100 dark:bg-zinc-800", children: img ? (
71
72
  // eslint-disable-next-line @next/next/no-img-element
72
- _jsx("img", { src: img, alt: item.title ?? item.id, className: "h-full w-full object-cover" })) : null }) }) }), _jsx(FieldRow, { label: labels.field.price, children: _jsx(Text, { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: priceLabel(item) }) }), _jsx(FieldRow, { label: labels.field.condition, children: _jsx(Text, { children: item.condition ? _jsx("span", { className: CHIP_CLASS, children: item.condition }) : "—" }) }), _jsx(FieldRow, { label: labels.field.brand, children: _jsx(Text, { children: item.brand ? _jsx("span", { className: CHIP_CLASS, children: item.brand }) : "—" }) }), _jsx(FieldRow, { label: labels.field.category, children: _jsx(Text, { children: item.categoryName || item.category ? (_jsx("span", { className: CHIP_CLASS, children: item.categoryName ?? item.category })) : ("—") }) }), _jsx(FieldRow, { label: labels.field.store, children: _jsx(Text, { className: "text-sm text-zinc-700 dark:text-zinc-300", children: item.storeName ?? "—" }) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", className: "mt-auto", children: _jsx(Link, { href: href, target: "_blank", rel: "noopener noreferrer", onClick: onClose, children: labels.view }) })] }));
73
+ _jsx("img", { src: img, alt: item.title ?? item.id, className: "h-full w-full object-cover" })) : null }) }) }), _jsx(FieldRow, { label: labels.field.price, children: _jsx(Text, { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: priceLabel(item) }) }), _jsx(FieldRow, { label: labels.field.condition, children: _jsx(Text, { children: item.condition ? _jsx("span", { className: CHIP_CLASS, children: item.condition }) : "—" }) }), _jsx(FieldRow, { label: labels.field.brand, children: _jsx(Text, { children: item.brand ? _jsx("span", { className: CHIP_CLASS, children: item.brand }) : "—" }) }), _jsx(FieldRow, { label: labels.field.category, children: _jsx(Text, { children: item.categoryName || item.category ? (_jsx("span", { className: CHIP_CLASS, children: item.categoryName ?? item.category })) : ("—") }) }), _jsx(FieldRow, { label: labels.field.store, children: _jsx(Text, { className: "text-sm text-zinc-700 dark:text-zinc-300", children: item.storeName ?? "—" }) }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", className: "mt-auto", children: _jsx(Link, { href: href, target: "_blank", rel: REL_NOOPENER, onClick: onClose, children: labels.view }) })] }));
73
74
  }
74
75
  function useFetchedProducts(productIds, passedItems, isOpen) {
75
76
  const [fetched, setFetched] = useState([]);
@@ -4,6 +4,9 @@ import { useState, useTransition } from "react";
4
4
  import { Button, Div, Input, LoginRequiredModal, Span, Text } from "../../../ui";
5
5
  import { isAuthError } from "../../../utils/auth-error";
6
6
  import { formatCurrency } from "../../../utils/number.formatter";
7
+ function isActiveOfferError(msg) {
8
+ return msg.includes("active offer") || msg.includes("ACTIVE_OFFER");
9
+ }
7
10
  export function MakeOfferButton({ productId, listedPrice, currency, minOfferPercent = 70, onMakeOffer, className = "", }) {
8
11
  const [state, setState] = useState("idle");
9
12
  const [errorMsg, setErrorMsg] = useState("");
@@ -25,6 +28,22 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
25
28
  if (!isNaN(parsed))
26
29
  setOfferAmount(parsed);
27
30
  }
31
+ function handleOfferError(err) {
32
+ if (isAuthError(err)) {
33
+ setState("idle");
34
+ setShowLoginModal(true);
35
+ }
36
+ else {
37
+ const msg = err instanceof Error ? err.message : String(err);
38
+ if (isActiveOfferError(msg)) {
39
+ setState("pending");
40
+ }
41
+ else {
42
+ setErrorMsg(msg || "Could not send offer. Please try again.");
43
+ setState("error");
44
+ }
45
+ }
46
+ }
28
47
  function handleSubmit() {
29
48
  if (offerAmount < minOffer) {
30
49
  setErrorMsg(`Minimum offer is ${fmt(minOffer)}`);
@@ -43,20 +62,7 @@ export function MakeOfferButton({ productId, listedPrice, currency, minOfferPerc
43
62
  setState("success");
44
63
  }
45
64
  catch (err) {
46
- if (isAuthError(err)) {
47
- setState("idle");
48
- setShowLoginModal(true);
49
- }
50
- else {
51
- const msg = err instanceof Error ? err.message : String(err);
52
- if (msg.includes("active offer") || msg.includes("ACTIVE_OFFER")) {
53
- setState("pending");
54
- }
55
- else {
56
- setErrorMsg(msg || "Could not send offer. Please try again.");
57
- setState("error");
58
- }
59
- }
65
+ handleOfferError(err);
60
66
  }
61
67
  });
62
68
  }
@@ -4,6 +4,7 @@ import { useCallback } from "react";
4
4
  import { useRouter } from "next/navigation";
5
5
  import { useLongPress } from "../../../react/hooks/useLongPress";
6
6
  import { ROUTES } from "../../../next";
7
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
7
8
  import { formatCurrency } from "../../../utils";
8
9
  import { getDefaultCurrency } from "../../../core/baseline-resolver";
9
10
  import { BaseListingCard, Button, Div, Row, Span, Text, TextLink, } from "../../../ui";
@@ -15,7 +16,7 @@ const DEFAULT_LABELS = {
15
16
  closedBadge: "Draw closed",
16
17
  entriesRemainingLabel: (remaining, max) => `${remaining}/${max} entries left`,
17
18
  pricePerEntryLabel: "per entry",
18
- enterDraw: "Enter draw",
19
+ enterDraw: ACTIONS.PRIZE_DRAW["enter-draw"].label,
19
20
  };
20
21
  function statusVariant(status) {
21
22
  switch (status) {
@@ -1,19 +1,8 @@
1
- /**
2
- * PrizeDrawCollage (SB4-B)
3
- *
4
- * Read-only display of a prize-draw's full prize pool. Each cell shows the
5
- * primary image, item-number badge, and title. Items with `isWon === true`
6
- * are dimmed under a diagonal overlay with a "Won" label so the public
7
- * collage truthfully reflects what's still up for grabs.
8
- *
9
- * Optional `highlightItemNumber` (passed by the reveal modal during animation)
10
- * adds an emphasis ring to the cell that just won.
11
- */
12
1
  import type { PrizeDrawItem } from "../schemas/firestore";
13
2
  export interface PrizeDrawCollageProps {
14
3
  items: PrizeDrawItem[];
15
4
  highlightItemNumber?: number;
16
- /** Custom click handler — e.g. open detail modal for an item. */
5
+ /** Custom click handler — overrides the built-in lightbox. */
17
6
  onItemClick?: (item: PrizeDrawItem) => void;
18
7
  /** Defaults to "Won". Use for localisation. */
19
8
  wonLabel?: string;
@@ -22,9 +11,6 @@ export interface PrizeDrawCollageProps {
22
11
  * renders — otherwise potential buyers would see their favorite prize is
23
12
  * already gone and drop out. The seller / admin / winner views leave this
24
13
  * `false` (default) to show real pool state.
25
- *
26
- * The product adapter for public reads should ALSO strip `isWon` before
27
- * sending the items array client-side; this prop is the visual fallback.
28
14
  */
29
15
  hideWonState?: boolean;
30
16
  }