@mohasinac/appkit 4.13.1 → 4.14.0

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 (377) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +14 -5
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +3 -0
  3. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +6 -0
  4. package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +8 -0
  5. package/dist/_internal/client/features/lottery/LotteryDetailView.js +4 -1
  6. package/dist/_internal/client/features/lottery/LotteryListView.d.ts +7 -0
  7. package/dist/_internal/client/features/lottery/LotteryListView.js +1 -1
  8. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +4 -1
  9. package/dist/_internal/client/features/lottery/slot-collage-items.d.ts +15 -0
  10. package/dist/_internal/client/features/lottery/slot-collage-items.js +13 -0
  11. package/dist/_internal/server/features/auctions/actions.js +6 -0
  12. package/dist/_internal/server/features/bundles/data.d.ts +16 -0
  13. package/dist/_internal/server/features/bundles/data.js +26 -0
  14. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  15. package/dist/_internal/server/features/bundles/index.js +1 -1
  16. package/dist/_internal/server/features/checkout/actions.js +6 -0
  17. package/dist/_internal/server/features/checkout/locked-lines.d.ts +38 -0
  18. package/dist/_internal/server/features/checkout/locked-lines.js +171 -0
  19. package/dist/_internal/server/features/homepage/index.d.ts +0 -1
  20. package/dist/_internal/server/features/homepage/index.js +7 -1
  21. package/dist/_internal/server/features/lottery/adapters.js +4 -0
  22. package/dist/_internal/server/features/products/data.js +15 -37
  23. package/dist/_internal/server/features/products/index.d.ts +1 -1
  24. package/dist/_internal/server/features/products/index.js +1 -1
  25. package/dist/_internal/server/features/products/list-public.d.ts +46 -20
  26. package/dist/_internal/server/features/products/list-public.js +266 -90
  27. package/dist/_internal/server/features/products/listing-tab-counts.d.ts +44 -0
  28. package/dist/_internal/server/features/products/listing-tab-counts.js +97 -0
  29. package/dist/_internal/server/features/site-settings/adapters.d.ts +51 -0
  30. package/dist/_internal/server/features/site-settings/adapters.js +75 -0
  31. package/dist/_internal/server/features/site-settings/index.d.ts +1 -0
  32. package/dist/_internal/server/features/site-settings/index.js +1 -0
  33. package/dist/_internal/server/features/stores/adapters.d.ts +56 -0
  34. package/dist/_internal/server/features/stores/adapters.js +91 -0
  35. package/dist/_internal/server/features/stores/index.d.ts +1 -0
  36. package/dist/_internal/server/features/stores/index.js +1 -0
  37. package/dist/_internal/server/jobs/core/auctionSettlement.js +56 -2
  38. package/dist/_internal/server/jobs/core/offerExpiry.js +45 -9
  39. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.d.ts +5 -0
  40. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.js +33 -1
  41. package/dist/_internal/server/jobs/handlers/messages.d.ts +7 -0
  42. package/dist/_internal/server/jobs/handlers/messages.js +10 -0
  43. package/dist/_internal/shared/actions/action-registry.js +15 -1
  44. package/dist/_internal/shared/checkout/lanes.d.ts +25 -0
  45. package/dist/_internal/shared/checkout/lanes.js +26 -0
  46. package/dist/_internal/shared/features/auctions/config.d.ts +27 -0
  47. package/dist/_internal/shared/features/auctions/config.js +27 -0
  48. package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
  49. package/dist/_internal/shared/features/cart/schema.d.ts +12 -12
  50. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
  51. package/dist/_internal/shared/features/emi/schedule.d.ts +24 -3
  52. package/dist/_internal/shared/features/emi/schedule.js +20 -5
  53. package/dist/_internal/shared/features/events/schema.d.ts +2 -2
  54. package/dist/_internal/shared/features/homepage/section-gate.d.ts +14 -0
  55. package/dist/_internal/shared/features/homepage/section-gate.js +51 -0
  56. package/dist/_internal/shared/features/offers/config.d.ts +30 -0
  57. package/dist/_internal/shared/features/offers/config.js +59 -0
  58. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  59. package/dist/_internal/shared/features/products/schema.d.ts +32 -32
  60. package/dist/_internal/shared/features/products/to-product-item.js +6 -0
  61. package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
  62. package/dist/_internal/shared/features/site-settings/fees.d.ts +60 -0
  63. package/dist/_internal/shared/features/site-settings/fees.js +39 -0
  64. package/dist/_internal/shared/features/site-settings/types.d.ts +40 -0
  65. package/dist/_internal/shared/features/site-settings/types.js +1 -0
  66. package/dist/_internal/shared/history/history.d.ts +66 -0
  67. package/dist/_internal/shared/history/history.js +158 -0
  68. package/dist/_internal/shared/history/index.d.ts +4 -0
  69. package/dist/_internal/shared/history/index.js +2 -0
  70. package/dist/_internal/shared/history/types.d.ts +61 -0
  71. package/dist/_internal/shared/history/types.js +6 -0
  72. package/dist/_internal/shared/listing-types/_registry.d.ts +54 -1
  73. package/dist/_internal/shared/listing-types/_registry.js +67 -0
  74. package/dist/_internal/shared/listing-types/art/config.d.ts +3 -0
  75. package/dist/_internal/shared/listing-types/art/config.js +5 -0
  76. package/dist/_internal/shared/listing-types/auction/config.d.ts +13 -0
  77. package/dist/_internal/shared/listing-types/auction/config.js +21 -0
  78. package/dist/_internal/shared/listing-types/availability.d.ts +79 -0
  79. package/dist/_internal/shared/listing-types/availability.js +104 -0
  80. package/dist/_internal/shared/listing-types/capabilities.d.ts +15 -0
  81. package/dist/_internal/shared/listing-types/capabilities.js +12 -0
  82. package/dist/_internal/shared/listing-types/classified/config.d.ts +3 -0
  83. package/dist/_internal/shared/listing-types/classified/config.js +5 -0
  84. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +11 -0
  85. package/dist/_internal/shared/listing-types/digital-code/config.js +22 -2
  86. package/dist/_internal/shared/listing-types/live/config.d.ts +3 -0
  87. package/dist/_internal/shared/listing-types/live/config.js +5 -0
  88. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +3 -0
  89. package/dist/_internal/shared/listing-types/pre-order/config.js +19 -0
  90. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +11 -0
  91. package/dist/_internal/shared/listing-types/prize-draw/config.js +15 -0
  92. package/dist/_internal/shared/listing-types/standard/config.d.ts +3 -0
  93. package/dist/_internal/shared/listing-types/standard/config.js +5 -0
  94. package/dist/_internal/shared/listing-types/stickers/config.d.ts +3 -0
  95. package/dist/_internal/shared/listing-types/stickers/config.js +5 -0
  96. package/dist/client.d.ts +7 -5
  97. package/dist/client.js +2 -2
  98. package/dist/constants/api-endpoints.d.ts +6 -0
  99. package/dist/constants/api-endpoints.js +2 -0
  100. package/dist/constants/field-names.d.ts +49 -2
  101. package/dist/constants/field-names.js +53 -2
  102. package/dist/constants/table-keys.d.ts +17 -0
  103. package/dist/constants/table-keys.js +17 -0
  104. package/dist/errors/error-codes.d.ts +13 -1
  105. package/dist/errors/error-codes.js +13 -1
  106. package/dist/errors/error-display-map.js +12 -3
  107. package/dist/errors/messages.d.ts +10 -2
  108. package/dist/errors/messages.js +10 -2
  109. package/dist/features/about/components/AboutView.d.ts +3 -0
  110. package/dist/features/about/components/AboutView.js +3 -2
  111. package/dist/features/about/components/PolicyPageView.d.ts +2 -1
  112. package/dist/features/about/components/PolicyPageView.js +61 -23
  113. package/dist/features/about/components/PublicProfileView.js +5 -0
  114. package/dist/features/about/schemas/firestore.d.ts +3 -0
  115. package/dist/features/account/components/UserOrdersView.js +7 -0
  116. package/dist/features/account/components/index.d.ts +0 -2
  117. package/dist/features/account/components/index.js +0 -1
  118. package/dist/features/account/schemas/index.d.ts +2 -2
  119. package/dist/features/account/types/index.d.ts +21 -2
  120. package/dist/features/addresses/schemas/index.d.ts +4 -4
  121. package/dist/features/admin/components/AdminArtView.js +8 -3
  122. package/dist/features/admin/components/AdminClassifiedView.js +8 -3
  123. package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
  124. package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
  125. package/dist/features/admin/components/AdminLiveView.js +8 -3
  126. package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
  127. package/dist/features/admin/components/AdminOffersView.js +186 -0
  128. package/dist/features/admin/components/AdminOrdersView.js +10 -1
  129. package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
  130. package/dist/features/admin/components/AdminProductsView.js +16 -29
  131. package/dist/features/admin/components/AdminSiteSettingsView.js +31 -13
  132. package/dist/features/admin/components/AdminSiteView.js +6 -2
  133. package/dist/features/admin/components/AdminStickersView.js +8 -3
  134. package/dist/features/admin/components/index.d.ts +2 -0
  135. package/dist/features/admin/components/index.js +1 -0
  136. package/dist/features/admin/constants/filter-tabs.d.ts +86 -0
  137. package/dist/features/admin/constants/filter-tabs.js +55 -0
  138. package/dist/features/admin/schemas/firestore.d.ts +122 -3
  139. package/dist/features/admin/schemas/firestore.js +1 -0
  140. package/dist/features/auctions/actions/bid-actions.d.ts +20 -5
  141. package/dist/features/auctions/actions/bid-actions.js +93 -20
  142. package/dist/features/auctions/components/AuctionDetailPageView.js +11 -6
  143. package/dist/features/auctions/components/AuctionsListView.js +5 -6
  144. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +7 -0
  145. package/dist/features/auctions/components/MarketplaceAuctionCard.js +19 -2
  146. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -2
  147. package/dist/features/auctions/components/PlaceBidFormClient.js +8 -4
  148. package/dist/features/auctions/repository/bid.repository.d.ts +13 -0
  149. package/dist/features/auctions/repository/bid.repository.js +23 -0
  150. package/dist/features/auctions/schemas/firestore.d.ts +17 -0
  151. package/dist/features/auctions/schemas/index.d.ts +12 -12
  152. package/dist/features/auth/permissions/constants.d.ts +1 -1
  153. package/dist/features/auth/permissions/constants.js +10 -1
  154. package/dist/features/blog/actions/blog-actions.d.ts +7 -7
  155. package/dist/features/blog/schemas/index.d.ts +6 -6
  156. package/dist/features/cart/repository/cart.repository.js +1 -0
  157. package/dist/features/cart/schemas/firestore.d.ts +15 -0
  158. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  159. package/dist/features/categories/components/BrandDetailPageView.js +23 -40
  160. package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -7
  161. package/dist/features/categories/components/BrandDetailTabs.js +8 -12
  162. package/dist/features/categories/components/BundleCollage.js +1 -1
  163. package/dist/features/categories/components/CategoryDetailPageView.js +53 -57
  164. package/dist/features/categories/components/CategoryDetailTabs.d.ts +10 -8
  165. package/dist/features/categories/components/CategoryDetailTabs.js +11 -16
  166. package/dist/features/categories/components/CategoryProductsListing.js +11 -1
  167. package/dist/features/categories/components/ConcernCard.js +7 -1
  168. package/dist/features/categories/schemas/firestore.d.ts +13 -0
  169. package/dist/features/categories/schemas/index.d.ts +26 -26
  170. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +16 -1
  171. package/dist/features/classified/components/ClassifiedDetailPageView.js +23 -3
  172. package/dist/features/events/components/AdminEventsView.js +15 -11
  173. package/dist/features/events/components/EventFilters.d.ts +9 -1
  174. package/dist/features/events/components/EventFilters.js +45 -21
  175. package/dist/features/events/components/EventsIndexListing.d.ts +6 -1
  176. package/dist/features/events/components/EventsIndexListing.js +2 -2
  177. package/dist/features/events/components/EventsListPageView.js +13 -4
  178. package/dist/features/events/repository/events.repository.d.ts +17 -0
  179. package/dist/features/events/repository/events.repository.js +27 -0
  180. package/dist/features/events/schemas/firestore.d.ts +12 -0
  181. package/dist/features/events/schemas/firestore.js +12 -0
  182. package/dist/features/events/schemas/index.d.ts +50 -50
  183. package/dist/features/events/types/index.d.ts +11 -0
  184. package/dist/features/events/types/index.js +57 -1
  185. package/dist/features/faq/actions/faq-actions.d.ts +12 -12
  186. package/dist/features/faq/schemas/index.d.ts +8 -8
  187. package/dist/features/filters/FilterFacetSection.d.ts +7 -1
  188. package/dist/features/filters/FilterFacetSection.js +11 -3
  189. package/dist/features/grouped/schemas/index.d.ts +4 -4
  190. package/dist/features/homepage/components/AnnouncementBar.d.ts +7 -1
  191. package/dist/features/homepage/components/AnnouncementBar.js +3 -3
  192. package/dist/features/homepage/components/MarketplaceHomepageView.js +20 -6
  193. package/dist/features/homepage/hooks/homepage-query.d.ts +16 -0
  194. package/dist/features/homepage/hooks/homepage-query.js +45 -0
  195. package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +10 -0
  196. package/dist/features/homepage/hooks/useFeaturedAuctions.js +28 -8
  197. package/dist/features/homepage/hooks/useFeaturedPreOrders.js +17 -8
  198. package/dist/features/homepage/hooks/useFeaturedProducts.js +15 -8
  199. package/dist/features/homepage/schemas/index.d.ts +2 -2
  200. package/dist/features/layout/AppLayoutShell.d.ts +11 -1
  201. package/dist/features/layout/AppLayoutShell.js +15 -14
  202. package/dist/features/layout/BackToTop.d.ts +9 -0
  203. package/dist/features/layout/BackToTop.js +10 -1
  204. package/dist/features/layout/BottomActions.js +28 -25
  205. package/dist/features/layout/BottomChrome.d.ts +34 -0
  206. package/dist/features/layout/BottomChrome.js +55 -0
  207. package/dist/features/layout/BottomNavLayout.d.ts +6 -0
  208. package/dist/features/layout/BottomNavLayout.js +7 -1
  209. package/dist/features/layout/BottomNavbar.js +15 -2
  210. package/dist/features/layout/FooterLayout.js +11 -1
  211. package/dist/features/layout/NavbarLayout.js +1 -1
  212. package/dist/features/layout/TitleBarLayout.d.ts +7 -0
  213. package/dist/features/layout/TitleBarLayout.js +28 -5
  214. package/dist/features/layout/index.d.ts +2 -0
  215. package/dist/features/layout/index.js +1 -0
  216. package/dist/features/lottery/types/index.d.ts +3 -0
  217. package/dist/features/media/MediaPickerModal.js +5 -1
  218. package/dist/features/messages/schemas/index.d.ts +2 -2
  219. package/dist/features/orders/components/OrderScopeTabs.d.ts +20 -0
  220. package/dist/features/orders/components/OrderScopeTabs.js +31 -0
  221. package/dist/features/orders/constants/order-scope.d.ts +48 -0
  222. package/dist/features/orders/constants/order-scope.js +111 -0
  223. package/dist/features/orders/repository/orders.repository.d.ts +54 -7
  224. package/dist/features/orders/repository/orders.repository.js +116 -22
  225. package/dist/features/orders/schemas/firestore.d.ts +117 -0
  226. package/dist/features/orders/schemas/firestore.js +30 -0
  227. package/dist/features/orders/schemas/index.d.ts +18 -18
  228. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +14 -9
  229. package/dist/features/pre-orders/components/PreOrdersListView.js +6 -5
  230. package/dist/features/pre-orders/schemas/index.d.ts +1 -1
  231. package/dist/features/products/actions/product-actions.d.ts +14 -0
  232. package/dist/features/products/actions/product-actions.js +49 -11
  233. package/dist/features/products/api/route.d.ts +12 -0
  234. package/dist/features/products/api/route.js +42 -100
  235. package/dist/features/products/components/ArtStickersListView.js +2 -2
  236. package/dist/features/products/components/AuctionsIndexListing.js +13 -11
  237. package/dist/features/products/components/AvailabilityTabs.d.ts +11 -0
  238. package/dist/features/products/components/AvailabilityTabs.js +47 -0
  239. package/dist/features/products/components/MakeOfferButton.js +48 -49
  240. package/dist/features/products/components/PrizeDrawCollage.d.ts +20 -4
  241. package/dist/features/products/components/PrizeDrawCollage.js +9 -1
  242. package/dist/features/products/components/PrizeDrawsIndexListing.js +14 -12
  243. package/dist/features/products/components/PrizeDrawsListingView.js +2 -2
  244. package/dist/features/products/components/PrizeDrawsSection.d.ts +7 -1
  245. package/dist/features/products/components/PrizeDrawsSection.js +23 -18
  246. package/dist/features/products/components/ProductDetailPageView.d.ts +4 -1
  247. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  248. package/dist/features/products/components/ProductForm.js +5 -2
  249. package/dist/features/products/components/ProductGrid.js +6 -1
  250. package/dist/features/products/components/ProductsIndexListing.js +22 -32
  251. package/dist/features/products/components/ProductsIndexPageView.js +6 -9
  252. package/dist/features/products/components/index.d.ts +2 -2
  253. package/dist/features/products/components/index.js +1 -1
  254. package/dist/features/products/config/listing-type-listing-config.d.ts +20 -0
  255. package/dist/features/products/config/listing-type-listing-config.js +22 -10
  256. package/dist/features/products/constants/action-defs.d.ts +2 -0
  257. package/dist/features/products/constants/action-defs.js +6 -0
  258. package/dist/features/products/hooks/useAvailabilityScope.d.ts +13 -0
  259. package/dist/features/products/hooks/useAvailabilityScope.js +19 -0
  260. package/dist/features/products/hooks/useProducts.d.ts +5 -0
  261. package/dist/features/products/hooks/useProducts.js +13 -2
  262. package/dist/features/products/repository/products.repository.d.ts +31 -2
  263. package/dist/features/products/repository/products.repository.js +53 -21
  264. package/dist/features/products/schemas/firestore.d.ts +17 -3
  265. package/dist/features/products/schemas/firestore.js +7 -2
  266. package/dist/features/products/schemas/index.d.ts +66 -66
  267. package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
  268. package/dist/features/products/types/index.d.ts +16 -1
  269. package/dist/features/promotions/schemas/index.d.ts +2 -2
  270. package/dist/features/reviews/schemas/index.d.ts +16 -16
  271. package/dist/features/search/schemas/index.d.ts +4 -4
  272. package/dist/features/seller/actions/offer-actions.js +25 -2
  273. package/dist/features/seller/components/SellerArtView.js +5 -0
  274. package/dist/features/seller/components/SellerAuctionsView.js +13 -15
  275. package/dist/features/seller/components/SellerClassifiedView.js +5 -0
  276. package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
  277. package/dist/features/seller/components/SellerLiveView.js +5 -0
  278. package/dist/features/seller/components/SellerOrdersView.js +31 -9
  279. package/dist/features/seller/components/SellerPayoutSettingsView.js +19 -18
  280. package/dist/features/seller/components/SellerPreOrdersView.js +5 -0
  281. package/dist/features/seller/components/SellerPrizeDrawsView.js +14 -16
  282. package/dist/features/seller/components/SellerProductShell.js +7 -1
  283. package/dist/features/seller/components/SellerProductsView.js +11 -8
  284. package/dist/features/seller/components/SellerStickersView.js +5 -0
  285. package/dist/features/seller/components/index.d.ts +0 -2
  286. package/dist/features/seller/components/index.js +0 -1
  287. package/dist/features/seller/repository/offer.repository.d.ts +73 -16
  288. package/dist/features/seller/repository/offer.repository.js +38 -3
  289. package/dist/features/seller/schemas/index.d.ts +2 -2
  290. package/dist/features/seller/schemas/offer-forms.d.ts +25 -0
  291. package/dist/features/seller/schemas/offer-forms.js +21 -0
  292. package/dist/features/seller/server.d.ts +0 -1
  293. package/dist/features/seller/server.js +0 -1
  294. package/dist/features/shell/SectionForm.d.ts +99 -0
  295. package/dist/features/shell/SectionForm.js +128 -0
  296. package/dist/features/shell/entity-form.d.ts +109 -0
  297. package/dist/features/shell/entity-form.js +96 -0
  298. package/dist/features/shell/index.d.ts +4 -0
  299. package/dist/features/shell/index.js +2 -0
  300. package/dist/features/stores/api/[storeSlug]/route.js +2 -25
  301. package/dist/features/stores/components/StoreAuctionsListing.js +15 -1
  302. package/dist/features/stores/components/StoreClassifiedsListing.js +12 -2
  303. package/dist/features/stores/components/StoreDetailLayoutView.js +24 -71
  304. package/dist/features/stores/components/StoreDigitalCodesListing.js +12 -2
  305. package/dist/features/stores/components/StoreLiveItemsListing.js +12 -2
  306. package/dist/features/stores/components/StorePreOrdersListing.js +12 -1
  307. package/dist/features/stores/components/StoreProductsListing.js +11 -1
  308. package/dist/features/stores/repository/store.repository.js +6 -0
  309. package/dist/features/stores/schemas/index.d.ts +6 -6
  310. package/dist/features/support/schemas/index.d.ts +4 -4
  311. package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
  312. package/dist/features/tester/seed-data/offers-tester-seed-data.js +119 -3
  313. package/dist/features/tester/seed-data/products-tester-seed-data.js +131 -1
  314. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +105 -1
  315. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  316. package/dist/index.d.ts +13 -10
  317. package/dist/index.js +14 -9
  318. package/dist/next/routing/route-map.d.ts +7 -1
  319. package/dist/next/routing/route-map.js +5 -0
  320. package/dist/providers/db-firebase/base.repository.d.ts +30 -0
  321. package/dist/providers/db-firebase/base.repository.js +50 -1
  322. package/dist/providers/db-firebase/index.d.ts +1 -1
  323. package/dist/providers/db-firebase/index.js +1 -1
  324. package/dist/providers/db-firebase/sieve.d.ts +25 -0
  325. package/dist/providers/db-firebase/sieve.js +52 -18
  326. package/dist/react/hooks/useUrlTable.js +10 -1
  327. package/dist/schemas/registry.d.ts +68 -68
  328. package/dist/seed/_helpers/category-forest.d.ts +2 -0
  329. package/dist/seed/_helpers/category-forest.js +309 -0
  330. package/dist/seed/_helpers/category-tree.d.ts +27 -0
  331. package/dist/seed/_helpers/category-tree.js +103 -0
  332. package/dist/seed/catalogue-seed-data.js +12 -12
  333. package/dist/seed/categories-seed-data.js +177 -162
  334. package/dist/seed/events-seed-data.js +61 -10
  335. package/dist/seed/homepage-sections-seed-data.js +5 -1
  336. package/dist/seed/offers-seed-data.js +32 -27
  337. package/dist/seed/products-art-seed-data.js +50 -12
  338. package/dist/seed/products-auctions-seed-data.js +97 -18
  339. package/dist/seed/products-classifieds-seed-data.js +80 -16
  340. package/dist/seed/products-digital-codes-seed-data.js +66 -18
  341. package/dist/seed/products-live-items-seed-data.js +64 -13
  342. package/dist/seed/products-preorders-seed-data.d.ts +0 -1
  343. package/dist/seed/products-preorders-seed-data.js +52 -13
  344. package/dist/seed/products-prize-draws-seed-data.js +63 -14
  345. package/dist/seed/products-standard-seed-data.js +99 -26
  346. package/dist/seed/products-stickers-seed-data.js +47 -12
  347. package/dist/seed/reviews-seed-data.js +6 -1
  348. package/dist/seed/site-settings-seed-data.d.ts +1 -38
  349. package/dist/seed/site-settings-seed-data.js +38 -76
  350. package/dist/seed/stores-seed-data.js +71 -1
  351. package/dist/seed/users-seed-data.js +15 -5
  352. package/dist/server-entry.d.ts +1 -2
  353. package/dist/server-entry.js +4 -3
  354. package/dist/server.d.ts +4 -3
  355. package/dist/server.js +8 -4
  356. package/dist/styles.css +78 -12
  357. package/dist/tailwind-utilities.css +1 -1
  358. package/dist/ui/components/Button.style.css +19 -4
  359. package/dist/ui/components/CollapsibleSection.d.ts +17 -1
  360. package/dist/ui/components/CollapsibleSection.js +6 -2
  361. package/dist/ui/components/FilterChipGroup.d.ts +25 -1
  362. package/dist/ui/components/FilterChipGroup.js +21 -4
  363. package/dist/ui/components/HandMode.style.css +47 -0
  364. package/dist/ui/components/Layout.d.ts +9 -2
  365. package/dist/ui/components/Layout.js +1 -1
  366. package/dist/ui/components/ListingLayout.js +8 -8
  367. package/dist/ui/components/ListingLayout.style.css +8 -7
  368. package/dist/ui/components/StickyToolbar.d.ts +3 -1
  369. package/dist/ui/components/StickyToolbar.js +62 -20
  370. package/dist/ui/components/bottom-chrome-slot.d.ts +21 -0
  371. package/dist/ui/components/bottom-chrome-slot.js +29 -0
  372. package/dist/ui/components/index.style.css +3 -0
  373. package/dist/ui/forms/FormShell.d.ts +43 -13
  374. package/dist/ui/forms/FormShell.js +22 -11
  375. package/dist/ui/forms/index.d.ts +1 -1
  376. package/dist/ui/index.d.ts +1 -1
  377. package/package.json +1 -1
