@mohasinac/appkit 4.13.0 → 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 +2 -2
@@ -10,6 +10,14 @@
10
10
  * primitive and stay in sync when statuses are added.
11
11
  */
12
12
  export const ALL_TAB = { id: "All", label: "All" };
13
+ // COVERAGE (2026-08-24): `audit-filter-tab-enums` gained a check that every
14
+ // value of the backing enum HAS a chip, not just that every chip is a real
15
+ // value. It immediately found ten gaps across the seller sets — `in_review`
16
+ // (where a submission waits on admin approval, arguably the state a seller most
17
+ // wants to filter for), `archived`, `returned`, and the offer states
18
+ // `countered` / `withdrawn` / `paid`. Rows in those states were visible under
19
+ // "All" and isolatable by nothing. All ten now have chips; anything genuinely
20
+ // not worth a chip goes in that audit entry's `omit` list with a reason.
13
21
  /** Admin > Products — listing status filter chip set. Real `ProductStatus`
14
22
  * is draft|published|in_review|archived — "pending" fixed to "in_review"
15
23
  * (its real meaning); the previous id never matched any document. */
@@ -105,12 +113,17 @@ export const ADMIN_AUDIT_LOG_ACTION_TABS = [
105
113
  export const ADMIN_ORDER_STATUS_TABS = [
106
114
  ALL_TAB,
107
115
  { id: "pending", label: "Pending" },
116
+ // `confirmed` and `returned` were both missing until 2026-08-24 even though
117
+ // both are real `OrderStatusValues` an order reaches — so an admin could see
118
+ // such an order in the All tab and had no chip that would isolate it.
119
+ { id: "confirmed", label: "Confirmed" },
108
120
  { id: "processing", label: "Processing" },
109
121
  { id: "shipped", label: "Shipped" },
110
122
  { id: "delivered", label: "Delivered" },
111
123
  { id: "cancelled", label: "Cancelled" },
112
124
  { id: "refunded", label: "Refunded" },
113
125
  { id: "return_requested", label: "Return Requested" },
126
+ { id: "returned", label: "Returned" },
114
127
  ];
115
128
  /**
116
129
  * Admin > Orders — manual-payment (cash / UPI / EMI) review queue.
@@ -177,11 +190,19 @@ export const ADMIN_EVENT_ENTRY_STATUS_TABS = [
177
190
  * feature's own events/types/index.ts). A "Published" chip previously
178
191
  * returned zero rows every time — removed rather than aliased since
179
192
  * "Active" already covers the intended meaning. */
193
+ /**
194
+ * Every `EventStatus`, not a subset. `paused` and `cancelled` were missing
195
+ * until 2026-08-24 — a real status with no chip is unreachable, and
196
+ * `audit-filter-tab-enums` never caught it because that audit only checks the
197
+ * reverse (that no chip names a DEAD value).
198
+ */
180
199
  export const ADMIN_EVENT_STATUS_TABS = [
181
200
  ALL_TAB,
182
201
  { id: "draft", label: "Draft" },
183
202
  { id: "active", label: "Active" },
203
+ { id: "paused", label: "Paused" },
184
204
  { id: "ended", label: "Ended" },
205
+ { id: "cancelled", label: "Cancelled" },
185
206
  ];
186
207
  /** Admin > Scammers — scammer profile status filter chip set. */
187
208
  export const ADMIN_SCAMMER_STATUS_TABS = [
@@ -242,6 +263,7 @@ export const SELLER_PRODUCT_STATUS_TABS = [
242
263
  { id: "published", label: "Active" },
243
264
  { id: "draft", label: "Draft" },
244
265
  { id: "archived", label: "Archived" },
266
+ { id: "in_review", label: "In Review" },
245
267
  ];
246
268
  /** Seller > Auctions — auction-state filter chip set. Real `ProductStatus`
247
269
  * has no "active"/"ended"/"cancelled" values. "Active" fixed to
@@ -257,6 +279,8 @@ export const SELLER_AUCTION_STATUS_TABS = [
257
279
  ALL_TAB,
258
280
  { id: "published", label: "Active" },
259
281
  { id: "draft", label: "Draft" },
282
+ { id: "in_review", label: "In Review" },
283
+ { id: "archived", label: "Archived" },
260
284
  ];
261
285
  /** Seller > Pre-orders — pre-order-state filter chip set. Same root cause
262
286
  * and fix as SELLER_AUCTION_STATUS_TABS above — no "active"/"cancelled"
@@ -267,6 +291,7 @@ export const SELLER_PRE_ORDER_STATUS_TABS = [
267
291
  { id: "published", label: "Active" },
268
292
  { id: "draft", label: "Draft" },
269
293
  { id: "archived", label: "Archived" },
294
+ { id: "in_review", label: "In Review" },
270
295
  ];
271
296
  /** Seller > Prize Draws — draw-state filter chip set. Same root cause and
272
297
  * fix as SELLER_AUCTION_STATUS_TABS — "ended" is derived from
@@ -276,6 +301,8 @@ export const SELLER_PRIZE_DRAW_STATUS_TABS = [
276
301
  ALL_TAB,
277
302
  { id: "published", label: "Active" },
278
303
  { id: "draft", label: "Draft" },
304
+ { id: "in_review", label: "In Review" },
305
+ { id: "archived", label: "Archived" },
279
306
  ];
280
307
  /** Seller > Orders — order-state filter chip set. Subset of
281
308
  * `ADMIN_ORDER_STATUS_TABS` — sellers don't see `RETURN_REQUESTED` until
@@ -286,11 +313,36 @@ export const SELLER_PRIZE_DRAW_STATUS_TABS = [
286
313
  export const SELLER_ORDER_STATUS_TABS = [
287
314
  ALL_TAB,
288
315
  { id: "pending", label: "Pending" },
316
+ // `confirmed` is settable from SellerOrdersView's own Update-Status dropdown
317
+ // but had no chip here, so a seller could move an order into a state they
318
+ // could then never filter for. `return_requested` was excluded on the theory
319
+ // that sellers never see one — they do, it is the state a buyer's return
320
+ // request lands in and the seller is the one who has to act on it.
321
+ { id: "confirmed", label: "Confirmed" },
289
322
  { id: "processing", label: "Processing" },
290
323
  { id: "shipped", label: "Shipped" },
291
324
  { id: "delivered", label: "Delivered" },
292
325
  { id: "cancelled", label: "Cancelled" },
293
326
  { id: "refunded", label: "Refunded" },
327
+ { id: "return_requested", label: "Return Requested" },
328
+ { id: "returned", label: "Returned" },
329
+ ];
330
+ /**
331
+ * Admin > Offers — the FULL `OfferStatus` union, unlike the seller variant
332
+ * below, which deliberately surfaces only the four states a seller acts on.
333
+ * An admin is auditing the whole lifecycle, so `countered`, `withdrawn` and
334
+ * `paid` all need to be reachable. Cross-checked against the real union by
335
+ * `audit-filter-tab-enums`.
336
+ */
337
+ export const ADMIN_OFFER_STATUS_TABS = [
338
+ ALL_TAB,
339
+ { id: "pending", label: "Pending" },
340
+ { id: "countered", label: "Countered" },
341
+ { id: "accepted", label: "Accepted" },
342
+ { id: "paid", label: "Paid" },
343
+ { id: "declined", label: "Declined" },
344
+ { id: "expired", label: "Expired" },
345
+ { id: "withdrawn", label: "Withdrawn" },
294
346
  ];
295
347
  /** Seller > Offers — offer-state filter chip set. Real `OfferStatus` is
296
348
  * pending|accepted|declined|countered|expired|withdrawn|paid — there is no
@@ -303,6 +355,9 @@ export const SELLER_OFFER_STATUS_TABS = [
303
355
  { id: "accepted", label: "Accepted" },
304
356
  { id: "declined", label: "Rejected" },
305
357
  { id: "expired", label: "Expired" },
358
+ { id: "countered", label: "Countered" },
359
+ { id: "withdrawn", label: "Withdrawn" },
360
+ { id: "paid", label: "Paid" },
306
361
  ];
307
362
  /** Seller > Bids — bid-state filter chip set. Uses the empty-sentinel
308
363
  * variant (see `EMPTY_TAB`). */
@@ -275,15 +275,82 @@ export interface SiteSettingsTheme {
275
275
  /** Theme id applied when the user's resolved mode is `"dark"`. */
276
276
  defaultDarkThemeId?: string;
277
277
  }
278
+ /** Third-party analytics/tag IDs. Public by nature — they ship in page markup. */
279
+ export interface SiteSettingsIntegrations {
280
+ googleAnalyticsId?: string;
281
+ facebookPixelId?: string;
282
+ gtmContainerId?: string;
283
+ }
284
+ /** Admin-tunable platform caps. Enforced server-side; not buyer-facing. */
285
+ export interface SiteSettingsPlatformLimits {
286
+ maxProductsPerStore?: number;
287
+ maxImagesPerProduct?: number;
288
+ maxVideoSizeMb?: number;
289
+ maxCustomFieldsPerProduct?: number;
290
+ maxCustomSectionsPerProduct?: number;
291
+ orderCancellationWindowHours?: number;
292
+ }
293
+ /** Embedded in siteSettings.adSettings — a type alias, not a collection root. */
294
+ export type SiteSettingsAdPlacement = {
295
+ id: string;
296
+ label: string;
297
+ enabled: boolean;
298
+ reservedHeight: number;
299
+ };
300
+ /** Embedded in siteSettings.adSettings — a type alias, not a collection root. */
301
+ export type SiteSettingsAdInventoryItem = {
302
+ id: string;
303
+ name: string;
304
+ provider: "manual" | "adsense" | "thirdParty";
305
+ status: "draft" | "active" | "scheduled" | "paused";
306
+ placementIds: string[];
307
+ requiresConsent: boolean;
308
+ priority: number;
309
+ startAt?: string;
310
+ endAt?: string;
311
+ createdAt: string;
312
+ updatedAt: string;
313
+ updatedBy: string;
314
+ creative: {
315
+ title?: string;
316
+ body?: string;
317
+ imageUrl?: string;
318
+ ctaLabel?: string;
319
+ ctaHref?: string;
320
+ adsenseSlot?: string;
321
+ thirdPartyUrl?: string;
322
+ };
323
+ };
324
+ /**
325
+ * Ad inventory + provider config.
326
+ *
327
+ * `providerCredentials` is masked by `GET /api/admin/ads` and must never be
328
+ * returned by a public endpoint; `inventory` holds draft/paused/scheduled
329
+ * entries that `GET /api/ads` deliberately filters out. Both are therefore in
330
+ * `PRIVATE_SITE_SETTINGS_FIELDS` — the whole group is server-side only.
331
+ */
332
+ export interface SiteSettingsAdSettings {
333
+ consentRequired: boolean;
334
+ placements: readonly SiteSettingsAdPlacement[];
335
+ providerCredentials: {
336
+ adsenseClientId: string;
337
+ thirdPartyScriptUrl: string;
338
+ };
339
+ inventory: SiteSettingsAdInventoryItem[];
340
+ }
278
341
  export interface SiteSettingsDocument extends BaseDocument {
279
342
  id: "global";
280
343
  siteName: string;
344
+ /** Short strapline shown beside the site name in admin chrome. */
345
+ tagline?: string;
281
346
  motto: string;
282
347
  logo: {
283
348
  url: string;
284
349
  alt: string;
285
350
  format: "svg" | "png";
286
351
  };
352
+ /** Favicon URL. Distinct from `logo.url`, which is the wordmark. */
353
+ favicon?: string;
287
354
  background: {
288
355
  light: {
289
356
  type: "color" | "gradient" | "image" | "video";
@@ -452,6 +519,19 @@ export interface SiteSettingsDocument extends BaseDocument {
452
519
  notifications: boolean;
453
520
  sellerRegistration: boolean;
454
521
  preOrders: boolean;
522
+ /**
523
+ * Buyer price negotiation (Make an Offer / counter-offer).
524
+ *
525
+ * The global kill switch. Per-listing opt-in still applies on top of it via
526
+ * `ProductDocument.allowOffers`, and only certain listing types support
527
+ * offers at all (`LISTING_TYPE_CAPABILITIES.canMakeOffer`) — a product needs
528
+ * all three to show the CTA.
529
+ *
530
+ * Guards NEW offers only. Switching it off must not strand in-flight ones:
531
+ * sellers keep responding, buyers keep checking out accepted offers, and
532
+ * both dashboards stay reachable.
533
+ */
534
+ offers: boolean;
455
535
  /** When true, admin users see a bypass button in checkout that skips OTP and payment. Server-enforced. */
456
536
  adminCheckoutBypass?: boolean;
457
537
  listingTypes?: {
@@ -471,11 +551,26 @@ export interface SiteSettingsDocument extends BaseDocument {
471
551
  bundle?: boolean;
472
552
  };
473
553
  };
554
+ /**
555
+ * Admin-authored HTML overrides for the public policy pages. When a key holds
556
+ * a non-empty string, `PolicyPageView` renders it instead of the i18n
557
+ * `sections[]` fallback for that page.
558
+ *
559
+ * These key names are load-bearing — they must match `POLICY_META[*].firestoreField`
560
+ * in `features/about/components/PolicyPageView.tsx` (read side) and the
561
+ * `legalPages` payload built by `AdminSiteSettingsView` (write side). Until
562
+ * 2026-08-24 this type declared `termsOfService`/`privacyPolicy`/`shippingPolicy`,
563
+ * three names that neither side ever used; the read path went through an
564
+ * `as any` cast, so `tsc` never caught the drift (Root Cause #38 family).
565
+ */
474
566
  legalPages: {
475
- termsOfService: string;
476
- privacyPolicy: string;
567
+ terms: string;
568
+ privacy: string;
569
+ cookies: string;
477
570
  refundPolicy: string;
478
- shippingPolicy: string;
571
+ shipping: string;
572
+ ethics: string;
573
+ codeOfConduct: string;
479
574
  };
480
575
  shipping: {
481
576
  estimatedDays: number;
@@ -508,9 +603,21 @@ export interface SiteSettingsDocument extends BaseDocument {
508
603
  };
509
604
  newsletterEnabled?: boolean;
510
605
  };
606
+ /**
607
+ * `message` is canonical — it is what `AnnouncementBar` and the homepage
608
+ * read. `AdminSiteSettingsView` used to write the text under a `text` key
609
+ * instead, so anything typed in the main Site Settings editor was saved and
610
+ * never displayed; `link`/`backgroundColor` were collected and had no
611
+ * renderer at all. Unified on `message` 2026-08-24 (migration
612
+ * `scripts/migrate-settings-wiring.mjs` copies any stray `text` across).
613
+ */
511
614
  announcementBar?: {
512
615
  enabled: boolean;
513
616
  message: string;
617
+ /** Optional destination — renders the message as a link. */
618
+ link?: string;
619
+ /** Optional CSS colour override for the bar background. */
620
+ backgroundColor?: string;
514
621
  };
515
622
  /**
516
623
  * Image watermark configuration applied by the `/api/media/[...slug]` CDN
@@ -544,6 +651,18 @@ export interface SiteSettingsDocument extends BaseDocument {
544
651
  };
545
652
  /** Encrypted provider credentials � never return raw to the client. */
546
653
  credentials?: SiteSettingsCredentials;
654
+ /**
655
+ * These three were written by `AdminSiteSettingsView.buildFullPayload()` and
656
+ * read back by it, but were never declared here. Being absent from the type
657
+ * meant no type-driven check could see them — and the public site-settings
658
+ * deny-list, which strips named fields off a spread, shipped all three to
659
+ * anonymous callers by default (`adSettings.providerCredentials` unmasked,
660
+ * plus every draft/paused ad). Declared 2026-08-24 so they are visible to
661
+ * `audit-public-projection-parity`.
662
+ */
663
+ integrations?: SiteSettingsIntegrations;
664
+ platformLimits?: SiteSettingsPlatformLimits;
665
+ adSettings?: SiteSettingsAdSettings;
547
666
  /**
548
667
  * Theme color overrides — injected as CSS custom properties by LayoutShellClient.
549
668
  * Keys map to --appkit-color-* variables (e.g. primary → --appkit-color-primary).
@@ -241,6 +241,7 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
241
241
  notifications: true,
242
242
  sellerRegistration: true,
243
243
  preOrders: true,
244
+ offers: true,
244
245
  // W1-37 2026-05-23 — Phase 2 listing types enabled by default; all per-type
245
246
  // surfaces (seller + admin via W1-29 + public) are now shipped.
246
247
  listingTypes: {
@@ -14,16 +14,31 @@ export interface BuyNowAuctionInput {
14
14
  }
15
15
  export interface BuyNowAuctionResult {
16
16
  /**
17
- * Buy-Now no longer creates an order directly. It writes a LOCKED CART LINE
18
- * (same shape auction settlement produces for a winning bidder) and the buyer
19
- * completes the real order through the auction checkout lane. The old
20
- * behaviour created a document that no orders UI could render and no payment
21
- * path could reach — see auctionSettlement.ts for the full writeup.
17
+ * Buy-Now creates neither an order nor a sale. It places a real BID at the
18
+ * listing's buy-now price and writes a LOCKED CART LINE pointing at it; the
19
+ * buyer then completes a normal order through the auction checkout lane, and
20
+ * only THAT closes the auction (see `claimAuctionForCheckout`).
21
+ *
22
+ * Two earlier shapes were both wrong. The original created an order document
23
+ * no orders UI could render and no payment path could reach (see
24
+ * auctionSettlement.ts). Its replacement wrote a locked cart line with no
25
+ * `bidId`, which every downstream sweep filters on — so the line could never
26
+ * lapse, could never be removed (it is `locked: true`), and permanently
27
+ * blocked the buyer's other two lanes.
28
+ *
29
+ * The auction itself is deliberately left ALONE here. It keeps running, keeps
30
+ * taking bids, and keeps accepting other buyouts. Nothing is sold until
31
+ * somebody pays, and which of several racing buyers that is gets decided by a
32
+ * transaction at order time rather than by a first-come lock here.
22
33
  */
23
34
  cartLocked: true;
24
35
  productId: string;
25
36
  amount: number;
26
37
  currency: string;
38
+ /** The bid placed at the buy-now price. Won only once the order completes. */
39
+ bidId: string;
40
+ /** When this claim lapses — 1h, clamped to the auction's own end time. */
41
+ checkoutDeadline: Date;
27
42
  /** Where to send the buyer to actually pay. */
28
43
  checkoutUrl: string;
29
44
  }
@@ -17,7 +17,7 @@ import { storeRepository } from "../../stores/repository/store.repository";
17
17
  import { siteSettingsRepository } from "../../../repositories";
18
18
  import { getAdminRealtimeDb } from "../../../providers/db-firebase";
19
19
  import { maskPublicBid } from "../../../security";
20
- import { resolveMinBid, resolveMinBidIncrement } from "../../../_internal/shared/features/auctions/config";
20
+ import { isBuyNowAvailable, resolveMinBid, resolveMinBidIncrement, } from "../../../_internal/shared/features/auctions/config";
21
21
  import { sendNotification } from "../../admin/actions/notification-actions";
22
22
  import { BID_MESSAGES } from "../../../_internal/server/jobs/handlers/messages";
23
23
  import { ERROR_MESSAGES, AuthorizationError, ValidationError, NotFoundError, } from "../../../errors";
@@ -25,13 +25,7 @@ import { BID_ERROR_CODES } from "../../../errors/error-codes";
25
25
  import { increment } from "../../../contracts/field-ops";
26
26
  import { getDefaultCurrency } from "../../../core/baseline-resolver";
27
27
  import { cartRepository } from "../../cart/repository/cart.repository";
28
- import { ROUTES } from "../../../next/routing/route-map";
29
- import { CART_LANE, LANE_CHECKOUT_WINDOW_MS } from "../../../_internal/shared/checkout/lanes";
30
- /**
31
- * Buy-Now and auction settlement give the buyer the same 48h window to pay for
32
- * a locked line. Kept identical on purpose — both are "you now owe this".
33
- */
34
- const AUCTION_CHECKOUT_WINDOW_MS = LANE_CHECKOUT_WINDOW_MS.auction;
28
+ import { AUCTION_BUYOUT_WINDOW_MINUTES, AUCTION_BUYOUT_WINDOW_MS, AUCTION_CHECKOUT_URL, } from "../../../_internal/shared/checkout/lanes";
35
29
  import { resolveDate } from "../../../utils";
36
30
  // --- Domain Functions ----------------------------------------------------------
37
31
  export async function placeBid(userId, userEmail, input) {
@@ -260,17 +254,62 @@ export async function buyNowAuction(userId, userName, userEmail, input) {
260
254
  if (buyNowPrice === null || buyNowPrice <= 0) {
261
255
  throw new ValidationError(ERROR_MESSAGES.BID.BUY_NOW_NO_PRICE, { code: BID_ERROR_CODES.BUY_NOW_NO_PRICE });
262
256
  }
263
- if (product.bidsHaveStarted === true) {
264
- throw new ValidationError(ERROR_MESSAGES.BID.BUY_NOW_BIDS_STARTED, { code: BID_ERROR_CODES.BUY_NOW_BIDS_STARTED });
265
- }
266
- const currency = getDefaultCurrency();
267
- await unitOfWork.runBatch((batch) => {
268
- unitOfWork.products.updateInBatch(batch, productId, {
269
- isSold: true,
270
- availableQuantity: 0,
271
- auctionEndDate: new Date(),
257
+ // The standing price this buyout has to beat. Read from the bids as well as
258
+ // the product because `product.currentBid` is a denormalised mirror, and a
259
+ // pending buyout deliberately never writes to it (see BidDocument.isBuyout) —
260
+ // so the bids are the authority here and buyout bids are excluded from it.
261
+ const activeBids = await bidRepository.getActiveByProduct(productId);
262
+ const standingBid = Math.max(product.currentBid ?? 0, ...activeBids.filter((e) => e.data.isBuyout !== true).map((e) => e.data.bidAmount ?? 0), 0);
263
+ // Replaces the old `bidsHaveStarted` gate. Buy Now used to vanish the instant
264
+ // anybody bid, eBay-style, which hid it on almost every real auction. What
265
+ // actually matters is whether it is still a better deal than the bidding.
266
+ if (!isBuyNowAvailable({ buyNowPrice, currentBid: standingBid, isEnded: false, isSold: product.isSold })) {
267
+ throw new ValidationError(ERROR_MESSAGES.BID.BUY_NOW_UNAVAILABLE, {
268
+ code: BID_ERROR_CODES.BUY_NOW_UNAVAILABLE,
272
269
  });
270
+ }
271
+ // Idempotency, NOT exclusivity. Re-clicking Buy Now (or a double submit) must
272
+ // not stack a second hold on the same buyer — but a DIFFERENT buyer holding
273
+ // one is fine and deliberate: whoever pays first gets it.
274
+ const existingHold = activeBids.find((e) => e.data.isBuyout === true && e.data.userId === userId);
275
+ if (existingHold) {
276
+ return {
277
+ cartLocked: true,
278
+ productId,
279
+ amount: existingHold.data.bidAmount,
280
+ currency: existingHold.data.currency,
281
+ bidId: existingHold.data.id,
282
+ checkoutDeadline: new Date(Date.now() + AUCTION_BUYOUT_WINDOW_MS),
283
+ checkoutUrl: AUCTION_CHECKOUT_URL,
284
+ };
285
+ }
286
+ // The product's own currency, not the site default — the cart line, the bid
287
+ // and the listing all have to agree, and settlement uses the bid's currency.
288
+ const currency = product.currency || getDefaultCurrency();
289
+ const profile = await userRepository.findById(userId);
290
+ // A real bid at the buy-now price — this is what "the bid placed is the
291
+ // buyout price by the buyer" means. It starts `active`/`isWinning: false`
292
+ // like any other bid (bidRepository.create forces both) and is promoted to
293
+ // `won` by claimAuctionForCheckout when the order actually lands.
294
+ const bid = await bidRepository.create({
295
+ productId,
296
+ productTitle: product.title,
297
+ userId,
298
+ userName: profile?.displayName ?? userName ?? userEmail ?? "Anonymous",
299
+ userEmail: profile?.email ?? userEmail ?? "",
300
+ bidAmount: buyNowPrice,
301
+ currency,
302
+ bidDate: new Date(),
303
+ autoMaxBid: buyNowPrice,
304
+ isBuyout: true,
273
305
  });
306
+ // Clamped to the auction's own end: a buyout that hasn't been paid for by the
307
+ // time the clock runs out fails anyway, because settlement will have awarded
308
+ // the item to whoever actually won the bidding. Showing a deadline past that
309
+ // point would promise a window that does not exist.
310
+ const auctionEnd = product.auctionEndDate ? resolveDate(product.auctionEndDate) : null;
311
+ const softDeadline = Date.now() + AUCTION_BUYOUT_WINDOW_MS;
312
+ const checkoutDeadline = new Date(auctionEnd ? Math.min(softDeadline, auctionEnd.getTime()) : softDeadline);
274
313
  // Locked cart line, not an order — see BuyNowAuctionResult above. Written
275
314
  // through the repository directly (auctions are canAddToCart:false for
276
315
  // user-initiated adds), the same deliberate bypass checkoutOffer uses.
@@ -285,18 +324,52 @@ export async function buyNowAuction(userId, userName, userEmail, input) {
285
324
  storeName: product.storeName ?? "",
286
325
  listingType: "auction",
287
326
  isAuctionWin: true,
327
+ isBuyout: true,
288
328
  auctionId: productId,
329
+ bidId: bid.id,
289
330
  lockedPrice: buyNowPrice,
290
- checkoutDeadline: new Date(Date.now() + AUCTION_CHECKOUT_WINDOW_MS),
331
+ checkoutDeadline,
291
332
  locked: true,
292
333
  });
293
- const checkoutUrl = `${String(ROUTES.USER.CHECKOUT)}?lane=${CART_LANE.AUCTION}`;
334
+ // Best-effort, exactly like placeBid's outbid notice: the hold is already
335
+ // committed and a failed notification must never undo it.
336
+ try {
337
+ await sendNotification({
338
+ userId,
339
+ type: "bid_won",
340
+ priority: "high",
341
+ title: BID_MESSAGES.BUY_NOW_HELD_TITLE,
342
+ message: BID_MESSAGES.BUY_NOW_HELD_MESSAGE(product.title, currency, buyNowPrice, AUCTION_BUYOUT_WINDOW_MINUTES),
343
+ relatedId: productId,
344
+ relatedType: "product",
345
+ actionUrl: AUCTION_CHECKOUT_URL,
346
+ actionLabel: BID_MESSAGES.BUY_NOW_HELD_ACTION,
347
+ });
348
+ }
349
+ catch (notifyErr) {
350
+ void normalizeError(notifyErr);
351
+ serverLogger.warn("buyNowAuction: hold notification failed", {
352
+ error: notifyErr,
353
+ productId,
354
+ userId,
355
+ });
356
+ }
294
357
  serverLogger.info("buyNowAuction", {
295
358
  productId,
296
359
  userId,
360
+ bidId: bid.id,
297
361
  amount: buyNowPrice,
362
+ checkoutDeadline: checkoutDeadline.toISOString(),
298
363
  });
299
- return { cartLocked: true, productId, amount: buyNowPrice, currency, checkoutUrl };
364
+ return {
365
+ cartLocked: true,
366
+ productId,
367
+ amount: buyNowPrice,
368
+ currency,
369
+ bidId: bid.id,
370
+ checkoutDeadline,
371
+ checkoutUrl: AUCTION_CHECKOUT_URL,
372
+ };
300
373
  }
301
374
  export async function listBidsByProduct(productId, params) {
302
375
  const result = await bidRepository.list({
@@ -15,7 +15,7 @@ import { formatCurrency } from "../../../utils/number.formatter";
15
15
  import { normalizeRichTextHtml } from "../../../utils/string.formatter";
16
16
  import { safeDisplayName } from "../../../security";
17
17
  import { getSiteSettingsGlobal } from "../../admin/utils/getSiteSettingsGlobal";
18
- import { resolveMinBid, resolveMinBidIncrement, } from "../../../_internal/shared/features/auctions/config";
18
+ import { isBuyNowAvailable, resolveMinBid, resolveMinBidIncrement, } from "../../../_internal/shared/features/auctions/config";
19
19
  import { Button, Container, CountdownDisplay, Div, Heading, Input, Main, Nav, RichText, Row, Section, Span, Stack, Text, Ul, Li, Dl, Dt, Dd, } from "../../../ui";
20
20
  import { AuctionDetailView } from "../../products/components/AuctionDetailView";
21
21
  import { ProductTabsShell } from "../../products/components/ProductTabsShell";
@@ -45,8 +45,8 @@ function toDescriptionHtml(raw) {
45
45
  return normalizeRichTextHtml(s);
46
46
  }
47
47
  function renderAuctionInfoPanel(props) {
48
- const { productId, title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref } = props;
49
- return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_LIVE_BADGE, children: "\uD83C\uDFF7\uFE0F Live Auction" }), isEnded ? (_jsx(Span, { color: "error", surface: "danger-surface", size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", children: "Ended" })) : (_jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", children: "Active" }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-0.5", color: "muted", size: "xs", children: "Current bid" }), _jsx(LiveBidPrice, { productId: productId, currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "2xl", badgeSize: "sm" }), endDate && !isEnded && (_jsxs(Text, { className: "mt-1.5", color: "muted", size: "sm", children: ["Ends in ", _jsx(Span, { weight: "medium", color: "muted", children: _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" }) })] })), endDate && _jsxs(Text, { className: "mt-0.5", color: "faint", size: "xs", children: [isEnded ? "Ended" : "Ends", " ", endDate.toLocaleString()] })] }), buyNowPrice !== null && !isEnded && (_jsxs(Row, { align: "center", gap: "sm", className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)] bg-primary-50 dark:bg-primary-900/20", padding: "inlineSm", rounded: "lg", children: [_jsx(Span, { size: "xs", color: "muted", children: "Buy Now:" }), _jsx(Span, { size: "base", weight: "bold", className: "text-primary-700 dark:text-primary-300", children: formatCurrency(buyNowPrice, currency) })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, labels: { featured: "Featured", fasterDelivery: "Faster Delivery", ratedSeller: "Rated Seller", condition: "Condition", conditionNew: "New", conditionUsed: "Used", conditionBroken: "For Parts", conditionRefurbished: "Refurbished", returnable: "Returnable", freeShipping: "Free Shipping", codAvailable: "Cash on Delivery", emiAvailable: "EMI Available", wishlistCount: (n) => `${n} wishlisted`, categoryProductCount: (n, cat) => `${n} in ${cat}` } }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryName || category }), !category && categoryName && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: categoryName }), brand && brandSlug && _jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand }), brand && !brandSlug && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand })] })), productFeatures && features.length > 0 && _jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures }), !productFeatures && features.length > 0 && (_jsxs(Div, { border: "subtle", surface: "muted", padding: "inline", rounded: "xl", children: [_jsx(Text, { className: "mb-2 tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "About this item" }), _jsx(Ul, { spacing: "comfortable", size: "sm", color: "primary", children: features.map((f, i) => _jsxs(Li, { layout: "flex-start", gap: "2", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i)) })] })), descriptionHtml && _jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" }), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Listed by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && _jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" })] }) }))] }));
48
+ const { productId, title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, buyNowAvailable, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref } = props;
49
+ return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_LIVE_BADGE, children: "\uD83C\uDFF7\uFE0F Live Auction" }), isEnded ? (_jsx(Span, { color: "error", surface: "danger-surface", size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", children: "Ended" })) : (_jsx(Span, { color: "success", surface: "success-surface", size: "xs", weight: "medium", className: "inline-block", padding: "pill-sm", rounded: "full", children: "Active" }))] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-0.5", color: "muted", size: "xs", children: "Current bid" }), _jsx(LiveBidPrice, { productId: productId, currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "2xl", badgeSize: "sm" }), endDate && !isEnded && (_jsxs(Text, { className: "mt-1.5", color: "muted", size: "sm", children: ["Ends in ", _jsx(Span, { weight: "medium", color: "muted", children: _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" }) })] })), endDate && _jsxs(Text, { className: "mt-0.5", color: "faint", size: "xs", children: [isEnded ? "Ended" : "Ends", " ", endDate.toLocaleString()] })] }), buyNowAvailable && buyNowPrice !== null && (_jsxs(Row, { align: "center", gap: "sm", className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)] bg-primary-50 dark:bg-primary-900/20", padding: "inlineSm", rounded: "lg", children: [_jsx(Span, { size: "xs", color: "muted", children: "Buy Now:" }), _jsx(Span, { size: "base", weight: "bold", className: "text-primary-700 dark:text-primary-300", children: formatCurrency(buyNowPrice, currency) })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, labels: { featured: "Featured", fasterDelivery: "Faster Delivery", ratedSeller: "Rated Seller", condition: "Condition", conditionNew: "New", conditionUsed: "Used", conditionBroken: "For Parts", conditionRefurbished: "Refurbished", returnable: "Returnable", freeShipping: "Free Shipping", codAvailable: "Cash on Delivery", emiAvailable: "EMI Available", wishlistCount: (n) => `${n} wishlisted`, categoryProductCount: (n, cat) => `${n} in ${cat}` } }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryName || category }), !category && categoryName && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: categoryName }), brand && brandSlug && _jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand }), brand && !brandSlug && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand })] })), productFeatures && features.length > 0 && _jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures }), !productFeatures && features.length > 0 && (_jsxs(Div, { border: "subtle", surface: "muted", padding: "inline", rounded: "xl", children: [_jsx(Text, { className: "mb-2 tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "About this item" }), _jsx(Ul, { spacing: "comfortable", size: "sm", color: "primary", children: features.map((f, i) => _jsxs(Li, { layout: "flex-start", gap: "2", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i)) })] })), descriptionHtml && _jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" }), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Listed by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && _jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" })] }) }))] }));
50
50
  }
51
51
  /**
52
52
  * Store reviews for the auction's seller.
@@ -98,7 +98,12 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, on
98
98
  const isEnded = endDate ? endDate < new Date() : false;
99
99
  const bidCount = typeof p.bidCount === "number" ? p.bidCount : 0;
100
100
  const buyNowPrice = typeof p.buyNowPrice === "number" ? p.buyNowPrice : null;
101
- const bidsHaveStarted = p.bidsHaveStarted === true;
101
+ const buyNowAvailable = isBuyNowAvailable({
102
+ buyNowPrice,
103
+ currentBid,
104
+ isEnded,
105
+ isSold: p.isSold === true,
106
+ });
102
107
  const condition = typeof p.condition === "string" ? p.condition : null;
103
108
  const featured = p.featured === true;
104
109
  const shippingPaidBy = p.shippingPaidBy;
@@ -149,9 +154,9 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, on
149
154
  price: typeof p.price === "number" ? p.price : undefined,
150
155
  storeId: typeof p.storeId === "string" ? p.storeId : undefined,
151
156
  storeName: typeof p.storeName === "string" ? p.storeName : undefined,
152
- } }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: CLS_BREADCRUMB_LINK, children: "Auctions" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(AuctionDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, video: productVideo, productName: title })), renderInfo: () => renderAuctionInfoPanel({ productId: String(product.id), title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref }), renderBidForm: () => onPlaceBid ? (_jsx(Stack, { id: "auction-bid-form", gap: "3", children: _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(LiveBidPrice, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "xl", badgeSize: "xs" }), !isEnded && (_jsx(LiveMinIncrement, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, tiers: bidIncrementTiers, minBidIncrementOverride: minBidIncrementOverride }))] }), !isEnded && endDate && (_jsxs(Text, { align: "center", size: "xs", color: "muted", children: ["Ends in ", _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" })] })), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, tiers: bidIncrementTiers, minBidIncrementOverride: minBidIncrementOverride, currency: currency, isEnded: isEnded, auctionEndDate: endDate, buyNowPrice: buyNowPrice, bidsHaveStarted: bidsHaveStarted, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] }) })) : (
157
+ } }), _jsxs(Container, { size: "xl", padding: "y-lg", children: [_jsxs(Row, { className: "mb-4", align: "center", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Nav, { "aria-label": "Breadcrumb", layout: "flex-wrap", gap: "2xs", textSize: "xs", color: "muted", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: CLS_BREADCRUMB_LINK, children: "Auctions" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "truncate max-w-[200px]", color: "muted", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(AuctionDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, video: productVideo, productName: title })), renderInfo: () => renderAuctionInfoPanel({ productId: String(product.id), title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, buyNowAvailable, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref }), renderBidForm: () => onPlaceBid ? (_jsx(Stack, { id: "auction-bid-form", gap: "3", children: _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(LiveBidPrice, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "xl", badgeSize: "xs" }), !isEnded && (_jsx(LiveMinIncrement, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, tiers: bidIncrementTiers, minBidIncrementOverride: minBidIncrementOverride }))] }), !isEnded && endDate && (_jsxs(Text, { align: "center", size: "xs", color: "muted", children: ["Ends in ", _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" })] })), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, tiers: bidIncrementTiers, minBidIncrementOverride: minBidIncrementOverride, currency: currency, isEnded: isEnded, auctionEndDate: endDate, buyNowPrice: buyNowPrice, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] }) })) : (
153
158
  /* Read-only bid panel — shown when no bid action is wired (preview/demo) */
154
- _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: [_jsx(Stack, { gap: "xs", children: _jsxs(Text, { size: "xs", color: "muted", children: ["Starting bid: ", formatCurrency(startingBid, currency), !isEnded && _jsxs(_Fragment, { children: [" \u00B7 min increment ", formatCurrency(initialMinBidIncrement, currency)] })] }) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number", placeholder: `At least ${formatCurrency(initialMinBid, currency)}`, min: initialMinBid, "aria-label": "Your bid amount", disabled: isEnded }), !isEnded && endDate && (_jsxs(Text, { align: "center", size: "xs", color: "muted", children: ["Ends in ", _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" })] })), _jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: isEnded, children: isEnded ? "Auction Ended" : "Place Bid" }), buyNowPrice !== null && !isEnded && !bidsHaveStarted && (_jsxs(Button, { variant: "secondary", size: "md", className: "w-full", disabled: true, children: ["Buy Now \u2014 ", formatCurrency(buyNowPrice, currency)] }))] }), tags.length > 0 && (_jsx(Div, { border: "default", className: "border-t", padding: "t-md", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { size: "xs", rounded: "full", padding: "pill-sm-tall", surface: "subtle", color: "muted", children: tag }, tag))) }) }))] })), renderMobileBidForm: () => !isEnded && onPlaceBid ? (_jsxs(Stack, { className: `lg:hidden ${__P.p4}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsx(LiveBidPrice, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "base", badgeSize: "xs" }), endDate && (_jsxs(Text, { align: "center", size: "xs", color: "muted", children: ["Ends in ", _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" })] })), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, tiers: bidIncrementTiers, minBidIncrementOverride: minBidIncrementOverride, currency: currency, isEnded: isEnded, auctionEndDate: endDate, buyNowPrice: buyNowPrice, bidsHaveStarted: bidsHaveStarted, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] })) : !isEnded ? (_jsxs(Div, { className: `${__P.p4} lg:hidden`, border: "subtle", rounded: "xl", surface: "muted", children: [_jsxs(Row, { align: "center", gap: "sm", className: "mb-3", children: [_jsx(Span, { size: "base", weight: "bold", className: "text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { size: "xs", color: "muted", children: [bidCount, " bids"] })] }), endDate && (_jsxs(Text, { align: "center", className: "mb-3", size: "xs", color: "muted", children: ["Ends in ", _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" })] })), _jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Place Bid" })] })) : null, renderSublistingSection: sublistingCategoryId
159
+ _jsxs(Stack, { className: `${__P.p5}`, border: "subtle", gap: "md", rounded: "xl", surface: "muted", children: [_jsx(Stack, { gap: "xs", children: _jsxs(Text, { size: "xs", color: "muted", children: ["Starting bid: ", formatCurrency(startingBid, currency), !isEnded && _jsxs(_Fragment, { children: [" \u00B7 min increment ", formatCurrency(initialMinBidIncrement, currency)] })] }) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number", placeholder: `At least ${formatCurrency(initialMinBid, currency)}`, min: initialMinBid, "aria-label": "Your bid amount", disabled: isEnded }), !isEnded && endDate && (_jsxs(Text, { align: "center", size: "xs", color: "muted", children: ["Ends in ", _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" })] })), _jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: isEnded, children: isEnded ? "Auction Ended" : "Place Bid" }), buyNowAvailable && buyNowPrice !== null && (_jsxs(Text, { align: "center", size: "xs", color: "muted", children: ["Buy Now available at ", formatCurrency(buyNowPrice, currency)] }))] }), tags.length > 0 && (_jsx(Div, { border: "default", className: "border-t", padding: "t-md", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { size: "xs", rounded: "full", padding: "pill-sm-tall", surface: "subtle", color: "muted", children: tag }, tag))) }) }))] })), renderMobileBidForm: () => !isEnded && onPlaceBid ? (_jsxs(Stack, { className: `lg:hidden ${__P.p4}`, border: "subtle", gap: "3", rounded: "xl", surface: "muted", children: [_jsx(LiveBidPrice, { productId: String(product.id), currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "base", badgeSize: "xs" }), endDate && (_jsxs(Text, { align: "center", size: "xs", color: "muted", children: ["Ends in ", _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" })] })), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, tiers: bidIncrementTiers, minBidIncrementOverride: minBidIncrementOverride, currency: currency, isEnded: isEnded, auctionEndDate: endDate, buyNowPrice: buyNowPrice, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] })) : !isEnded ? (_jsxs(Div, { className: `${__P.p4} lg:hidden`, border: "subtle", rounded: "xl", surface: "muted", children: [_jsxs(Row, { align: "center", gap: "sm", className: "mb-3", children: [_jsx(Span, { size: "base", weight: "bold", className: "text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { size: "xs", color: "muted", children: [bidCount, " bids"] })] }), endDate && (_jsxs(Text, { align: "center", className: "mb-3", size: "xs", color: "muted", children: ["Ends in ", _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" })] })), _jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Place Bid" })] })) : null, renderSublistingSection: sublistingCategoryId
155
160
  ? () => (_jsx(SublistingCarouselSection, { sublistingCategoryId: sublistingCategoryId, currentListingId: String(product.id) }))
156
161
  : undefined, renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-[var(--appkit-color-text-muted)]" })) : undefined, specsContent: specs.length > 0 ? (_jsx(Dl, { divide: "subtle", rounded: "xl", border: "subtle", className: "overflow-hidden", children: specs.map((s, i) => (_jsxs(Row, { gap: "md", oddEven: "zebra", surface: "default", padding: "inline", children: [_jsx(Dt, { className: "w-36 flex-shrink-0", color: "primary", weight: "medium", children: s.name }), _jsxs(Dd, { className: "flex-1", color: "muted", children: [s.value, s.unit ? ` ${s.unit}` : ""] })] }, i))) })) : undefined, customTabs: customSections.map((s) => ({
157
162
  id: s.id,
@@ -2,20 +2,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Container, Heading, Main, Section } from "../../../ui";
3
3
  import { AdSlot } from "../../homepage/components/AdSlot";
4
4
  import { AuctionsIndexListing } from "../../products/components/AuctionsIndexListing";
5
- import { listPublicProducts, parsePublicProductParams, } from "../../../_internal/server/features/products/list-public";
5
+ import { listPublicProducts, parsePublicProductParams, defaultAvailabilityForListingTypes, } from "../../../_internal/server/features/products/list-public";
6
6
  const AUCTION_LISTING_TYPES = ["auction"];
7
7
  const DEFAULT_PAGE_SIZE = 24;
8
8
  const DEFAULT_SORT = "auctionEndDate";
9
9
  export async function AuctionsListView({ searchParams = {} }) {
10
- // Shared with /api/products. `hideEndedByDefault` mirrors
11
- // AuctionsIndexListing's `showEnded ? … : dateFrom=now` default; because the
12
- // default sort IS auctionEndDate, listPublicProducts pushes that range into
13
- // Firestore rather than filtering in memory.
10
+ // Shared with /api/products, and derived rather than hardcoded — this view
11
+ // carried a literal `hideEndedByDefault: true` until 2026-08-24, which is
12
+ // exactly the mirrored-literal shape Root Cause #30 is written as.
14
13
  const initial = await listPublicProducts(parsePublicProductParams(searchParams, {
15
14
  listingTypes: AUCTION_LISTING_TYPES,
16
15
  pageSize: DEFAULT_PAGE_SIZE,
17
16
  sorts: DEFAULT_SORT,
18
- hideEndedByDefault: true,
17
+ ...defaultAvailabilityForListingTypes(AUCTION_LISTING_TYPES),
19
18
  }));
20
19
  return (_jsx(Main, { children: _jsx(Section, { padding: "y-2xl", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, className: "mb-8", color: "primary", size: "3xl", weight: "semibold", children: "Live Auctions" }), _jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-6" }), _jsx(AuctionsIndexListing, { initialData: initial }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] }) }) }));
21
20
  }