@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
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Document fields with a proven `useSiteSettings()` reader. Every entry below
3
+ * was traced to a call site; anything without one belongs in the private list,
4
+ * because an unused public field is pure attack surface.
5
+ */
6
+ export const PUBLIC_SITE_SETTINGS_FIELDS = [
7
+ "contact", // user/orders/[id]/payment — contact.whatsappNumber (narrowed: no address/upiVpa)
8
+ "payment", // CheckoutRouteClient — payment.otpCheckoutThreshold (narrowed)
9
+ "featureFlags", // useListingTypeFlags — featureFlags.listingTypes (narrowed: no adminCheckoutBypass)
10
+ "notificationChannels", // NotificationPreferencesPanel + CheckoutRouteClient (whatsapp.otpEnabled)
11
+ "announcementBar", // AdminSiteView; also the homepage banner
12
+ "navConfig", // NavbarWithSettings, DashboardLayoutClient
13
+ "actionConfig", // useAuthGate
14
+ "background", // DashboardLayoutClient (background.light / .dark)
15
+ "watermark", // MediaVideo, as the fallback behind effectiveWatermark
16
+ ];
17
+ /** Everything else, with the reason it stays server-side. */
18
+ export const PRIVATE_SITE_SETTINGS_FIELDS = [
19
+ "id", // singleton bookkeeping
20
+ "createdAt", // bookkeeping
21
+ "updatedAt", // bookkeeping
22
+ "credentials", // encrypted provider secrets — never leaves the server in any form
23
+ "emailSettings", // internal sender config + staff digest recipient addresses
24
+ "legalPages", // served by their own pages; large HTML blobs with no client reader
25
+ "commissions", // internal economics; the buyer-facing subset ships via toBuyerFacingFees
26
+ "emi", // ditto — surchargeSellerSharePercent is a platform/seller revenue split
27
+ "gst", // GSTIN, registered legal name and address
28
+ "laborRate", // internal procurement cost basis
29
+ "adSettings", // providerCredentials + unpublished ad inventory; /api/ads serves the filtered public view
30
+ "integrations", // analytics/tag IDs are injected server-side into markup, not fetched by the client
31
+ "platformLimits", // server-enforced caps; no client reader
32
+ "auctionConfig", // read by AuctionDetailPageView, a Server Component doing its own settings read
33
+ "disabledRoutes", // read by app/[locale]/layout.tsx (RSC) to gate routing
34
+ "siteName", // delivered as an SSR prop / rendered server-side
35
+ "tagline", // admin chrome only
36
+ "motto", // rendered server-side
37
+ "logo", // delivered as an SSR prop by app/[locale]/layout.tsx
38
+ "favicon", // emitted by generateMetadata, server-side
39
+ "seo", // consumed by generateMetadata, server-side
40
+ "socialLinks", // rendered server-side in the footer
41
+ "features", // rendered server-side
42
+ "shipping", // rendered server-side
43
+ "returns", // rendered server-side
44
+ "faq", // faq.variables are interpolated server-side by /api/faqs
45
+ "aboutContent", // rendered by the /about RSC
46
+ "navbarConfig", // rendered server-side
47
+ "footerConfig", // rendered server-side
48
+ "theme", // delivered as an SSR prop to ThemeProvider by app/[locale]/layout.tsx
49
+ "featuredResults", // rendered server-side by search
50
+ ];
51
+ export function toPublicSiteSettings(doc, opts) {
52
+ return {
53
+ // Explicit sub-literals, never spreads. These two are the ones that carry
54
+ // real weight: `contact` drops `address` + `upiVpa`, and `featureFlags`
55
+ // drops `adminCheckoutBypass` — the flag that turns off payment and OTP.
56
+ contact: {
57
+ email: doc.contact?.email ?? "",
58
+ phone: doc.contact?.phone ?? "",
59
+ whatsappNumber: doc.contact?.whatsappNumber ?? "",
60
+ },
61
+ payment: {
62
+ otpCheckoutThreshold: doc.payment?.otpCheckoutThreshold,
63
+ },
64
+ featureFlags: {
65
+ listingTypes: doc.featureFlags?.listingTypes,
66
+ },
67
+ notificationChannels: doc.notificationChannels,
68
+ announcementBar: doc.announcementBar,
69
+ navConfig: doc.navConfig,
70
+ actionConfig: doc.actionConfig,
71
+ background: doc.background,
72
+ watermark: doc.watermark,
73
+ effectiveWatermark: opts?.effectiveWatermark,
74
+ };
75
+ }
@@ -0,0 +1 @@
1
+ export { toPublicSiteSettings, PUBLIC_SITE_SETTINGS_FIELDS, PRIVATE_SITE_SETTINGS_FIELDS, type ToPublicSiteSettingsOptions, } from "./adapters";
@@ -0,0 +1 @@
1
+ export { toPublicSiteSettings, PUBLIC_SITE_SETTINGS_FIELDS, PRIVATE_SITE_SETTINGS_FIELDS, } from "./adapters";
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Store adapters — the single allow-list projection for every public store
3
+ * response, HTTP or SSR.
4
+ *
5
+ * Every field of `StoreDocument` is triaged into exactly one of
6
+ * `PUBLIC_STORE_FIELDS` (mapped into the returned literal) or
7
+ * `PRIVATE_STORE_FIELDS` (with a stated reason). A field added to the schema
8
+ * and to neither list fails `scripts/audit-public-projection-parity.mjs`, so
9
+ * new fields are private by default.
10
+ *
11
+ * Why this exists (2026-08-24): `src/app/[locale]/stores/[storeSlug]/about/
12
+ * page.tsx` passed a raw `StoreDocument` into a `"use client"` component via
13
+ * `as unknown as StoreDetail` — a cast with no runtime effect — so Next.js
14
+ * serialised the whole document into the RSC flight payload embedded in that
15
+ * page's PUBLIC HTML, including the Meta WhatsApp Business `accessToken`
16
+ * (decrypted on every read by `StoreRepository.mapDoc` at the time),
17
+ * `adminNotes`, `suspensionReason` and `customCommissionRate`. The public
18
+ * store *APIs* were clean only because each had hand-rolled its own literal;
19
+ * there were three such copies and the SSR page had none. This module is the
20
+ * one copy they all now share.
21
+ */
22
+ import type { StoreDocument } from "../../../../features/stores/schemas/firestore";
23
+ import type { StoreDetail, StoreListItem } from "../../../../features/stores/types/index";
24
+ /**
25
+ * Document fields that may reach an unauthenticated client. Keep in sync with
26
+ * the object literals below — the audit asserts both directions.
27
+ */
28
+ export declare const PUBLIC_STORE_FIELDS: readonly ["id", "storeSlug", "ownerId", "storeName", "storeDescription", "storeCategory", "storeLogoURL", "storeBannerURL", "status", "isPublic", "stats", "bio", "location", "website", "socialLinks", "returnPolicy", "shippingPolicy", "isVacationMode", "vacationMessage", "createdAt"];
29
+ /** Everything else, with the reason it stays server-side. */
30
+ export declare const PRIVATE_STORE_FIELDS: readonly ["updatedAt", "whatsappConfig", "adminNotes", "isVerified", "suspensionReason", "capabilities", "customCommissionRate", "googleReviews", "shippingConfig", "emiEnabled", "isFeatured", "vacationReturnDate", "isTestData", "testDataExpiresAt"];
31
+ /**
32
+ * Accepted input: a `StoreDocument` (the common case) or a loosely-typed
33
+ * Firestore row. The legacy top-level counters below predate `stats` and are
34
+ * still present on older documents, so they stay as declared fallbacks rather
35
+ * than an index signature — `Record<string, unknown>` would not accept a
36
+ * plain `StoreDocument`.
37
+ */
38
+ export type StoreProjectionSource = Partial<StoreDocument> & {
39
+ totalProducts?: number;
40
+ itemsSold?: number;
41
+ totalReviews?: number;
42
+ averageRating?: number;
43
+ };
44
+ export interface ToStoreListItemOptions {
45
+ /** Reserved for future per-caller shaping (see CLAUDE.md § Encapsulation + Override Contract). */
46
+ readonly _reserved?: never;
47
+ }
48
+ /**
49
+ * Grid/list projection. Stats are flattened off `stats`, falling back to the
50
+ * legacy top-level counters some older documents still carry.
51
+ */
52
+ export declare function toStoreListItem(doc: StoreProjectionSource, _opts?: ToStoreListItemOptions): StoreListItem;
53
+ export interface ToStoreDetailOptions extends ToStoreListItemOptions {
54
+ }
55
+ /** Full public store profile — everything `StoreAboutView` renders, nothing more. */
56
+ export declare function toStoreDetail(doc: StoreProjectionSource, opts?: ToStoreDetailOptions): StoreDetail;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Document fields that may reach an unauthenticated client. Keep in sync with
3
+ * the object literals below — the audit asserts both directions.
4
+ */
5
+ export const PUBLIC_STORE_FIELDS = [
6
+ "id",
7
+ "storeSlug",
8
+ "ownerId",
9
+ "storeName",
10
+ "storeDescription",
11
+ "storeCategory",
12
+ "storeLogoURL",
13
+ "storeBannerURL",
14
+ "status",
15
+ "isPublic",
16
+ "stats",
17
+ "bio",
18
+ "location",
19
+ "website",
20
+ "socialLinks",
21
+ "returnPolicy",
22
+ "shippingPolicy",
23
+ "isVacationMode",
24
+ "vacationMessage",
25
+ "createdAt",
26
+ ];
27
+ /** Everything else, with the reason it stays server-side. */
28
+ export const PRIVATE_STORE_FIELDS = [
29
+ "updatedAt", // bookkeeping; no public reader
30
+ "whatsappConfig", // Meta access token + WABA/catalog ids — secret
31
+ "adminNotes", // schema comment: never shown to the owner or the public
32
+ "isVerified", // admin moderation state; surfaced via a separate badge feed if ever needed
33
+ "suspensionReason", // admin moderation note
34
+ "capabilities", // internal feature gating; server-enforced, never client-read
35
+ "customCommissionRate", // commercial terms for this seller
36
+ "googleReviews", // placeId is billed-API config; the RSC reads it server-side
37
+ "shippingConfig", // resolved server-side into per-item shipping at cart time
38
+ "emiEnabled", // read server-side by the checkout/product RSCs, never from a public payload
39
+ "isFeatured", // ranking input; not part of the public store profile
40
+ "vacationReturnDate", // not rendered by StoreAboutView; add to PUBLIC when a reader exists
41
+ "isTestData", // tester-sandbox tagging; filtered by filterTestDataForViewer
42
+ "testDataExpiresAt", // ditto
43
+ ];
44
+ /** `createdAt` is `Date` on the document and `string` on the client types. */
45
+ function toIsoString(value) {
46
+ if (!value)
47
+ return undefined;
48
+ if (value instanceof Date)
49
+ return value.toISOString();
50
+ if (typeof value === "string")
51
+ return value;
52
+ return undefined;
53
+ }
54
+ /**
55
+ * Grid/list projection. Stats are flattened off `stats`, falling back to the
56
+ * legacy top-level counters some older documents still carry.
57
+ */
58
+ export function toStoreListItem(doc, _opts) {
59
+ const stats = doc.stats;
60
+ return {
61
+ id: doc.id,
62
+ storeSlug: doc.storeSlug,
63
+ ownerId: doc.ownerId,
64
+ storeName: doc.storeName,
65
+ storeDescription: doc.storeDescription,
66
+ storeCategory: doc.storeCategory,
67
+ storeLogoURL: doc.storeLogoURL,
68
+ storeBannerURL: doc.storeBannerURL,
69
+ status: doc.status,
70
+ isPublic: doc.isPublic,
71
+ totalProducts: stats?.totalProducts ?? doc.totalProducts,
72
+ itemsSold: stats?.itemsSold ?? doc.itemsSold,
73
+ totalReviews: stats?.totalReviews ?? doc.totalReviews,
74
+ averageRating: stats?.averageRating ?? doc.averageRating,
75
+ createdAt: toIsoString(doc.createdAt),
76
+ };
77
+ }
78
+ /** Full public store profile — everything `StoreAboutView` renders, nothing more. */
79
+ export function toStoreDetail(doc, opts) {
80
+ return {
81
+ ...toStoreListItem(doc, opts),
82
+ bio: doc.bio,
83
+ location: doc.location,
84
+ website: doc.website,
85
+ socialLinks: doc.socialLinks,
86
+ returnPolicy: doc.returnPolicy,
87
+ shippingPolicy: doc.shippingPolicy,
88
+ isVacationMode: doc.isVacationMode,
89
+ vacationMessage: doc.vacationMessage,
90
+ };
91
+ }
@@ -1,3 +1,4 @@
1
1
  export { getStoreForDetail, listStoreProductsInitial, listStoreAuctionsInitial, listStorePreOrdersInitial, listSitemapStores, } from "./data";
