@mohasinac/appkit 4.13.1 → 4.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (493) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +14 -5
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +3 -0
  3. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +6 -0
  4. package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +8 -0
  5. package/dist/_internal/client/features/lottery/LotteryDetailView.js +4 -1
  6. package/dist/_internal/client/features/lottery/LotteryListView.d.ts +7 -0
  7. package/dist/_internal/client/features/lottery/LotteryListView.js +1 -1
  8. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +4 -1
  9. package/dist/_internal/client/features/lottery/slot-collage-items.d.ts +15 -0
  10. package/dist/_internal/client/features/lottery/slot-collage-items.js +13 -0
  11. package/dist/_internal/server/features/auctions/actions.js +8 -2
  12. package/dist/_internal/server/features/bundles/data.d.ts +16 -0
  13. package/dist/_internal/server/features/bundles/data.js +26 -0
  14. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  15. package/dist/_internal/server/features/bundles/index.js +1 -1
  16. package/dist/_internal/server/features/cart/service.d.ts +3 -3
  17. package/dist/_internal/server/features/checkout/actions.js +25 -0
  18. package/dist/_internal/server/features/checkout/locked-lines.d.ts +38 -0
  19. package/dist/_internal/server/features/checkout/locked-lines.js +171 -0
  20. package/dist/_internal/server/features/checkout/source-context.d.ts +5 -0
  21. package/dist/_internal/server/features/checkout/source-context.js +157 -0
  22. package/dist/_internal/server/features/homepage/index.d.ts +0 -1
  23. package/dist/_internal/server/features/homepage/index.js +7 -1
  24. package/dist/_internal/server/features/lottery/adapters.js +4 -0
  25. package/dist/_internal/server/features/offers/adapters.d.ts +103 -0
  26. package/dist/_internal/server/features/offers/adapters.js +88 -0
  27. package/dist/_internal/server/features/offers/index.d.ts +2 -0
  28. package/dist/_internal/server/features/offers/index.js +1 -0
  29. package/dist/_internal/server/features/orders/adapters.js +24 -0
  30. package/dist/_internal/server/features/products/data.js +15 -37
  31. package/dist/_internal/server/features/products/index.d.ts +1 -1
  32. package/dist/_internal/server/features/products/index.js +1 -1
  33. package/dist/_internal/server/features/products/list-public.d.ts +46 -20
  34. package/dist/_internal/server/features/products/list-public.js +266 -90
  35. package/dist/_internal/server/features/products/listing-tab-counts.d.ts +44 -0
  36. package/dist/_internal/server/features/products/listing-tab-counts.js +97 -0
  37. package/dist/_internal/server/features/site-settings/adapters.d.ts +51 -0
  38. package/dist/_internal/server/features/site-settings/adapters.js +75 -0
  39. package/dist/_internal/server/features/site-settings/index.d.ts +1 -0
  40. package/dist/_internal/server/features/site-settings/index.js +1 -0
  41. package/dist/_internal/server/features/stores/adapters.d.ts +56 -0
  42. package/dist/_internal/server/features/stores/adapters.js +91 -0
  43. package/dist/_internal/server/features/stores/index.d.ts +1 -0
  44. package/dist/_internal/server/features/stores/index.js +1 -0
  45. package/dist/_internal/server/jobs/core/auctionSettlement.js +56 -2
  46. package/dist/_internal/server/jobs/core/offerExpiry.js +55 -19
  47. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.d.ts +5 -0
  48. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.js +33 -1
  49. package/dist/_internal/server/jobs/handlers/messages.d.ts +7 -0
  50. package/dist/_internal/server/jobs/handlers/messages.js +10 -0
  51. package/dist/_internal/shared/actions/action-registry.js +17 -1
  52. package/dist/_internal/shared/checkout/lanes.d.ts +25 -0
  53. package/dist/_internal/shared/checkout/lanes.js +26 -0
  54. package/dist/_internal/shared/features/auctions/config.d.ts +27 -0
  55. package/dist/_internal/shared/features/auctions/config.js +27 -0
  56. package/dist/_internal/shared/features/auctions/schema.d.ts +14 -2
  57. package/dist/_internal/shared/features/auctions/schema.js +13 -1
  58. package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
  59. package/dist/_internal/shared/features/cart/schema.d.ts +20 -15
  60. package/dist/_internal/shared/features/cart/schema.js +15 -0
  61. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
  62. package/dist/_internal/shared/features/emi/schedule.d.ts +24 -3
  63. package/dist/_internal/shared/features/emi/schedule.js +20 -5
  64. package/dist/_internal/shared/features/events/schema.d.ts +2 -2
  65. package/dist/_internal/shared/features/homepage/section-gate.d.ts +14 -0
  66. package/dist/_internal/shared/features/homepage/section-gate.js +51 -0
  67. package/dist/_internal/shared/features/offers/config.d.ts +30 -0
  68. package/dist/_internal/shared/features/offers/config.js +59 -0
  69. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  70. package/dist/_internal/shared/features/products/schema.d.ts +30 -30
  71. package/dist/_internal/shared/features/products/to-product-item.js +6 -0
  72. package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
  73. package/dist/_internal/shared/features/site-settings/fees.d.ts +60 -0
  74. package/dist/_internal/shared/features/site-settings/fees.js +39 -0
  75. package/dist/_internal/shared/features/site-settings/types.d.ts +40 -0
  76. package/dist/_internal/shared/features/site-settings/types.js +1 -0
  77. package/dist/_internal/shared/history/history.d.ts +88 -0
  78. package/dist/_internal/shared/history/history.js +198 -0
  79. package/dist/_internal/shared/history/index.d.ts +4 -0
  80. package/dist/_internal/shared/history/index.js +2 -0
  81. package/dist/_internal/shared/history/types.d.ts +61 -0
  82. package/dist/_internal/shared/history/types.js +6 -0
  83. package/dist/_internal/shared/listing-types/_registry.d.ts +54 -1
  84. package/dist/_internal/shared/listing-types/_registry.js +67 -0
  85. package/dist/_internal/shared/listing-types/art/config.d.ts +3 -0
  86. package/dist/_internal/shared/listing-types/art/config.js +5 -0
  87. package/dist/_internal/shared/listing-types/auction/config.d.ts +13 -0
  88. package/dist/_internal/shared/listing-types/auction/config.js +21 -0
  89. package/dist/_internal/shared/listing-types/availability.d.ts +79 -0
  90. package/dist/_internal/shared/listing-types/availability.js +104 -0
  91. package/dist/_internal/shared/listing-types/capabilities.d.ts +15 -0
  92. package/dist/_internal/shared/listing-types/capabilities.js +12 -0
  93. package/dist/_internal/shared/listing-types/classified/config.d.ts +3 -0
  94. package/dist/_internal/shared/listing-types/classified/config.js +5 -0
  95. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +11 -0
  96. package/dist/_internal/shared/listing-types/digital-code/config.js +22 -2
  97. package/dist/_internal/shared/listing-types/live/config.d.ts +3 -0
  98. package/dist/_internal/shared/listing-types/live/config.js +5 -0
  99. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +3 -0
  100. package/dist/_internal/shared/listing-types/pre-order/config.js +19 -0
  101. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +11 -0
  102. package/dist/_internal/shared/listing-types/prize-draw/config.js +15 -0
  103. package/dist/_internal/shared/listing-types/standard/config.d.ts +3 -0
  104. package/dist/_internal/shared/listing-types/standard/config.js +5 -0
  105. package/dist/_internal/shared/listing-types/stickers/config.d.ts +3 -0
  106. package/dist/_internal/shared/listing-types/stickers/config.js +5 -0
  107. package/dist/client.d.ts +20 -7
  108. package/dist/client.js +22 -4
  109. package/dist/constants/api-endpoints.d.ts +6 -0
  110. package/dist/constants/api-endpoints.js +2 -0
  111. package/dist/constants/field-names.d.ts +57 -8
  112. package/dist/constants/field-names.js +101 -8
  113. package/dist/constants/table-keys.d.ts +17 -0
  114. package/dist/constants/table-keys.js +17 -0
  115. package/dist/errors/error-codes.d.ts +13 -1
  116. package/dist/errors/error-codes.js +13 -1
  117. package/dist/errors/error-display-map.js +12 -3
  118. package/dist/errors/messages.d.ts +10 -2
  119. package/dist/errors/messages.js +10 -2
  120. package/dist/features/about/components/AboutView.d.ts +3 -0
  121. package/dist/features/about/components/AboutView.js +3 -2
  122. package/dist/features/about/components/PolicyPageView.d.ts +2 -1
  123. package/dist/features/about/components/PolicyPageView.js +61 -23
  124. package/dist/features/about/components/PublicProfileView.js +5 -0
  125. package/dist/features/about/schemas/firestore.d.ts +3 -0
  126. package/dist/features/account/components/UserBidsView.js +12 -4
  127. package/dist/features/account/components/UserOrdersView.js +7 -0
  128. package/dist/features/account/components/UserReturnsView.d.ts +3 -0
  129. package/dist/features/account/components/UserReturnsView.js +17 -1
  130. package/dist/features/account/components/index.d.ts +0 -2
  131. package/dist/features/account/components/index.js +0 -1
  132. package/dist/features/account/schemas/index.d.ts +2 -2
  133. package/dist/features/account/types/index.d.ts +21 -2
  134. package/dist/features/addresses/schemas/index.d.ts +4 -4
  135. package/dist/features/admin/components/AdminArtView.js +8 -3
  136. package/dist/features/admin/components/AdminAuditLogView.js +1 -0
  137. package/dist/features/admin/components/AdminBidsView.js +15 -2
  138. package/dist/features/admin/components/AdminBlogEditorView.js +65 -33
  139. package/dist/features/admin/components/AdminClassifiedView.js +8 -3
  140. package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
  141. package/dist/features/admin/components/AdminFeatureEditorView.js +35 -6
  142. package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
  143. package/dist/features/admin/components/AdminLiveView.js +8 -3
  144. package/dist/features/admin/components/AdminNavEditorView.js +15 -5
  145. package/dist/features/admin/components/AdminNewsletterView.js +18 -2
  146. package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
  147. package/dist/features/admin/components/AdminOffersView.js +230 -0
  148. package/dist/features/admin/components/AdminOrdersView.js +10 -1
  149. package/dist/features/admin/components/AdminPaymentMethodsView.js +24 -4
  150. package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
  151. package/dist/features/admin/components/AdminProductsView.js +16 -29
  152. package/dist/features/admin/components/AdminScammerEditorView.d.ts +2 -1
  153. package/dist/features/admin/components/AdminScammerEditorView.js +3 -13
  154. package/dist/features/admin/components/AdminScammersView.js +3 -8
  155. package/dist/features/admin/components/AdminSectionsView.js +32 -23
  156. package/dist/features/admin/components/AdminSessionsView.js +17 -2
  157. package/dist/features/admin/components/AdminSiteSettingsView.js +31 -13
  158. package/dist/features/admin/components/AdminSiteView.js +6 -2
  159. package/dist/features/admin/components/AdminStickersView.js +8 -3
  160. package/dist/features/admin/components/ViewAuditLogEntryModal.js +1 -0
  161. package/dist/features/admin/components/index.d.ts +2 -0
  162. package/dist/features/admin/components/index.js +1 -0
  163. package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -2
  164. package/dist/features/admin/components/sections/adminSectionsTypes.js +45 -27
  165. package/dist/features/admin/constants/filter-tabs.d.ts +89 -0
  166. package/dist/features/admin/constants/filter-tabs.js +56 -0
  167. package/dist/features/admin/schemas/firestore.d.ts +122 -3
  168. package/dist/features/admin/schemas/firestore.js +1 -0
  169. package/dist/features/admin/schemas/homepage-section-form.d.ts +22 -0
  170. package/dist/features/admin/schemas/homepage-section-form.js +68 -0
  171. package/dist/features/admin/schemas/nav-item-form.d.ts +74 -0
  172. package/dist/features/admin/schemas/nav-item-form.js +72 -0
  173. package/dist/features/auctions/actions/bid-actions.d.ts +20 -5
  174. package/dist/features/auctions/actions/bid-actions.js +93 -20
  175. package/dist/features/auctions/components/AuctionBidsTable.d.ts +10 -1
  176. package/dist/features/auctions/components/AuctionBidsTable.js +22 -4
  177. package/dist/features/auctions/components/AuctionDetailPageView.js +11 -6
  178. package/dist/features/auctions/components/AuctionsListView.js +5 -6
  179. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +7 -0
  180. package/dist/features/auctions/components/MarketplaceAuctionCard.js +19 -2
  181. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -2
  182. package/dist/features/auctions/components/PlaceBidFormClient.js +8 -4
  183. package/dist/features/auctions/components/bid-detail-fields.d.ts +29 -0
  184. package/dist/features/auctions/components/bid-detail-fields.js +103 -0
  185. package/dist/features/auctions/repository/bid.repository.d.ts +13 -0
  186. package/dist/features/auctions/repository/bid.repository.js +23 -0
  187. package/dist/features/auctions/schemas/firestore.d.ts +17 -0
  188. package/dist/features/auctions/schemas/index.d.ts +12 -12
  189. package/dist/features/audit-log/schemas/firestore.d.ts +14 -0
  190. package/dist/features/audit-log/schemas/firestore.js +7 -0
  191. package/dist/features/auth/permissions/constants.d.ts +1 -1
  192. package/dist/features/auth/permissions/constants.js +10 -1
  193. package/dist/features/blog/actions/blog-actions.d.ts +7 -7
  194. package/dist/features/blog/schemas/blog-form.d.ts +80 -0
  195. package/dist/features/blog/schemas/blog-form.js +100 -0
  196. package/dist/features/blog/schemas/index.d.ts +6 -6
  197. package/dist/features/cart/repository/cart.repository.js +1 -0
  198. package/dist/features/cart/schemas/firestore.d.ts +15 -0
  199. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  200. package/dist/features/categories/components/BrandDetailPageView.js +23 -40
  201. package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -7
  202. package/dist/features/categories/components/BrandDetailTabs.js +8 -12
  203. package/dist/features/categories/components/BundleCollage.js +1 -1
  204. package/dist/features/categories/components/CategoryDetailPageView.js +53 -57
  205. package/dist/features/categories/components/CategoryDetailTabs.d.ts +10 -8
  206. package/dist/features/categories/components/CategoryDetailTabs.js +11 -16
  207. package/dist/features/categories/components/CategoryProductsListing.js +11 -1
  208. package/dist/features/categories/components/ConcernCard.js +7 -1
  209. package/dist/features/categories/schemas/firestore.d.ts +13 -0
  210. package/dist/features/categories/schemas/index.d.ts +26 -26
  211. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +16 -1
  212. package/dist/features/classified/components/ClassifiedDetailPageView.js +23 -3
  213. package/dist/features/events/components/AdminEventEditorView.js +32 -54
  214. package/dist/features/events/components/AdminEventEntriesView.js +22 -3
  215. package/dist/features/events/components/AdminEventsView.js +15 -11
  216. package/dist/features/events/components/EventFilters.d.ts +9 -1
  217. package/dist/features/events/components/EventFilters.js +45 -21
  218. package/dist/features/events/components/EventsIndexListing.d.ts +6 -1
  219. package/dist/features/events/components/EventsIndexListing.js +2 -2
  220. package/dist/features/events/components/EventsListPageView.js +13 -4
  221. package/dist/features/events/repository/events.repository.d.ts +17 -0
  222. package/dist/features/events/repository/events.repository.js +27 -0
  223. package/dist/features/events/schemas/event-form.d.ts +113 -0
  224. package/dist/features/events/schemas/event-form.js +82 -0
  225. package/dist/features/events/schemas/firestore.d.ts +12 -0
  226. package/dist/features/events/schemas/firestore.js +12 -0
  227. package/dist/features/events/schemas/index.d.ts +50 -50
  228. package/dist/features/events/types/index.d.ts +11 -0
  229. package/dist/features/events/types/index.js +57 -1
  230. package/dist/features/faq/actions/faq-actions.d.ts +12 -12
  231. package/dist/features/faq/schemas/index.d.ts +8 -8
  232. package/dist/features/filters/FilterFacetSection.d.ts +7 -1
  233. package/dist/features/filters/FilterFacetSection.js +11 -3
  234. package/dist/features/grouped/schemas/grouped-listing-form.d.ts +101 -0
  235. package/dist/features/grouped/schemas/grouped-listing-form.js +89 -0
  236. package/dist/features/grouped/schemas/index.d.ts +4 -4
  237. package/dist/features/homepage/actions/homepage-section-actions.d.ts +11 -3
  238. package/dist/features/homepage/actions/homepage-section-actions.js +33 -1
  239. package/dist/features/homepage/components/AnnouncementBar.d.ts +7 -1
  240. package/dist/features/homepage/components/AnnouncementBar.js +3 -3
  241. package/dist/features/homepage/components/MarketplaceHomepageView.js +20 -6
  242. package/dist/features/homepage/hooks/homepage-query.d.ts +16 -0
  243. package/dist/features/homepage/hooks/homepage-query.js +45 -0
  244. package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +10 -0
  245. package/dist/features/homepage/hooks/useFeaturedAuctions.js +28 -8
  246. package/dist/features/homepage/hooks/useFeaturedPreOrders.js +17 -8
  247. package/dist/features/homepage/hooks/useFeaturedProducts.js +15 -8
  248. package/dist/features/homepage/schemas/index.d.ts +33 -2
  249. package/dist/features/homepage/schemas/index.js +24 -0
  250. package/dist/features/layout/AppLayoutShell.d.ts +11 -1
  251. package/dist/features/layout/AppLayoutShell.js +15 -14
  252. package/dist/features/layout/BackToTop.d.ts +9 -0
  253. package/dist/features/layout/BackToTop.js +10 -1
  254. package/dist/features/layout/BottomActions.js +28 -25
  255. package/dist/features/layout/BottomChrome.d.ts +34 -0
  256. package/dist/features/layout/BottomChrome.js +55 -0
  257. package/dist/features/layout/BottomNavLayout.d.ts +6 -0
  258. package/dist/features/layout/BottomNavLayout.js +7 -1
  259. package/dist/features/layout/BottomNavbar.js +15 -2
  260. package/dist/features/layout/FooterLayout.js +11 -1
  261. package/dist/features/layout/NavbarLayout.js +1 -1
  262. package/dist/features/layout/TitleBarLayout.d.ts +7 -0
  263. package/dist/features/layout/TitleBarLayout.js +28 -5
  264. package/dist/features/layout/index.d.ts +2 -0
  265. package/dist/features/layout/index.js +1 -0
  266. package/dist/features/lottery/types/index.d.ts +3 -0
  267. package/dist/features/media/MediaPickerModal.js +5 -1
  268. package/dist/features/messages/schemas/index.d.ts +2 -2
  269. package/dist/features/orders/components/OrderScopeTabs.d.ts +20 -0
  270. package/dist/features/orders/components/OrderScopeTabs.js +31 -0
  271. package/dist/features/orders/components/OrderStatusTimeline.d.ts +5 -1
  272. package/dist/features/orders/components/OrderStatusTimeline.js +39 -20
  273. package/dist/features/orders/constants/order-scope.d.ts +48 -0
  274. package/dist/features/orders/constants/order-scope.js +111 -0
  275. package/dist/features/orders/repository/orders.repository.d.ts +54 -7
  276. package/dist/features/orders/repository/orders.repository.js +121 -22
  277. package/dist/features/orders/schemas/firestore.d.ts +152 -0
  278. package/dist/features/orders/schemas/firestore.js +30 -0
  279. package/dist/features/orders/schemas/index.d.ts +18 -18
  280. package/dist/features/orders/types/index.d.ts +35 -7
  281. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +14 -9
  282. package/dist/features/pre-orders/components/PreOrdersListView.js +6 -5
  283. package/dist/features/pre-orders/schemas/index.d.ts +1 -1
  284. package/dist/features/products/actions/product-actions.d.ts +14 -0
  285. package/dist/features/products/actions/product-actions.js +49 -11
  286. package/dist/features/products/api/route.d.ts +12 -0
  287. package/dist/features/products/api/route.js +42 -100
  288. package/dist/features/products/components/ArtStickersListView.js +2 -2
  289. package/dist/features/products/components/AuctionsIndexListing.js +13 -11
  290. package/dist/features/products/components/AvailabilityTabs.d.ts +11 -0
  291. package/dist/features/products/components/AvailabilityTabs.js +47 -0
  292. package/dist/features/products/components/GroupSettingsPanel.js +5 -1
  293. package/dist/features/products/components/MakeOfferButton.js +48 -49
  294. package/dist/features/products/components/PrizeDrawCollage.d.ts +20 -4
  295. package/dist/features/products/components/PrizeDrawCollage.js +9 -1
  296. package/dist/features/products/components/PrizeDrawsIndexListing.js +14 -12
  297. package/dist/features/products/components/PrizeDrawsListingView.js +2 -2
  298. package/dist/features/products/components/PrizeDrawsSection.d.ts +7 -1
  299. package/dist/features/products/components/PrizeDrawsSection.js +23 -18
  300. package/dist/features/products/components/ProductDetailPageView.d.ts +4 -1
  301. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  302. package/dist/features/products/components/ProductForm.js +5 -2
  303. package/dist/features/products/components/ProductGrid.js +6 -1
  304. package/dist/features/products/components/ProductsIndexListing.js +22 -32
  305. package/dist/features/products/components/ProductsIndexPageView.js +6 -9
  306. package/dist/features/products/components/index.d.ts +2 -2
  307. package/dist/features/products/components/index.js +1 -1
  308. package/dist/features/products/config/listing-type-listing-config.d.ts +20 -0
  309. package/dist/features/products/config/listing-type-listing-config.js +22 -10
  310. package/dist/features/products/constants/action-defs.d.ts +2 -0
  311. package/dist/features/products/constants/action-defs.js +6 -0
  312. package/dist/features/products/hooks/useAvailabilityScope.d.ts +13 -0
  313. package/dist/features/products/hooks/useAvailabilityScope.js +19 -0
  314. package/dist/features/products/hooks/useProducts.d.ts +5 -0
  315. package/dist/features/products/hooks/useProducts.js +13 -2
  316. package/dist/features/products/repository/products.repository.d.ts +31 -2
  317. package/dist/features/products/repository/products.repository.js +53 -21
  318. package/dist/features/products/schemas/firestore.d.ts +17 -3
  319. package/dist/features/products/schemas/firestore.js +7 -2
  320. package/dist/features/products/schemas/index.d.ts +64 -64
  321. package/dist/features/products/schemas/product-features.validators.d.ts +79 -6
  322. package/dist/features/products/schemas/product-features.validators.js +63 -0
  323. package/dist/features/products/types/index.d.ts +16 -1
  324. package/dist/features/promotions/schemas/index.d.ts +2 -2
  325. package/dist/features/reviews/schemas/index.d.ts +16 -16
  326. package/dist/features/scams/constants/scammer-status-display.d.ts +28 -0
  327. package/dist/features/scams/constants/scammer-status-display.js +79 -0
  328. package/dist/features/search/schemas/index.d.ts +4 -4
  329. package/dist/features/seller/actions/offer-actions.js +57 -4
  330. package/dist/features/seller/components/OfferPhaseTimeline.d.ts +59 -0
  331. package/dist/features/seller/components/OfferPhaseTimeline.js +107 -0
  332. package/dist/features/seller/components/SellerArtView.js +5 -0
  333. package/dist/features/seller/components/SellerAuctionsView.js +13 -15
  334. package/dist/features/seller/components/SellerBidsView.js +11 -4
  335. package/dist/features/seller/components/SellerClassifiedView.js +5 -0
  336. package/dist/features/seller/components/SellerCouponEditorView.js +17 -17
  337. package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
  338. package/dist/features/seller/components/SellerLiveView.js +5 -0
  339. package/dist/features/seller/components/SellerOffersView.js +2 -1
  340. package/dist/features/seller/components/SellerOrdersView.js +31 -9
  341. package/dist/features/seller/components/SellerPayoutSettingsView.js +56 -31
  342. package/dist/features/seller/components/SellerPreOrdersView.js +5 -0
  343. package/dist/features/seller/components/SellerPrizeDrawsView.js +14 -16
  344. package/dist/features/seller/components/SellerProductShell.js +7 -1
  345. package/dist/features/seller/components/SellerProductsView.js +11 -8
  346. package/dist/features/seller/components/SellerStickersView.js +5 -0
  347. package/dist/features/seller/components/index.d.ts +0 -2
  348. package/dist/features/seller/components/index.js +0 -1
  349. package/dist/features/seller/repository/offer.repository.d.ts +142 -24
  350. package/dist/features/seller/repository/offer.repository.js +165 -26
  351. package/dist/features/seller/schemas/coupon-form.d.ts +72 -0
  352. package/dist/features/seller/schemas/coupon-form.js +116 -0
  353. package/dist/features/seller/schemas/firestore.d.ts +56 -2
  354. package/dist/features/seller/schemas/firestore.js +27 -0
  355. package/dist/features/seller/schemas/index.d.ts +2 -2
  356. package/dist/features/seller/schemas/offer-forms.d.ts +46 -0
  357. package/dist/features/seller/schemas/offer-forms.js +42 -0
  358. package/dist/features/seller/server.d.ts +0 -1
  359. package/dist/features/seller/server.js +0 -1
  360. package/dist/features/shell/SectionForm.d.ts +99 -0
  361. package/dist/features/shell/SectionForm.js +128 -0
  362. package/dist/features/shell/build-sections.d.ts +63 -0
  363. package/dist/features/shell/build-sections.js +207 -0
  364. package/dist/features/shell/entity-form.d.ts +109 -0
  365. package/dist/features/shell/entity-form.js +96 -0
  366. package/dist/features/shell/field-ui-meta.d.ts +115 -0
  367. package/dist/features/shell/field-ui-meta.js +324 -0
  368. package/dist/features/shell/index.d.ts +8 -0
  369. package/dist/features/shell/index.js +4 -0
  370. package/dist/features/status-history/components/StatusTimeline.d.ts +74 -0
  371. package/dist/features/status-history/components/StatusTimeline.js +46 -0
  372. package/dist/features/status-history/index.d.ts +11 -0
  373. package/dist/features/status-history/index.js +10 -0
  374. package/dist/features/store-extensions/components/ReviewDecisionModal.d.ts +27 -0
  375. package/dist/features/store-extensions/components/ReviewDecisionModal.js +82 -0
  376. package/dist/features/store-extensions/index.d.ts +12 -0
  377. package/dist/features/store-extensions/index.js +12 -0
  378. package/dist/features/store-extensions/schemas/admin-notification-form.d.ts +36 -0
  379. package/dist/features/store-extensions/schemas/admin-notification-form.js +114 -0
  380. package/dist/features/store-extensions/schemas/analytics-forms.d.ts +57 -0
  381. package/dist/features/store-extensions/schemas/analytics-forms.js +158 -0
  382. package/dist/features/store-extensions/schemas/custom-role-form.d.ts +85 -0
  383. package/dist/features/store-extensions/schemas/custom-role-form.js +118 -0
  384. package/dist/features/store-extensions/schemas/firestore.d.ts +21 -1
  385. package/dist/features/store-extensions/schemas/firestore.js +60 -0
  386. package/dist/features/store-extensions/schemas/google-config-form.d.ts +53 -0
  387. package/dist/features/store-extensions/schemas/google-config-form.js +105 -0
  388. package/dist/features/store-extensions/schemas/item-request-create-form.d.ts +28 -0
  389. package/dist/features/store-extensions/schemas/item-request-create-form.js +80 -0
  390. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +78 -0
  391. package/dist/features/store-extensions/schemas/listing-template-form.js +87 -0
  392. package/dist/features/store-extensions/schemas/moderation-review-form.d.ts +49 -0
  393. package/dist/features/store-extensions/schemas/moderation-review-form.js +101 -0
  394. package/dist/features/store-extensions/schemas/payout-method-form.d.ts +153 -0
  395. package/dist/features/store-extensions/schemas/payout-method-form.js +155 -0
  396. package/dist/features/store-extensions/schemas/report-create-form.d.ts +25 -0
  397. package/dist/features/store-extensions/schemas/report-create-form.js +79 -0
  398. package/dist/features/store-extensions/schemas/report-review-form.d.ts +47 -0
  399. package/dist/features/store-extensions/schemas/report-review-form.js +111 -0
  400. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +148 -0
  401. package/dist/features/store-extensions/schemas/shipping-config-form.js +104 -0
  402. package/dist/features/store-extensions/schemas/store-category-form.d.ts +54 -0
  403. package/dist/features/store-extensions/schemas/store-category-form.js +73 -0
  404. package/dist/features/stores/api/[storeSlug]/route.js +2 -25
  405. package/dist/features/stores/components/StoreAuctionsListing.js +15 -1
  406. package/dist/features/stores/components/StoreClassifiedsListing.js +12 -2
  407. package/dist/features/stores/components/StoreDetailLayoutView.js +24 -71
  408. package/dist/features/stores/components/StoreDigitalCodesListing.js +12 -2
  409. package/dist/features/stores/components/StoreLiveItemsListing.js +12 -2
  410. package/dist/features/stores/components/StorePreOrdersListing.js +12 -1
  411. package/dist/features/stores/components/StoreProductsListing.js +11 -1
  412. package/dist/features/stores/repository/store.repository.js +6 -0
  413. package/dist/features/stores/schemas/index.d.ts +6 -6
  414. package/dist/features/support/schemas/index.d.ts +4 -4
  415. package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
  416. package/dist/features/tester/seed-data/offers-tester-seed-data.js +119 -3
  417. package/dist/features/tester/seed-data/products-tester-seed-data.js +131 -1
  418. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +431 -1
  419. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  420. package/dist/index.d.ts +25 -12
  421. package/dist/index.js +26 -11
  422. package/dist/next/routing/route-map.d.ts +23 -3
  423. package/dist/next/routing/route-map.js +14 -2
  424. package/dist/providers/db-firebase/base.repository.d.ts +30 -0
  425. package/dist/providers/db-firebase/base.repository.js +50 -1
  426. package/dist/providers/db-firebase/index.d.ts +1 -1
  427. package/dist/providers/db-firebase/index.js +1 -1
  428. package/dist/providers/db-firebase/sieve.d.ts +25 -0
  429. package/dist/providers/db-firebase/sieve.js +52 -18
  430. package/dist/react/hooks/useUrlTable.js +10 -1
  431. package/dist/schemas/firestore-value.d.ts +8 -0
  432. package/dist/schemas/firestore-value.js +44 -0
  433. package/dist/schemas/index.d.ts +1 -1
  434. package/dist/schemas/index.js +1 -1
  435. package/dist/schemas/registry.d.ts +702 -69
  436. package/dist/schemas/registry.js +64 -1
  437. package/dist/seed/_helpers/category-forest.d.ts +2 -0
  438. package/dist/seed/_helpers/category-forest.js +309 -0
  439. package/dist/seed/_helpers/category-tree.d.ts +27 -0
  440. package/dist/seed/_helpers/category-tree.js +103 -0
  441. package/dist/seed/bids-seed-data.js +30 -0
  442. package/dist/seed/catalogue-seed-data.js +12 -12
  443. package/dist/seed/categories-seed-data.js +177 -162
  444. package/dist/seed/events-seed-data.js +61 -10
  445. package/dist/seed/homepage-sections-seed-data.js +5 -1
  446. package/dist/seed/offers-seed-data.js +244 -34
  447. package/dist/seed/orders-seed-data.js +336 -1
  448. package/dist/seed/products-art-seed-data.js +50 -12
  449. package/dist/seed/products-auctions-seed-data.js +138 -18
  450. package/dist/seed/products-classifieds-seed-data.js +80 -16
  451. package/dist/seed/products-digital-codes-seed-data.js +66 -18
  452. package/dist/seed/products-live-items-seed-data.js +64 -13
  453. package/dist/seed/products-preorders-seed-data.d.ts +0 -1
  454. package/dist/seed/products-preorders-seed-data.js +52 -13
  455. package/dist/seed/products-prize-draws-seed-data.js +63 -14
  456. package/dist/seed/products-standard-seed-data.js +99 -26
  457. package/dist/seed/products-stickers-seed-data.js +47 -12
  458. package/dist/seed/reviews-seed-data.js +6 -1
  459. package/dist/seed/site-settings-seed-data.d.ts +1 -38
  460. package/dist/seed/site-settings-seed-data.js +38 -76
  461. package/dist/seed/stores-seed-data.js +71 -1
  462. package/dist/seed/users-seed-data.js +15 -5
  463. package/dist/server-entry.d.ts +1 -2
  464. package/dist/server-entry.js +4 -3
  465. package/dist/server.d.ts +6 -3
  466. package/dist/server.js +13 -4
  467. package/dist/styles.css +78 -12
  468. package/dist/tailwind-utilities.css +1 -1
  469. package/dist/ui/components/Button.style.css +19 -4
  470. package/dist/ui/components/CollapsibleSection.d.ts +17 -1
  471. package/dist/ui/components/CollapsibleSection.js +6 -2
  472. package/dist/ui/components/FilterChipGroup.d.ts +25 -1
  473. package/dist/ui/components/FilterChipGroup.js +21 -4
  474. package/dist/ui/components/HandMode.style.css +47 -0
  475. package/dist/ui/components/Layout.d.ts +9 -2
  476. package/dist/ui/components/Layout.js +1 -1
  477. package/dist/ui/components/ListingLayout.js +8 -8
  478. package/dist/ui/components/ListingLayout.style.css +8 -7
  479. package/dist/ui/components/RecordDetailModal.d.ts +10 -1
  480. package/dist/ui/components/RecordDetailModal.js +2 -2
  481. package/dist/ui/components/StickyToolbar.d.ts +3 -1
  482. package/dist/ui/components/StickyToolbar.js +62 -20
  483. package/dist/ui/components/bottom-chrome-slot.d.ts +21 -0
  484. package/dist/ui/components/bottom-chrome-slot.js +29 -0
  485. package/dist/ui/components/index.style.css +3 -0
  486. package/dist/ui/forms/FormShell.d.ts +43 -13
  487. package/dist/ui/forms/FormShell.js +22 -11
  488. package/dist/ui/forms/index.d.ts +1 -1
  489. package/dist/ui/index.d.ts +1 -1
  490. package/package.json +2 -1
  491. package/scripts/node-esm-loader.mjs +65 -0
  492. package/scripts/seed-cli-loader.mjs +6 -49
  493. package/scripts/seed-cli.mjs +1 -1