@@ -0,0 +1,96 @@
1
+ /** `"storeName"` / `"store_name"` / `"store.name"` -> `"Store name"`. */
2
+ export function humaniseFieldName(name) {
3
+ const leaf = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1) : name;
4
+ const spaced = leaf
5
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
6
+ .replace(/[_-]+/g, " ")
7
+ .trim();
8
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
9
+ }
10
+ /**
11
+ * The quick-mode field set: every field owned by a `quick: true` section,
12
+ * minus anything `derive()` produces.
13
+ *
14
+ * Derived, never declared — so quick and full modes cannot drift apart.
15
+ */
16
+ export function deriveQuickFieldNames(def) {
17
+ const derived = new Set(def.derivedFields ?? []);
18
+ const seen = new Set();
19
+ const out = [];
20
+ for (const section of def.sections) {
21
+ if (!section.quick)
22
+ continue;
23
+ for (const field of section.fields ?? []) {
24
+ if (derived.has(field) || seen.has(field))
25
+ continue;
26
+ seen.add(field);
27
+ out.push(field);
28
+ }
29
+ }
30
+ return out;
31
+ }
32
+ /** Quick-mode field descriptors — feed straight into QuickFormDrawer's `fields` prop. */
33
+ export function deriveQuickFields(def) {
34
+ const required = new Set(def.requiredFields ?? []);
35
+ return deriveQuickFieldNames(def).map((name) => ({
36
+ name,
37
+ label: def.quickFieldLabels?.[name] ?? humaniseFieldName(name),
38
+ type: def.quickFieldTypes?.[name] ?? "text",
39
+ required: required.has(name),
40
+ }));
41
+ }
42
+ /**
43
+ * Throws when a `derive()` output is also offered as a quick field.
44
+ *
45
+ * That combination means the form asks the user to type something the
46
+ * definition computes for them, and whichever runs last silently wins. Call
47
+ * this once per definition — it is cheap and catches the mistake at the point
48
+ * the definition is written rather than at the point data is lost.
49
+ */
50
+ export function assertNoDeriveCollision(def) {
51
+ const derived = new Set(def.derivedFields ?? []);
52
+ if (derived.size === 0)
53
+ return;
54
+ const collisions = [];
55
+ for (const section of def.sections) {
56
+ if (!section.quick)
57
+ continue;
58
+ for (const field of section.fields ?? []) {
59
+ if (derived.has(field))
60
+ collisions.push(field);
61
+ }
62
+ }
63
+ if (collisions.length > 0) {
64
+ throw new Error(`EntityFormDefinition("${def.entity}"): ${collisions.join(", ")} ` +
65
+ `${collisions.length === 1 ? "is" : "are"} produced by derive() and also ` +
66
+ `marked as quick field(s). A derived field must not be asked of the user — ` +
67
+ `drop it from the quick section's \`fields\`, or stop deriving it.`);
68
+ }
69
+ }
70
+ /**
71
+ * Every field name the definition can reach — the union of all section fields
72
+ * plus everything `derive()` produces.
73
+ *
74
+ * This is what a schema must be a superset of. A document field missing from
75
+ * this set is unreachable in the UI; a schema field missing from it can never
76
+ * be populated. `audit-form-field-roundtrip` checks both directions.
77
+ */
78
+ export function allDefinitionFields(def) {
79
+ const out = new Set(def.derivedFields ?? []);
80
+ for (const section of def.sections) {
81
+ for (const field of section.fields ?? [])
82
+ out.add(field);
83
+ }
84
+ return [...out];
85
+ }
86
+ /**
87
+ * Apply `derive()` to a pending change, without clobbering user input.
88
+ *
89
+ * Returns the partial to merge: the caller's own change plus whatever the
90
+ * definition computed from it.
91
+ */
92
+ export function applyDerive(def, partial, prev, mode) {
93
+ if (!def.derive)
94
+ return partial;
95
+ return { ...partial, ...def.derive(partial, prev, { mode }) };
96
+ }
@@ -6,5 +6,9 @@ export type { QuickFieldType, QuickFieldDef, QuickFormDrawerProps } from "./Quic
6
6
  export { QuickFormDrawer } from "./QuickFormDrawer";