2
2
  export { STORES_PAGE_SIZE, STORES_PRODUCTS_PAGE_SIZE, STORES_SITEMAP_LIMIT, STORES_FEATURED_LIMIT, } from "../../../shared/features/stores/config";
3
3
  export { renderStoreOgImage, renderStoreOg, type StoreOgData } from "./og";
4
+ export { toStoreDetail, toStoreListItem, PUBLIC_STORE_FIELDS, PRIVATE_STORE_FIELDS, type ToStoreDetailOptions, type ToStoreListItemOptions, type StoreProjectionSource, } from "./adapters";
@@ -1,3 +1,4 @@
1
1
  export { getStoreForDetail, listStoreProductsInitial, listStoreAuctionsInitial, listStorePreOrdersInitial, listSitemapStores, } from "./data";
2
2
  export { STORES_PAGE_SIZE, STORES_PRODUCTS_PAGE_SIZE, STORES_SITEMAP_LIMIT, STORES_FEATURED_LIMIT, } from "../../../shared/features/stores/config";
3
3
  export { renderStoreOgImage, renderStoreOg } from "./og";
4
+ export { toStoreDetail, toStoreListItem, PUBLIC_STORE_FIELDS, PRIVATE_STORE_FIELDS, } from "./adapters";
@@ -1,6 +1,7 @@
1
1
  import { bidRepository, cartRepository, productRepository, storeRepository, } from "../../../../repositories";
