@mohasinac/appkit 4.13.1 → 4.15.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 (493) 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 +8 -2
  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/cart/service.d.ts +3 -3
  17. package/dist/_internal/server/features/checkout/actions.js +25 -0
  18. package/dist/_internal/server/features/checkout/locked-lines.d.ts +38 -0
  19. package/dist/_internal/server/features/checkout/locked-lines.js +171 -0
  20. package/dist/_internal/server/features/checkout/source-context.d.ts +5 -0
  21. package/dist/_internal/server/features/checkout/source-context.js +157 -0
  22. package/dist/_internal/server/features/homepage/index.d.ts +0 -1
  23. package/dist/_internal/server/features/homepage/index.js +7 -1
  24. package/dist/_internal/server/features/lottery/adapters.js +4 -0
  25. package/dist/_internal/server/features/offers/adapters.d.ts +103 -0
  26. package/dist/_internal/server/features/offers/adapters.js +88 -0
  27. package/dist/_internal/server/features/offers/index.d.ts +2 -0
  28. package/dist/_internal/server/features/offers/index.js +1 -0
  29. package/dist/_internal/server/features/orders/adapters.js +24 -0
  30. package/dist/_internal/server/features/products/data.js +15 -37
  31. package/dist/_internal/server/features/products/index.d.ts +1 -1
  32. package/dist/_internal/server/features/products/index.js +1 -1
  33. package/dist/_internal/server/features/products/list-public.d.ts +46 -20
  34. package/dist/_internal/server/features/products/list-public.js +266 -90
  35. package/dist/_internal/server/features/products/listing-tab-counts.d.ts +44 -0
  36. package/dist/_internal/server/features/products/listing-tab-counts.js +97 -0
  37. package/dist/_internal/server/features/site-settings/adapters.d.ts +51 -0
  38. package/dist/_internal/server/features/site-settings/adapters.js +75 -0
  39. package/dist/_internal/server/features/site-settings/index.d.ts +1 -0
  40. package/dist/_internal/server/features/site-settings/index.js +1 -0
  41. package/dist/_internal/server/features/stores/adapters.d.ts +56 -0
  42. package/dist/_internal/server/features/stores/adapters.js +91 -0
  43. package/dist/_internal/server/features/stores/index.d.ts +1 -0
  44. package/dist/_internal/server/features/stores/index.js +1 -0
  45. package/dist/_internal/server/jobs/core/auctionSettlement.js +56 -2
  46. package/dist/_internal/server/jobs/core/offerExpiry.js +55 -19
  47. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.d.ts +5 -0
  48. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.js +33 -1
  49. package/dist/_internal/server/jobs/handlers/messages.d.ts +7 -0
  50. package/dist/_internal/server/jobs/handlers/messages.js +10 -0
  51. package/dist/_internal/shared/actions/action-registry.js +17 -1
  52. package/dist/_internal/shared/checkout/lanes.d.ts +25 -0
  53. package/dist/_internal/shared/checkout/lanes.js +26 -0
  54. package/dist/_internal/shared/features/auctions/config.d.ts +27 -0
  55. package/dist/_internal/shared/features/auctions/config.js +27 -0
  56. package/dist/_internal/shared/features/auctions/schema.d.ts +14 -2
  57. package/dist/_internal/shared/features/auctions/schema.js +13 -1
  58. package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
  59. package/dist/_internal/shared/features/cart/schema.d.ts +20 -15
  60. package/dist/_internal/shared/features/cart/schema.js +15 -0
  61. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
  62. package/dist/_internal/shared/features/emi/schedule.d.ts +24 -3
  63. package/dist/_internal/shared/features/emi/schedule.js +20 -5
  64. package/dist/_internal/shared/features/events/schema.d.ts +2 -2
  65. package/dist/_internal/shared/features/homepage/section-gate.d.ts +14 -0
  66. package/dist/_internal/shared/features/homepage/section-gate.js +51 -0
  67. package/dist/_internal/shared/features/offers/config.d.ts +30 -0
  68. package/dist/_internal/shared/features/offers/config.js +59 -0
  69. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  70. package/dist/_internal/shared/features/products/schema.d.ts +30 -30
  71. package/dist/_internal/shared/features/products/to-product-item.js +6 -0
  72. package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
  73. package/dist/_internal/shared/features/site-settings/fees.d.ts +60 -0
  74. package/dist/_internal/shared/features/site-settings/fees.js +39 -0
  75. package/dist/_internal/shared/features/site-settings/types.d.ts +40 -0
  76. package/dist/_internal/shared/features/site-settings/types.js +1 -0
  77. package/dist/_internal/shared/history/history.d.ts +88 -0
  78. package/dist/_internal/shared/history/history.js +198 -0
  79. package/dist/_internal/shared/history/index.d.ts +4 -0
  80. package/dist/_internal/shared/history/index.js +2 -0
  81. package/dist/_internal/shared/history/types.d.ts +61 -0
  82. package/dist/_internal/shared/history/types.js +6 -0
  83. package/dist/_internal/shared/listing-types/_registry.d.ts +54 -1
  84. package/dist/_internal/shared/listing-types/_registry.js +67 -0
  85. package/dist/_internal/shared/listing-types/art/config.d.ts +3 -0
  86. package/dist/_internal/shared/listing-types/art/config.js +5 -0
  87. package/dist/_internal/shared/listing-types/auction/config.d.ts +13 -0
  88. package/dist/_internal/shared/listing-types/auction/config.js +21 -0
  89. package/dist/_internal/shared/listing-types/availability.d.ts +79 -0
  90. package/dist/_internal/shared/listing-types/availability.js +104 -0
  91. package/dist/_internal/shared/listing-types/capabilities.d.ts +15 -0
  92. package/dist/_internal/shared/listing-types/capabilities.js +12 -0
  93. package/dist/_internal/shared/listing-types/classified/config.d.ts +3 -0
  94. package/dist/_internal/shared/listing-types/classified/config.js +5 -0
  95. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +11 -0
  96. package/dist/_internal/shared/listing-types/digital-code/config.js +22 -2
  97. package/dist/_internal/shared/listing-types/live/config.d.ts +3 -0
  98. package/dist/_internal/shared/listing-types/live/config.js +5 -0
  99. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +3 -0
  100. package/dist/_internal/shared/listing-types/pre-order/config.js +19 -0
  101. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +11 -0
  102. package/dist/_internal/shared/listing-types/prize-draw/config.js +15 -0
  103. package/dist/_internal/shared/listing-types/standard/config.d.ts +3 -0
  104. package/dist/_internal/shared/listing-types/standard/config.js +5 -0
  105. package/dist/_internal/shared/listing-types/stickers/config.d.ts +3 -0
  106. package/dist/_internal/shared/listing-types/stickers/config.js +5 -0
  107. package/dist/client.d.ts +20 -7
  108. package/dist/client.js +22 -4
  109. package/dist/constants/api-endpoints.d.ts +6 -0
  110. package/dist/constants/api-endpoints.js +2 -0
  111. package/dist/constants/field-names.d.ts +57 -8
  112. package/dist/constants/field-names.js +101 -8
  113. package/dist/constants/table-keys.d.ts +17 -0
  114. package/dist/constants/table-keys.js +17 -0
  115. package/dist/errors/error-codes.d.ts +13 -1
  116. package/dist/errors/error-codes.js +13 -1
  117. package/dist/errors/error-display-map.js +12 -3
  118. package/dist/errors/messages.d.ts +10 -2
  119. package/dist/errors/messages.js +10 -2
  120. package/dist/features/about/components/AboutView.d.ts +3 -0
  121. package/dist/features/about/components/AboutView.js +3 -2
  122. package/dist/features/about/components/PolicyPageView.d.ts +2 -1
  123. package/dist/features/about/components/PolicyPageView.js +61 -23
  124. package/dist/features/about/components/PublicProfileView.js +5 -0
  125. package/dist/features/about/schemas/firestore.d.ts +3 -0
  126. package/dist/features/account/components/UserBidsView.js +12 -4
  127. package/dist/features/account/components/UserOrdersView.js +7 -0
  128. package/dist/features/account/components/UserReturnsView.d.ts +3 -0
  129. package/dist/features/account/components/UserReturnsView.js +17 -1
  130. package/dist/features/account/components/index.d.ts +0 -2
  131. package/dist/features/account/components/index.js +0 -1
  132. package/dist/features/account/schemas/index.d.ts +2 -2
  133. package/dist/features/account/types/index.d.ts +21 -2
  134. package/dist/features/addresses/schemas/index.d.ts +4 -4
  135. package/dist/features/admin/components/AdminArtView.js +8 -3
  136. package/dist/features/admin/components/AdminAuditLogView.js +1 -0
  137. package/dist/features/admin/components/AdminBidsView.js +15 -2
  138. package/dist/features/admin/components/AdminBlogEditorView.js +65 -33
  139. package/dist/features/admin/components/AdminClassifiedView.js +8 -3
  140. package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
  141. package/dist/features/admin/components/AdminFeatureEditorView.js +35 -6
  142. package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
  143. package/dist/features/admin/components/AdminLiveView.js +8 -3
  144. package/dist/features/admin/components/AdminNavEditorView.js +15 -5
  145. package/dist/features/admin/components/AdminNewsletterView.js +18 -2
  146. package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
  147. package/dist/features/admin/components/AdminOffersView.js +230 -0
  148. package/dist/features/admin/components/AdminOrdersView.js +10 -1
  149. package/dist/features/admin/components/AdminPaymentMethodsView.js +24 -4
  150. package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
  151. package/dist/features/admin/components/AdminProductsView.js +16 -29
  152. package/dist/features/admin/components/AdminScammerEditorView.d.ts +2 -1
  153. package/dist/features/admin/components/AdminScammerEditorView.js +3 -13
  154. package/dist/features/admin/components/AdminScammersView.js +3 -8
  155. package/dist/features/admin/components/AdminSectionsView.js +32 -23
  156. package/dist/features/admin/components/AdminSessionsView.js +17 -2
  157. package/dist/features/admin/components/AdminSiteSettingsView.js +31 -13
  158. package/dist/features/admin/components/AdminSiteView.js +6 -2
  159. package/dist/features/admin/components/AdminStickersView.js +8 -3
  160. package/dist/features/admin/components/ViewAuditLogEntryModal.js +1 -0
  161. package/dist/features/admin/components/index.d.ts +2 -0
  162. package/dist/features/admin/components/index.js +1 -0
  163. package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -2
  164. package/dist/features/admin/components/sections/adminSectionsTypes.js +45 -27
  165. package/dist/features/admin/constants/filter-tabs.d.ts +89 -0
  166. package/dist/features/admin/constants/filter-tabs.js +56 -0
  167. package/dist/features/admin/schemas/firestore.d.ts +122 -3
  168. package/dist/features/admin/schemas/firestore.js +1 -0
  169. package/dist/features/admin/schemas/homepage-section-form.d.ts +22 -0
  170. package/dist/features/admin/schemas/homepage-section-form.js +68 -0
  171. package/dist/features/admin/schemas/nav-item-form.d.ts +74 -0
  172. package/dist/features/admin/schemas/nav-item-form.js +72 -0
  173. package/dist/features/auctions/actions/bid-actions.d.ts +20 -5
  174. package/dist/features/auctions/actions/bid-actions.js +93 -20
  175. package/dist/features/auctions/components/AuctionBidsTable.d.ts +10 -1
  176. package/dist/features/auctions/components/AuctionBidsTable.js +22 -4
  177. package/dist/features/auctions/components/AuctionDetailPageView.js +11 -6
  178. package/dist/features/auctions/components/AuctionsListView.js +5 -6
  179. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +7 -0
  180. package/dist/features/auctions/components/MarketplaceAuctionCard.js +19 -2
  181. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -2
  182. package/dist/features/auctions/components/PlaceBidFormClient.js +8 -4
  183. package/dist/features/auctions/components/bid-detail-fields.d.ts +29 -0
  184. package/dist/features/auctions/components/bid-detail-fields.js +103 -0
  185. package/dist/features/auctions/repository/bid.repository.d.ts +13 -0
  186. package/dist/features/auctions/repository/bid.repository.js +23 -0
  187. package/dist/features/auctions/schemas/firestore.d.ts +17 -0
  188. package/dist/features/auctions/schemas/index.d.ts +12 -12
  189. package/dist/features/audit-log/schemas/firestore.d.ts +14 -0
  190. package/dist/features/audit-log/schemas/firestore.js +7 -0
  191. package/dist/features/auth/permissions/constants.d.ts +1 -1
  192. package/dist/features/auth/permissions/constants.js +10 -1
  193. package/dist/features/blog/actions/blog-actions.d.ts +7 -7
  194. package/dist/features/blog/schemas/blog-form.d.ts +80 -0
  195. package/dist/features/blog/schemas/blog-form.js +100 -0
  196. package/dist/features/blog/schemas/index.d.ts +6 -6
  197. package/dist/features/cart/repository/cart.repository.js +1 -0
  198. package/dist/features/cart/schemas/firestore.d.ts +15 -0
  199. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  200. package/dist/features/categories/components/BrandDetailPageView.js +23 -40
  201. package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -7
  202. package/dist/features/categories/components/BrandDetailTabs.js +8 -12
  203. package/dist/features/categories/components/BundleCollage.js +1 -1
  204. package/dist/features/categories/components/CategoryDetailPageView.js +53 -57
  205. package/dist/features/categories/components/CategoryDetailTabs.d.ts +10 -8
  206. package/dist/features/categories/components/CategoryDetailTabs.js +11 -16
  207. package/dist/features/categories/components/CategoryProductsListing.js +11 -1
  208. package/dist/features/categories/components/ConcernCard.js +7 -1
  209. package/dist/features/categories/schemas/firestore.d.ts +13 -0
  210. package/dist/features/categories/schemas/index.d.ts +26 -26
  211. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +16 -1
  212. package/dist/features/classified/components/ClassifiedDetailPageView.js +23 -3
  213. package/dist/features/events/components/AdminEventEditorView.js +32 -54
  214. package/dist/features/events/components/AdminEventEntriesView.js +22 -3
  215. package/dist/features/events/components/AdminEventsView.js +15 -11
  216. package/dist/features/events/components/EventFilters.d.ts +9 -1
  217. package/dist/features/events/components/EventFilters.js +45 -21
  218. package/dist/features/events/components/EventsIndexListing.d.ts +6 -1
  219. package/dist/features/events/components/EventsIndexListing.js +2 -2
  220. package/dist/features/events/components/EventsListPageView.js +13 -4
  221. package/dist/features/events/repository/events.repository.d.ts +17 -0
  222. package/dist/features/events/repository/events.repository.js +27 -0
  223. package/dist/features/events/schemas/event-form.d.ts +113 -0
  224. package/dist/features/events/schemas/event-form.js +82 -0
  225. package/dist/features/events/schemas/firestore.d.ts +12 -0
  226. package/dist/features/events/schemas/firestore.js +12 -0
  227. package/dist/features/events/schemas/index.d.ts +50 -50
  228. package/dist/features/events/types/index.d.ts +11 -0
  229. package/dist/features/events/types/index.js +57 -1
  230. package/dist/features/faq/actions/faq-actions.d.ts +12 -12
  231. package/dist/features/faq/schemas/index.d.ts +8 -8
  232. package/dist/features/filters/FilterFacetSection.d.ts +7 -1
  233. package/dist/features/filters/FilterFacetSection.js +11 -3
  234. package/dist/features/grouped/schemas/grouped-listing-form.d.ts +101 -0
  235. package/dist/features/grouped/schemas/grouped-listing-form.js +89 -0
  236. package/dist/features/grouped/schemas/index.d.ts +4 -4
  237. package/dist/features/homepage/actions/homepage-section-actions.d.ts +11 -3
  238. package/dist/features/homepage/actions/homepage-section-actions.js +33 -1
  239. package/dist/features/homepage/components/AnnouncementBar.d.ts +7 -1
  240. package/dist/features/homepage/components/AnnouncementBar.js +3 -3
  241. package/dist/features/homepage/components/MarketplaceHomepageView.js +20 -6
  242. package/dist/features/homepage/hooks/homepage-query.d.ts +16 -0
  243. package/dist/features/homepage/hooks/homepage-query.js +45 -0
  244. package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +10 -0
  245. package/dist/features/homepage/hooks/useFeaturedAuctions.js +28 -8
  246. package/dist/features/homepage/hooks/useFeaturedPreOrders.js +17 -8
  247. package/dist/features/homepage/hooks/useFeaturedProducts.js +15 -8
  248. package/dist/features/homepage/schemas/index.d.ts +33 -2
  249. package/dist/features/homepage/schemas/index.js +24 -0
  250. package/dist/features/layout/AppLayoutShell.d.ts +11 -1
  251. package/dist/features/layout/AppLayoutShell.js +15 -14
  252. package/dist/features/layout/BackToTop.d.ts +9 -0
  253. package/dist/features/layout/BackToTop.js +10 -1
  254. package/dist/features/layout/BottomActions.js +28 -25
  255. package/dist/features/layout/BottomChrome.d.ts +34 -0
  256. package/dist/features/layout/BottomChrome.js +55 -0
  257. package/dist/features/layout/BottomNavLayout.d.ts +6 -0
  258. package/dist/features/layout/BottomNavLayout.js +7 -1
  259. package/dist/features/layout/BottomNavbar.js +15 -2
  260. package/dist/features/layout/FooterLayout.js +11 -1
  261. package/dist/features/layout/NavbarLayout.js +1 -1
  262. package/dist/features/layout/TitleBarLayout.d.ts +7 -0
  263. package/dist/features/layout/TitleBarLayout.js +28 -5
  264. package/dist/features/layout/index.d.ts +2 -0
  265. package/dist/features/layout/index.js +1 -0
  266. package/dist/features/lottery/types/index.d.ts +3 -0
  267. package/dist/features/media/MediaPickerModal.js +5 -1
  268. package/dist/features/messages/schemas/index.d.ts +2 -2
  269. package/dist/features/orders/components/OrderScopeTabs.d.ts +20 -0
  270. package/dist/features/orders/components/OrderScopeTabs.js +31 -0
  271. package/dist/features/orders/components/OrderStatusTimeline.d.ts +5 -1
  272. package/dist/features/orders/components/OrderStatusTimeline.js +39 -20
  273. package/dist/features/orders/constants/order-scope.d.ts +48 -0
  274. package/dist/features/orders/constants/order-scope.js +111 -0
  275. package/dist/features/orders/repository/orders.repository.d.ts +54 -7
  276. package/dist/features/orders/repository/orders.repository.js +121 -22
  277. package/dist/features/orders/schemas/firestore.d.ts +152 -0
  278. package/dist/features/orders/schemas/firestore.js +30 -0
  279. package/dist/features/orders/schemas/index.d.ts +18 -18
  280. package/dist/features/orders/types/index.d.ts +35 -7
  281. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +14 -9
  282. package/dist/features/pre-orders/components/PreOrdersListView.js +6 -5
  283. package/dist/features/pre-orders/schemas/index.d.ts +1 -1
  284. package/dist/features/products/actions/product-actions.d.ts +14 -0
  285. package/dist/features/products/actions/product-actions.js +49 -11
  286. package/dist/features/products/api/route.d.ts +12 -0
  287. package/dist/features/products/api/route.js +42 -100
  288. package/dist/features/products/components/ArtStickersListView.js +2 -2
  289. package/dist/features/products/components/AuctionsIndexListing.js +13 -11
  290. package/dist/features/products/components/AvailabilityTabs.d.ts +11 -0
  291. package/dist/features/products/components/AvailabilityTabs.js +47 -0
  292. package/dist/features/products/components/GroupSettingsPanel.js +5 -1
  293. package/dist/features/products/components/MakeOfferButton.js +48 -49
  294. package/dist/features/products/components/PrizeDrawCollage.d.ts +20 -4
  295. package/dist/features/products/components/PrizeDrawCollage.js +9 -1
  296. package/dist/features/products/components/PrizeDrawsIndexListing.js +14 -12
  297. package/dist/features/products/components/PrizeDrawsListingView.js +2 -2
  298. package/dist/features/products/components/PrizeDrawsSection.d.ts +7 -1
  299. package/dist/features/products/components/PrizeDrawsSection.js +23 -18
  300. package/dist/features/products/components/ProductDetailPageView.d.ts +4 -1
  301. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  302. package/dist/features/products/components/ProductForm.js +5 -2
  303. package/dist/features/products/components/ProductGrid.js +6 -1
  304. package/dist/features/products/components/ProductsIndexListing.js +22 -32
  305. package/dist/features/products/components/ProductsIndexPageView.js +6 -9
  306. package/dist/features/products/components/index.d.ts +2 -2
  307. package/dist/features/products/components/index.js +1 -1
  308. package/dist/features/products/config/listing-type-listing-config.d.ts +20 -0
  309. package/dist/features/products/config/listing-type-listing-config.js +22 -10
  310. package/dist/features/products/constants/action-defs.d.ts +2 -0
  311. package/dist/features/products/constants/action-defs.js +6 -0
  312. package/dist/features/products/hooks/useAvailabilityScope.d.ts +13 -0
  313. package/dist/features/products/hooks/useAvailabilityScope.js +19 -0
  314. package/dist/features/products/hooks/useProducts.d.ts +5 -0
  315. package/dist/features/products/hooks/useProducts.js +13 -2
  316. package/dist/features/products/repository/products.repository.d.ts +31 -2
  317. package/dist/features/products/repository/products.repository.js +53 -21
  318. package/dist/features/products/schemas/firestore.d.ts +17 -3
  319. package/dist/features/products/schemas/firestore.js +7 -2
  320. package/dist/features/products/schemas/index.d.ts +64 -64
  321. package/dist/features/products/schemas/product-features.validators.d.ts +79 -6
  322. package/dist/features/products/schemas/product-features.validators.js +63 -0
  323. package/dist/features/products/types/index.d.ts +16 -1
  324. package/dist/features/promotions/schemas/index.d.ts +2 -2
  325. package/dist/features/reviews/schemas/index.d.ts +16 -16
  326. package/dist/features/scams/constants/scammer-status-display.d.ts +28 -0
  327. package/dist/features/scams/constants/scammer-status-display.js +79 -0
  328. package/dist/features/search/schemas/index.d.ts +4 -4
  329. package/dist/features/seller/actions/offer-actions.js +57 -4
  330. package/dist/features/seller/components/OfferPhaseTimeline.d.ts +59 -0
  331. package/dist/features/seller/components/OfferPhaseTimeline.js +107 -0
  332. package/dist/features/seller/components/SellerArtView.js +5 -0
  333. package/dist/features/seller/components/SellerAuctionsView.js +13 -15
  334. package/dist/features/seller/components/SellerBidsView.js +11 -4
  335. package/dist/features/seller/components/SellerClassifiedView.js +5 -0
  336. package/dist/features/seller/components/SellerCouponEditorView.js +17 -17
  337. package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
  338. package/dist/features/seller/components/SellerLiveView.js +5 -0
  339. package/dist/features/seller/components/SellerOffersView.js +2 -1
  340. package/dist/features/seller/components/SellerOrdersView.js +31 -9
  341. package/dist/features/seller/components/SellerPayoutSettingsView.js +56 -31
  342. package/dist/features/seller/components/SellerPreOrdersView.js +5 -0
  343. package/dist/features/seller/components/SellerPrizeDrawsView.js +14 -16
  344. package/dist/features/seller/components/SellerProductShell.js +7 -1
  345. package/dist/features/seller/components/SellerProductsView.js +11 -8
  346. package/dist/features/seller/components/SellerStickersView.js +5 -0
  347. package/dist/features/seller/components/index.d.ts +0 -2
  348. package/dist/features/seller/components/index.js +0 -1
  349. package/dist/features/seller/repository/offer.repository.d.ts +142 -24
  350. package/dist/features/seller/repository/offer.repository.js +165 -26
  351. package/dist/features/seller/schemas/coupon-form.d.ts +72 -0
  352. package/dist/features/seller/schemas/coupon-form.js +116 -0
  353. package/dist/features/seller/schemas/firestore.d.ts +56 -2
  354. package/dist/features/seller/schemas/firestore.js +27 -0
  355. package/dist/features/seller/schemas/index.d.ts +2 -2
  356. package/dist/features/seller/schemas/offer-forms.d.ts +46 -0
  357. package/dist/features/seller/schemas/offer-forms.js +42 -0
  358. package/dist/features/seller/server.d.ts +0 -1
  359. package/dist/features/seller/server.js +0 -1
  360. package/dist/features/shell/SectionForm.d.ts +99 -0
  361. package/dist/features/shell/SectionForm.js +128 -0
  362. package/dist/features/shell/build-sections.d.ts +63 -0
  363. package/dist/features/shell/build-sections.js +207 -0
  364. package/dist/features/shell/entity-form.d.ts +109 -0
  365. package/dist/features/shell/entity-form.js +96 -0
  366. package/dist/features/shell/field-ui-meta.d.ts +115 -0
  367. package/dist/features/shell/field-ui-meta.js +324 -0
  368. package/dist/features/shell/index.d.ts +8 -0
  369. package/dist/features/shell/index.js +4 -0
  370. package/dist/features/status-history/components/StatusTimeline.d.ts +74 -0
  371. package/dist/features/status-history/components/StatusTimeline.js +46 -0
  372. package/dist/features/status-history/index.d.ts +11 -0
  373. package/dist/features/status-history/index.js +10 -0
  374. package/dist/features/store-extensions/components/ReviewDecisionModal.d.ts +27 -0
  375. package/dist/features/store-extensions/components/ReviewDecisionModal.js +82 -0
  376. package/dist/features/store-extensions/index.d.ts +12 -0
  377. package/dist/features/store-extensions/index.js +12 -0
  378. package/dist/features/store-extensions/schemas/admin-notification-form.d.ts +36 -0
  379. package/dist/features/store-extensions/schemas/admin-notification-form.js +114 -0
  380. package/dist/features/store-extensions/schemas/analytics-forms.d.ts +57 -0
  381. package/dist/features/store-extensions/schemas/analytics-forms.js +158 -0
  382. package/dist/features/store-extensions/schemas/custom-role-form.d.ts +85 -0
  383. package/dist/features/store-extensions/schemas/custom-role-form.js +118 -0
  384. package/dist/features/store-extensions/schemas/firestore.d.ts +21 -1
  385. package/dist/features/store-extensions/schemas/firestore.js +60 -0
  386. package/dist/features/store-extensions/schemas/google-config-form.d.ts +53 -0
  387. package/dist/features/store-extensions/schemas/google-config-form.js +105 -0
  388. package/dist/features/store-extensions/schemas/item-request-create-form.d.ts +28 -0
  389. package/dist/features/store-extensions/schemas/item-request-create-form.js +80 -0
  390. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +78 -0
  391. package/dist/features/store-extensions/schemas/listing-template-form.js +87 -0
  392. package/dist/features/store-extensions/schemas/moderation-review-form.d.ts +49 -0
  393. package/dist/features/store-extensions/schemas/moderation-review-form.js +101 -0
  394. package/dist/features/store-extensions/schemas/payout-method-form.d.ts +153 -0
  395. package/dist/features/store-extensions/schemas/payout-method-form.js +155 -0
  396. package/dist/features/store-extensions/schemas/report-create-form.d.ts +25 -0
  397. package/dist/features/store-extensions/schemas/report-create-form.js +79 -0
  398. package/dist/features/store-extensions/schemas/report-review-form.d.ts +47 -0
  399. package/dist/features/store-extensions/schemas/report-review-form.js +111 -0
  400. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +148 -0
  401. package/dist/features/store-extensions/schemas/shipping-config-form.js +104 -0
  402. package/dist/features/store-extensions/schemas/store-category-form.d.ts +54 -0
  403. package/dist/features/store-extensions/schemas/store-category-form.js +73 -0
  404. package/dist/features/stores/api/[storeSlug]/route.js +2 -25
  405. package/dist/features/stores/components/StoreAuctionsListing.js +15 -1
  406. package/dist/features/stores/components/StoreClassifiedsListing.js +12 -2
  407. package/dist/features/stores/components/StoreDetailLayoutView.js +24 -71
  408. package/dist/features/stores/components/StoreDigitalCodesListing.js +12 -2
  409. package/dist/features/stores/components/StoreLiveItemsListing.js +12 -2
  410. package/dist/features/stores/components/StorePreOrdersListing.js +12 -1
  411. package/dist/features/stores/components/StoreProductsListing.js +11 -1
  412. package/dist/features/stores/repository/store.repository.js +6 -0
  413. package/dist/features/stores/schemas/index.d.ts +6 -6
  414. package/dist/features/support/schemas/index.d.ts +4 -4
  415. package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
  416. package/dist/features/tester/seed-data/offers-tester-seed-data.js +119 -3
  417. package/dist/features/tester/seed-data/products-tester-seed-data.js +131 -1
  418. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +431 -1
  419. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  420. package/dist/index.d.ts +25 -12
  421. package/dist/index.js +26 -11
  422. package/dist/next/routing/route-map.d.ts +23 -3
  423. package/dist/next/routing/route-map.js +14 -2
  424. package/dist/providers/db-firebase/base.repository.d.ts +30 -0
  425. package/dist/providers/db-firebase/base.repository.js +50 -1
  426. package/dist/providers/db-firebase/index.d.ts +1 -1
  427. package/dist/providers/db-firebase/index.js +1 -1
  428. package/dist/providers/db-firebase/sieve.d.ts +25 -0
  429. package/dist/providers/db-firebase/sieve.js +52 -18
  430. package/dist/react/hooks/useUrlTable.js +10 -1
  431. package/dist/schemas/firestore-value.d.ts +8 -0
  432. package/dist/schemas/firestore-value.js +44 -0
  433. package/dist/schemas/index.d.ts +1 -1
  434. package/dist/schemas/index.js +1 -1
  435. package/dist/schemas/registry.d.ts +702 -69
  436. package/dist/schemas/registry.js +64 -1
  437. package/dist/seed/_helpers/category-forest.d.ts +2 -0
  438. package/dist/seed/_helpers/category-forest.js +309 -0
  439. package/dist/seed/_helpers/category-tree.d.ts +27 -0
  440. package/dist/seed/_helpers/category-tree.js +103 -0
  441. package/dist/seed/bids-seed-data.js +30 -0
  442. package/dist/seed/catalogue-seed-data.js +12 -12
  443. package/dist/seed/categories-seed-data.js +177 -162
  444. package/dist/seed/events-seed-data.js +61 -10
  445. package/dist/seed/homepage-sections-seed-data.js +5 -1
  446. package/dist/seed/offers-seed-data.js +244 -34
  447. package/dist/seed/orders-seed-data.js +336 -1
  448. package/dist/seed/products-art-seed-data.js +50 -12
  449. package/dist/seed/products-auctions-seed-data.js +138 -18
  450. package/dist/seed/products-classifieds-seed-data.js +80 -16
  451. package/dist/seed/products-digital-codes-seed-data.js +66 -18
  452. package/dist/seed/products-live-items-seed-data.js +64 -13
  453. package/dist/seed/products-preorders-seed-data.d.ts +0 -1
  454. package/dist/seed/products-preorders-seed-data.js +52 -13
  455. package/dist/seed/products-prize-draws-seed-data.js +63 -14
  456. package/dist/seed/products-standard-seed-data.js +99 -26
  457. package/dist/seed/products-stickers-seed-data.js +47 -12
  458. package/dist/seed/reviews-seed-data.js +6 -1
  459. package/dist/seed/site-settings-seed-data.d.ts +1 -38
  460. package/dist/seed/site-settings-seed-data.js +38 -76
  461. package/dist/seed/stores-seed-data.js +71 -1
  462. package/dist/seed/users-seed-data.js +15 -5
  463. package/dist/server-entry.d.ts +1 -2
  464. package/dist/server-entry.js +4 -3
  465. package/dist/server.d.ts +6 -3
  466. package/dist/server.js +13 -4
  467. package/dist/styles.css +78 -12
  468. package/dist/tailwind-utilities.css +1 -1
  469. package/dist/ui/components/Button.style.css +19 -4
  470. package/dist/ui/components/CollapsibleSection.d.ts +17 -1
  471. package/dist/ui/components/CollapsibleSection.js +6 -2
  472. package/dist/ui/components/FilterChipGroup.d.ts +25 -1
  473. package/dist/ui/components/FilterChipGroup.js +21 -4
  474. package/dist/ui/components/HandMode.style.css +47 -0
  475. package/dist/ui/components/Layout.d.ts +9 -2
  476. package/dist/ui/components/Layout.js +1 -1
  477. package/dist/ui/components/ListingLayout.js +8 -8
  478. package/dist/ui/components/ListingLayout.style.css +8 -7
  479. package/dist/ui/components/RecordDetailModal.d.ts +10 -1
  480. package/dist/ui/components/RecordDetailModal.js +2 -2
  481. package/dist/ui/components/StickyToolbar.d.ts +3 -1
  482. package/dist/ui/components/StickyToolbar.js +62 -20
  483. package/dist/ui/components/bottom-chrome-slot.d.ts +21 -0
  484. package/dist/ui/components/bottom-chrome-slot.js +29 -0
  485. package/dist/ui/components/index.style.css +3 -0
  486. package/dist/ui/forms/FormShell.d.ts +43 -13
  487. package/dist/ui/forms/FormShell.js +22 -11
  488. package/dist/ui/forms/index.d.ts +1 -1
  489. package/dist/ui/index.d.ts +1 -1
  490. package/package.json +2 -1
  491. package/scripts/node-esm-loader.mjs +65 -0
  492. package/scripts/seed-cli-loader.mjs +6 -49
  493. package/scripts/seed-cli.mjs +1 -1
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Tracked-field diffing and capped append for entity status history.
3
+ *
4
+ * These are deliberately plain functions rather than a `BaseRepository`
5
+ * override: the repository method that owns a transition already knows the
6
+ * actor and the trigger, and a generic hook would have to guess both. The
7
+ * repository calls `buildHistoryEntry` + `appendHistoryEntry` inside its own
8
+ * write primitive — which is the point, since that is what keeps the 32
9
+ * call sites above it unchanged.
10
+ */
11
+ import { normalizeError } from "../../../errors/normalize";
12
+ import { STATUS_HISTORY_MAX, } from "./types";
13
+ /**
14
+ * Structural equality for field values.
15
+ *
16
+ * Tracked fields are not all scalars, so identity comparison would flag an
17
+ * unchanged object as changed on every write. Dates compare by instant,
18
+ * because Firestore hands back a fresh object on every read — without this,
19
+ * re-saving a document with no edits would append a history entry.
20
+ */
21
+ function isSameValue(a, b) {
22
+ if (a === b)
23
+ return true;
24
+ if (a == null || b == null)
25
+ return a == null && b == null;
26
+ const aDate = toDateOrNull(a);
27
+ const bDate = toDateOrNull(b);
28
+ if (aDate && bDate)
29
+ return aDate.getTime() === bDate.getTime();
30
+ if (aDate || bDate)
31
+ return false;
32
+ if (typeof a !== "object" || typeof b !== "object")
33
+ return false;
34
+ try {
35
+ return JSON.stringify(a) === JSON.stringify(b);
36
+ }
37
+ catch (err) {
38
+ void normalizeError(err);
39
+ // Circular or non-serialisable — treat as changed rather than throwing
40
+ // inside a write path.
41
+ return false;
42
+ }
43
+ }
44
+ /** Firestore Timestamps expose `toDate()`; plain Dates are used directly. */
45
+ function toDateOrNull(v) {
46
+ if (v instanceof Date)
47
+ return v;
48
+ if (typeof v === "object" && v !== null && typeof v.toDate === "function") {
49
+ try {
50
+ return v.toDate();
51
+ }
52
+ catch (err) {
53
+ void normalizeError(err);
54
+ // A Timestamp-like object whose toDate() threw is not a usable date;
55
+ // the caller treats null as "not a date" and falls through to structural
56
+ // comparison, so there is nothing to report.
57
+ return null;
58
+ }
59
+ }
60
+ return null;
61
+ }
62
+ /** Read a possibly-dotted path (`emiInstallments.0.status`) off an object. */
63
+ function readPath(source, path) {
64
+ if (!source)
65
+ return undefined;
66
+ if (!path.includes("."))
67
+ return source[path];
68
+ let cursor = source;
69
+ for (const segment of path.split(".")) {
70
+ if (cursor == null || typeof cursor !== "object")
71
+ return undefined;
72
+ cursor = cursor[segment];
73
+ }
74
+ return cursor;
75
+ }
76
+ /**
77
+ * Diff only the fields an entity declares as tracked.
78
+ *
79
+ * The tracked list is explicit per entity, never "every field": a whole-
80
+ * document diff would record the `updatedAt` bump on every single write and
81
+ * exhaust the 50-entry cap within days, burying the transitions anyone
82
+ * actually wants to read.
83
+ *
84
+ * A field absent from `patch` is untouched — this diffs the patch against
85
+ * the current document, not two full documents.
86
+ */
87
+ export function diffTrackedFields(current, patch, tracked) {
88
+ const changes = {};
89
+ for (const field of tracked) {
90
+ if (!(field in patch) && !field.includes("."))
91
+ continue;
92
+ const to = readPath(patch, field);
93
+ if (to === undefined)
94
+ continue;
95
+ const from = readPath(current, field);
96
+ if (isSameValue(from, to))
97
+ continue;
98
+ changes[field] = { from: from ?? null, to };
99
+ }
100
+ return changes;
101
+ }
102
+ /**
103
+ * Remove PII-named keys from a `changes` map, at any depth.
104
+ *
105
+ * Two passes are needed because PII can arrive two ways: as a tracked field
106
+ * that happens to be PII (`buyerEmail` in `changes`), or nested inside a
107
+ * value a caller contributed via `extraChanges` (a refund's `reason`, an
108
+ * offer note object). `encryptPiiFields` catches neither — it only ever
109
+ * looks at top-level string properties of the document itself.
110
+ *
111
+ * Scrubbed rather than redacted-in-place: an entry that silently omits a
112
+ * field is honest, whereas one containing `"[redacted]"` invites a reader to
113
+ * assume the real value is recoverable somewhere.
114
+ */
115
+ function scrubPii(changes, piiFields) {
116
+ if (!piiFields?.length)
117
+ return changes;
118
+ const pii = new Set(piiFields);
119
+ const stripNested = (v) => {
120
+ if (v == null || typeof v !== "object")
121
+ return v;
122
+ if (v instanceof Date)
123
+ return v;
124
+ if (Array.isArray(v))
125
+ return v.map(stripNested);
126
+ const out = {};
127
+ for (const [k, val] of Object.entries(v)) {
128
+ if (pii.has(k))
129
+ continue;
130
+ out[k] = stripNested(val);
131
+ }
132
+ return out;
133
+ };
134
+ const result = {};
135
+ for (const [field, change] of Object.entries(changes)) {
136
+ if (pii.has(field))
137
+ continue; // the tracked field itself is PII
138
+ result[field] = { from: stripNested(change.from), to: stripNested(change.to) };
139
+ }
140
+ return result;
141
+ }
142
+ export function buildHistoryEntry(input) {
143
+ const entry = {
144
+ at: input.now ?? new Date(),
145
+ actorRole: input.actor.role,
146
+ changes: input.changes,
147
+ trigger: input.trigger,
148
+ };
149
+ if (input.actor.uid)
150
+ entry.actorUid = input.actor.uid;
151
+ if (input.reason)
152
+ entry.reason = input.reason;
153
+ if (input.note)
154
+ entry.note = input.note;
155
+ return entry;
156
+ }
157
+ /**
158
+ * Append with a FIFO cap. Returns the new array plus the running count of
159
+ * entries dropped over the document's lifetime, so the caller can persist it
160
+ * and the UI can be honest about what it is not showing.
161
+ */
162
+ export function appendHistoryEntry(existing, entry, previousTruncatedCount = 0, cap = STATUS_HISTORY_MAX) {
163
+ const next = [...(existing ?? []), entry];
164
+ if (next.length <= cap) {
165
+ return { history: next, truncatedCount: previousTruncatedCount };
166
+ }
167
+ const overflow = next.length - cap;
168
+ return {
169
+ history: next.slice(overflow),
170
+ truncatedCount: previousTruncatedCount + overflow,
171
+ };
172
+ }
173
+ /**
174
+ * The whole flow in one call: diff, and return the patch additions needed to
175
+ * record it. Returns `null` when no tracked field changed — the caller then
176
+ * writes its patch unchanged, so an untracked update never grows the array.
177
+ */
178
+ export function withHistory(current, patch, opts) {
179
+ const changes = scrubPii({ ...diffTrackedFields(current, patch, opts.tracked), ...opts.extraChanges }, opts.piiFields);
180
+ if (Object.keys(changes).length === 0)
181
+ return null;
182
+ const historyField = opts.historyField ?? "statusHistory";
183
+ const truncatedField = opts.truncatedField ?? "statusHistoryTruncated";
184
+ const entry = buildHistoryEntry({
185
+ actor: opts.actor,
186
+ trigger: opts.trigger,
187
+ changes,
188
+ reason: opts.reason,
189
+ note: opts.note,
190
+ now: opts.now,
191
+ });
192
+ const { history, truncatedCount } = appendHistoryEntry(readPath(current, historyField) ?? [], entry, readPath(current, truncatedField) ?? 0, opts.cap);
193
+ return {
194
+ ...patch,
195
+ [historyField]: history,
196
+ ...(truncatedCount > 0 ? { [truncatedField]: truncatedCount } : {}),
197
+ };
198
+ }
@@ -0,0 +1,4 @@
1
+ export type { HistoryActorRole, HistoryActor, FieldChange, StatusChangeEntry, AppendHistoryResult, } from "./types";
2
+ export { STATUS_HISTORY_MAX } from "./types";
3
+ export type { BuildHistoryEntryInput } from "./history";
4
+ export { diffTrackedFields, buildHistoryEntry, appendHistoryEntry, withHistory, } from "./history";
@@ -0,0 +1,2 @@
1
+ export { STATUS_HISTORY_MAX } from "./types";
2
+ export { diffTrackedFields, buildHistoryEntry, appendHistoryEntry, withHistory, } from "./history";
@@ -0,0 +1,61 @@
1
+ import type { FirestoreValue } from "../../../schemas/types";
2
+ /**
3
+ * Generic entity status history — the shape shared by every entity that
4
+ * records how it got to its current state.
5
+ *
6
+ * Built for orders first (W2) and adopted by stores, payouts, offers, bids,
7
+ * support tickets, scammer profiles, catalogue approvals, shipments and
8
+ * returns in W15. Before this existed, exactly ONE of ~40 status-bearing
9
+ * entities had any history at all — `ads`, built inline in a route handler
10
+ * with no type and no reuse.
11
+ */
12
+ export type HistoryActorRole = "buyer" | "seller" | "admin" | "system";
13
+ export interface HistoryActor {
14
+ /** Firebase Auth uid. Absent for `system` (cron jobs, Firestore triggers). */
15
+ uid?: string;
16
+ role: HistoryActorRole;
17
+ }
18
+ /** A single field's before/after. `from` is `null` on the creation entry. */
19
+ export interface FieldChange {
20
+ from: FirestoreValue;
21
+ to: FirestoreValue;
22
+ }
23
+ export interface StatusChangeEntry {
24
+ at: Date;
25
+ actorUid?: string;
26
+ actorRole: HistoryActorRole;
27
+ /**
28
+ * DELTA ONLY — the tracked fields that actually changed, never a copy of
29
+ * the whole document. A full snapshot per transition would duplicate the
30
+ * record on every status change; the whole point of this shape is that it
31
+ * does not.
32
+ */
33
+ changes: Record<string, FieldChange>;
34
+ /** Why, when a human supplied a reason (cancellation, rejection). */
35
+ reason?: string;
36
+ /** Free-text note from an admin/seller action. */
37
+ note?: string;
38
+ /**
39
+ * The function, route or job that caused the change — `"customShipOrder"`,
40
+ * `"paymentWindowTimeout"`, `"adminVerifyPayment"`. This is what the
41
+ * timeline renders by, which is why per-path events (proof uploaded,
42
+ * installment paid, winner revealed) need NO new status values.
43
+ */
44
+ trigger: string;
45
+ }
46
+ /**
47
+ * Cap for any history array. Matches `HISTORY_MAX`'s reasoning: an unbounded
48
+ * array on a hot document inflates every read of that document (Rule #6), and
49
+ * an order that changes status more than this has a different problem.
50
+ */
51
+ export declare const STATUS_HISTORY_MAX = 50;
52
+ export interface AppendHistoryResult {
53
+ history: StatusChangeEntry[];
54
+ /**
55
+ * How many entries have been dropped off the front across the document's
56
+ * lifetime. Surfaced so the UI can say "earlier history trimmed" instead
57
+ * of implying none existed — silent truncation reads as "this is all of
58
+ * it", which is the same lie as a silently dropped field.
59
+ */
60
+ truncatedCount: number;
61
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Cap for any history array. Matches `HISTORY_MAX`'s reasoning: an unbounded
3
+ * array on a hot document inflates every read of that document (Rule #6), and
4
+ * an order that changes status more than this has a different problem.
5
+ */
6
+ export const STATUS_HISTORY_MAX = 50;
@@ -22,8 +22,10 @@
22
22
  * that doesn't cover the full union.
23
23
  */
24
24
  import { LISTING_TYPE_CAPABILITIES } from "./capabilities";
25
+ import { type AvailabilityRow, type UnavailableClause } from "./availability";
25
26
  import type { ListingType } from "../../../features/products/types/index";
26
27
  import type { SortOption } from "../../../features/products/constants/sieve";
28
+ import { type AvailabilityFilter } from "../../../constants/field-names";
27
29
  /**
28
30
  * Which "Show X" quick-filter a listing type's browse surfaces default to
29
31
  * hiding. Drives both the toolbar toggle label and the SSR default filter,
@@ -78,8 +80,29 @@ export interface ListingTypePlugin {
78
80
  * /products when exactly this type is selected.
79
81
  */
80
82
  browseRoute: string | null;
81
- /** Which "Show X" default this type's browse surfaces apply. */
83
+ /**
84
+ * Which "Show X" default this type's browse surfaces apply. Since 2026-08-24
85
+ * this drives the LABEL of the availability tab bar's middle tab
86
+ * (`availabilityTabsFor`) rather than a toggle pill's wording.
87
+ */
82
88
  hideDefault: ListingHideDefault;
89
+ /**
90
+ * Is this row still buyable / biddable / enterable? Type-specific checks
91
+ * ONLY — `baseAvailable` (isSold / availableQuantity / stockQuantity) is
92
+ * applied to every type by `isListingRowAvailable` before this runs, so a
93
+ * type with no extra rule returns `true`.
94
+ *
95
+ * Must stay pure and client-safe: the same predicate runs inside the SSR
96
+ * query, the homepage fetch and the related-items carousel.
97
+ */
98
+ isAvailable: (row: AvailabilityRow, now: Date) => boolean;
99
+ /**
100
+ * The query half of the same question — see `UnavailableClause`. Used to
101
+ * front-load the "Sold & Ended" scope, which is sparse and therefore
102
+ * expressible as an OR of equalities, unlike "available", which is dense and
103
+ * negation-shaped and stays an in-memory predicate.
104
+ */
105
+ unavailableClauses: readonly UnavailableClause[];
83
106
  /** Sort set offered on admin/seller dashboards for this type. */
84
107
  sortOptions: readonly SortOption[];
85
108
  /** Narrower sort set offered on public browse surfaces for this type. */
@@ -124,4 +147,34 @@ export declare function commonSortOptionsFor(types: readonly ListingType[], vari
124
147
  * so this returns every distinct default across the set.
125
148
  */
126
149
  export declare function hideDefaultsFor(types: readonly ListingType[]): ListingHideDefault[];
150
+ export interface AvailabilityTab {
151
+ id: AvailabilityFilter;
152
+ label: string;
153
+ }
154
+ /**
155
+ * The three-tab availability bar for a set of listing types.
156
+ *
157
+ * The middle label is derived, not a constant: `/auctions` reads "Ended",
158
+ * `/art` reads "Sold", and `/products` — which spans both kinds — reads
159
+ * "Sold & Ended". Ordering is fixed rather than set-iteration order so the
160
+ * label can never flip to "Ended & Sold" between renders.
161
+ */
162
+ export declare function availabilityTabsFor(types: readonly ListingType[]): AvailabilityTab[];
163
+ /**
164
+ * THE definition of "this listing is still available", shared by the listing
165
+ * query, the homepage fetch and the related-items carousel. Before this there
166
+ * were four partial versions of it and they disagreed — see the header of
167
+ * `./availability`.
168
+ *
169
+ * An unrecognised `listingType` falls back to `standard` rather than throwing:
170
+ * these rows come from Firestore and a bad value must degrade to the base
171
+ * checks, not blank the page.
172
+ */
173
+ export declare function isListingRowAvailable(row: AvailabilityRow, now: Date): boolean;
174
+ /**
175
+ * Every distinct `UnavailableClause` across a set of listing types, deduped by
176
+ * field+op+value. `/products` (all nine types) collapses to five clauses, so
177
+ * the "Sold & Ended" scope costs five bounded queries, not nine.
178
+ */
179
+ export declare function unavailableClausesFor(types: readonly ListingType[]): UnavailableClause[];
127
180
  export { LISTING_TYPE_CAPABILITIES };
@@ -33,6 +33,8 @@ import * as live from "./live/config";
33
33
  import * as art from "./art/config";
34
34
  import * as stickers from "./stickers/config";
35
35
  import { LISTING_TYPE_CAPABILITIES } from "./capabilities";
36
+ import { baseAvailable, } from "./availability";
37
+ import { AVAILABILITY_VALUES, } from "../../../constants/field-names";
36
38
  // SB-UNI-D — bundle entry removed; bundles are a categoryType, not a listingType.
37
39
  // SB-UNI-F 2026-05-13 — classified / digital-code / live added.
38
40
  // EMI/art-stickers session — art / stickers added.
@@ -112,6 +114,71 @@ export function hideDefaultsFor(types) {
112
114
  seen.add(LISTING_TYPE_REGISTRY[type].hideDefault);
113
115
  return [...seen];
114
116
  }
117
+ // ---------------------------------------------------------------------------
118
+ // Availability scope
119
+ // ---------------------------------------------------------------------------
120
+ /**
121
+ * The user-facing noun for each hide-default. "ended" and "closed" are one
122
+ * query mechanic and one word to a buyer — an auction that ended and a prize
123
+ * draw that closed are both simply over.
124
+ *
125
+ * `Record<ListingHideDefault, …>` on purpose: adding a fourth hide-default is
126
+ * then a compile error rather than a tab that silently renders `undefined`.
127
+ */
128
+ const HIDE_DEFAULT_NOUN = {
129
+ sold: "Sold",
130
+ ended: "Ended",
131
+ closed: "Ended",
132
+ };
133
+ /**
134
+ * The three-tab availability bar for a set of listing types.
135
+ *
136
+ * The middle label is derived, not a constant: `/auctions` reads "Ended",
137
+ * `/art` reads "Sold", and `/products` — which spans both kinds — reads
138
+ * "Sold & Ended". Ordering is fixed rather than set-iteration order so the
139
+ * label can never flip to "Ended & Sold" between renders.
140
+ */
141
+ export function availabilityTabsFor(types) {
142
+ const nouns = new Set((types.length > 0 ? types : Object.keys(LISTING_TYPE_REGISTRY)).map((t) => HIDE_DEFAULT_NOUN[LISTING_TYPE_REGISTRY[t].hideDefault]));
143
+ const unavailableLabel = ["Sold", "Ended"].filter((n) => nouns.has(n)).join(" & ") || "Sold";
144
+ return [
145
+ { id: AVAILABILITY_VALUES.AVAILABLE, label: "Available" },
146
+ { id: AVAILABILITY_VALUES.UNAVAILABLE, label: unavailableLabel },
147
+ { id: AVAILABILITY_VALUES.ALL, label: "All" },
148
+ ];
149
+ }
150
+ /**
151
+ * THE definition of "this listing is still available", shared by the listing
152
+ * query, the homepage fetch and the related-items carousel. Before this there
153
+ * were four partial versions of it and they disagreed — see the header of
154
+ * `./availability`.
155
+ *
156
+ * An unrecognised `listingType` falls back to `standard` rather than throwing:
157
+ * these rows come from Firestore and a bad value must degrade to the base
158
+ * checks, not blank the page.
159
+ */
160
+ export function isListingRowAvailable(row, now) {
161
+ if (!baseAvailable(row))
162
+ return false;
163
+ const raw = row.listingType;
164
+ const type = typeof raw === "string" && raw in LISTING_TYPE_REGISTRY ? raw : "standard";
165
+ return LISTING_TYPE_REGISTRY[type].isAvailable(row, now);
166
+ }
167
+ /**
168
+ * Every distinct `UnavailableClause` across a set of listing types, deduped by
169
+ * field+op+value. `/products` (all nine types) collapses to five clauses, so
170
+ * the "Sold & Ended" scope costs five bounded queries, not nine.
171
+ */
172
+ export function unavailableClausesFor(types) {
173
+ const effective = types.length > 0 ? types : Object.keys(LISTING_TYPE_REGISTRY);
174
+ const seen = new Map();
175
+ for (const type of effective) {
176
+ for (const clause of LISTING_TYPE_REGISTRY[type].unavailableClauses) {
177
+ seen.set(`${clause.field}|${clause.op}|${String(clause.value)}`, clause);
178
+ }
179
+ }
180
+ return [...seen.values()];
181
+ }
115
182
  // Re-export the capability map so consumers can pull both surfaces from
116
183
  // the same module path.
117
184
  export { LISTING_TYPE_CAPABILITIES };
@@ -1,3 +1,4 @@
1
+ import { alwaysAvailable } from "../availability";
1
2
  export declare const LISTING_TYPE: "art";
2
3
  export declare const capability: import("../capabilities").ListingTypeCapability;
3
4
  export declare const config: {
@@ -17,6 +18,8 @@ export declare const config: {
17
18
  chipLabel: string;
18
19
  browseRoute: string;
19
20
  hideDefault: "sold";
21
+ isAvailable: typeof alwaysAvailable;
22
+ unavailableClauses: readonly import("../availability").UnavailableClause[];
20
23
  sortOptions: readonly [{
21
24
  readonly value: string;
22
25
  readonly label: "Newest First";
@@ -1,4 +1,5 @@
1
1
  import { LISTING_TYPE_CAPABILITIES } from "../capabilities";
2
+ import { alwaysAvailable, STOCK_ONLY_UNAVAILABLE_CLAUSES, } from "../availability";
2
3
  import { ROUTES } from "../../../../next/routing/route-map";
3
4
  import { STANDARD_SORT_OPTIONS, STANDARD_PUBLIC_SORT_OPTIONS, } from "../../../../features/products/constants/sieve";
4
5
  export const LISTING_TYPE = "art";
@@ -20,6 +21,10 @@ export const config = {
20
21
  // dedicated browse surface even though detailRoute points at /products.
21
22
  browseRoute: String(ROUTES.PUBLIC.ART),
22
23
  hideDefault: "sold",
24
+ // Runs out rather than ending — no time dimension, so the shared
25
+ // stock-only rules are the whole story for this type.
26
+ isAvailable: alwaysAvailable,
27
+ unavailableClauses: STOCK_ONLY_UNAVAILABLE_CLAUSES,
23
28
  sortOptions: STANDARD_SORT_OPTIONS,
24
29
  publicSortOptions: STANDARD_PUBLIC_SORT_OPTIONS,
25
30
  extraFacetKeys: [],
@@ -1,3 +1,4 @@
1
+ import { type AvailabilityRow } from "../availability";
1
2
  export declare const LISTING_TYPE: "auction";
2
3
  export declare const capability: import("../capabilities").ListingTypeCapability;
3
4
  export declare const config: {
@@ -17,6 +18,18 @@ export declare const config: {
17
18
  chipLabel: string;
18
19
  browseRoute: string;
19
20
  hideDefault: "ended";
21
+ isAvailable: (row: AvailabilityRow, now: Date) => boolean;
22
+ unavailableClauses: ({
23
+ field: "auctionEndDate";
24
+ op: "lt";
25
+ value: "NOW";
26
+ sortField: "auctionEndDate";
27
+ } | {
28
+ field: "isSold";
29
+ op: "eq";
30
+ value: boolean;
31
+ sortField?: undefined;
32
+ })[];
20
33
  sortOptions: readonly [{
21
34
  readonly value: string;
22
35
  readonly label: "Ending Soon";
@@ -1,6 +1,8 @@
1
1
  import { LISTING_TYPE_CAPABILITIES } from "../capabilities";
2
+ import { toDate } from "../availability";
2
3
  import { ROUTES } from "../../../../next/routing/route-map";
3
4
  import { TABLE_KEYS } from "../../../../constants/table-keys";
5
+ import { PRODUCT_FIELDS } from "../../../../constants/field-names";
4
6
  import { AUCTION_SORT_OPTIONS, AUCTION_PUBLIC_SORT_OPTIONS, } from "../../../../features/products/constants/sieve";
5
7
  export const LISTING_TYPE = "auction";
6
8
  export const capability = LISTING_TYPE_CAPABILITIES.auction;
@@ -20,6 +22,25 @@ export const config = {
20
22
  // An auction is never "sold out" — it ends. Hiding by stock would hide
21
23
  // every live auction, since auctions don't carry meaningful stock.
22
24
  hideDefault: "ended",
25
+ // There is no `auctionStatus` field — "ended" is purely the end date having
26
+ // passed. An auction with no end date at all is treated as live rather than
27
+ // hidden; a missing date is a data defect, not a signal that it is over.
28
+ isAvailable: (row, now) => {
29
+ const end = toDate(row[PRODUCT_FIELDS.AUCTION_END_DATE]);
30
+ return end === null || end > now;
31
+ },
32
+ unavailableClauses: [
33
+ // The ONLY inequality in the registry — safe because it names the sort
34
+ // field the query must order by, which is also the natural sort for an
35
+ // "ended auctions" archive ("Recently Ended").
36
+ {
37
+ field: PRODUCT_FIELDS.AUCTION_END_DATE,
38
+ op: "lt",
39
+ value: "NOW",
40
+ sortField: PRODUCT_FIELDS.AUCTION_END_DATE,
41
+ },
42
+ { field: PRODUCT_FIELDS.IS_SOLD, op: "eq", value: true },
43
+ ],
23
44
  sortOptions: AUCTION_SORT_OPTIONS,
24
45
  publicSortOptions: AUCTION_PUBLIC_SORT_OPTIONS,
25
46
  extraFacetKeys: [
@@ -0,0 +1,79 @@
1
+ import type { FirestoreValue } from "../../../schemas/types";
2
+ /**
3
+ * A product row as it reaches a predicate.
4
+ *
5
+ * Deliberately the project’s sanctioned loose Firestore-row alias rather than
6
+ * `ProductDocument`: the same row arrives here in three shapes — a Firestore
7
+ * document (Date / Timestamp fields), an already-serialised payload from the
8
+ * colocated `listingProcessor` Function (ISO strings), and JSON from
9
+ * `/api/products`. Narrowing to any one of them would just push a cast to
10
+ * every call site.
11
+ */
12
+ export type AvailabilityRow = Record<string, FirestoreValue>;
13
+ /**
14
+ * Everything a timestamp field can be by the time it reaches `toDate`. The
15
+ * `{ toDate() }` arm is a Firestore `Timestamp`, which cannot be imported
16
+ * here — this module is client-safe and firebase-admin is not.
17
+ */
18
+ export type DateLike = FirestoreValue | {
19
+ toDate: () => Date;
20
+ };
21
+ /**
22
+ * Firestore Timestamp | Date | ISO string | epoch millis → Date, or null when
23
+ * the value is absent or unparseable. Firestore hands back a `Timestamp` with
24
+ * a `toDate()` method server-side but a plain ISO string once serialised, and
25
+ * a predicate that handles only one of those silently returns the wrong answer
26
+ * on the other runtime.
27
+ */
28
+ export declare function toDate(value: DateLike): Date | null;
29
+ /** Read a numeric field, or `null` when absent/non-numeric. */
30
+ export declare function num(value: DateLike): number | null;
31
+ /**
32
+ * The checks every listing type shares, regardless of `listingType`.
33
+ *
34
+ * Note what is NOT here: `status`. Publication state (draft / in_review /
35
+ * archived) is a different axis from availability — an archived listing is
36
+ * hidden from the public entirely, while a sold one is still browsable in the
37
+ * "Sold & Ended" scope. Conflating them is what made `isValidRelatedItem`
38
+ * test for a `status: "sold"` value that has never existed.
39
+ *
40
+ * A field that is absent is treated as "not a reason to hide" — most rows
41
+ * carry `isSold: false` and a real quantity, but older documents predate both
42
+ * and must not vanish from the default scope.
43
+ */
44
+ export declare function baseAvailable(row: AvailabilityRow): boolean;
45
+ /**
46
+ * Whether a row may be shown on a public surface at all. Separate from
47
+ * availability on purpose — see `baseAvailable`.
48
+ */
49
+ export declare function isPubliclyVisible(row: AvailabilityRow): boolean;
50
+ /**
51
+ * The Firestore-expressible half of "this row is UNAVAILABLE".
52
+ *
53
+ * Every entry must be an EQUALITY, or an inequality that names its own
54
+ * `sortField` — because Firestore implicitly appends an inequality's field to
55
+ * the `orderBy`, so pairing one with an unrelated sort demands a composite
56
+ * index in an order nobody declares and fails with FAILED_PRECONDITION
57
+ * (Recurrent Root Cause #59). Equalities carry no such constraint, which is
58
+ * what makes the "Sold & Ended" fan-out safe on a mixed type selection.
59
+ */
60
+ export interface UnavailableClause {
61
+ field: string;
62
+ op: "eq" | "lt";
63
+ /** `"NOW"` is substituted with the query's timestamp at build time. */
64
+ value: string | number | boolean | "NOW";
65
+ /** Required when `op !== "eq"` — the query MUST order by this field. */
66
+ sortField?: string;
67
+ }
68
+ /**
69
+ * For types whose only end-state is running out: standard, classified, live,
70
+ * art, stickers. Shared rather than copy-pasted into five plugin configs,
71
+ * which is exactly the duplication the registry exists to prevent.
72
+ */
73
+ export declare const STOCK_ONLY_UNAVAILABLE_CLAUSES: readonly UnavailableClause[];
74
+ /**
75
+ * `isAvailable` for a type with no rule beyond `baseAvailable`. Named rather
76
+ * than an inline `() => true` so the five configs that use it read as
77
+ * deliberately sharing one decision, not as five independent stubs.
78
+ */
79
+ export declare function alwaysAvailable(): boolean;