7
7
  export type { StepDef, StepFormProps, StepFormActionsProps } from "./StepForm";
8
8
  export { StepForm, StepFormActions, StepIndicator } from "./StepForm";
9
+ export type { SectionDef, SectionFormProps, UseSectionFormNavResult, } from "./SectionForm";
10
+ export { SectionForm, useSectionFormNav, orderSections, buildFieldToSectionIndex, sectionAnchorId, } from "./SectionForm";
11
+ export type { EntityFormDefinition } from "./entity-form";
12
+ export { deriveQuickFields, deriveQuickFieldNames, allDefinitionFields, applyDerive, assertNoDeriveCollision, humaniseFieldName, } from "./entity-form";
9
13
  export type { TitleDescriptionGroupProps, ImageFieldGroupProps, SeoFieldGroupProps, StatusFieldGroupProps, StatusFieldGroupOption, } from "./field-groups";
10
14
  export { TitleDescriptionGroup, ImageFieldGroup, SeoFieldGroup, StatusFieldGroup, } from "./field-groups";
@@ -2,4 +2,6 @@ export { FormShell, useFormShell, FormSchemaContext } from "./FormShell";
2
2
  export { CommandPalette, useCommandPaletteHotkey } from "./CommandPalette";
3
3
  export { QuickFormDrawer } from "./QuickFormDrawer";