@@ -4,35 +4,103 @@
4
4
  * Data access layer for the `offers` Firestore collection.
5
5
  * Handles buyer make-an-offer, seller counter/accept/decline lifecycle.
6
6
  */
7
- import { BaseRepository } from "../../../providers/db-firebase";
7
+ import { BaseRepository, parseSieveDateValue } from "../../../providers/db-firebase";
8
8
  import type { SieveModel, FirebaseSieveResult } from "../../../providers/db-firebase";
9
9
  import type { OfferDocument, OfferCreateInput, OfferUpdateInput } from "../schemas";
10
+ import { type HistoryActor } from "../../../_internal/shared/history/index";
11
+ /**
12
+ * Who/why for a write that lands in an offer's `statusHistory`.
13
+ * Optional throughout so existing callers keep compiling and record `system`.
14
+ */
15
+ export interface OfferWriteContext {
16
+ actor?: HistoryActor;
17
+ trigger?: string;
18
+ reason?: string;
19
+ note?: string;
20
+ }
10
21
  declare class OfferRepository extends BaseRepository<OfferDocument> {
11
22
  constructor();
12
23
  /** Override mapDoc to auto-decrypt PII on every offer read */
13
24
  protected mapDoc<D = OfferDocument>(snap: import("../../../providers/db-firebase").DocumentSnapshot): D;
25
+ /**
26
+ * Every Timestamp field here carries `parseValue: parseSieveDateValue`.
27
+ *
28
+ * Without it a GTE/LTE on a Timestamp field silently matches ZERO documents:
29
+ * sievejs's default coercion leaves an ISO string as a string, and Firestore
30
+ * requires an inequality's value to match the stored type. That is Root Cause
31
+ * #47, and `createdAt` flipping from `canFilter: false` to `true` here is
32
+ * exactly the transition that introduces it.
33
+ *
34
+ * `checkoutDeadline` is added by hand: `audit-sieve-date-fields` recognises
35
+ * Timestamp fields by an `...At` / `...Date` / `...Time` suffix, which this
36
+ * name does not have, so the audit cannot flag it for you.
37
+ */
14
38
  static readonly SIEVE_FIELDS: {
15
- readonly status: {
16
- readonly canFilter: true;
17
- readonly canSort: false;
39
+ id: {
40
+ canFilter: boolean;
41
+ canSort: boolean;
42
+ };
43
+ status: {
44
+ canFilter: boolean;
45
+ canSort: boolean;
18
46
  };
19
- readonly productId: {
20
- readonly canFilter: true;
21
- readonly canSort: false;
47
+ productId: {
48
+ canFilter: boolean;
49
+ canSort: boolean;
22
50
  };
23
- readonly buyerUid: {
24
- readonly canFilter: true;
25
- readonly canSort: false;
51
+ productTitle: {
52
+ canFilter: boolean;
53
+ canSort: boolean;
26
54
  };
27
- readonly storeId: {
28
- readonly canFilter: true;
29
- readonly canSort: false;
55
+ buyerUid: {
56
+ canFilter: boolean;
57
+ canSort: boolean;
30
58
  };
31
- readonly createdAt: {
32
- readonly canFilter: false;
33
- readonly canSort: true;
59
+ buyerName: {
60
+ canFilter: boolean;
61
+ canSort: boolean;
62
+ };
63
+ storeId: {
64
+ canFilter: boolean;
65
+ canSort: boolean;
66
+ };
67
+ storeName: {
68
+ canFilter: boolean;
69
+ canSort: boolean;
70
+ };
71
+ offerAmount: {
72
+ canFilter: boolean;
73
+ canSort: boolean;
74
+ };
75
+ listedPrice: {
76
+ canFilter: boolean;
77
+ canSort: boolean;
78
+ };
79
+ createdAt: {
80
+ canFilter: boolean;
81
+ canSort: boolean;
82
+ parseValue: typeof parseSieveDateValue;
83
+ };
84
+ expiresAt: {
85
+ canFilter: boolean;
86
+ canSort: boolean;
87
+ parseValue: typeof parseSieveDateValue;
88
+ };
89
+ checkoutDeadline: {
90
+ canFilter: boolean;
91
+ canSort: boolean;
92
+ parseValue: typeof parseSieveDateValue;
34
93
  };
35
94
  };
95
+ /**
96
+ * Paginated, Firestore-native offer list (admin use).
97
+ *
98
+ * Mirrors `BidRepository.list`. The seller- and buyer-scoped reads have their
99
+ * own dedicated methods (`findByStore` / `findByBuyer`) because they carry
100
+ * mandatory ownership filters; this one is deliberately unscoped and is only
101
+ * reachable behind `admin:offers:read`.
102
+ */
103
+ list(model: SieveModel): Promise<FirebaseSieveResult<OfferDocument>>;
36
104
  create(input: OfferCreateInput): Promise<OfferDocument>;
37
105
  findByBuyer(buyerUid: string, model?: SieveModel): Promise<FirebaseSieveResult<OfferDocument>>;
38
106
  findByStore(storeId: string, model?: SieveModel): Promise<FirebaseSieveResult<OfferDocument>>;
@@ -50,12 +118,32 @@ declare class OfferRepository extends BaseRepository<OfferDocument> {
50
118
  * `product.updatedAt` advances past the previously-created offer dates.
51
119
  */
52
120
  countByBuyerAndProduct(buyerUid: string, productId: string, since: Date): Promise<number>;
53
- updateStatus(offerId: string, patch: OfferUpdateInput): Promise<OfferDocument>;
54
- accept(offerId: string, lockedPrice: number, sellerNote?: string, checkoutDeadline?: Date): Promise<OfferDocument>;
55
- decline(offerId: string, sellerNote?: string): Promise<OfferDocument>;
56
- counter(offerId: string, counterAmount: number, sellerNote?: string): Promise<OfferDocument>;
57
- acceptCounter(offerId: string, checkoutDeadline?: Date): Promise<OfferDocument>;
58
- withdraw(offerId: string): Promise<OfferDocument>;
121
+ /**
122
+ * The single write choke point for offers, so `statusHistory` is appended
123
+ * in ONE place rather than at every call site — the same shape
124
+ * `OrderRepository.updateWithHistory` uses.
125
+ *
126
+ * `prior` is the already-fetched document when the caller has one. Every
127
+ * real caller does (`respondToOffer`, `acceptCounterOffer`, `withdrawOffer`,
128
+ * `counterOfferByBuyer`, `finalizeLockedLines`, the admin PATCH, both expiry
129
+ * sweeps), so threading it through means history costs **zero extra reads**.
130
+ */
131
+ updateStatus(offerId: string, patch: OfferUpdateInput, ctx?: OfferWriteContext, prior?: OfferDocument | null): Promise<OfferDocument>;
132
+ accept(offerId: string, lockedPrice: number, sellerNote?: string, checkoutDeadline?: Date, ctx?: OfferWriteContext, prior?: OfferDocument | null): Promise<OfferDocument>;
133
+ decline(offerId: string, sellerNote?: string, ctx?: OfferWriteContext, prior?: OfferDocument | null): Promise<OfferDocument>;
134
+ counter(offerId: string, counterAmount: number, sellerNote?: string, ctx?: OfferWriteContext, prior?: OfferDocument | null): Promise<OfferDocument>;
135
+ acceptCounter(offerId: string, checkoutDeadline?: Date, ctx?: OfferWriteContext, prior?: OfferDocument | null): Promise<OfferDocument>;
136
+ /**
137
+ * Two distinct events share this method:
138
+ * - the buyer walks away → status alone
139
+ * - the buyer counters → status + `supersededByOfferId`
140
+ *
141
+ * The second is what makes the chain walkable forward, and it is ONE write
142
+ * with ONE history entry. A `withdrawn` document carrying
143
+ * `supersededByOfferId` renders as *Superseded* (neutral) rather than
144
+ * *Withdrawn* (negative) — the negotiation continued, it did not end.
145
+ */
146
+ withdraw(offerId: string, supersededByOfferId?: string, ctx?: OfferWriteContext, prior?: OfferDocument | null): Promise<OfferDocument>;
59
147
  /**
60
148
  * Terminal transition, called once the buyer's order for this offer exists.
61
149
  *
@@ -64,7 +152,21 @@ declare class OfferRepository extends BaseRepository<OfferDocument> {
64
152
  * client-side patch in UserOffersPanel, which meant a reload showed the offer
65
153
  * back at "accepted" and it could be added to the cart and ordered again.
66
154
  */
67
- markPaid(offerId: string, orderId: string): Promise<OfferDocument>;
155
+ markPaid(offerId: string, orderId: string, prior?: OfferDocument | null): Promise<OfferDocument>;
156
+ /**
157
+ * Every round of one negotiation, oldest first.
158
+ *
159
+ * A single-field equality — served by Firestore's AUTOMATIC index, so this
160
+ * adds no composite index and none of the existing `offers` indexes is
161
+ * affected. The sort is done in memory because the result is at most 3 rows
162
+ * (the per-buyer offer limit), which is cheaper than the index a compound
163
+ * orderBy would demand.
164
+ *
165
+ * Offers written before `chainRootOfferId` existed return ZERO rows, not
166
+ * one — callers must fall back to `[offer]` and never read empty as
167
+ * "this offer is missing".
168
+ */
169
+ findChain(chainRootOfferId: string): Promise<OfferDocument[]>;
68
170
  /**
69
171
  * Cloud Functions compatibility: pending/countered offers already expired.
70
172
  */
@@ -76,7 +178,23 @@ declare class OfferRepository extends BaseRepository<OfferDocument> {
76
178
  * price remained claimable long after the seller expected it to lapse.
77
179
  */
78
180
  findExpiredAccepted(now: Date): Promise<OfferDocument[]>;
79
- expireMany(offerIds: string[]): Promise<void>;
181
+ /**
182
+ * Bulk-expire, taking the DOCUMENTS rather than their ids.
183
+ *
184
+ * The signature changed deliberately. `arrayUnion` would let a batch append
185
+ * without a read, but it **cannot enforce the FIFO cap** — so history would
186
+ * grow unbounded on exactly the path that touches the most documents at
187
+ * once. Folding entries in memory and writing whole arrays keeps the cap,
188
+ * and costs **zero extra reads**: `findExpiredActive`, `findExpiredAccepted`
189
+ * and the admin cancel route all already hold full `OfferDocument`s.
190
+ *
191
+ * Also carries the admin path: a reasoned cancel is an expiry with an actor
192
+ * and a reason, recorded in the offer's own history as well as in
193
+ * `adminAuditLog`.
194
+ */
195
+ expireMany(offers: OfferDocument[], ctx?: OfferWriteContext & {
196
+ cancelledByAdminUid?: string;
197
+ }): Promise<void>;
80
198
  }
81
199
  export declare const offerRepository: OfferRepository;
82
200
  export { OfferRepository };
@@ -4,9 +4,12 @@
4
4
  * Data access layer for the `offers` Firestore collection.
5
5
  * Handles buyer make-an-offer, seller counter/accept/decline lifecycle.
6
6
  */
7
- import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
8
- import { OFFER_COLLECTION, OFFER_FIELDS, createOfferId } from "../schemas";
7
+ import { BaseRepository, prepareForFirestore, parseSieveDateValue, } from "../../../providers/db-firebase";
8
+ import { OFFER_COLLECTION, OFFER_FIELDS, OFFER_TRACKED_FIELDS, createOfferId } from "../schemas";
9
+ import { withHistory } from "../../../_internal/shared/history/index";
9
10
  import { encryptPiiFields, decryptPiiFields, OFFER_PII_FIELDS, } from "../../../security";
11
+ /** Max rounds in one negotiation chain — matches the per-buyer offer limit. */
12
+ const OFFER_CHAIN_MAX = 3;
10
13
  class OfferRepository extends BaseRepository {
11
14
  constructor() {
12
15
  super(OFFER_COLLECTION);
@@ -18,6 +21,20 @@ class OfferRepository extends BaseRepository {
18
21
  ...OFFER_PII_FIELDS,
19
22
  ]);
20
23
  }
24
+ /**
25
+ * Paginated, Firestore-native offer list (admin use).
26
+ *
27
+ * Mirrors `BidRepository.list`. The seller- and buyer-scoped reads have their
28
+ * own dedicated methods (`findByStore` / `findByBuyer`) because they carry
29
+ * mandatory ownership filters; this one is deliberately unscoped and is only
30
+ * reachable behind `admin:offers:read`.
31
+ */
32
+ async list(model) {
33
+ return this.sieveQuery(model, OfferRepository.SIEVE_FIELDS, {
34
+ defaultPageSize: 50,
35
+ maxPageSize: 200,
36
+ });
37
+ }
21
38
  // --- Create --------------------------------------------------------------
22
39
  async create(input) {
23
40
  const now = new Date();
@@ -33,6 +50,13 @@ class OfferRepository extends BaseRepository {
33
50
  expiresAt,
34
51
  createdAt: now,
35
52
  updatedAt: now,
53
+ // The root of a chain stores its OWN id. Without this, round 1 is the
54
+ // only document with no `chainRootOfferId`, so `findChain(root.id)`
55
+ // would return rounds 2..N and silently omit the round the buyer
56
+ // actually started with. Set here rather than at the call site because
57
+ // the id is generated in this method.
58
+ chainRootOfferId: input.chainRootOfferId ?? id,
59
+ counterRound: input.counterRound ?? 1,
36
60
  };
37
61
  // Encrypt PII fields before persisting
38
62
  const encrypted = encryptPiiFields({ ...data }, [...OFFER_PII_FIELDS]);
@@ -128,13 +152,35 @@ class OfferRepository extends BaseRepository {
128
152
  return snap.size;
129
153
  }
130
154
  // --- Mutations -----------------------------------------------------------
131
- async updateStatus(offerId, patch) {
132
- return this.update(offerId, {
133
- ...patch,
134
- updatedAt: new Date(),
155
+ /**
156
+ * The single write choke point for offers, so `statusHistory` is appended
157
+ * in ONE place rather than at every call site — the same shape
158
+ * `OrderRepository.updateWithHistory` uses.
159
+ *
160
+ * `prior` is the already-fetched document when the caller has one. Every
161
+ * real caller does (`respondToOffer`, `acceptCounterOffer`, `withdrawOffer`,
162
+ * `counterOfferByBuyer`, `finalizeLockedLines`, the admin PATCH, both expiry
163
+ * sweeps), so threading it through means history costs **zero extra reads**.
164
+ */
165
+ async updateStatus(offerId, patch, ctx, prior) {
166
+ const full = { ...patch, updatedAt: new Date() };
167
+ const current = prior !== undefined ? prior : await this.findById(offerId);
168
+ const withEntry = withHistory(current, full, {
169
+ tracked: OFFER_TRACKED_FIELDS,
170
+ actor: ctx?.actor ?? { role: "system" },
171
+ trigger: ctx?.trigger ?? "offerRepository.updateStatus",
172
+ reason: ctx?.reason,
173
+ note: ctx?.note,
174
+ // buyerName/buyerEmail live on this document. `encryptPiiFields` is a
175
+ // flat top-level loop and never descends into arrays, so without this
176
+ // they would persist in PLAINTEXT inside statusHistory.
177
+ piiFields: OFFER_PII_FIELDS,
178
+ historyField: OFFER_FIELDS.STATUS_HISTORY,
179
+ truncatedField: OFFER_FIELDS.STATUS_HISTORY_TRUNCATED,
135
180
  });
181
+ return this.update(offerId, (withEntry ?? full));
136
182
  }
137
- async accept(offerId, lockedPrice, sellerNote, checkoutDeadline) {
183
+ async accept(offerId, lockedPrice, sellerNote, checkoutDeadline, ctx, prior) {
138
184
  // One write, not two. `checkoutDeadline` used to be missing from
139
185
  // OfferUpdateInput, which forced callers to follow every accept() with a
140
186
  // separate generic update() — leaving a window where an offer was
@@ -146,25 +192,28 @@ class OfferRepository extends BaseRepository {
146
192
  acceptedAt: new Date(),
147
193
  respondedAt: new Date(),
148
194
  ...(checkoutDeadline ? { checkoutDeadline } : {}),
149
- });
195
+ }, { actor: { role: "seller" }, trigger: "respondToOffer:accept", note: sellerNote, ...ctx }, prior);
150
196
  }
151
- async decline(offerId, sellerNote) {
197
+ async decline(offerId, sellerNote, ctx, prior) {
152
198
  return this.updateStatus(offerId, {
153
199
  status: "declined",
154
200
  sellerNote,
155
201
  respondedAt: new Date(),
156
- });
202
+ }, { actor: { role: "seller" }, trigger: "respondToOffer:decline", note: sellerNote, ...ctx }, prior);
157
203
  }
158
- async counter(offerId, counterAmount, sellerNote) {
204
+ async counter(offerId, counterAmount, sellerNote, ctx, prior) {
159
205
  return this.updateStatus(offerId, {
160
206
  status: "countered",
161
207
  counterAmount,
162
208
  sellerNote,
163
209
  respondedAt: new Date(),
164
- });
210
+ }, { actor: { role: "seller" }, trigger: "respondToOffer:counter", note: sellerNote, ...ctx }, prior);
165
211
  }
166
- async acceptCounter(offerId, checkoutDeadline) {
167
- const offer = await this.findById(offerId);
212
+ async acceptCounter(offerId, checkoutDeadline, ctx, prior) {
213
+ // Reuse the caller's document when supplied — this method used to always
214
+ // re-read, so passing `prior` makes the feature land one read CHEAPER
215
+ // than before rather than one more expensive.
216
+ const offer = prior !== undefined && prior !== null ? prior : await this.findById(offerId);
168
217
  if (!offer || !offer.counterAmount)
169
218
  throw new Error("Offer or counter not found");
170
219
  return this.updateStatus(offerId, {
@@ -173,13 +222,29 @@ class OfferRepository extends BaseRepository {
173
222
  acceptedAt: new Date(),
174
223
  respondedAt: new Date(),
175
224
  ...(checkoutDeadline ? { checkoutDeadline } : {}),
176
- });
225
+ }, { actor: { role: "buyer" }, trigger: "acceptCounterOffer", ...ctx }, offer);
177
226
  }
178
- async withdraw(offerId) {
227
+ /**
228
+ * Two distinct events share this method:
229
+ * - the buyer walks away → status alone
230
+ * - the buyer counters → status + `supersededByOfferId`
231
+ *
232
+ * The second is what makes the chain walkable forward, and it is ONE write
233
+ * with ONE history entry. A `withdrawn` document carrying
234
+ * `supersededByOfferId` renders as *Superseded* (neutral) rather than
235
+ * *Withdrawn* (negative) — the negotiation continued, it did not end.
236
+ */
237
+ async withdraw(offerId, supersededByOfferId, ctx, prior) {
179
238
  return this.updateStatus(offerId, {
180
239
  status: "withdrawn",
181
240
  respondedAt: new Date(),
182
- });
241
+ ...(supersededByOfferId ? { supersededByOfferId } : {}),
242
+ }, {
243
+ actor: { role: "buyer" },
244
+ trigger: supersededByOfferId ? "counterOfferByBuyer:supersede" : "withdrawOffer",
245
+ ...(supersededByOfferId ? { reason: "Superseded by the buyer's counter" } : {}),
246
+ ...ctx,
247
+ }, prior);
183
248
  }
184
249
  /**
185
250
  * Terminal transition, called once the buyer's order for this offer exists.
@@ -189,12 +254,35 @@ class OfferRepository extends BaseRepository {
189
254
  * client-side patch in UserOffersPanel, which meant a reload showed the offer
190
255
  * back at "accepted" and it could be added to the cart and ordered again.
191
256
  */
192
- async markPaid(offerId, orderId) {
257
+ async markPaid(offerId, orderId, prior) {
193
258
  return this.updateStatus(offerId, {
194
259
  status: "paid",
195
260
  paidOrderId: orderId,
196
261
  paidAt: new Date(),
197
- });
262
+ }, { actor: { role: "system" }, trigger: "finalizeLockedLines" }, prior);
263
+ }
264
+ /**
265
+ * Every round of one negotiation, oldest first.
266
+ *
267
+ * A single-field equality — served by Firestore's AUTOMATIC index, so this
268
+ * adds no composite index and none of the existing `offers` indexes is
269
+ * affected. The sort is done in memory because the result is at most 3 rows
270
+ * (the per-buyer offer limit), which is cheaper than the index a compound
271
+ * orderBy would demand.
272
+ *
273
+ * Offers written before `chainRootOfferId` existed return ZERO rows, not
274
+ * one — callers must fall back to `[offer]` and never read empty as
275
+ * "this offer is missing".
276
+ */
277
+ async findChain(chainRootOfferId) {
278
+ const snapshot = await this.db
279
+ .collection(this.collection)
280
+ .where(OFFER_FIELDS.CHAIN_ROOT_OFFER_ID, "==", chainRootOfferId)
281
+ .limit(OFFER_CHAIN_MAX)
282
+ .get();
283
+ return snapshot.docs
284
+ .map((doc) => this.mapDoc(doc))
285
+ .sort((a, b) => (a.counterRound ?? 1) - (b.counterRound ?? 1));
198
286
  }
199
287
  /**
200
288
  * Cloud Functions compatibility: pending/countered offers already expired.
@@ -223,24 +311,75 @@ class OfferRepository extends BaseRepository {
223
311
  .get();
224
312
  return snapshot.docs.map((doc) => this.mapDoc(doc));
225
313
  }
226
- async expireMany(offerIds) {
314
+ /**
315
+ * Bulk-expire, taking the DOCUMENTS rather than their ids.
316
+ *
317
+ * The signature changed deliberately. `arrayUnion` would let a batch append
318
+ * without a read, but it **cannot enforce the FIFO cap** — so history would
319
+ * grow unbounded on exactly the path that touches the most documents at
320
+ * once. Folding entries in memory and writing whole arrays keeps the cap,
321
+ * and costs **zero extra reads**: `findExpiredActive`, `findExpiredAccepted`
322
+ * and the admin cancel route all already hold full `OfferDocument`s.
323
+ *
324
+ * Also carries the admin path: a reasoned cancel is an expiry with an actor
325
+ * and a reason, recorded in the offer's own history as well as in
326
+ * `adminAuditLog`.
327
+ */
328
+ async expireMany(offers, ctx) {
329
+ if (offers.length === 0)
330
+ return;
227
331
  const batch = this.db.batch();
228
332
  const now = new Date();
229
- for (const id of offerIds) {
230
- batch.update(this.db.collection(this.collection).doc(id), {
231
- [OFFER_FIELDS.STATUS]: "expired",
232
- [OFFER_FIELDS.UPDATED_AT]: now,
333
+ for (const offer of offers) {
334
+ const patch = {
335
+ status: "expired",
336
+ updatedAt: now,
337
+ ...(ctx?.cancelledByAdminUid
338
+ ? { cancelledByAdminUid: ctx.cancelledByAdminUid, cancelReason: ctx.reason }
339
+ : {}),
340
+ };
341
+ const withEntry = withHistory(offer, patch, {
342
+ tracked: OFFER_TRACKED_FIELDS,
343
+ actor: ctx?.actor ?? { role: "system" },
344
+ trigger: ctx?.trigger ?? "runOfferExpiry",
345
+ reason: ctx?.reason,
346
+ piiFields: OFFER_PII_FIELDS,
347
+ historyField: OFFER_FIELDS.STATUS_HISTORY,
348
+ truncatedField: OFFER_FIELDS.STATUS_HISTORY_TRUNCATED,
349
+ now,
233
350
  });
351
+ batch.update(this.db.collection(this.collection).doc(offer.id), (withEntry ?? patch));
234
352
  }
235
353
  await batch.commit();
236
354
  }
237
355
  }
356
+ /**
357
+ * Every Timestamp field here carries `parseValue: parseSieveDateValue`.
358
+ *
359
+ * Without it a GTE/LTE on a Timestamp field silently matches ZERO documents:
360
+ * sievejs's default coercion leaves an ISO string as a string, and Firestore
361
+ * requires an inequality's value to match the stored type. That is Root Cause
362
+ * #47, and `createdAt` flipping from `canFilter: false` to `true` here is
363
+ * exactly the transition that introduces it.
364
+ *
365
+ * `checkoutDeadline` is added by hand: `audit-sieve-date-fields` recognises
366
+ * Timestamp fields by an `...At` / `...Date` / `...Time` suffix, which this
367
+ * name does not have, so the audit cannot flag it for you.
368
+ */
238
369
  OfferRepository.SIEVE_FIELDS = {
239
- status: { canFilter: true, canSort: false },
370
+ id: { canFilter: true, canSort: false },
371
+ status: { canFilter: true, canSort: true },
240
372
  productId: { canFilter: true, canSort: false },
373
+ productTitle: { canFilter: true, canSort: true },
241
374
  buyerUid: { canFilter: true, canSort: false },
375
+ buyerName: { canFilter: true, canSort: true },
242
376
  storeId: { canFilter: true, canSort: false },
243
- createdAt: { canFilter: false, canSort: true },
377
+ storeName: { canFilter: true, canSort: true },
378
+ offerAmount: { canFilter: true, canSort: true },
379
+ listedPrice: { canFilter: true, canSort: true },
380
+ createdAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
381
+ expiresAt: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
382
+ checkoutDeadline: { canFilter: true, canSort: true, parseValue: parseSieveDateValue },
244
383
  };
245
384
  export const offerRepository = new OfferRepository();
246
385
  export { OfferRepository };
@@ -0,0 +1,72 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
4
+ * by schema instance and every zod wrapper returns a new one.
5
+ */
6
+ export declare const sellerCouponFormSchema: z.ZodEffects<z.ZodObject<{
7
+ code: z.ZodString;
8
+ type: z.ZodEnum<["percentage", "fixed", "free_shipping"]>;
9
+ value: z.ZodEffects<z.ZodString, string, string>;
10
+ minPurchase: z.ZodEffects<z.ZodString, string, string>;
11
+ maxDiscount: z.ZodEffects<z.ZodString, string, string>;
12
+ totalLimit: z.ZodEffects<z.ZodString, string, string>;
13
+ perUserLimit: z.ZodEffects<z.ZodString, string, string>;
14
+ startDate: z.ZodString;
15
+ endDate: z.ZodString;
16
+ isActive: z.ZodBoolean;
17
+ applicableProducts: z.ZodArray<z.ZodString, "many">;
18
+ applicableCategories: z.ZodArray<z.ZodString, "many">;
19
+ }, "strip", z.ZodTypeAny, {
20
+ value: string;
21
+ code: string;
22
+ type: "fixed" | "percentage" | "free_shipping";
23
+ isActive: boolean;
24
+ endDate: string;
25
+ maxDiscount: string;
26
+ minPurchase: string;
27
+ applicableProducts: string[];
28
+ applicableCategories: string[];
29
+ totalLimit: string;
30
+ perUserLimit: string;
31
+ startDate: string;
32
+ }, {
33
+ value: string;
34
+ code: string;
35
+ type: "fixed" | "percentage" | "free_shipping";
36
+ isActive: boolean;
37
+ endDate: string;
38
+ maxDiscount: string;
39
+ minPurchase: string;
40
+ applicableProducts: string[];
41
+ applicableCategories: string[];
42
+ totalLimit: string;
43
+ perUserLimit: string;
44
+ startDate: string;
45
+ }>, {
46
+ value: string;
47
+ code: string;
48
+ type: "fixed" | "percentage" | "free_shipping";
49
+ isActive: boolean;
50
+ endDate: string;
51
+ maxDiscount: string;
52
+ minPurchase: string;
53
+ applicableProducts: string[];
54
+ applicableCategories: string[];
55
+ totalLimit: string;
56
+ perUserLimit: string;
57
+ startDate: string;
58
+ }, {
59
+ value: string;
60
+ code: string;
61
+ type: "fixed" | "percentage" | "free_shipping";
62
+ isActive: boolean;
63
+ endDate: string;
64
+ maxDiscount: string;
65
+ minPurchase: string;
66
+ applicableProducts: string[];
67
+ applicableCategories: string[];
68
+ totalLimit: string;
69
+ perUserLimit: string;
70
+ startDate: string;
71
+ }>;
72
+ export type SellerCouponFormValues = z.infer<typeof sellerCouponFormSchema>;
@@ -0,0 +1,116 @@
1
+ /*
2
+ * WHY: `SellerCouponEditorView` rendered `<Form onSubmit noValidate>` with no
3
+ * schema at all, while `AdminCouponEditorView` — the same entity, one
4
+ * portal over — has a Zod schema wired through `<Form schema>`. The
5
+ * seller side relied on four hand-rolled `if` checks that covered
6
+ * presence and date order and nothing else, so a coupon could be saved
7
+ * with a **500% discount**, a non-numeric value, a negative minimum spend
8
+ * or a per-user limit above the total limit. Every failure surfaced in a
9
+ * single banner rather than on the field that caused it.
10
+ * WHAT: The seller coupon form's schema.
11
+ *
12
+ * ## Everything is a string, on purpose
13
+ *
14
+ * `CouponEditorDraft` holds every numeric field as a string because they come
15
+ * from `<input>`s. Coercing inside the schema keeps that boundary in one place
16
+ * — the alternative is a `Number()` at each call site, which is how a `NaN`
17
+ * reaches Firestore the first time one is forgotten.
18
+ *
19
+ * ## Why the rules are conditional
20
+ *
21
+ * `free_shipping` has no discount value, `percentage` is bounded 1–100, and
22
+ * `fixed` is any positive amount. A flat per-field rule cannot say that, so
23
+ * the cross-field rules live in one `superRefine` keyed by type.
24
+ *
25
+ * EXPORTS:
26
+ * sellerCouponFormSchema, type SellerCouponFormValues
27
+ *
28
+ * @tag domain:seller,promotions
29
+ * @tag layer:schema
30
+ * @tag pattern:none
31
+ * @tag access:isomorphic
32
+ * @tag consumers:SellerCouponEditorView
33
+ * @tag sideEffects:none
34
+ */
35
+ import { z } from "zod";
36
+ import { annotate } from "../../shell/field-ui-meta";
37
+ /** A blank optional numeric string — the form's "not set". */
38
+ const numericString = (label) => z
39
+ .string()
40
+ .refine((v) => v.trim() === "" || (!Number.isNaN(Number(v)) && Number(v) >= 0), {
41
+ message: `${label} must be a number of zero or more.`,
42
+ });
43
+ /**
44
+ * 🛑 `annotate()` must be the OUTERMOST call on each field — it keys a WeakMap
45
+ * by schema instance and every zod wrapper returns a new one.
46
+ */
47
+ export const sellerCouponFormSchema = z
48
+ .object({
49
+ code: annotate(z
50
+ .string()
51
+ .min(2, "A coupon code needs at least 2 characters.")
52
+ .max(40, "Keep the code under 40 characters.")
53
+ .regex(/^[A-Z0-9_-]+$/i, "Use letters, numbers, hyphens and underscores only."), {
54
+ section: "basics", sectionLabel: "Coupon", sectionRequired: true,
55
+ quick: true, order: 1, row: "pair",
56
+ }),
57
+ type: annotate(z.enum(["percentage", "fixed", "free_shipping"]), {
58
+ section: "basics", quick: true, order: 2, row: "pair",
59
+ }),
60
+ value: annotate(numericString("The discount value"), {
61
+ section: "basics", quick: true, order: 3, row: "pair",
62
+ }),
63
+ minPurchase: annotate(numericString("Minimum purchase"), {
64
+ section: "limits", sectionLabel: "Limits", order: 1, row: "pair",
65
+ }),
66
+ maxDiscount: annotate(numericString("Maximum discount"), {
67
+ section: "limits", order: 2, row: "pair",
68
+ }),
69
+ totalLimit: annotate(numericString("Total uses"), {
70
+ section: "limits", order: 3, row: "pair",
71
+ }),
72
+ perUserLimit: annotate(numericString("Uses per customer"), {
73
+ section: "limits", order: 4, row: "pair",
74
+ }),
75
+ startDate: annotate(z.string().min(1, "A start date is required."), {
76
+ section: "validity", sectionLabel: "Validity", quick: true, order: 1, row: "pair", kind: "date",
77
+ }),
78
+ endDate: annotate(z.string().min(1, "An end date is required."), {
79
+ section: "validity", quick: true, order: 2, row: "pair", kind: "date",
80
+ }),
81
+ isActive: annotate(z.boolean(), {
82
+ section: "validity", order: 3, row: "quarter",
83
+ }),
84
+ applicableProducts: annotate(z.array(z.string()), {
85
+ section: "scope", sectionLabel: "Applies to", order: 1, row: "full", kind: "list",
86
+ }),
87
+ applicableCategories: annotate(z.array(z.string()), {
88
+ section: "scope", order: 2, row: "full", kind: "list",
89
+ }),
90
+ })
91
+ .superRefine((v, ctx) => {
92
+ const issue = (path, message) => ctx.addIssue({ code: z.ZodIssueCode.custom, path: [path], message });
93
+ // A discount value is required for everything except free shipping, and
94
+ // a percentage above 100 is not a discount — it is the store paying the
95
+ // customer. Nothing bounded this before.
96
+ if (v.type !== "free_shipping") {
97
+ const amount = Number(v.value);
98
+ if (v.value.trim() === "")
99
+ issue("value", "A discount value is required.");
100
+ else if (amount <= 0)
101
+ issue("value", "The discount must be greater than zero.");
102
+ else if (v.type === "percentage" && amount > 100) {
103
+ issue("value", "A percentage discount cannot exceed 100%.");
104
+ }
105
+ }
106
+ if (v.startDate && v.endDate && v.startDate > v.endDate) {
107
+ issue("endDate", "The end date must be after the start date.");
108
+ }
109
+ // A per-customer limit above the total makes the total unreachable — the
110
+ // coupon reads as more generous than it can ever be.
111
+ const total = Number(v.totalLimit);
112
+ const perUser = Number(v.perUserLimit);
113
+ if (v.totalLimit.trim() && v.perUserLimit.trim() && perUser > total) {
114
+ issue("perUserLimit", "Uses per customer cannot exceed the total uses.");
115
+ }
116
+ });