2
2
  import { sendNotification } from "../../../../features/admin/actions/notification-actions";
3
- import { AUCTION_MESSAGES } from "../handlers/messages";
3
+ import { normalizeError } from "../../../../errors/normalize";
4
+ import { AUCTION_MESSAGES, BID_MESSAGES } from "../handlers/messages";
4
5
  import { ROUTES } from "../../../../next/routing/route-map";
5
6
  import { CART_LANE, LANE_CHECKOUT_WINDOW_MS } from "../../../shared/checkout/lanes";
6
7
  /**
@@ -13,8 +14,61 @@ import { CART_LANE, LANE_CHECKOUT_WINDOW_MS } from "../../../shared/checkout/lan
13
14
  const AUCTION_CHECKOUT_WINDOW_MS = LANE_CHECKOUT_WINDOW_MS.auction;
14
15
  /** Deep link the winner straight into the auction checkout lane. */
15
16
  const AUCTION_CHECKOUT_URL = `${String(ROUTES.USER.CHECKOUT)}?lane=${CART_LANE.AUCTION}`;
17
+ /**
18
+ * Clear every unpaid Buy Now hold on an auction whose clock just ran out.
19
+ *
20
+ * This is the user's explicit rule: "if by the time the checkout is not
21
+ * finished but auction timeout ends the buyout should fail as well." The item
22
+ * goes to whoever actually won the bidding, and the claimants get their carts
23
+ * back — the line is `locked: true`, so they cannot clear it themselves.
24
+ *
25
+ * `cancelled`, never `forfeited`: forfeiture means "won, then defaulted" and
26
+ * carries a warning about account restrictions. Nobody won anything here, and
27
+ * losing a race against a clock is not misconduct.
28
+ *
29
+ * Best-effort per hold — one failure must not abort settling the auction.
30
+ */
31
+ async function releasePendingBuyouts(ctx, product, holds) {
32
+ for (const hold of holds) {
33
+ try {
34
+ await cartRepository.removeItemsByBidId(hold.data.userId, hold.id);
35
+ await bidRepository.markCancelled(hold.id);
36
+ await sendNotification({
37
+ userId: hold.data.userId,
38
+ type: "auction_ended",
39
+ priority: "high",
40
+ title: BID_MESSAGES.BUY_NOW_AUCTION_ENDED_TITLE,
41
+ message: BID_MESSAGES.BUY_NOW_AUCTION_ENDED_MESSAGE(product.title),
42
+ relatedId: product.id,
43
+ relatedType: "product",
44
+ });
45
+ }
46
+ catch (err) {
47
+ void normalizeError(err);
48
+ ctx.logger.warn(`Failed to release buyout hold ${hold.id}`, {
49
+ error: err instanceof Error ? err.message : String(err),
50
+ });
51
+ }
52
+ }
53
+ ctx.logger.info(`Released ${holds.length} unpaid buyout hold(s) on ${product.id}`);
54
+ }
16
55
  async function settleAuction(ctx, product) {
17
- const activeBids = await bidRepository.getActiveByProduct(product.id);
56
+ const allActive = await bidRepository.getActiveByProduct(product.id);
57
+ /**
58
+ * A pending Buy Now claim is `active` and sits at the BIN price, so it would
59
+ * be `activeBids[0]` and win by default the instant the clock ran out —
60
+ * handing the item to somebody who explicitly did NOT complete their purchase
61
+ * in time. A buyout only counts once it has been paid for, at which point
62
+ * `claimAuctionForCheckout` has already marked it `won` and flipped the
63
+ * product to `isSold`, so this job never sees that auction at all.
64
+ *
65
+ * The stale holds are released below, after the real winner is decided.
66
+ */
67
+ const pendingBuyouts = allActive.filter((e) => e.data.isBuyout === true);
68
+ const activeBids = allActive.filter((e) => e.data.isBuyout !== true);
69
+ if (pendingBuyouts.length > 0) {
70
+ await releasePendingBuyouts(ctx, product, pendingBuyouts);
71
+ }
18
72
  if (activeBids.length === 0) {
19
73
  const batch = ctx.db.batch();
20
74
  productRepository.updateStatusInBatch(batch, product.id, "archived");
@@ -1,6 +1,7 @@
1
1
  import { normalizeError } from "../../../../errors/normalize";
2
2
  import { bidRepository, cartRepository, offerRepository, storeRepository } from "../../../../repositories";
3
3
  import { sendNotification } from "../../../../features/admin/actions/notification-actions";
4
+ import { BID_MESSAGES } from "../handlers/messages";
4
5
  export async function runOfferExpiry(ctx) {
5
6
  ctx.logger.info("Starting offer expiry sweep");
6
7
  let expiredOffers;
@@ -116,16 +117,25 @@ async function expireAcceptedPastCheckoutDeadline(ctx) {
116
117
  }
117
118
  }
118
119
  /**
119
- * A won auction the buyer never paid for.
120
+ * An auction line the buyer never paid for. TWO different situations, and the
121
+ * difference matters to both parties.
120
122
  *
121
- * Folded into this job rather than given its own scheduled function: both are
122
- * "a locked cart line whose claim has lapsed", they want the same cadence, and
123
- * Cloud Scheduler bills per registered job (see CLAUDE.md's Firebase budget
124
- * table — 27 jobs is already an accepted, documented cost).
123
+ * Folded into this job rather than given its own scheduled function: all of
124
+ * these are "a locked cart line whose claim has lapsed", they want the same
125
+ * cadence, and Cloud Scheduler bills per registered job (see CLAUDE.md's
126
+ * Firebase budget table — 27 jobs is already an accepted, documented cost).
125
127
  *
126
- * The listing is NOT silently relisted. A forfeited win is a seller decision
127
- * (relist, offer to the runner-up, or ban the non-payer), so this clears the
128
- * line, marks the bid forfeited, and notifies both sides.
128
+ * **A settled win** (`!isBuyout`) is a finished auction the buyer committed to
129
+ * by bidding. The listing is NOT silently relisted — that is a seller decision
130
+ * (relist, approach the runner-up, or ban the non-payer) — so the line is
131
+ * cleared, the bid is `forfeited`, and both sides are told.
132
+ *
133
+ * **A buyout hold** (`isBuyout`) never won anything. `buyNowAuction` left the
134
+ * auction completely untouched, so there is no sale to reverse and nothing for
135
+ * the seller to decide: the line is cleared, the bid is `cancelled`, and only
136
+ * the buyer hears about it. This is what makes flipping nothing on the product
137
+ * at claim time pay off — the old buy-now marked the listing sold immediately,
138
+ * so an abandoned buyout permanently killed an auction that still had time left.
129
139
  */
130
140
  async function lapseUnpaidAuctionWins(ctx) {
131
141
  let stale;
@@ -137,7 +147,33 @@ async function lapseUnpaidAuctionWins(ctx) {
137
147
  ctx.logger.error("Failed to scan for expired locked cart lines", err);
138
148
  return;
139
149
  }
140
- const auctionLines = stale.filter(({ item }) => item.bidId && item.isAuctionWin);
150
+ const allAuctionLines = stale.filter(({ item }) => item.bidId && item.isAuctionWin);
151
+ const buyoutHolds = allAuctionLines.filter(({ item }) => item.isBuyout === true);
152
+ const auctionLines = allAuctionLines.filter(({ item }) => item.isBuyout !== true);
153
+ if (buyoutHolds.length > 0) {
154
+ ctx.logger.info(`Found ${buyoutHolds.length} expired Buy Now hold(s)`);
155
+ for (const { userId, item } of buyoutHolds) {
156
+ try {
157
+ await cartRepository.removeItemsByBidId(userId, item.bidId);
158
+ await bidRepository.markCancelled(item.bidId);
159
+ await sendNotification({
160
+ userId,
161
+ type: "auction_ended",
162
+ priority: "normal",
163
+ title: BID_MESSAGES.BUY_NOW_LAPSED_TITLE,
164
+ message: BID_MESSAGES.BUY_NOW_LAPSED_MESSAGE(item.productTitle),
165
+ relatedId: item.auctionId ?? item.productId,
166
+ relatedType: "product",
167
+ });
168
+ }
169
+ catch (err) {
170
+ void normalizeError(err);
171
+ ctx.logger.warn(`Failed to lapse buyout hold ${item.bidId}`, {
172
+ error: err instanceof Error ? err.message : String(err),
173
+ });
174
+ }
175
+ }
176
+ }
141
177
  if (auctionLines.length === 0)
142
178
  return;
143
179
  ctx.logger.info(`Found ${auctionLines.length} unpaid auction win(s) past deadline`);
@@ -6,5 +6,10 @@ import type { JobContext } from "../runtime/types";
6
6
  * effect, plus `autoApproved`/`autoApprovedAt` so it's visibly distinguishable
7
7
  * from a manually-reviewed order. Buyer/seller can raise a dispute afterward
8
8
  * via `raiseOrderDisputeAction` — this sweep never reverses itself.
9
+ *
10
+ * Buyout orders are held back to 24 hours (see `BUYOUT_AUTO_APPROVE_HOURS`).
11
+ * The QUERY still runs at the 2-hour cutoff and the longer window is applied
12
+ * in-memory, so this keeps one query on one existing composite index rather
13
+ * than needing a second scan on a second cutoff.
9
14
  */
10
15
  export declare function runPaymentReviewAutoApprove(ctx: JobContext): Promise<void>;
@@ -3,6 +3,18 @@ import { sendNotification } from "../../../../features/admin/actions/notificatio
3
3
  import { normalizeError } from "../../../../errors/normalize";
4
4
  import { ORDER_MESSAGES } from "../handlers/messages";
5
5
  const AUTO_APPROVE_HOURS = 2;
6
+ /**
7
+ * Buyout orders get a full day of admin review instead of two hours.
8
+ *
9
+ * A Buy Now purchase is settled in a one-hour scramble against a still-live
10
+ * auction, and the amounts involved are whole-listing rather than incremental —
11
+ * so an admin should get a proper working day to look at the proof before the
12
+ * safety net fires, rather than a window that can elapse overnight.
13
+ */
14
+ const BUYOUT_AUTO_APPROVE_HOURS = 24;
15
+ function autoApproveHoursFor(order) {
16
+ return order.isBuyout === true ? BUYOUT_AUTO_APPROVE_HOURS : AUTO_APPROVE_HOURS;
17
+ }
6
18
  /**
7
19
  * 2-hour auto-approve safety net — a manual-payment proof nobody has acted
8
20
  * on (no admin approve / re-upload-request / fraud-reject) within 2 hours of
@@ -10,10 +22,30 @@ const AUTO_APPROVE_HOURS = 2;
10
22
  * effect, plus `autoApproved`/`autoApprovedAt` so it's visibly distinguishable
11
23
  * from a manually-reviewed order. Buyer/seller can raise a dispute afterward
12
24
  * via `raiseOrderDisputeAction` — this sweep never reverses itself.
25
+ *
26
+ * Buyout orders are held back to 24 hours (see `BUYOUT_AUTO_APPROVE_HOURS`).
27
+ * The QUERY still runs at the 2-hour cutoff and the longer window is applied
28
+ * in-memory, so this keeps one query on one existing composite index rather
29
+ * than needing a second scan on a second cutoff.
13
30
  */
14
31
  export async function runPaymentReviewAutoApprove(ctx) {
15
32
  ctx.logger.info(`Scanning unreviewed payment proofs > ${AUTO_APPROVE_HOURS}h old`);
16
- const unreviewed = await orderRepository.getUnreviewedProofPastDeadline(AUTO_APPROVE_HOURS);
33
+ const scanned = await orderRepository.getUnreviewedProofPastDeadline(AUTO_APPROVE_HOURS);
34
+ const unreviewed = scanned.filter((entry) => {
35
+ const hours = autoApproveHoursFor(entry.data);
36
+ if (hours === AUTO_APPROVE_HOURS)
37
+ return true;
38
+ const uploadedAt = entry.data.paymentProofUploadedAt
39
+ ? new Date(entry.data.paymentProofUploadedAt)
40
+ : null;
41
+ if (!uploadedAt)
42
+ return false;
43
+ return ctx.now.getTime() - uploadedAt.getTime() >= hours * 60 * 60 * 1000;
44
+ });
45
+ const held = scanned.length - unreviewed.length;
46
+ if (held > 0) {
47
+ ctx.logger.info(`Holding ${held} buyout order(s) for the ${BUYOUT_AUTO_APPROVE_HOURS}h admin window`);
48
+ }
17
49
  if (unreviewed.length === 0) {
18
50
  ctx.logger.info("No unreviewed payment proofs past the auto-approve window");
19
51
  return;
@@ -19,6 +19,13 @@ export declare const AUCTION_MESSAGES: {
19
19
  export declare const BID_MESSAGES: {
20
20
  OUTBID_TITLE: string;
21
21
  OUTBID_MESSAGE: (productTitle: string, currency: string, currentBid: number) => string;
22
+ BUY_NOW_HELD_TITLE: string;
23
+ BUY_NOW_HELD_MESSAGE: (productTitle: string, currency: string, amount: number, minutes: number) => string;
24
+ BUY_NOW_HELD_ACTION: string;
25
+ BUY_NOW_LAPSED_TITLE: string;
26
+ BUY_NOW_LAPSED_MESSAGE: (productTitle: string) => string;
27
+ BUY_NOW_AUCTION_ENDED_TITLE: string;
28
+ BUY_NOW_AUCTION_ENDED_MESSAGE: (productTitle: string) => string;
22
29
  };
23
30
  export declare const ORDER_MESSAGES: {
24
31
  CANCELLED_TITLE: string;
@@ -19,6 +19,16 @@ export const AUCTION_MESSAGES = {
19
19
  export const BID_MESSAGES = {
20
20
  OUTBID_TITLE: "You've been outbid",
21
21
  OUTBID_MESSAGE: (productTitle, currency, currentBid) => `Someone placed a higher bid on "${productTitle}". Current bid: ${currency} ${currentBid}.`,
22
+ // Buy Now places a HOLD, not a purchase — the auction stays live and other
23
+ // buyers can still bid or buy it out, so the copy has to convey urgency
24
+ // without claiming the item is already theirs.
25
+ BUY_NOW_HELD_TITLE: "Complete your Buy Now purchase",
26
+ BUY_NOW_HELD_MESSAGE: (productTitle, currency, amount, minutes) => `You've claimed "${productTitle}" at the Buy Now price of ${currency} ${amount}. The auction is still live, so it's yours only once you've paid — complete checkout within ${minutes} minutes.`,
27
+ BUY_NOW_HELD_ACTION: "Pay now",
28
+ BUY_NOW_LAPSED_TITLE: "Buy Now claim expired",
29
+ BUY_NOW_LAPSED_MESSAGE: (productTitle) => `Your Buy Now claim on "${productTitle}" expired before payment was completed, so it's been released. The auction is still running if you'd like to bid.`,
30
+ BUY_NOW_AUCTION_ENDED_TITLE: "Buy Now claim released",
31
+ BUY_NOW_AUCTION_ENDED_MESSAGE: (productTitle) => `The auction for "${productTitle}" ended before you completed your Buy Now purchase, so your claim was released and the item went to the winning bidder.`,
22
32
  };
23
33
  export const ORDER_MESSAGES = {
24
34
  CANCELLED_TITLE: "Order cancelled",
@@ -134,7 +134,7 @@ export const ACTIONS = {
134
134
  "buy-it-now": {
135
135
  id: "auction.buy-it-now",
136
136
  label: "Buy It Now",
137
- description: "Skip bidding and purchase at the BIN price (SB-UNI-H). Hidden once bidsHaveStarted is true.",
137
+ description: "Claim the listing at the buy-now price (SB-UNI-H). Offered while the auction is unsold, before its end date, and while the buy-now price still beats the standing bid. Places a bid at that price plus a 1h locked cart line — the auction stays live until the buyer pays.",
138
138
  kind: "secondary",
139
139
  listingTypeScope: ["auction"],
140
140
  },
@@ -1876,6 +1876,20 @@ export const ACTIONS = {
1876
1876
  confirmKind: "danger",
1877
1877
  },
1878
1878
  },
1879
+ "cancel-offer": {
1880
+ id: "admin.cancel-offer",
1881
+ label: "Cancel Offer",
1882
+ ariaLabel: "Cancel this offer",
1883
+ description: "Expire the offer, clear it from the buyer's cart, and notify them. Admin cannot accept or counter on a seller's behalf.",
1884
+ kind: "danger",
1885
+ permissions: ["admin", "moderator"],
1886
+ confirmation: {
1887
+ title: "Cancel this offer?",
1888
+ body: "The offer will be expired and removed from the buyer's cart, and they'll be notified. This cannot be undone — the buyer would need to make a new offer.",
1889
+ confirmLabel: "Cancel offer",
1890
+ confirmKind: "danger",
1891
+ },
1892
+ },
1879
1893
  // ── Address ban management ─────────────────────────────────────────────
1880
1894
  "ban-address": {
1881
1895
  id: "admin.ban-address",
@@ -31,6 +31,31 @@ export declare const CART_LANE_LABELS: Record<CartLane, string>;
31
31
  export declare const LANE_CHECKOUT_WINDOW_MS: Record<"auction" | "offer", number>;
32
32
  /** 2 days, in hours — for buyer-facing copy ("pay within 48 hours"). */
33
33
  export declare const LANE_CHECKOUT_WINDOW_HOURS: Record<"auction" | "offer", number>;
34
+ /**
35
+ * A BUYOUT hold is a third window, deliberately much shorter than the two above,
36
+ * even though it lands in the same auction lane.
37
+ *
38
+ * A settled win (`LANE_CHECKOUT_WINDOW_MS.auction`) is a finished auction: the
39
+ * clock already ran, nobody else is waiting, and 48 hours costs no one anything.
40
+ * A buyout is the opposite — the auction is still live and still taking bids
41
+ * while the hold exists, so a long window would let one buyer sit on a listing
42
+ * they may never pay for. One hour, and the hold is additionally clamped to the
43
+ * auction's own end time by `buyNowAuction` (see `AUCTION_BUYOUT_WINDOW_MS`
44
+ * callers): a buyout that hasn't been paid for by the time the auction ends
45
+ * fails, because by then the auction has settled to whoever actually won it.
46
+ */
47
+ export declare const AUCTION_BUYOUT_WINDOW_MS: number;
48
+ /** 1 hour, in minutes — for buyer-facing copy ("pay within 60 minutes"). */
49
+ export declare const AUCTION_BUYOUT_WINDOW_MINUTES: number;
50
+ /**
51
+ * Deep link that drops a buyer straight into the auction lane at checkout.
52
+ *
53
+ * Declared once here, with the lane it selects, because it was previously a
54
+ * const in `auctionSettlement.ts` AND an inline template literal in
55
+ * `bid-actions.ts` — the same two-copies problem `LANE_CHECKOUT_WINDOW_MS` was
56
+ * extracted to fix, one layer up.
57
+ */
58
+ export declare const AUCTION_CHECKOUT_URL: string;
34
59
  /** The two lanes whose price is fixed outside the listing. */
35
60
  export declare function isLockedLane(lane: CartLane): boolean;
36
61
  /** Minimal shape this module needs — works on cart documents and client rows alike. */
@@ -24,6 +24,7 @@
24
24
  * @tag consumers:CartRouteClient,CheckoutRouteClient,cart-actions,order-splitter,checkout/actions
25
25
  * @tag sideEffects:none
26
26
  */
27
+ import { ROUTES } from "../../../next/routing/route-map";
27
28
  export const CART_LANE = {
28
29
  AUCTION: "auction",
29
30
  OFFER: "offer",
@@ -72,6 +73,31 @@ export const LANE_CHECKOUT_WINDOW_HOURS = {
72
73
  auction: LANE_CHECKOUT_WINDOW_MS.auction / (60 * 60 * 1000),
73
74
  offer: LANE_CHECKOUT_WINDOW_MS.offer / (60 * 60 * 1000),
74
75
  };
76
+ /**
77
+ * A BUYOUT hold is a third window, deliberately much shorter than the two above,
78
+ * even though it lands in the same auction lane.
79
+ *
80
+ * A settled win (`LANE_CHECKOUT_WINDOW_MS.auction`) is a finished auction: the
81
+ * clock already ran, nobody else is waiting, and 48 hours costs no one anything.
82
+ * A buyout is the opposite — the auction is still live and still taking bids
83
+ * while the hold exists, so a long window would let one buyer sit on a listing
84
+ * they may never pay for. One hour, and the hold is additionally clamped to the
85
+ * auction's own end time by `buyNowAuction` (see `AUCTION_BUYOUT_WINDOW_MS`
86
+ * callers): a buyout that hasn't been paid for by the time the auction ends
87
+ * fails, because by then the auction has settled to whoever actually won it.
88
+ */
89
+ export const AUCTION_BUYOUT_WINDOW_MS = 60 * 60 * 1000;
90
+ /** 1 hour, in minutes — for buyer-facing copy ("pay within 60 minutes"). */
91
+ export const AUCTION_BUYOUT_WINDOW_MINUTES = AUCTION_BUYOUT_WINDOW_MS / (60 * 1000);
92
+ /**
93
+ * Deep link that drops a buyer straight into the auction lane at checkout.
94
+ *
95
+ * Declared once here, with the lane it selects, because it was previously a
96
+ * const in `auctionSettlement.ts` AND an inline template literal in
97
+ * `bid-actions.ts` — the same two-copies problem `LANE_CHECKOUT_WINDOW_MS` was
98
+ * extracted to fix, one layer up.
99
+ */
100
+ export const AUCTION_CHECKOUT_URL = `${String(ROUTES.USER.CHECKOUT)}?lane=${CART_LANE.AUCTION}`;
75
101
  /** The two lanes whose price is fixed outside the listing. */
76
102
  export function isLockedLane(lane) {
77
103
  return lane === CART_LANE.AUCTION || lane === CART_LANE.OFFER;
@@ -49,6 +49,33 @@ export interface ResolveMinBidOptions {
49
49
  * together with `{ hasBids: false }`.
50
50
  */
51
51
  export declare function resolveMinBid(currentBidAmount: number, tiers: BidIncrementTier[], override?: number | null, opts?: ResolveMinBidOptions): number;
52
+ export interface BuyNowAvailabilityInput {
53
+ buyNowPrice: number | null | undefined;
54
+ /** The auction's live visible price. Pass the SSE value client-side. */
55
+ currentBid: number | null | undefined;
56
+ isEnded: boolean;
57
+ isSold?: boolean;
58
+ }
59
+ /**
60
+ * The single definition of "can this auction be bought outright right now".
61
+ *
62
+ * Written once for the same reason `resolveMinBid` is: the predicate had been
63
+ * hand-written in five places (the bid form, three spots on the auction detail
64
+ * page, the marketplace card) and they disagreed — the detail page advertised
65
+ * "Buy Now: ₹5,999" from a two-condition check while the button beneath it used
66
+ * a three-condition one, so most auctions showed a price with no button under
67
+ * it. That is the drift shape Root Cause #30 describes.
68
+ *
69
+ * The old third condition was `!bidsHaveStarted` (eBay's rule: BIN vanishes the
70
+ * moment anyone bids). It is deliberately gone. Buyout now stays available for
71
+ * as long as it is still a better deal than the standing bid, which is the
72
+ * condition that actually matters — a BIN price the bidding has already passed
73
+ * is meaningless, and one it hasn't is still a real offer.
74
+ *
75
+ * `isSold` is optional because client callers rendering a live auction page
76
+ * already know it isn't sold; server callers should always pass it.
77
+ */
78
+ export declare function isBuyNowAvailable({ buyNowPrice, currentBid, isEnded, isSold, }: BuyNowAvailabilityInput): boolean;
52
79
  /**
53
80
  * Preset step multipliers offered by the bid form, as multiples of the
54
81
  * effective minimum increment. With a ₹100 increment this renders