4
4
  export { StepForm, StepFormActions, StepIndicator } from "./StepForm";
5
+ export { SectionForm, useSectionFormNav, orderSections, buildFieldToSectionIndex, sectionAnchorId, } from "./SectionForm";
6
+ export { deriveQuickFields, deriveQuickFieldNames, allDefinitionFields, applyDerive, assertNoDeriveCollision, humaniseFieldName, } from "./entity-form";
5
7
  export { TitleDescriptionGroup, ImageFieldGroup, SeoFieldGroup, StatusFieldGroup, } from "./field-groups";
@@ -13,6 +13,7 @@
13
13
  */
14
14
  import { NextResponse } from "next/server.js";
15
15
  import { getProviders } from "../../../../contracts";
16
+ import { toStoreDetail } from "../../../../_internal/server/features/stores/adapters";
16
17
  import { normalizeError } from "../../../../errors/normalize";
17
18
  // --- GET /api/stores/[storeSlug] ---------------------------------------------
18
19
  export async function GET(_request, context) {
@@ -33,31 +34,7 @@ export async function GET(_request, context) {
33
34
  return NextResponse.json({ success: false, error: "Store not found" }, { status: 404 });
34
35
  }
35
36
  // Map to public-safe StoreDetail shape (strip internal/sensitive fields)
36
- const store = {
37
- id: raw.id,
38
- storeSlug: raw.storeSlug,
39
- ownerId: raw.ownerId,
40
- storeName: raw.storeName,
41
- storeDescription: raw.storeDescription,
42
- storeCategory: raw.storeCategory,
43
- storeLogoURL: raw.storeLogoURL,
44
- storeBannerURL: raw.storeBannerURL,
45
- status: raw.status,
46
- isPublic: raw.isPublic,
47
- totalProducts: raw.stats?.totalProducts ?? raw.totalProducts ?? undefined,
48
- itemsSold: raw.stats?.itemsSold ?? raw.itemsSold ?? undefined,
49
- totalReviews: raw.stats?.totalReviews ?? raw.totalReviews ?? undefined,
50
- averageRating: raw.stats?.averageRating ?? raw.averageRating,
51
- bio: raw.bio,
52
- location: raw.location,
53
- website: raw.website,
54
- socialLinks: raw.socialLinks,
55
- returnPolicy: raw.returnPolicy,
56
- shippingPolicy: raw.shippingPolicy,
57
- isVacationMode: raw.isVacationMode,
58
- vacationMessage: raw.vacationMessage,
59
- createdAt: raw.createdAt,
60
- };
37
+ const store = toStoreDetail(raw);
61
38
  return NextResponse.json({ success: true, data: store });
62
39
  }
63
40
  catch (error) {
@@ -13,6 +13,10 @@ import { getDefaultCurrency } from "../../../core/baseline-resolver";
13
13
  import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
14
14
  import { pushWishlistOp } from "../../cart/utils/pending-ops";
15
15
  import { AUCTION_PUBLIC_SORT_OPTIONS } from "../../products/constants/sieve";
16
+ import { AvailabilityTabs } from "../../products/components/AvailabilityTabs";
17
+ import { AVAILABILITY_VALUES } from "../../../constants/field-names";
18
+ import { TABLE_KEYS } from "../../../constants/table-keys";
19
+ const STORE_AUCTION_TYPES = ["auction"];
16
20
  const __P = {
17
21
  p3: "p-[var(--appkit-space-3)]",
18
22
  };
@@ -45,6 +49,12 @@ export function StoreAuctionsListing({ storeId, initialData }) {
45
49
  const hasActiveState = !!table.get("q") ||
46
50
  table.get("sort") !== DEFAULT_SORT ||
47
51
  filterActiveCount > 0;
52
+ // Absent means "available" — the same default `listStoreProducts`
53
+ // resolves server-side. These six store tabs sent NO availability
54
+ // param at all until 2026-08-24, while their SSR fetch did apply one,
55
+ // so any client refetch silently widened the list (Root Cause #30).
56
+ const availability = table.get(TABLE_KEYS.AVAILABILITY) ||
57
+ AVAILABILITY_VALUES.AVAILABLE;
48
58
  const params = {
49
59
  q: table.get("q") || undefined,
50
60
  minBid: table.get("minBid") ? Number(table.get("minBid")) : undefined,
@@ -56,6 +66,7 @@ export function StoreAuctionsListing({ storeId, initialData }) {
56
66
  perPage: table.getNumber("pageSize", 24),
57
67
  storeId: storeId || undefined,
58
68
  listingType: "auction",
69
+ availability,
59
70
  };
60
71
  const { products: rawAuctions, totalPages, page, isLoading } = useProducts(params, { initialData });
61
72
  const auctions = rawAuctions.map((p) => ({
@@ -75,6 +86,9 @@ export function StoreAuctionsListing({ storeId, initialData }) {
75
86
  status: p.status,
76
87
  slug: p.slug,
77
88
  buyNowPrice: p.buyNowPrice,
89
+ // Read by isBuyNowAvailable() on the card — without it a sold auction still
90
+ // offers a Buyout chip.
91
+ isSold: p.isSold,
78
92
  }));
79
93
  const commitSearch = useCallback(() => {
80
94
  // table.set("q", v) already resets page to 1 — see root-cause #13.
@@ -107,7 +121,7 @@ export function StoreAuctionsListing({ storeId, initialData }) {
107
121
  return;
108
122
  setView(v);
109
123
  table.set("view", v);
110
- }, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-[var(--appkit-space-4)]" : gridClass, wishlistActions: wishlistActions, labels: {
124
+ }, onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_AUCTION_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-[var(--appkit-space-4)]" : gridClass, wishlistActions: wishlistActions, labels: {
111
125
  emptyTitle: "No auctions yet",
112
126
  emptyDescription: "This store has no active auctions.",
113
127
  } })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9" }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
@@ -8,9 +8,12 @@ import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text, StickyToolbar
8
8
  import { ClassifiedFilters } from "../../classified/components/ClassifiedFilters";
9
9
  import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
10
10
  import { PRODUCT_FIELDS } from "../../../constants/field-names";
11
- import { TABLE_KEYS } from "../../../constants/table-keys";
12
11
  import { sortBy } from "../../../constants/sort";
13
12
  import { ROUTES } from "../../../next";
13
+ import { AvailabilityTabs } from "../../products/components/AvailabilityTabs";
14
+ import { AVAILABILITY_VALUES } from "../../../constants/field-names";
15
+ import { TABLE_KEYS } from "../../../constants/table-keys";
16
+ const STORE_CLASSIFIED_TYPES = ["classified"];
14
17
  const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
15
18
  const SORT_OPTIONS = [
16
19
  { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
@@ -28,6 +31,12 @@ export function StoreClassifiedsListing({ storeId, initialData }) {
28
31
  const applyFilters = useCallback(() => { onFilterApply(); setFilterOpen(false); }, [onFilterApply]);
29
32
  const resetAll = useCallback(() => { onResetAll({ q: "", sort: "" }); setSearchInput(""); }, [onResetAll]);
30
33
  const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || filterActiveCount > 0;
34
+ // Absent means "available" — the same default `listStoreProducts`
35
+ // resolves server-side. These six store tabs sent NO availability
36
+ // param at all until 2026-08-24, while their SSR fetch did apply one,
37
+ // so any client refetch silently widened the list (Root Cause #30).
38
+ const availability = table.get(TABLE_KEYS.AVAILABILITY) ||
39
+ AVAILABILITY_VALUES.AVAILABLE;
31
40
  const params = {
32
41
  q: table.get("q") || undefined,
33
42
  minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
@@ -44,10 +53,11 @@ export function StoreClassifiedsListing({ storeId, initialData }) {
44
53
  [TABLE_KEYS.NEGOTIABLE]: table.get(TABLE_KEYS.NEGOTIABLE),
45
54
  [TABLE_KEYS.ACCEPTS_SHIPPING]: table.get(TABLE_KEYS.ACCEPTS_SHIPPING),
46
55
  },
56
+ availability,
47
57
  };
48
58
  const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
49
59
  const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
50
- return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store classifieds\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
60
+ return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store classifieds\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_CLASSIFIED_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
51
61
  ? Array.from({ length: 8 }).map((_, i) => _jsx(Div, { surface: "muted", rounded: "xl", className: "aspect-square animate-pulse" }, i))
52
62
  : products.length === 0
53
63
  ? _jsx(Div, { paddingY: "y-4xl", className: "col-span-full text-left", children: _jsx(Text, { color: "muted", children: "No classified listings in this store yet." }) })
@@ -1,14 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { sieveFilter, sieveAnd, SIEVE_OP } from "@mohasinac/appkit";
3
3
  import { cache } from "react";
4
- import { storeRepository, productRepository, categoriesRepository, siteSettingsRepository, reviewRepository, couponsRepository } from "../../../repositories";
4
+ import { storeRepository, siteSettingsRepository, reviewRepository, couponsRepository } from "../../../repositories";
5
5
  import { ROUTES } from "../../../next";
6
6
  import { Container, Main, Section, Text } from "../../../ui";
7
7
  import { STORE_PAGE_TABS } from "../../products/constants/listing-tabs";
8
8
  import { isListingTypeEnabled, isCategoryTypeEnabled } from "../../../_internal/shared/listing-types/feature-flags";
9
+ import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
10
+ import { listingTabCounts, } from "../../../_internal/server/features/products/listing-tab-counts";
9
11
  import { getSellerTrustStatus } from "../../scams/actions/scam-actions";
10
12
  import { StoreHeader } from "./StoreHeader";
11
13
  import { StoreNavTabs } from "./StoreNavTabs";
14
+ import { toStoreDetail } from "../../../_internal/server/features/stores/adapters";
12
15
  const STORE_LISTING_HREF = {
13
16
  products: (slug) => String(ROUTES.PUBLIC.STORE_PRODUCTS(slug)),
14
17
  auctions: (slug) => String(ROUTES.PUBLIC.STORE_AUCTIONS(slug)),
@@ -41,46 +44,14 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, sc
41
44
  ? getSellerTrustStatus(storeId).catch(() => undefined)
42
45
  : Promise.resolve(undefined),
43
46
  ]);
44
- const [productsCount, auctionsCount, preOrdersCount, prizeDrawsCount, bundlesCount, classifiedsCount, digitalCodesCount, liveCount, artStickersCount, couponsCount, reviewsCount] = storeId
47
+ // One derived batch instead of nine copy-pasted per-type queries. The tabs
48
+ // themselves say which listing types to count (including the combined
49
+ // `art|stickers` OR-group), so a tenth type is counted automatically — the
50
+ // hand-written `TAB_LISTING_TYPE` map this replaces was unguarded by any
51
+ // audit and already omitted art/stickers (Root Cause #61).
52
+ const [listingCounts, couponsCount, reviewsCount] = storeId
45
53
  ? await Promise.all([
46
- productRepository
47
- .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "standard")), page: 1, pageSize: 1 })
48
- .then((r) => r.total)
49
- .catch(() => 0),
50
- productRepository
51
- .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "auction")), page: 1, pageSize: 1 })
52
- .then((r) => r.total)
53
- .catch(() => 0),
54
- productRepository
55
- .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "pre-order")), page: 1, pageSize: 1 })
56
- .then((r) => r.total)
57
- .catch(() => 0),
58
- productRepository
59
- .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "prize-draw")), page: 1, pageSize: 1 })
60
- .then((r) => r.total)
61
- .catch(() => 0),
62
- // SB-UNI-D — bundles live on categories with categoryType:"bundle"
63
- // + createdByStoreId scoping.
64
- categoriesRepository
65
- .listByType("bundle", { activeOnly: true, limit: 100 })
66
- .then((rows) => rows.filter((c) => c.createdByStoreId === storeId).length)
67
- .catch(() => 0),
68
- productRepository
69
- .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "classified")), page: 1, pageSize: 1 })
70
- .then((r) => r.total)
71
- .catch(() => 0),
72
- productRepository
73
- .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "digital-code")), page: 1, pageSize: 1 })
74
- .then((r) => r.total)
75
- .catch(() => 0),
76
- productRepository
77
- .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "live")), page: 1, pageSize: 1 })
78
- .then((r) => r.total)
79
- .catch(() => 0),
80
- productRepository
81
- .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "art|stickers")), page: 1, pageSize: 1 })
82
- .then((r) => r.total)
83
- .catch(() => 0),
54
+ listingTabCounts(STORE_PAGE_TABS, { storeId }),
84
55
  couponsRepository
85
56
  .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("validity.isActive", SIEVE_OP.EQ, "true")), page: 1, pageSize: 1 })
86
57
  .then((r) => r.total)
@@ -90,46 +61,28 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, sc
90
61
  .then((r) => r.total)
91
62
  .catch(() => 0),
92
63
  ])
93
- : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
94
- const listingCounts = {
95
- products: productsCount,
96
- auctions: auctionsCount,
97
- "pre-orders": preOrdersCount,
98
- "prize-draws": prizeDrawsCount,
99
- bundles: bundlesCount,
100
- classifieds: classifiedsCount,
101
- "digital-codes": digitalCodesCount,
102
- live: liveCount,
103
- art: artStickersCount,
104
- };
105
- const TAB_LISTING_TYPE = {
106
- products: "standard",
107
- auctions: "auction",
108
- "pre-orders": "pre-order",
109
- "prize-draws": "prize-draw",
110
- classifieds: "classified",
111
- "digital-codes": "digital-code",
112
- live: "live",
113
- };
64
+ : [{}, 0, 0];
114
65
  const visibleStoreTabs = STORE_PAGE_TABS.filter((tab) => {
115
66
  if (tab.id === "bundles") {
116
67
  if (!isCategoryTypeEnabled("bundle", settings))
117
68
  return false;
118
- return listingCounts[tab.id] > 0;
119
69
  }
120
- // Combined tab — visible if either underlying listing type is enabled.
121
- if (tab.id === "art") {
70
+ else if (tab.id === pluginFor("art").tabSlug) {
71
+ // Combined tab — visible if either underlying listing type is enabled.
122
72
  if (!(isListingTypeEnabled("art", settings) || isListingTypeEnabled("stickers", settings)))
123
73
  return false;
124
- return listingCounts[tab.id] > 0;
125
74
  }
126
- const lt = TAB_LISTING_TYPE[tab.id];
127
- if (lt && !isListingTypeEnabled(lt, settings))
128
- return false;
75
+ else if (tab.listingType) {
76
+ if (!isListingTypeEnabled(tab.listingType, settings))
77
+ return false;
78
+ }
129
79
  // A store with zero items of a given listing type shouldn't offer a tab
130
80
  // that leads to an empty page — matches the "hide empty tab" rule below
131
- // for coupons/reviews.
132
- return listingCounts[tab.id] > 0;
81
+ // for coupons/reviews. `undefined` means the count query FAILED, not zero,
82
+ // so the tab stays visible rather than hiding real stock behind a
83
+ // swallowed error (Root Cause #59) — this used to be `.catch(() => 0)`.
84
+ const count = listingCounts[tab.id];
85
+ return count === undefined || count > 0;
133
86
  });
134
87
  const dropdownTabs = visibleStoreTabs.map((tab) => ({
135
88
  value: tab.id,
@@ -145,5 +98,5 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, sc
145
98
  : []),
146
99
  { value: "about", label: "About", href: String(ROUTES.PUBLIC.STORE_ABOUT(storeSlug)) },
147
100
  ];
148
- return (_jsxs(Main, { children: [_jsx(StoreHeader, { store: store, trust: trust }), _jsxs(Container, { size: "xl", className: "mt-6", children: [_jsx(StoreNavTabs, { dropdownTabs: dropdownTabs, dropdownPlaceholder: "Browse listings", tabs: tabs, activeValue: activeTab }), _jsx(Section, { padding: "t-lg", children: children })] })] }));
101
+ return (_jsxs(Main, { children: [_jsx(StoreHeader, { store: toStoreDetail(store), trust: trust }), _jsxs(Container, { size: "xl", className: "mt-6", children: [_jsx(StoreNavTabs, { dropdownTabs: dropdownTabs, dropdownPlaceholder: "Browse listings", tabs: tabs, activeValue: activeTab }), _jsx(Section, { padding: "t-lg", children: children })] })] }));
149
102
  }
@@ -8,9 +8,12 @@ import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text, StickyToolbar
8
8
  import { DigitalCodeFilters } from "../../digital-codes/components/DigitalCodeFilters";
9
9
  import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
10
10
  import { PRODUCT_FIELDS } from "../../../constants/field-names";
11
- import { TABLE_KEYS } from "../../../constants/table-keys";
12
11
  import { sortBy } from "../../../constants/sort";
13
12
  import { ROUTES } from "../../../next";
13
+ import { AvailabilityTabs } from "../../products/components/AvailabilityTabs";
14
+ import { AVAILABILITY_VALUES } from "../../../constants/field-names";
15
+ import { TABLE_KEYS } from "../../../constants/table-keys";
16
+ const STORE_DIGITAL_CODE_TYPES = ["digital-code"];
14
17
  const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
15
18
  const SORT_OPTIONS = [
16
19
  { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
@@ -28,6 +31,12 @@ export function StoreDigitalCodesListing({ storeId, initialData }) {
28
31
  const applyFilters = useCallback(() => { onFilterApply(); setFilterOpen(false); }, [onFilterApply]);
29
32
  const resetAll = useCallback(() => { onResetAll({ q: "", sort: "" }); setSearchInput(""); }, [onResetAll]);
30
33
  const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || filterActiveCount > 0;
34
+ // Absent means "available" — the same default `listStoreProducts`
35
+ // resolves server-side. These six store tabs sent NO availability
36
+ // param at all until 2026-08-24, while their SSR fetch did apply one,
37
+ // so any client refetch silently widened the list (Root Cause #30).
38
+ const availability = table.get(TABLE_KEYS.AVAILABILITY) ||
39
+ AVAILABILITY_VALUES.AVAILABLE;
31
40
  const params = {
32
41
  q: table.get("q") || undefined,
33
42
  minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
@@ -42,10 +51,11 @@ export function StoreDigitalCodesListing({ storeId, initialData }) {
42
51
  typeFacets: {
43
52
  [TABLE_KEYS.DELIVERY_METHOD]: table.get(TABLE_KEYS.DELIVERY_METHOD),
44
53
  },
54
+ availability,
45
55
  };
46
56
  const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
47
57
  const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
48
- return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search digital codes\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
58
+ return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search digital codes\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_DIGITAL_CODE_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
49
59
  ? Array.from({ length: 8 }).map((_, i) => _jsx(Div, { surface: "muted", rounded: "xl", className: "aspect-square animate-pulse" }, i))
50
60
  : products.length === 0
51
61
  ? _jsx(Div, { paddingY: "y-4xl", className: "col-span-full text-left", children: _jsx(Text, { color: "muted", children: "No digital codes in this store yet." }) })
@@ -8,9 +8,12 @@ import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text, StickyToolbar
8
8
  import { LiveItemFilters } from "../../live/components/LiveItemFilters";
9
9
  import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
10
10
  import { PRODUCT_FIELDS } from "../../../constants/field-names";
11
- import { TABLE_KEYS } from "../../../constants/table-keys";
12
11
  import { sortBy } from "../../../constants/sort";
13
12
  import { ROUTES } from "../../../next";
13
+ import { AvailabilityTabs } from "../../products/components/AvailabilityTabs";
14
+ import { AVAILABILITY_VALUES } from "../../../constants/field-names";
15
+ import { TABLE_KEYS } from "../../../constants/table-keys";
16
+ const STORE_LIVE_TYPES = ["live"];
14
17
  const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
15
18
  const SORT_OPTIONS = [
16
19
  { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
@@ -28,6 +31,12 @@ export function StoreLiveItemsListing({ storeId, initialData }) {
28
31
  const applyFilters = useCallback(() => { onFilterApply(); setFilterOpen(false); }, [onFilterApply]);
29
32
  const resetAll = useCallback(() => { onResetAll({ q: "", sort: "" }); setSearchInput(""); }, [onResetAll]);
30
33
  const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || filterActiveCount > 0;
34
+ // Absent means "available" — the same default `listStoreProducts`
35
+ // resolves server-side. These six store tabs sent NO availability
36
+ // param at all until 2026-08-24, while their SSR fetch did apply one,
37
+ // so any client refetch silently widened the list (Root Cause #30).
38
+ const availability = table.get(TABLE_KEYS.AVAILABILITY) ||
39
+ AVAILABILITY_VALUES.AVAILABLE;
31
40
  const params = {
32
41
  q: table.get("q") || undefined,
33
42
  minPrice: table.get(TABLE_KEYS.MIN_PRICE) ? Number(table.get(TABLE_KEYS.MIN_PRICE)) : undefined,
@@ -44,10 +53,11 @@ export function StoreLiveItemsListing({ storeId, initialData }) {
44
53
  [TABLE_KEYS.LIVE_SEX]: table.get(TABLE_KEYS.LIVE_SEX),
45
54
  [TABLE_KEYS.JURISDICTION]: table.get(TABLE_KEYS.JURISDICTION),
46
55
  },
56
+ availability,
47
57
  };
48
58
  const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
49
59
  const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
50
- return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search live animals & plants\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
60
+ return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search live animals & plants\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_LIVE_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
51
61
  ? Array.from({ length: 8 }).map((_, i) => _jsx(Div, { surface: "muted", rounded: "xl", className: "aspect-square animate-pulse" }, i))
52
62
  : products.length === 0
53
63
  ? _jsx(Div, { paddingY: "y-4xl", className: "col-span-full text-left", children: _jsx(Text, { color: "muted", children: "No live items in this store yet." }) })
@@ -9,6 +9,10 @@ import { MarketplacePreorderCard } from "../../pre-orders/components/Marketplace
9
9
  import { ProductFilters } from "../../products/components/ProductFilters";
10
10
  import { ROUTES } from "../../../next";
11
11
  import { PREORDER_PUBLIC_SORT_OPTIONS } from "../../products/constants/sieve";
12
+ import { AvailabilityTabs } from "../../products/components/AvailabilityTabs";
13
+ import { AVAILABILITY_VALUES } from "../../../constants/field-names";
14
+ import { TABLE_KEYS } from "../../../constants/table-keys";
15
+ const STORE_PRE_ORDER_TYPES = ["pre-order"];
12
16
  const __P = {
13
17
  p3: "p-[var(--appkit-space-3)]",
14
18
  };
@@ -38,6 +42,12 @@ export function StorePreOrdersListing({ storeId, initialData }) {
38
42
  const hasActiveState = !!table.get("q") ||
39
43
  table.get("sort") !== DEFAULT_SORT ||
40
44
  filterActiveCount > 0;
45
+ // Absent means "available" — the same default `listStoreProducts`
46
+ // resolves server-side. These six store tabs sent NO availability
47
+ // param at all until 2026-08-24, while their SSR fetch did apply one,
48
+ // so any client refetch silently widened the list (Root Cause #30).
49
+ const availability = table.get(TABLE_KEYS.AVAILABILITY) ||
50
+ AVAILABILITY_VALUES.AVAILABLE;
41
51
  const params = {
42
52
  q: table.get("q") || undefined,
43
53
  minPrice: table.get("minPrice") ? Number(table.get("minPrice")) : undefined,
@@ -47,6 +57,7 @@ export function StorePreOrdersListing({ storeId, initialData }) {
47
57
  perPage: table.getNumber("pageSize", 24),
48
58
  storeId: storeId || undefined,
49
59
  listingType: "pre-order",
60
+ availability,
50
61
  };
51
62
  const { products: preOrders, totalPages, page, isLoading } = useProducts(params, { initialData });
52
63
  const commitSearch = useCallback(() => {
@@ -61,5 +72,5 @@ export function StorePreOrdersListing({ storeId, initialData }) {
61
72
  return;
62
73
  setView(v);
63
74
  table.set("view", v);
64
- }, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "This store has no pre-orders yet." })) : view === "list" ? (_jsx(Stack, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", border: "subtle", rounded: "xl", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.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)) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9" }) })] }));
75
+ }, onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_PRE_ORDER_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "This store has no pre-orders yet." })) : view === "list" ? (_jsx(Stack, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", border: "subtle", rounded: "xl", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.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)) }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9" }) })] }));
65
76
  }
@@ -13,6 +13,9 @@ import { ProductFilters, PRODUCT_PUBLIC_SORT_OPTIONS } from "../../products/comp
13
13
  import { useGuestCart } from "../../cart/hooks/useGuestCart";
14
14
  import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
15
15
  import { pushCartOp, pushWishlistOp } from "../../cart/utils/pending-ops";
16
+ import { AvailabilityTabs } from "../../products/components/AvailabilityTabs";
17
+ import { AVAILABILITY_VALUES } from "../../../constants/field-names";
18
+ import { TABLE_KEYS } from "../../../constants/table-keys";
16
19
  const __P = {
17
20
  p3: "p-[var(--appkit-space-3)]",
18
21
  };
@@ -43,6 +46,12 @@ export function StoreProductsListing({ storeId, listingTypes = ["standard"], ini
43
46
  const hasActiveState = !!table.get("q") ||
44
47
  table.get("sort") !== "-createdAt" ||
45
48
  filterActiveCount > 0;
49
+ // Absent means "available" — the same default `listStoreProducts`
50
+ // resolves server-side. These six store tabs sent NO availability
51
+ // param at all until 2026-08-24, while their SSR fetch did apply one,
52
+ // so any client refetch silently widened the list (Root Cause #30).
53
+ const availability = table.get(TABLE_KEYS.AVAILABILITY) ||
54
+ AVAILABILITY_VALUES.AVAILABLE;
46
55
  const params = {
47
56
  q: table.get("q") || undefined,
48
57
  condition: table.get("condition") || undefined,
@@ -55,6 +64,7 @@ export function StoreProductsListing({ storeId, listingTypes = ["standard"], ini
55
64
  storeId: storeId || undefined,
56
65
  // Pipe-joined OR-group when the tab spans several types.
57
66
  listingType: listingTypes.join("|"),
67
+ availability,
58
68
  };
59
69
  const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
60
70
  const commitSearch = useCallback(() => {
@@ -94,5 +104,5 @@ export function StoreProductsListing({ storeId, listingTypes = ["standard"], ini
94
104
  }, [localCart, showToast]);
95
105
  return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store products...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "-createdAt", sortOptions: PRODUCT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => {
96
106
  table.set("sort", v);
97
- }, view: view === "card" ? "grid" : "list", onViewChange: (v) => handleViewToggle(v === "grid" ? "card" : "list"), onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4", children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: view, emptyLabel: "This store has no products yet.", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart })) }), _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 })] }));
107
+ }, view: view === "card" ? "grid" : "list", onViewChange: (v) => handleViewToggle(v === "grid" ? "card" : "list"), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: listingTypes }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4", children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => String(ROUTES.PUBLIC.PRODUCT_DETAIL(p.slug || p.id)), view: view, emptyLabel: "This store has no products yet.", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart })) }), _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 })] }));
98
108
  }
@@ -118,6 +118,12 @@ export class StoreRepository extends BaseRepository {
118
118
  [STORE_FIELDS.STORE_CATEGORY]: { canFilter: true, canSort: false },
119
119
  [STORE_FIELDS.STATUS]: { canFilter: true, canSort: false },
120
120
  [STORE_FIELDS.IS_PUBLIC]: { canFilter: true, canSort: false },
121
+ // The public /stores toolbar has always emitted `isFeatured==true` for its
122
+ // "Featured" toggle, and the API allowlist has always accepted it — but it
123
+ // was absent from this map, so sievejs (`throwExceptions: false`) dropped
124
+ // the clause and the toggle did nothing at all. Root Cause #62's
125
+ // EMITTED_BUT_UNFILTERABLE, live in production.
126
+ [STORE_FIELDS.IS_FEATURED]: { canFilter: true, canSort: false },
121
127
  [STORE_FIELDS.CREATED_AT]: { canFilter: false, canSort: true },
122
128
  "stats.itemsSold": { canFilter: false, canSort: true },
123
129
  "stats.averageRating": { canFilter: false, canSort: true },