@mohasinac/appkit 4.13.1 → 4.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (493) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +14 -5
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.d.ts +3 -0
  3. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +6 -0
  4. package/dist/_internal/client/features/lottery/LotteryDetailView.d.ts +8 -0
  5. package/dist/_internal/client/features/lottery/LotteryDetailView.js +4 -1
  6. package/dist/_internal/client/features/lottery/LotteryListView.d.ts +7 -0
  7. package/dist/_internal/client/features/lottery/LotteryListView.js +1 -1
  8. package/dist/_internal/client/features/lottery/PrizeDrawLotteryDetailView.js +4 -1
  9. package/dist/_internal/client/features/lottery/slot-collage-items.d.ts +15 -0
  10. package/dist/_internal/client/features/lottery/slot-collage-items.js +13 -0
  11. package/dist/_internal/server/features/auctions/actions.js +8 -2
  12. package/dist/_internal/server/features/bundles/data.d.ts +16 -0
  13. package/dist/_internal/server/features/bundles/data.js +26 -0
  14. package/dist/_internal/server/features/bundles/index.d.ts +1 -1
  15. package/dist/_internal/server/features/bundles/index.js +1 -1
  16. package/dist/_internal/server/features/cart/service.d.ts +3 -3
  17. package/dist/_internal/server/features/checkout/actions.js +25 -0
  18. package/dist/_internal/server/features/checkout/locked-lines.d.ts +38 -0
  19. package/dist/_internal/server/features/checkout/locked-lines.js +171 -0
  20. package/dist/_internal/server/features/checkout/source-context.d.ts +5 -0
  21. package/dist/_internal/server/features/checkout/source-context.js +157 -0
  22. package/dist/_internal/server/features/homepage/index.d.ts +0 -1
  23. package/dist/_internal/server/features/homepage/index.js +7 -1
  24. package/dist/_internal/server/features/lottery/adapters.js +4 -0
  25. package/dist/_internal/server/features/offers/adapters.d.ts +103 -0
  26. package/dist/_internal/server/features/offers/adapters.js +88 -0
  27. package/dist/_internal/server/features/offers/index.d.ts +2 -0
  28. package/dist/_internal/server/features/offers/index.js +1 -0
  29. package/dist/_internal/server/features/orders/adapters.js +24 -0
  30. package/dist/_internal/server/features/products/data.js +15 -37
  31. package/dist/_internal/server/features/products/index.d.ts +1 -1
  32. package/dist/_internal/server/features/products/index.js +1 -1
  33. package/dist/_internal/server/features/products/list-public.d.ts +46 -20
  34. package/dist/_internal/server/features/products/list-public.js +266 -90
  35. package/dist/_internal/server/features/products/listing-tab-counts.d.ts +44 -0
  36. package/dist/_internal/server/features/products/listing-tab-counts.js +97 -0
  37. package/dist/_internal/server/features/site-settings/adapters.d.ts +51 -0
  38. package/dist/_internal/server/features/site-settings/adapters.js +75 -0
  39. package/dist/_internal/server/features/site-settings/index.d.ts +1 -0
  40. package/dist/_internal/server/features/site-settings/index.js +1 -0
  41. package/dist/_internal/server/features/stores/adapters.d.ts +56 -0
  42. package/dist/_internal/server/features/stores/adapters.js +91 -0
  43. package/dist/_internal/server/features/stores/index.d.ts +1 -0
  44. package/dist/_internal/server/features/stores/index.js +1 -0
  45. package/dist/_internal/server/jobs/core/auctionSettlement.js +56 -2
  46. package/dist/_internal/server/jobs/core/offerExpiry.js +55 -19
  47. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.d.ts +5 -0
  48. package/dist/_internal/server/jobs/core/paymentReviewAutoApprove.js +33 -1
  49. package/dist/_internal/server/jobs/handlers/messages.d.ts +7 -0
  50. package/dist/_internal/server/jobs/handlers/messages.js +10 -0
  51. package/dist/_internal/shared/actions/action-registry.js +17 -1
  52. package/dist/_internal/shared/checkout/lanes.d.ts +25 -0
  53. package/dist/_internal/shared/checkout/lanes.js +26 -0
  54. package/dist/_internal/shared/features/auctions/config.d.ts +27 -0
  55. package/dist/_internal/shared/features/auctions/config.js +27 -0
  56. package/dist/_internal/shared/features/auctions/schema.d.ts +14 -2
  57. package/dist/_internal/shared/features/auctions/schema.js +13 -1
  58. package/dist/_internal/shared/features/blog/schema.d.ts +2 -2
  59. package/dist/_internal/shared/features/cart/schema.d.ts +20 -15
  60. package/dist/_internal/shared/features/cart/schema.js +15 -0
  61. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
  62. package/dist/_internal/shared/features/emi/schedule.d.ts +24 -3
  63. package/dist/_internal/shared/features/emi/schedule.js +20 -5
  64. package/dist/_internal/shared/features/events/schema.d.ts +2 -2
  65. package/dist/_internal/shared/features/homepage/section-gate.d.ts +14 -0
  66. package/dist/_internal/shared/features/homepage/section-gate.js +51 -0
  67. package/dist/_internal/shared/features/offers/config.d.ts +30 -0
  68. package/dist/_internal/shared/features/offers/config.js +59 -0
  69. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  70. package/dist/_internal/shared/features/products/schema.d.ts +30 -30
  71. package/dist/_internal/shared/features/products/to-product-item.js +6 -0
  72. package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
  73. package/dist/_internal/shared/features/site-settings/fees.d.ts +60 -0
  74. package/dist/_internal/shared/features/site-settings/fees.js +39 -0
  75. package/dist/_internal/shared/features/site-settings/types.d.ts +40 -0
  76. package/dist/_internal/shared/features/site-settings/types.js +1 -0
  77. package/dist/_internal/shared/history/history.d.ts +88 -0
  78. package/dist/_internal/shared/history/history.js +198 -0
  79. package/dist/_internal/shared/history/index.d.ts +4 -0
  80. package/dist/_internal/shared/history/index.js +2 -0
  81. package/dist/_internal/shared/history/types.d.ts +61 -0
  82. package/dist/_internal/shared/history/types.js +6 -0
  83. package/dist/_internal/shared/listing-types/_registry.d.ts +54 -1
  84. package/dist/_internal/shared/listing-types/_registry.js +67 -0
  85. package/dist/_internal/shared/listing-types/art/config.d.ts +3 -0
  86. package/dist/_internal/shared/listing-types/art/config.js +5 -0
  87. package/dist/_internal/shared/listing-types/auction/config.d.ts +13 -0
  88. package/dist/_internal/shared/listing-types/auction/config.js +21 -0
  89. package/dist/_internal/shared/listing-types/availability.d.ts +79 -0
  90. package/dist/_internal/shared/listing-types/availability.js +104 -0
  91. package/dist/_internal/shared/listing-types/capabilities.d.ts +15 -0
  92. package/dist/_internal/shared/listing-types/capabilities.js +12 -0
  93. package/dist/_internal/shared/listing-types/classified/config.d.ts +3 -0
  94. package/dist/_internal/shared/listing-types/classified/config.js +5 -0
  95. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +11 -0
  96. package/dist/_internal/shared/listing-types/digital-code/config.js +22 -2
  97. package/dist/_internal/shared/listing-types/live/config.d.ts +3 -0
  98. package/dist/_internal/shared/listing-types/live/config.js +5 -0
  99. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +3 -0
  100. package/dist/_internal/shared/listing-types/pre-order/config.js +19 -0
  101. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +11 -0
  102. package/dist/_internal/shared/listing-types/prize-draw/config.js +15 -0
  103. package/dist/_internal/shared/listing-types/standard/config.d.ts +3 -0
  104. package/dist/_internal/shared/listing-types/standard/config.js +5 -0
  105. package/dist/_internal/shared/listing-types/stickers/config.d.ts +3 -0
  106. package/dist/_internal/shared/listing-types/stickers/config.js +5 -0
  107. package/dist/client.d.ts +20 -7
  108. package/dist/client.js +22 -4
  109. package/dist/constants/api-endpoints.d.ts +6 -0
  110. package/dist/constants/api-endpoints.js +2 -0
  111. package/dist/constants/field-names.d.ts +57 -8
  112. package/dist/constants/field-names.js +101 -8
  113. package/dist/constants/table-keys.d.ts +17 -0
  114. package/dist/constants/table-keys.js +17 -0
  115. package/dist/errors/error-codes.d.ts +13 -1
  116. package/dist/errors/error-codes.js +13 -1
  117. package/dist/errors/error-display-map.js +12 -3
  118. package/dist/errors/messages.d.ts +10 -2
  119. package/dist/errors/messages.js +10 -2
  120. package/dist/features/about/components/AboutView.d.ts +3 -0
  121. package/dist/features/about/components/AboutView.js +3 -2
  122. package/dist/features/about/components/PolicyPageView.d.ts +2 -1
  123. package/dist/features/about/components/PolicyPageView.js +61 -23
  124. package/dist/features/about/components/PublicProfileView.js +5 -0
  125. package/dist/features/about/schemas/firestore.d.ts +3 -0
  126. package/dist/features/account/components/UserBidsView.js +12 -4
  127. package/dist/features/account/components/UserOrdersView.js +7 -0
  128. package/dist/features/account/components/UserReturnsView.d.ts +3 -0
  129. package/dist/features/account/components/UserReturnsView.js +17 -1
  130. package/dist/features/account/components/index.d.ts +0 -2
  131. package/dist/features/account/components/index.js +0 -1
  132. package/dist/features/account/schemas/index.d.ts +2 -2
  133. package/dist/features/account/types/index.d.ts +21 -2
  134. package/dist/features/addresses/schemas/index.d.ts +4 -4
  135. package/dist/features/admin/components/AdminArtView.js +8 -3
  136. package/dist/features/admin/components/AdminAuditLogView.js +1 -0
  137. package/dist/features/admin/components/AdminBidsView.js +15 -2
  138. package/dist/features/admin/components/AdminBlogEditorView.js +65 -33
  139. package/dist/features/admin/components/AdminClassifiedView.js +8 -3
  140. package/dist/features/admin/components/AdminDigitalCodesView.js +8 -3
  141. package/dist/features/admin/components/AdminFeatureEditorView.js +35 -6
  142. package/dist/features/admin/components/AdminFeatureFlagsView.js +1 -0
  143. package/dist/features/admin/components/AdminLiveView.js +8 -3
  144. package/dist/features/admin/components/AdminNavEditorView.js +15 -5
  145. package/dist/features/admin/components/AdminNewsletterView.js +18 -2
  146. package/dist/features/admin/components/AdminOffersView.d.ts +4 -0
  147. package/dist/features/admin/components/AdminOffersView.js +230 -0
  148. package/dist/features/admin/components/AdminOrdersView.js +10 -1
  149. package/dist/features/admin/components/AdminPaymentMethodsView.js +24 -4
  150. package/dist/features/admin/components/AdminPrizeDrawsView.js +5 -0
  151. package/dist/features/admin/components/AdminProductsView.js +16 -29
  152. package/dist/features/admin/components/AdminScammerEditorView.d.ts +2 -1
  153. package/dist/features/admin/components/AdminScammerEditorView.js +3 -13
  154. package/dist/features/admin/components/AdminScammersView.js +3 -8
  155. package/dist/features/admin/components/AdminSectionsView.js +32 -23
  156. package/dist/features/admin/components/AdminSessionsView.js +17 -2
  157. package/dist/features/admin/components/AdminSiteSettingsView.js +31 -13
  158. package/dist/features/admin/components/AdminSiteView.js +6 -2
  159. package/dist/features/admin/components/AdminStickersView.js +8 -3
  160. package/dist/features/admin/components/ViewAuditLogEntryModal.js +1 -0
  161. package/dist/features/admin/components/index.d.ts +2 -0
  162. package/dist/features/admin/components/index.js +1 -0
  163. package/dist/features/admin/components/sections/adminSectionsTypes.d.ts +4 -2
  164. package/dist/features/admin/components/sections/adminSectionsTypes.js +45 -27
  165. package/dist/features/admin/constants/filter-tabs.d.ts +89 -0
  166. package/dist/features/admin/constants/filter-tabs.js +56 -0
  167. package/dist/features/admin/schemas/firestore.d.ts +122 -3
  168. package/dist/features/admin/schemas/firestore.js +1 -0
  169. package/dist/features/admin/schemas/homepage-section-form.d.ts +22 -0
  170. package/dist/features/admin/schemas/homepage-section-form.js +68 -0
  171. package/dist/features/admin/schemas/nav-item-form.d.ts +74 -0
  172. package/dist/features/admin/schemas/nav-item-form.js +72 -0
  173. package/dist/features/auctions/actions/bid-actions.d.ts +20 -5
  174. package/dist/features/auctions/actions/bid-actions.js +93 -20
  175. package/dist/features/auctions/components/AuctionBidsTable.d.ts +10 -1
  176. package/dist/features/auctions/components/AuctionBidsTable.js +22 -4
  177. package/dist/features/auctions/components/AuctionDetailPageView.js +11 -6
  178. package/dist/features/auctions/components/AuctionsListView.js +5 -6
  179. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +7 -0
  180. package/dist/features/auctions/components/MarketplaceAuctionCard.js +19 -2
  181. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -2
  182. package/dist/features/auctions/components/PlaceBidFormClient.js +8 -4
  183. package/dist/features/auctions/components/bid-detail-fields.d.ts +29 -0
  184. package/dist/features/auctions/components/bid-detail-fields.js +103 -0
  185. package/dist/features/auctions/repository/bid.repository.d.ts +13 -0
  186. package/dist/features/auctions/repository/bid.repository.js +23 -0
  187. package/dist/features/auctions/schemas/firestore.d.ts +17 -0
  188. package/dist/features/auctions/schemas/index.d.ts +12 -12
  189. package/dist/features/audit-log/schemas/firestore.d.ts +14 -0
  190. package/dist/features/audit-log/schemas/firestore.js +7 -0
  191. package/dist/features/auth/permissions/constants.d.ts +1 -1
  192. package/dist/features/auth/permissions/constants.js +10 -1
  193. package/dist/features/blog/actions/blog-actions.d.ts +7 -7
  194. package/dist/features/blog/schemas/blog-form.d.ts +80 -0
  195. package/dist/features/blog/schemas/blog-form.js +100 -0
  196. package/dist/features/blog/schemas/index.d.ts +6 -6
  197. package/dist/features/cart/repository/cart.repository.js +1 -0
  198. package/dist/features/cart/schemas/firestore.d.ts +15 -0
  199. package/dist/features/catalogue/schemas/validation.d.ts +4 -4
  200. package/dist/features/categories/components/BrandDetailPageView.js +23 -40
  201. package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -7
  202. package/dist/features/categories/components/BrandDetailTabs.js +8 -12
  203. package/dist/features/categories/components/BundleCollage.js +1 -1
  204. package/dist/features/categories/components/CategoryDetailPageView.js +53 -57
  205. package/dist/features/categories/components/CategoryDetailTabs.d.ts +10 -8
  206. package/dist/features/categories/components/CategoryDetailTabs.js +11 -16
  207. package/dist/features/categories/components/CategoryProductsListing.js +11 -1
  208. package/dist/features/categories/components/ConcernCard.js +7 -1
  209. package/dist/features/categories/schemas/firestore.d.ts +13 -0
  210. package/dist/features/categories/schemas/index.d.ts +26 -26
  211. package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +16 -1
  212. package/dist/features/classified/components/ClassifiedDetailPageView.js +23 -3
  213. package/dist/features/events/components/AdminEventEditorView.js +32 -54
  214. package/dist/features/events/components/AdminEventEntriesView.js +22 -3
  215. package/dist/features/events/components/AdminEventsView.js +15 -11
  216. package/dist/features/events/components/EventFilters.d.ts +9 -1
  217. package/dist/features/events/components/EventFilters.js +45 -21
  218. package/dist/features/events/components/EventsIndexListing.d.ts +6 -1
  219. package/dist/features/events/components/EventsIndexListing.js +2 -2
  220. package/dist/features/events/components/EventsListPageView.js +13 -4
  221. package/dist/features/events/repository/events.repository.d.ts +17 -0
  222. package/dist/features/events/repository/events.repository.js +27 -0
  223. package/dist/features/events/schemas/event-form.d.ts +113 -0
  224. package/dist/features/events/schemas/event-form.js +82 -0
  225. package/dist/features/events/schemas/firestore.d.ts +12 -0
  226. package/dist/features/events/schemas/firestore.js +12 -0
  227. package/dist/features/events/schemas/index.d.ts +50 -50
  228. package/dist/features/events/types/index.d.ts +11 -0
  229. package/dist/features/events/types/index.js +57 -1
  230. package/dist/features/faq/actions/faq-actions.d.ts +12 -12
  231. package/dist/features/faq/schemas/index.d.ts +8 -8
  232. package/dist/features/filters/FilterFacetSection.d.ts +7 -1
  233. package/dist/features/filters/FilterFacetSection.js +11 -3
  234. package/dist/features/grouped/schemas/grouped-listing-form.d.ts +101 -0
  235. package/dist/features/grouped/schemas/grouped-listing-form.js +89 -0
  236. package/dist/features/grouped/schemas/index.d.ts +4 -4
  237. package/dist/features/homepage/actions/homepage-section-actions.d.ts +11 -3
  238. package/dist/features/homepage/actions/homepage-section-actions.js +33 -1
  239. package/dist/features/homepage/components/AnnouncementBar.d.ts +7 -1
  240. package/dist/features/homepage/components/AnnouncementBar.js +3 -3
  241. package/dist/features/homepage/components/MarketplaceHomepageView.js +20 -6
  242. package/dist/features/homepage/hooks/homepage-query.d.ts +16 -0
  243. package/dist/features/homepage/hooks/homepage-query.js +45 -0
  244. package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +10 -0
  245. package/dist/features/homepage/hooks/useFeaturedAuctions.js +28 -8
  246. package/dist/features/homepage/hooks/useFeaturedPreOrders.js +17 -8
  247. package/dist/features/homepage/hooks/useFeaturedProducts.js +15 -8
  248. package/dist/features/homepage/schemas/index.d.ts +33 -2
  249. package/dist/features/homepage/schemas/index.js +24 -0
  250. package/dist/features/layout/AppLayoutShell.d.ts +11 -1
  251. package/dist/features/layout/AppLayoutShell.js +15 -14
  252. package/dist/features/layout/BackToTop.d.ts +9 -0
  253. package/dist/features/layout/BackToTop.js +10 -1
  254. package/dist/features/layout/BottomActions.js +28 -25
  255. package/dist/features/layout/BottomChrome.d.ts +34 -0
  256. package/dist/features/layout/BottomChrome.js +55 -0
  257. package/dist/features/layout/BottomNavLayout.d.ts +6 -0
  258. package/dist/features/layout/BottomNavLayout.js +7 -1
  259. package/dist/features/layout/BottomNavbar.js +15 -2
  260. package/dist/features/layout/FooterLayout.js +11 -1
  261. package/dist/features/layout/NavbarLayout.js +1 -1
  262. package/dist/features/layout/TitleBarLayout.d.ts +7 -0
  263. package/dist/features/layout/TitleBarLayout.js +28 -5
  264. package/dist/features/layout/index.d.ts +2 -0
  265. package/dist/features/layout/index.js +1 -0
  266. package/dist/features/lottery/types/index.d.ts +3 -0
  267. package/dist/features/media/MediaPickerModal.js +5 -1
  268. package/dist/features/messages/schemas/index.d.ts +2 -2
  269. package/dist/features/orders/components/OrderScopeTabs.d.ts +20 -0
  270. package/dist/features/orders/components/OrderScopeTabs.js +31 -0
  271. package/dist/features/orders/components/OrderStatusTimeline.d.ts +5 -1
  272. package/dist/features/orders/components/OrderStatusTimeline.js +39 -20
  273. package/dist/features/orders/constants/order-scope.d.ts +48 -0
  274. package/dist/features/orders/constants/order-scope.js +111 -0
  275. package/dist/features/orders/repository/orders.repository.d.ts +54 -7
  276. package/dist/features/orders/repository/orders.repository.js +121 -22
  277. package/dist/features/orders/schemas/firestore.d.ts +152 -0
  278. package/dist/features/orders/schemas/firestore.js +30 -0
  279. package/dist/features/orders/schemas/index.d.ts +18 -18
  280. package/dist/features/orders/types/index.d.ts +35 -7
  281. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +14 -9
  282. package/dist/features/pre-orders/components/PreOrdersListView.js +6 -5
  283. package/dist/features/pre-orders/schemas/index.d.ts +1 -1
  284. package/dist/features/products/actions/product-actions.d.ts +14 -0
  285. package/dist/features/products/actions/product-actions.js +49 -11
  286. package/dist/features/products/api/route.d.ts +12 -0
  287. package/dist/features/products/api/route.js +42 -100
  288. package/dist/features/products/components/ArtStickersListView.js +2 -2
  289. package/dist/features/products/components/AuctionsIndexListing.js +13 -11
  290. package/dist/features/products/components/AvailabilityTabs.d.ts +11 -0
  291. package/dist/features/products/components/AvailabilityTabs.js +47 -0
  292. package/dist/features/products/components/GroupSettingsPanel.js +5 -1
  293. package/dist/features/products/components/MakeOfferButton.js +48 -49
  294. package/dist/features/products/components/PrizeDrawCollage.d.ts +20 -4
  295. package/dist/features/products/components/PrizeDrawCollage.js +9 -1
  296. package/dist/features/products/components/PrizeDrawsIndexListing.js +14 -12
  297. package/dist/features/products/components/PrizeDrawsListingView.js +2 -2
  298. package/dist/features/products/components/PrizeDrawsSection.d.ts +7 -1
  299. package/dist/features/products/components/PrizeDrawsSection.js +23 -18
  300. package/dist/features/products/components/ProductDetailPageView.d.ts +4 -1
  301. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  302. package/dist/features/products/components/ProductForm.js +5 -2
  303. package/dist/features/products/components/ProductGrid.js +6 -1
  304. package/dist/features/products/components/ProductsIndexListing.js +22 -32
  305. package/dist/features/products/components/ProductsIndexPageView.js +6 -9
  306. package/dist/features/products/components/index.d.ts +2 -2
  307. package/dist/features/products/components/index.js +1 -1
  308. package/dist/features/products/config/listing-type-listing-config.d.ts +20 -0
  309. package/dist/features/products/config/listing-type-listing-config.js +22 -10
  310. package/dist/features/products/constants/action-defs.d.ts +2 -0
  311. package/dist/features/products/constants/action-defs.js +6 -0
  312. package/dist/features/products/hooks/useAvailabilityScope.d.ts +13 -0
  313. package/dist/features/products/hooks/useAvailabilityScope.js +19 -0
  314. package/dist/features/products/hooks/useProducts.d.ts +5 -0
  315. package/dist/features/products/hooks/useProducts.js +13 -2
  316. package/dist/features/products/repository/products.repository.d.ts +31 -2
  317. package/dist/features/products/repository/products.repository.js +53 -21
  318. package/dist/features/products/schemas/firestore.d.ts +17 -3
  319. package/dist/features/products/schemas/firestore.js +7 -2
  320. package/dist/features/products/schemas/index.d.ts +64 -64
  321. package/dist/features/products/schemas/product-features.validators.d.ts +79 -6
  322. package/dist/features/products/schemas/product-features.validators.js +63 -0
  323. package/dist/features/products/types/index.d.ts +16 -1
  324. package/dist/features/promotions/schemas/index.d.ts +2 -2
  325. package/dist/features/reviews/schemas/index.d.ts +16 -16
  326. package/dist/features/scams/constants/scammer-status-display.d.ts +28 -0
  327. package/dist/features/scams/constants/scammer-status-display.js +79 -0
  328. package/dist/features/search/schemas/index.d.ts +4 -4
  329. package/dist/features/seller/actions/offer-actions.js +57 -4
  330. package/dist/features/seller/components/OfferPhaseTimeline.d.ts +59 -0
  331. package/dist/features/seller/components/OfferPhaseTimeline.js +107 -0
  332. package/dist/features/seller/components/SellerArtView.js +5 -0
  333. package/dist/features/seller/components/SellerAuctionsView.js +13 -15
  334. package/dist/features/seller/components/SellerBidsView.js +11 -4
  335. package/dist/features/seller/components/SellerClassifiedView.js +5 -0
  336. package/dist/features/seller/components/SellerCouponEditorView.js +17 -17
  337. package/dist/features/seller/components/SellerDigitalCodesView.js +5 -0
  338. package/dist/features/seller/components/SellerLiveView.js +5 -0
  339. package/dist/features/seller/components/SellerOffersView.js +2 -1
  340. package/dist/features/seller/components/SellerOrdersView.js +31 -9
  341. package/dist/features/seller/components/SellerPayoutSettingsView.js +56 -31
  342. package/dist/features/seller/components/SellerPreOrdersView.js +5 -0
  343. package/dist/features/seller/components/SellerPrizeDrawsView.js +14 -16
  344. package/dist/features/seller/components/SellerProductShell.js +7 -1
  345. package/dist/features/seller/components/SellerProductsView.js +11 -8
  346. package/dist/features/seller/components/SellerStickersView.js +5 -0
  347. package/dist/features/seller/components/index.d.ts +0 -2
  348. package/dist/features/seller/components/index.js +0 -1
  349. package/dist/features/seller/repository/offer.repository.d.ts +142 -24
  350. package/dist/features/seller/repository/offer.repository.js +165 -26
  351. package/dist/features/seller/schemas/coupon-form.d.ts +72 -0
  352. package/dist/features/seller/schemas/coupon-form.js +116 -0
  353. package/dist/features/seller/schemas/firestore.d.ts +56 -2
  354. package/dist/features/seller/schemas/firestore.js +27 -0
  355. package/dist/features/seller/schemas/index.d.ts +2 -2
  356. package/dist/features/seller/schemas/offer-forms.d.ts +46 -0
  357. package/dist/features/seller/schemas/offer-forms.js +42 -0
  358. package/dist/features/seller/server.d.ts +0 -1
  359. package/dist/features/seller/server.js +0 -1
  360. package/dist/features/shell/SectionForm.d.ts +99 -0
  361. package/dist/features/shell/SectionForm.js +128 -0
  362. package/dist/features/shell/build-sections.d.ts +63 -0
  363. package/dist/features/shell/build-sections.js +207 -0
  364. package/dist/features/shell/entity-form.d.ts +109 -0
  365. package/dist/features/shell/entity-form.js +96 -0
  366. package/dist/features/shell/field-ui-meta.d.ts +115 -0
  367. package/dist/features/shell/field-ui-meta.js +324 -0
  368. package/dist/features/shell/index.d.ts +8 -0
  369. package/dist/features/shell/index.js +4 -0
  370. package/dist/features/status-history/components/StatusTimeline.d.ts +74 -0
  371. package/dist/features/status-history/components/StatusTimeline.js +46 -0
  372. package/dist/features/status-history/index.d.ts +11 -0
  373. package/dist/features/status-history/index.js +10 -0
  374. package/dist/features/store-extensions/components/ReviewDecisionModal.d.ts +27 -0
  375. package/dist/features/store-extensions/components/ReviewDecisionModal.js +82 -0
  376. package/dist/features/store-extensions/index.d.ts +12 -0
  377. package/dist/features/store-extensions/index.js +12 -0
  378. package/dist/features/store-extensions/schemas/admin-notification-form.d.ts +36 -0
  379. package/dist/features/store-extensions/schemas/admin-notification-form.js +114 -0
  380. package/dist/features/store-extensions/schemas/analytics-forms.d.ts +57 -0
  381. package/dist/features/store-extensions/schemas/analytics-forms.js +158 -0
  382. package/dist/features/store-extensions/schemas/custom-role-form.d.ts +85 -0
  383. package/dist/features/store-extensions/schemas/custom-role-form.js +118 -0
  384. package/dist/features/store-extensions/schemas/firestore.d.ts +21 -1
  385. package/dist/features/store-extensions/schemas/firestore.js +60 -0
  386. package/dist/features/store-extensions/schemas/google-config-form.d.ts +53 -0
  387. package/dist/features/store-extensions/schemas/google-config-form.js +105 -0
  388. package/dist/features/store-extensions/schemas/item-request-create-form.d.ts +28 -0
  389. package/dist/features/store-extensions/schemas/item-request-create-form.js +80 -0
  390. package/dist/features/store-extensions/schemas/listing-template-form.d.ts +78 -0
  391. package/dist/features/store-extensions/schemas/listing-template-form.js +87 -0
  392. package/dist/features/store-extensions/schemas/moderation-review-form.d.ts +49 -0
  393. package/dist/features/store-extensions/schemas/moderation-review-form.js +101 -0
  394. package/dist/features/store-extensions/schemas/payout-method-form.d.ts +153 -0
  395. package/dist/features/store-extensions/schemas/payout-method-form.js +155 -0
  396. package/dist/features/store-extensions/schemas/report-create-form.d.ts +25 -0
  397. package/dist/features/store-extensions/schemas/report-create-form.js +79 -0
  398. package/dist/features/store-extensions/schemas/report-review-form.d.ts +47 -0
  399. package/dist/features/store-extensions/schemas/report-review-form.js +111 -0
  400. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +148 -0
  401. package/dist/features/store-extensions/schemas/shipping-config-form.js +104 -0
  402. package/dist/features/store-extensions/schemas/store-category-form.d.ts +54 -0
  403. package/dist/features/store-extensions/schemas/store-category-form.js +73 -0
  404. package/dist/features/stores/api/[storeSlug]/route.js +2 -25
  405. package/dist/features/stores/components/StoreAuctionsListing.js +15 -1
  406. package/dist/features/stores/components/StoreClassifiedsListing.js +12 -2
  407. package/dist/features/stores/components/StoreDetailLayoutView.js +24 -71
  408. package/dist/features/stores/components/StoreDigitalCodesListing.js +12 -2
  409. package/dist/features/stores/components/StoreLiveItemsListing.js +12 -2
  410. package/dist/features/stores/components/StorePreOrdersListing.js +12 -1
  411. package/dist/features/stores/components/StoreProductsListing.js +11 -1
  412. package/dist/features/stores/repository/store.repository.js +6 -0
  413. package/dist/features/stores/schemas/index.d.ts +6 -6
  414. package/dist/features/support/schemas/index.d.ts +4 -4
  415. package/dist/features/tester/actions/checklist-item-actions.d.ts +4 -4
  416. package/dist/features/tester/seed-data/offers-tester-seed-data.js +119 -3
  417. package/dist/features/tester/seed-data/products-tester-seed-data.js +131 -1
  418. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +431 -1
  419. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  420. package/dist/index.d.ts +25 -12
  421. package/dist/index.js +26 -11
  422. package/dist/next/routing/route-map.d.ts +23 -3
  423. package/dist/next/routing/route-map.js +14 -2
  424. package/dist/providers/db-firebase/base.repository.d.ts +30 -0
  425. package/dist/providers/db-firebase/base.repository.js +50 -1
  426. package/dist/providers/db-firebase/index.d.ts +1 -1
  427. package/dist/providers/db-firebase/index.js +1 -1
  428. package/dist/providers/db-firebase/sieve.d.ts +25 -0
  429. package/dist/providers/db-firebase/sieve.js +52 -18
  430. package/dist/react/hooks/useUrlTable.js +10 -1
  431. package/dist/schemas/firestore-value.d.ts +8 -0
  432. package/dist/schemas/firestore-value.js +44 -0
  433. package/dist/schemas/index.d.ts +1 -1
  434. package/dist/schemas/index.js +1 -1
  435. package/dist/schemas/registry.d.ts +702 -69
  436. package/dist/schemas/registry.js +64 -1
  437. package/dist/seed/_helpers/category-forest.d.ts +2 -0
  438. package/dist/seed/_helpers/category-forest.js +309 -0
  439. package/dist/seed/_helpers/category-tree.d.ts +27 -0
  440. package/dist/seed/_helpers/category-tree.js +103 -0
  441. package/dist/seed/bids-seed-data.js +30 -0
  442. package/dist/seed/catalogue-seed-data.js +12 -12
  443. package/dist/seed/categories-seed-data.js +177 -162
  444. package/dist/seed/events-seed-data.js +61 -10
  445. package/dist/seed/homepage-sections-seed-data.js +5 -1
  446. package/dist/seed/offers-seed-data.js +244 -34
  447. package/dist/seed/orders-seed-data.js +336 -1
  448. package/dist/seed/products-art-seed-data.js +50 -12
  449. package/dist/seed/products-auctions-seed-data.js +138 -18
  450. package/dist/seed/products-classifieds-seed-data.js +80 -16
  451. package/dist/seed/products-digital-codes-seed-data.js +66 -18
  452. package/dist/seed/products-live-items-seed-data.js +64 -13
  453. package/dist/seed/products-preorders-seed-data.d.ts +0 -1
  454. package/dist/seed/products-preorders-seed-data.js +52 -13
  455. package/dist/seed/products-prize-draws-seed-data.js +63 -14
  456. package/dist/seed/products-standard-seed-data.js +99 -26
  457. package/dist/seed/products-stickers-seed-data.js +47 -12
  458. package/dist/seed/reviews-seed-data.js +6 -1
  459. package/dist/seed/site-settings-seed-data.d.ts +1 -38
  460. package/dist/seed/site-settings-seed-data.js +38 -76
  461. package/dist/seed/stores-seed-data.js +71 -1
  462. package/dist/seed/users-seed-data.js +15 -5
  463. package/dist/server-entry.d.ts +1 -2
  464. package/dist/server-entry.js +4 -3
  465. package/dist/server.d.ts +6 -3
  466. package/dist/server.js +13 -4
  467. package/dist/styles.css +78 -12
  468. package/dist/tailwind-utilities.css +1 -1
  469. package/dist/ui/components/Button.style.css +19 -4
  470. package/dist/ui/components/CollapsibleSection.d.ts +17 -1
  471. package/dist/ui/components/CollapsibleSection.js +6 -2
  472. package/dist/ui/components/FilterChipGroup.d.ts +25 -1
  473. package/dist/ui/components/FilterChipGroup.js +21 -4
  474. package/dist/ui/components/HandMode.style.css +47 -0
  475. package/dist/ui/components/Layout.d.ts +9 -2
  476. package/dist/ui/components/Layout.js +1 -1
  477. package/dist/ui/components/ListingLayout.js +8 -8
  478. package/dist/ui/components/ListingLayout.style.css +8 -7
  479. package/dist/ui/components/RecordDetailModal.d.ts +10 -1
  480. package/dist/ui/components/RecordDetailModal.js +2 -2
  481. package/dist/ui/components/StickyToolbar.d.ts +3 -1
  482. package/dist/ui/components/StickyToolbar.js +62 -20
  483. package/dist/ui/components/bottom-chrome-slot.d.ts +21 -0
  484. package/dist/ui/components/bottom-chrome-slot.js +29 -0
  485. package/dist/ui/components/index.style.css +3 -0
  486. package/dist/ui/forms/FormShell.d.ts +43 -13
  487. package/dist/ui/forms/FormShell.js +22 -11
  488. package/dist/ui/forms/index.d.ts +1 -1
  489. package/dist/ui/index.d.ts +1 -1
  490. package/package.json +2 -1
  491. package/scripts/node-esm-loader.mjs +65 -0
  492. package/scripts/seed-cli-loader.mjs +6 -49
  493. package/scripts/seed-cli.mjs +1 -1
@@ -0,0 +1,79 @@
1
+ /*
2
+ * WHY: "How does a scammer status look and read" had FOUR hand-written answers
3
+ * and they had already diverged in a way that shipped a real defect:
4
+ *
5
+ * · `AdminScammersView.STATUS_BADGE` — Record<string, string>
6
+ * · `AdminScammerEditorView.STATUS_COLOR` — Record<string, string>
7
+ * · `AdminScammerEditorView.STATUS_OPTIONS` — a literal label list
8
+ * · `ADMIN_SCAMMER_STATUS_TABS` — the filter chips
9
+ *
10
+ * Both colour maps carried the SAME broken `removed` entry:
11
+ *
12
+ * "bg-[…surface] text-[…muted] bg-[…surface-elevated] text-[…muted]"
13
+ *
14
+ * — two backgrounds and two inks on one element. Tailwind emits all four
15
+ * and the winner is decided by stylesheet order, not by position in the
16
+ * class string, so which background actually painted was not what either
17
+ * map said. One bug, two copies: the Duplication Framework's bug-fix
18
+ * multiplier, which is what makes this a consolidate rather than a
19
+ * third copy.
20
+ *
21
+ * WHAT: One display module beside the union it describes.
22
+ *
23
+ * ## Everything here is keyed `Record<ScammerStatus, …>`
24
+ *
25
+ * Not `Record<string, …>`. A status added to the union without a label or a
26
+ * badge is then a COMPILE error rather than a row that silently renders with
27
+ * the fallback styling of a status it is not — which is precisely how the
28
+ * loose maps hid the broken `removed` entry.
29
+ *
30
+ * EXPORTS:
31
+ * SCAMMER_STATUS_LABEL, SCAMMER_STATUS_BADGE, SCAMMER_STATUS_OPTIONS,
32
+ * toScammerStatus
33
+ *
34
+ * @tag domain:scams
35
+ * @tag layer:constants
36
+ * @tag pattern:none
37
+ * @tag access:isomorphic
38
+ * @tag consumers:AdminScammersView,AdminScammerEditorView
39
+ * @tag sideEffects:none
40
+ */
41
+ import { ScammerStatusValues } from "../schemas/firestore";
42
+ /** Human-readable label. Replaces two `.replace(/_/g, " ")` call sites, which
43
+ * rendered "pending review" — mechanically correct, and not a written label. */
44
+ export const SCAMMER_STATUS_LABEL = {
45
+ pending_review: "Pending review",
46
+ verified: "Verified",
47
+ rejected: "Rejected",
48
+ removed: "Removed",
49
+ };
50
+ /**
51
+ * Inline-chip styling. Every entry is a `bg-{status}-surface` + `text-{status}`
52
+ * pair per Recurrent Root Cause #67 — the two tokens invert together with the
53
+ * theme, which a literal `text-white` would not.
54
+ *
55
+ * `removed` is the neutral case: it has no status colour because it is not a
56
+ * status outcome, it is a withdrawal. ONE background, ONE ink.
57
+ */
58
+ export const SCAMMER_STATUS_BADGE = {
59
+ pending_review: "bg-warning-surface text-warning",
60
+ verified: "bg-success-surface text-success",
61
+ rejected: "bg-error-surface text-error",
62
+ removed: "bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)]",
63
+ };
64
+ /** Dropdown options, derived from the union so one cannot go missing. */
65
+ export const SCAMMER_STATUS_OPTIONS = Object.values(ScammerStatusValues).map((value) => ({ value, label: SCAMMER_STATUS_LABEL[value] }));
66
+ /**
67
+ * Narrow an unknown API value to a real status.
68
+ *
69
+ * The list view used `toStringValue(item.status, "pending_review")`, which
70
+ * produces a `string` — so an unrecognised value flowed on as if it were a
71
+ * status and every consumer fell back to the pending styling. This maps
72
+ * anything unrecognised to `pending_review` explicitly, and the return type
73
+ * says so.
74
+ */
75
+ export function toScammerStatus(value) {
76
+ return Object.values(ScammerStatusValues).includes(value)
77
+ ? value
78
+ : ScammerStatusValues.PENDING_REVIEW;
79
+ }
@@ -26,22 +26,22 @@ export declare const searchProductItemSchema: z.ZodObject<{
26
26
  slug: string;
27
27
  currency?: string | undefined;
28
28
  status?: string | undefined;
29
+ price?: number | undefined;
30
+ mainImage?: string | undefined;
29
31
  featured?: boolean | undefined;
30
32
  listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "bundle" | undefined;
31
33
  isPromoted?: boolean | undefined;
32
- price?: number | undefined;
33
- mainImage?: string | undefined;
34
34
  }, {
35
35
  id: string;
36
36
  title: string;
37
37
  slug: string;
38
38
  currency?: string | undefined;
39
39
  status?: string | undefined;
40
+ price?: number | undefined;
41
+ mainImage?: string | undefined;
40
42
  featured?: boolean | undefined;
41
43
  listingType?: "standard" | "auction" | "pre-order" | "prize-draw" | "bundle" | undefined;
42
44
  isPromoted?: boolean | undefined;
43
- price?: number | undefined;
44
- mainImage?: string | undefined;
45
45
  }>;
46
46
  /**
47
47
  * Form/query schema for a search request.
@@ -14,7 +14,11 @@ import { storeRepository } from "../../stores/repository/store.repository";
14
14
  import { cartRepository } from "../../cart/repository/cart.repository";
15
15
  import { maskOfferForSeller } from "../../../security";
16
16
  import { ERROR_MESSAGES, AuthorizationError, ValidationError, NotFoundError, OFFER_ERROR_CODES, } from "../../../errors";
17
+ import { siteSettingsRepository } from "../../admin/repository/site-settings.repository";
18
+ import { canMakeOffer } from "../../../_internal/shared/listing-types/capabilities";
19
+ import { normalizeListingType } from "../../products/utils/listing-type";
17
20
  import { OfferStatusValues } from "../schemas";
21
+ import { DEFAULT_MIN_OFFER_PERCENT, minOfferAmount, } from "../../../_internal/shared/features/offers/config";
18
22
  /**
19
23
  * How long an accepted offer stays claimable at its locked price. Shared by the
20
24
  * seller-accept path, the buyer-accepts-counter path, and the expiry sweep so
@@ -26,14 +30,33 @@ const ERR_NOT_AUTHORISED = "Not authorized";
26
30
  // --- Buyer: Make an Offer --------------------------------------------------
27
31
  export async function makeOffer(userId, userEmail, input) {
28
32
  const { productId, offerAmount, buyerNote } = input;
33
+ /**
34
+ * Three independent gates, checked in widening-to-narrowing order. Only
35
+ * `makeOffer` is guarded — switching the site flag off must stop NEW offers
36
+ * without stranding in-flight ones, so accept/decline/counter/withdraw and
37
+ * `checkoutOffer` deliberately stay open.
38
+ */
39
+ const settings = await siteSettingsRepository.getSingleton().catch(() => null);
40
+ if (settings?.featureFlags?.offers === false) {
41
+ throw new ValidationError(ERROR_MESSAGES.OFFER.DISABLED, {
42
+ code: OFFER_ERROR_CODES.DISABLED,
43
+ });
44
+ }
29
45
  const product = await productRepository.findById(productId);
30
46
  if (!product)
31
47
  throw new NotFoundError(ERROR_MESSAGES.PRODUCT.NOT_FOUND);
48
+ // Some listing types cannot be negotiated at all — an auction already has
49
+ // bidding, a prize-draw sells fixed-price entries. This was never checked, so
50
+ // an offer could be (and in seed data was) attached to an auction, producing
51
+ // a cart line that claims `listingType: "standard"` in the offer lane.
52
+ if (!canMakeOffer(normalizeListingType(product))) {
53
+ throw new ValidationError("Offers aren't available for this kind of listing.");
54
+ }
32
55
  if (!product.allowOffers)
33
56
  throw new ValidationError("This product does not accept offers.");
34
- const minAllowed = Math.ceil(product.price * ((product.minOfferPercent ?? 70) / 100) * 100) / 100;
57
+ const minAllowed = minOfferAmount(product.price, product.minOfferPercent);
35
58
  if (offerAmount < minAllowed)
36
- throw new ValidationError(`Minimum offer is ₹${minAllowed} (${product.minOfferPercent ?? 70}% of listing price).`);
59
+ throw new ValidationError(`Minimum offer is ₹${minAllowed} (${product.minOfferPercent ?? DEFAULT_MIN_OFFER_PERCENT}% of listing price).`);
37
60
  if (offerAmount >= product.price)
38
61
  throw new ValidationError("Your offer must be below the listed price. Use Add to Cart instead.");
39
62
  const offerCount = await offerRepository.countByBuyerAndProduct(userId, productId, product.updatedAt);
@@ -187,7 +210,24 @@ export async function counterOfferByBuyer(userId, userEmail, input) {
187
210
  const offerCount = await offerRepository.countByBuyerAndProduct(userId, offer.productId, product.updatedAt);
188
211
  if (offerCount >= 3)
189
212
  throw new ValidationError(ERROR_MESSAGES.OFFER.LIMIT_REACHED);
190
- await offerRepository.withdraw(offerId);
213
+ // ── Create BEFORE superseding. Order matters. ───────────────────────────
214
+ //
215
+ // This used to `withdraw()` first and `create()` second. If `create` threw —
216
+ // a validation slip, a Firestore blip — the buyer's live counter was ALREADY
217
+ // withdrawn, no replacement existed, the negotiation was dead, and one of
218
+ // their three offer slots had been burnt for nothing.
219
+ //
220
+ // Reversed, the worst failure is a transient extra pending offer, which
221
+ // `hasActiveOffer` and the expiry sweep both already handle.
222
+ //
223
+ // The chain fields are what make a multi-round negotiation readable at all:
224
+ // `previousOfferId` used to exist ONLY inside the serverLogger call below
225
+ // and never reached Firestore, so three rounds were three unrelated
226
+ // documents. `chainRootOfferId` is denormalised onto every round so a list
227
+ // row can render "Round 2" with zero extra reads, and `findChain` stays a
228
+ // single-field equality served by the automatic index.
229
+ const chainRoot = offer.chainRootOfferId ?? offer.id;
230
+ const nextRound = (offer.counterRound ?? 1) + 1;
191
231
  const newOffer = await offerRepository.create({
192
232
  productId: offer.productId,
193
233
  productTitle: offer.productTitle,
@@ -202,7 +242,18 @@ export async function counterOfferByBuyer(userId, userEmail, input) {
202
242
  listedPrice: offer.listedPrice,
203
243
  currency: offer.currency,
204
244
  buyerNote,
245
+ // NOT carrying `sellerNote` forward: it would render as "Seller note" on
246
+ // the new round, making the seller look like they had already answered a
247
+ // counter they have never seen. Round N-1 keeps its own note and is now
248
+ // reachable via `previousOfferId`.
249
+ previousOfferId: offer.id,
250
+ chainRootOfferId: chainRoot,
251
+ counterRound: nextRound,
205
252
  });
253
+ // Now supersede the old round — one write, one history entry. A `withdrawn`
254
+ // document carrying `supersededByOfferId` renders as *Superseded* (neutral),
255
+ // not *Withdrawn* (negative): the buyer did not walk away, they countered.
256
+ await offerRepository.withdraw(offerId, newOffer.id, { actor: { uid: userId, role: "buyer" } }, offer);
206
257
  const buyerCounterStore = offer.storeId ? await storeRepository.findById(offer.storeId) : null;
207
258
  if (buyerCounterStore?.ownerId)
208
259
  await sendNotification({
@@ -214,12 +265,14 @@ export async function counterOfferByBuyer(userId, userEmail, input) {
214
265
  relatedId: newOffer.id,
215
266
  relatedType: "offer",
216
267
  });
268
+ // `previousOfferId` is no longer logged as its only record — it is a
269
+ // persisted field on `newOffer` now. A log line is not a data model.
217
270
  serverLogger.info("counterOfferByBuyer", {
218
- previousOfferId: offerId,
219
271
  newOfferId: newOffer.id,
220
272
  buyerUid: userId,
221
273
  productId: offer.productId,
222
274
  counterAmount,
275
+ counterRound: nextRound,
223
276
  });
224
277
  return newOffer;
225
278
  }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * OfferPhaseTimeline — the offer-shaped wrapper around `<StatusTimeline>`.
3
+ *
4
+ * An offer is an order with more phases in front of it, so it shares the rail
5
+ * and supplies only its own vocabulary:
6
+ *
7
+ * made → countered → accepted → paid
8
+ * with declined / withdrawn / expired as the terminal-negative branch
9
+ *
10
+ * ## `superseded` is a RENDER KEY, never an OfferStatus
11
+ *
12
+ * A document that is `withdrawn` **and** carries `supersededByOfferId` is not
13
+ * a walk-away — the buyer countered, and the negotiation continued into the
14
+ * next round. It renders neutral ("Superseded"), not negative ("Withdrawn").
15
+ *
16
+ * Deliberately NOT an eighth `OfferStatus` value: `ADMIN_OFFER_STATUS_TABS`
17
+ * would then gain a chip that matches zero stored rows, which is exactly the
18
+ * defect `audit-filter-tab-enums` exists to catch.
19
+ *
20
+ * ## Back-compat
21
+ *
22
+ * Offers written before `statusHistory` existed have none. Branch B derives
23
+ * what it can from the scalars — and `expired` gets **no date at all**,
24
+ * because `expireMany` never wrote one. `updatedAt` is "last write of any
25
+ * kind" and `expiresAt` is a deadline, not an event; using either would be a
26
+ * fabricated timestamp.
27
+ */
28
+ import { type TimelineEntry } from "../../status-history/components/StatusTimeline";
29
+ /** One round of a negotiation, as returned by the API. */
30
+ export interface OfferChainRound {
31
+ id: string;
32
+ counterRound: number;
33
+ status: string;
34
+ offerAmount: number;
35
+ counterAmount?: number;
36
+ superseded: boolean;
37
+ createdAt: string;
38
+ respondedAt?: string;
39
+ sellerNote?: string;
40
+ buyerNote?: string;
41
+ }
42
+ export interface OfferPhaseTimelineProps {
43
+ status: string;
44
+ timeline?: TimelineEntry[];
45
+ timelineTruncated?: number;
46
+ superseded?: boolean;
47
+ createdAt?: string;
48
+ respondedAt?: string;
49
+ acceptedAt?: string;
50
+ paidAt?: string;
51
+ /**
52
+ * Earlier rounds, oldest first. Concatenated ahead of this offer's own
53
+ * steps into ONE rail, each introduced by a "Round N" divider — a
54
+ * negotiation reads as one story, not N disconnected cards.
55
+ */
56
+ chain?: OfferChainRound[];
57
+ className?: string;
58
+ }
59
+ export declare function OfferPhaseTimeline({ status, timeline, timelineTruncated, superseded, createdAt, respondedAt, acceptedAt, paidAt, chain, className, }: OfferPhaseTimelineProps): import("react").JSX.Element;
@@ -0,0 +1,107 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /**
4
+ * OfferPhaseTimeline — the offer-shaped wrapper around `<StatusTimeline>`.
5
+ *
6
+ * An offer is an order with more phases in front of it, so it shares the rail
7
+ * and supplies only its own vocabulary:
8
+ *
9
+ * made → countered → accepted → paid
10
+ * with declined / withdrawn / expired as the terminal-negative branch
11
+ *
12
+ * ## `superseded` is a RENDER KEY, never an OfferStatus
13
+ *
14
+ * A document that is `withdrawn` **and** carries `supersededByOfferId` is not
15
+ * a walk-away — the buyer countered, and the negotiation continued into the
16
+ * next round. It renders neutral ("Superseded"), not negative ("Withdrawn").
17
+ *
18
+ * Deliberately NOT an eighth `OfferStatus` value: `ADMIN_OFFER_STATUS_TABS`
19
+ * would then gain a chip that matches zero stored rows, which is exactly the
20
+ * defect `audit-filter-tab-enums` exists to catch.
21
+ *
22
+ * ## Back-compat
23
+ *
24
+ * Offers written before `statusHistory` existed have none. Branch B derives
25
+ * what it can from the scalars — and `expired` gets **no date at all**,
26
+ * because `expireMany` never wrote one. `updatedAt` is "last write of any
27
+ * kind" and `expiresAt` is a deadline, not an event; using either would be a
28
+ * fabricated timestamp.
29
+ */
30
+ import { StatusTimeline, stepsFromEntries, } from "../../status-history/components/StatusTimeline";
31
+ const PHASE_LABELS = {
32
+ pending: "Offer made",
33
+ countered: "Countered",
34
+ accepted: "Accepted",
35
+ paid: "Paid",
36
+ declined: "Declined",
37
+ withdrawn: "Withdrawn",
38
+ expired: "Expired",
39
+ superseded: "Superseded",
40
+ };
41
+ const NEGATIVE = new Set(["declined", "withdrawn", "expired"]);
42
+ const labelFor = (status) => PHASE_LABELS[status] ?? status;
43
+ const isNegative = (status) => NEGATIVE.has(status);
44
+ /** Branch B — scalars only, for offers with no recorded history. */
45
+ function buildLegacySteps(o) {
46
+ const steps = [
47
+ { key: "pending", label: PHASE_LABELS.pending, date: o.createdAt },
48
+ ];
49
+ const s = (o.status ?? "").toLowerCase();
50
+ if (s === "countered") {
51
+ steps.push({ key: "countered", label: PHASE_LABELS.countered, date: o.respondedAt });
52
+ }
53
+ if (s === "accepted" || s === "paid") {
54
+ steps.push({
55
+ key: "accepted",
56
+ label: PHASE_LABELS.accepted,
57
+ date: o.acceptedAt ?? o.respondedAt,
58
+ });
59
+ }
60
+ if (s === "paid") {
61
+ steps.push({ key: "paid", label: PHASE_LABELS.paid, date: o.paidAt });
62
+ }
63
+ if (s === "declined") {
64
+ steps.push({ key: "declined", label: PHASE_LABELS.declined, date: o.respondedAt, negative: true });
65
+ }
66
+ if (s === "withdrawn") {
67
+ steps.push(o.superseded
68
+ ? { key: "superseded", label: PHASE_LABELS.superseded, date: o.respondedAt, muted: true }
69
+ : { key: "withdrawn", label: PHASE_LABELS.withdrawn, date: o.respondedAt, negative: true });
70
+ }
71
+ if (s === "expired") {
72
+ // No date, on purpose. `expireMany` never wrote one before W2.
73
+ steps.push({ key: "expired", label: PHASE_LABELS.expired, negative: true });
74
+ }
75
+ return steps;
76
+ }
77
+ /** Re-key a withdrawn+superseded step so it reads neutral, not negative. */
78
+ function applySupersede(steps, superseded) {
79
+ if (!superseded)
80
+ return steps;
81
+ return steps.map((s) => s.key === "withdrawn"
82
+ ? { ...s, key: "superseded", label: PHASE_LABELS.superseded, negative: false, muted: true }
83
+ : s);
84
+ }
85
+ export function OfferPhaseTimeline({ status, timeline, timelineTruncated, superseded = false, createdAt, respondedAt, acceptedAt, paidAt, chain, className = "", }) {
86
+ const own = applySupersede(stepsFromEntries(timeline, labelFor, isNegative).length
87
+ ? stepsFromEntries(timeline, labelFor, isNegative)
88
+ : buildLegacySteps({ status, createdAt, respondedAt, acceptedAt, paidAt, superseded }), superseded);
89
+ const priorRounds = (chain ?? []).filter((r) => r.id !== undefined);
90
+ const hasChain = priorRounds.length > 1;
91
+ const steps = hasChain
92
+ ? priorRounds.flatMap((round, idx) => {
93
+ const roundSteps = applySupersede(buildLegacySteps({
94
+ status: round.status,
95
+ createdAt: round.createdAt,
96
+ respondedAt: round.respondedAt,
97
+ superseded: round.superseded,
98
+ }), round.superseded);
99
+ return roundSteps.map((s, i) => ({
100
+ ...s,
101
+ roundLabel: i === 0 ? `Round ${round.counterRound || idx + 1}` : undefined,
102
+ note: i === roundSteps.length - 1 ? (round.sellerNote ?? round.buyerNote) : undefined,
103
+ }));
104
+ })
105
+ : own;
106
+ return (_jsx(StatusTimeline, { title: hasChain ? "Negotiation" : "Offer history", steps: steps, truncatedCount: timelineTruncated, emptyLabel: "No offer history yet", className: className }));
107
+ }
@@ -14,6 +14,8 @@ import { ROUTES } from "../../../constants/index";
14
14
  import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../../admin/hooks/useAdminListingData";
15
15
  import { DataListingView } from "../../admin/components/DataListingView";
16
16
  import { SELLER_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
17
+ import { useAvailabilityScope } from "../../products/hooks/useAvailabilityScope";
18
+ const LISTING_TYPES = ["art"];
17
19
  const COLUMNS = [
18
20
  { key: "title", header: "Listing", render: (row) => _jsx(Text, { size: "sm", weight: "medium", children: row.title }) },
19
21
  { key: "price", header: "Price", render: (row) => _jsx(Text, { className: "tabular-nums", size: "sm", children: row.price }) },
@@ -57,6 +59,7 @@ export function SellerArtView({ onCreateClick, onEditClick, onDelete, onBulkDele
57
59
  else
58
60
  window.location.href = String(ROUTES.STORE.ART_NEW);
59
61
  }, [onCreateClick]);
62
+ const scope = useAvailabilityScope(LISTING_TYPES);
60
63
  const config = {
61
64
  portal: "seller",
62
65
  title: "Art",
@@ -87,6 +90,8 @@ export function SellerArtView({ onCreateClick, onEditClick, onDelete, onBulkDele
87
90
  }),
88
91
  getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
89
92
  buildFilters: () => "listingType==art",
93
+ buildExtraParams: () => scope.extraParams,
94
+ renderAboveContent: scope.renderAboveContent,
90
95
  primaryAction: { label: "New Art Print", onClick: () => handleCreate() },
91
96
  // Mirrors the "Edit" row action below so table rows and cards both navigate on click.
92
97
  onRowClick: (row) => handleEdit(row.id),
@@ -4,23 +4,22 @@ import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit/client";
4
4
  import { sortBy } from "@mohasinac/appkit/client";
5
5
  import React, { useState, useCallback } from "react";
6
6
  import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
7
- import { useUrlTable } from "../../../react/hooks/useUrlTable";
8
7
  import { ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu } from "../../../ui";
9
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
10
- import { TABLE_KEYS } from "../../../constants/table-keys";
11
9
  import { SELLER_AUCTION_STATUS_TABS } from "../../admin/constants/filter-tabs";
12
10
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
13
11
  import { ROUTES } from "../../../constants";
14
12
  import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../../admin/hooks/useAdminListingData";
15
13
  import { DataListingView } from "../../admin/components/DataListingView";
16
14
  import { SELLER_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
15
+ import { useAvailabilityScope } from "../../products/hooks/useAvailabilityScope";
16
+ const AUCTION_TYPES = ["auction"];
17
17
  export function SellerAuctionsView({ children, onEditClick, onDelete, onBulkDelete, ...props }) {
18
18
  // Independent of DataListingView's own internal useUrlTable() — both read
19
19
  // and write the same URL, useUrlTable has no local state, so they stay in
20
20
  // sync (same pattern SellerProductsView already uses for its "Show sold"
21
21
  // toggle).
22
- const toggleTable = useUrlTable({});
23
- const showEnded = toggleTable.get(TABLE_KEYS.SHOW_ENDED) === "true";
22
+ const scope = useAvailabilityScope(AUCTION_TYPES);
24
23
  const [deleteTargetId, setDeleteTargetId] = useState(null);
25
24
  const { deletingId, handleDelete: performDelete } = useEntityDelete({
26
25
  endpoint: SELLER_ENDPOINTS.PRODUCT_BY_ID,
@@ -46,7 +45,7 @@ export function SellerAuctionsView({ children, onEditClick, onDelete, onBulkDele
46
45
  title: "Auctions",
47
46
  searchPlaceholder: "Search auctions by product name",
48
47
  emptyLabel: "No auctions found",
49
- filterKeys: ["status", TABLE_KEYS.SHOW_ENDED],
48
+ filterKeys: ["status"],
50
49
  defaultSort: sortBy("auctionEndDate", "ASC"),
51
50
  queryKey: ["seller", "auctions", "listing"],
52
51
  endpoint: SELLER_ENDPOINTS.AUCTIONS,
@@ -73,21 +72,20 @@ export function SellerAuctionsView({ children, onEditClick, onDelete, onBulkDele
73
72
  buildFilters: (state) => {
74
73
  // Previously missing entirely — the seller Auctions tab silently
75
74
  // listed the entire seller catalog, not just auctions.
75
+ //
76
+ // The hide-ended clause that used to live here is gone: it hard-coded
77
+ // `auctionEndDate >= now`, which is an INEQUALITY pushed straight into
78
+ // Firestore alongside whichever sort the toolbar happened to be on —
79
+ // the FAILED_PRECONDITION shape of Root Cause #59. The scope param now
80
+ // carries it, and `listPublicProducts` decides whether it can be pushed
81
+ // down or has to be applied over a bounded fetch.
76
82
  const parts = ["listingType==auction"];
77
83
  if (state.status && state.status !== "All")
78
84
  parts.push(sieveFilter("status", SIEVE_OP.EQ, state.status));
79
- if (state[TABLE_KEYS.SHOW_ENDED] !== "true") {
80
- parts.push(sieveFilter("auctionEndDate", SIEVE_OP.GTE, new Date().toISOString()));
81
- }
82
85
  return parts.join(",");
83
86
  },
84
- toggles: [
85
- {
86
- label: "Show ended",
87
- active: showEnded,
88
- onChange: (next) => toggleTable.set(TABLE_KEYS.SHOW_ENDED, next ? "true" : ""),
89
- },
90
- ],
87
+ buildExtraParams: () => scope.extraParams,
88
+ renderAboveContent: scope.renderAboveContent,
91
89
  // Rule #7: bulk-action array sourced from the SELLER_BULK_ACTIONS preset.
92
90
  buildBulkActions: onBulkDelete
93
91
  ? (selection) => SELLER_BULK_ACTIONS.auctions.map((id) => ({
@@ -1,10 +1,11 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { normalizeError } from "../../../errors/normalize";
4
4
  import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit/client";
5
5
  import { sortBy } from "@mohasinac/appkit/client";
6
6
  import { useState, useCallback } from "react";
7
- import { Badge, Button, Div, FilterChipGroup, Span, Text, useToast } from "../../../ui";
7
+ import { Badge, Button, Div, FilterChipGroup, RecordDetailModal, Span, Text, useToast } from "../../../ui";
8
+ import { buildBidDetailFields, bidStatusBadge } from "../../auctions/components/bid-detail-fields";
8
9
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
10
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
10
11
  import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
@@ -29,6 +30,7 @@ const STATUS_BADGE = {
29
30
  cancelled: "danger",
30
31
  };
31
32
  export function SellerBidsView({ endpoint = SELLER_ENDPOINTS.BIDS }) {
33
+ const [detail, setDetail] = useState(null);
32
34
  const { showToast } = useToast();
33
35
  const [collapsedGroups, setCollapsedGroups] = useState(new Set());
34
36
  const toggleGroup = (id) => setCollapsedGroups((s) => {
@@ -130,9 +132,14 @@ export function SellerBidsView({ endpoint = SELLER_ENDPOINTS.BIDS }) {
130
132
  }
131
133
  return (_jsx(Stack, { gap: "3", children: groupedRows.map((group) => {
132
134
  const collapsed = collapsedGroups.has(group.id);
133
- return (_jsxs(Div, { className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "lg", children: [_jsxs(Button, { variant: "ghost", type: "button", onClick: () => toggleGroup(group.id), justify: "between", paddingX: "md", paddingY: "y-xs-tall", textSize: "sm", weight: "semibold", textColor: "default", className: "w-full hover:bg-[var(--appkit-color-surface-raised)]", children: [_jsxs(Span, { className: "truncate", children: [group.title, " ", _jsxs(Span, { size: "xs", weight: "normal", className: "text-[var(--appkit-color-text-muted)]", children: ["\u00B7 ", group.bids.length, " bid", group.bids.length === 1 ? "" : "s"] })] }), _jsx(Span, { "aria-hidden": true, className: "text-[var(--appkit-color-text-muted)]", children: collapsed ? "▸" : "▾" })] }), !collapsed && (_jsx(Div, { className: "border-t border-[var(--appkit-color-border)]", children: _jsx(DataTable, { rows: group.bids, columns: columns, isLoading: false, emptyLabel: "", selectedIds: new Set(selection.selectedIds), onToggleSelect: selection.toggleSelect }) }))] }, group.id));
135
+ return (_jsxs(Div, { className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "lg", children: [_jsxs(Button, { variant: "ghost", type: "button", onClick: () => toggleGroup(group.id), justify: "between", paddingX: "md", paddingY: "y-xs-tall", textSize: "sm", weight: "semibold", textColor: "default", className: "w-full hover:bg-[var(--appkit-color-surface-raised)]", children: [_jsxs(Span, { className: "truncate", children: [group.title, " ", _jsxs(Span, { size: "xs", weight: "normal", className: "text-[var(--appkit-color-text-muted)]", children: ["\u00B7 ", group.bids.length, " bid", group.bids.length === 1 ? "" : "s"] })] }), _jsx(Span, { "aria-hidden": true, className: "text-[var(--appkit-color-text-muted)]", children: collapsed ? "▸" : "▾" })] }), !collapsed && (_jsx(Div, { className: "border-t border-[var(--appkit-color-border)]", children: _jsx(DataTable, { rows: group.bids, columns: columns, isLoading: false, emptyLabel: "", selectedIds: new Set(selection.selectedIds), onToggleSelect: selection.toggleSelect,
136
+ // Without this the row could be selected and bulk-acted
137
+ // on but never opened — a seller could cancel a bid they
138
+ // had no way to read (Root Cause #56).
139
+ onRowClick: (row) => setDetail(row) }) }))] }, group.id));
134
140
  }) }));
135
141
  },
136
142
  };
137
- return _jsx(DataListingView, { config: config });
143
+ const detailBadge = detail ? bidStatusBadge(detail.status) : null;
144
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(RecordDetailModal, { isOpen: detail !== null, onClose: () => setDetail(null), title: detail?.productTitle || "Bid", badges: detailBadge ? [{ label: detailBadge.label }] : undefined, fields: detail ? buildBidDetailFields(detail, "seller") : undefined })] }));
138
145
  }
@@ -10,6 +10,8 @@ import { ROUTES } from "../../../constants/index";
10
10
  import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../../admin/hooks/useAdminListingData";
11
11
  import { DataListingView } from "../../admin/components/DataListingView";
12
12
  import { SELLER_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
13
+ import { useAvailabilityScope } from "../../products/hooks/useAvailabilityScope";
14
+ const LISTING_TYPES = ["classified"];
13
15
  const COLUMNS = [
14
16
  { key: "title", header: "Listing", render: (row) => _jsx(Text, { size: "sm", weight: "medium", children: row.title }) },
15
17
  { key: "price", header: "Price", render: (row) => _jsx(Text, { className: "tabular-nums", size: "sm", children: row.price }) },
@@ -58,6 +60,7 @@ export function SellerClassifiedView({ onCreateClick, onEditClick, onDelete, onB
58
60
  else
59
61
  window.location.href = String(ROUTES.STORE.CLASSIFIED_NEW);
60
62
  }, [onCreateClick]);
63
+ const scope = useAvailabilityScope(LISTING_TYPES);
61
64
  const config = {
62
65
  portal: "seller",
63
66
  title: "Classified",
@@ -92,6 +95,8 @@ export function SellerClassifiedView({ onCreateClick, onEditClick, onDelete, onB
92
95
  ? response.meta.total
93
96
  : mappedRows.length,
94
97
  buildFilters: () => "listingType==classified",
98
+ buildExtraParams: () => scope.extraParams,
99
+ renderAboveContent: scope.renderAboveContent,
95
100
  primaryAction: { label: "New Classified", onClick: () => handleCreate() },
96
101
  // Mirrors the "Edit" row action below so table rows and cards both navigate on click.
97
102
  onRowClick: (row) => handleEdit(row.id),
@@ -5,6 +5,8 @@ import { Badge, Button, Checkbox, Div, Form, Heading, IconButton, Input, Label,
5
5
  import { ProductInlineSelect } from "./ProductInlineSelect";
6
6
  import { CategoryInlineSelect } from "./CategoryInlineSelect";
7
7
  import { normalizeError } from "../../../errors/normalize";
8
+ import { FormErrorSummary, applyZodIssues } from "../../../ui/forms";
9
+ import { sellerCouponFormSchema } from "../schemas/coupon-form";
8
10
  const __O = {
9
11
  hidden: "overflow-hidden",
10
12
  };
@@ -39,22 +41,20 @@ export function SellerCouponEditorView({ couponId, initial, onSave, onCancel, })
39
41
  const [saving, setSaving] = useState(false);
40
42
  const [error, setError] = useState(null);
41
43
  const set = (key, value) => setDraft((d) => ({ ...d, [key]: value }));
42
- const handleSubmit = async (e) => {
44
+ const handleSubmit = async (e, setFieldError) => {
43
45
  e.preventDefault();
44
- if (!draft.code.trim()) {
45
- setError("Coupon code is required");
46
- return;
47
- }
48
- if (draft.type !== "free_shipping" && !draft.value) {
49
- setError("Discount value is required");
50
- return;
51
- }
52
- if (!draft.startDate || !draft.endDate) {
53
- setError("Start and end dates are required");
54
- return;
55
- }
56
- if (draft.startDate > draft.endDate) {
57
- setError("Start date must be before end date");
46
+ // Replaces four hand-rolled `if` checks that covered presence and date
47
+ // order and nothing else — a 500% percentage discount, a non-numeric
48
+ // value, a negative minimum spend and a per-customer limit above the
49
+ // total all passed. Errors now land on the field, not in one banner.
50
+ const parsed = sellerCouponFormSchema.safeParse(draft);
51
+ if (!parsed.success) {
52
+ if (setFieldError) {
53
+ applyZodIssues(parsed.error.issues, setFieldError);
54
+ }
55
+ else {
56
+ setError(parsed.error.issues[0]?.message ?? "Please check the form.");
57
+ }
58
58
  return;
59
59
  }
60
60
  setError(null);
@@ -71,11 +71,11 @@ export function SellerCouponEditorView({ couponId, initial, onSave, onCancel, })
71
71
  }
72
72
  };
73
73
  const isEdit = Boolean(couponId);
74
- return (_jsx(Form, { onSubmit: handleSubmit, noValidate: true, children: _jsxs(Stack, { gap: "none", className: `max-w-lg mx-auto border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.hidden}`, rounded: "xl", shadow: "sm", children: [_jsx(Div, { className: "h-[3px] w-full [background:linear-gradient(to_right,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_55%,var(--appkit-color-secondary-400)_100%)]", "aria-hidden": "true" }), _jsx(Div, { border: "bottom", paddingY: "y-md-lg", padding: "x-lg", children: _jsxs(Row, { justify: "between", gap: "3", children: [_jsx(Heading, { level: 2, className: "text-[var(--appkit-color-text)]", size: "lg", weight: "semibold", children: isEdit ? "Edit Coupon" : "Create Coupon" }), isEdit && (_jsx(Badge, { variant: draft.isActive ? "success" : "default", children: draft.isActive ? "Active" : "Inactive" }))] }) }), _jsxs(Stack, { gap: "5", padding: "lg", children: [error && (_jsx(Div, { textSize: "sm", className: "border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "lg", children: error })), _jsx(Input, { label: "Coupon Code", value: draft.code, onChange: (e) => set("code", e.target.value.toUpperCase().replace(/\s+/g, "")), placeholder: "e.g. WELCOME10", required: true, disabled: isEdit, helperText: isEdit ? "Code cannot be changed after creation" : "Customers enter this at checkout" }), _jsx(Select, { label: "Discount Type", value: draft.type, options: TYPE_OPTIONS, onChange: (e) => set("type", e.target.value) }), draft.type !== "free_shipping" && (_jsx(Input, { label: draft.type === "percentage" ? "Discount Percentage (%)" : "Discount Amount (₹)", type: "number", min: 1, max: draft.type === "percentage" ? 100 : undefined, value: draft.value, onChange: (e) => set("value", e.target.value), placeholder: draft.type === "percentage" ? "e.g. 10" : "e.g. 50", required: true, helperText: draft.type === "percentage"
74
+ return (_jsx(Form, { schema: sellerCouponFormSchema, onSubmit: (e) => e.preventDefault(), children: ({ setFieldError }) => (_jsxs(Stack, { gap: "none", className: `max-w-lg mx-auto border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.hidden}`, rounded: "xl", shadow: "sm", children: [_jsx(Div, { className: "h-[3px] w-full [background:linear-gradient(to_right,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_55%,var(--appkit-color-secondary-400)_100%)]", "aria-hidden": "true" }), _jsx(Div, { border: "bottom", paddingY: "y-md-lg", padding: "x-lg", children: _jsxs(Row, { justify: "between", gap: "3", children: [_jsx(Heading, { level: 2, className: "text-[var(--appkit-color-text)]", size: "lg", weight: "semibold", children: isEdit ? "Edit Coupon" : "Create Coupon" }), isEdit && (_jsx(Badge, { variant: draft.isActive ? "success" : "default", children: draft.isActive ? "Active" : "Inactive" }))] }) }), _jsxs(Stack, { gap: "5", padding: "lg", children: [_jsx(FormErrorSummary, {}), error && (_jsx(Div, { textSize: "sm", className: "border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "lg", children: error })), _jsx(Input, { label: "Coupon Code", value: draft.code, onChange: (e) => set("code", e.target.value.toUpperCase().replace(/\s+/g, "")), placeholder: "e.g. WELCOME10", required: true, disabled: isEdit, helperText: isEdit ? "Code cannot be changed after creation" : "Customers enter this at checkout" }), _jsx(Select, { label: "Discount Type", value: draft.type, options: TYPE_OPTIONS, onChange: (e) => set("type", e.target.value) }), draft.type !== "free_shipping" && (_jsx(Input, { label: draft.type === "percentage" ? "Discount Percentage (%)" : "Discount Amount (₹)", type: "number", min: 1, max: draft.type === "percentage" ? 100 : undefined, value: draft.value, onChange: (e) => set("value", e.target.value), placeholder: draft.type === "percentage" ? "e.g. 10" : "e.g. 50", required: true, helperText: draft.type === "percentage"
75
75
  ? "Enter a value between 1 and 100"
76
76
  : "Fixed rupee discount applied to the order" })), draft.type === "percentage" && (_jsx(Input, { label: "Max Discount Cap (\u20B9, optional)", type: "number", min: 0, value: draft.maxDiscount, onChange: (e) => set("maxDiscount", e.target.value), placeholder: "Leave blank for no cap", helperText: "Maximum rupee discount regardless of percentage" })), _jsx(Input, { label: "Minimum Order Amount (\u20B9, optional)", type: "number", min: 0, value: draft.minPurchase, onChange: (e) => set("minPurchase", e.target.value), placeholder: "Leave blank for no minimum" }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: CLS_SECTION_LABEL, children: "Usage Limits" }), _jsxs(Div, { layout: "grid", gap: "3", className: "grid-cols-2", children: [_jsx(Input, { label: "Total Uses", type: "number", min: 0, value: draft.totalLimit, onChange: (e) => set("totalLimit", e.target.value), placeholder: "0 = unlimited" }), _jsx(Input, { label: "Per Customer", type: "number", min: 0, value: draft.perUserLimit, onChange: (e) => set("perUserLimit", e.target.value), placeholder: "0 = unlimited" })] })] }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: CLS_SECTION_LABEL, children: "Validity Period" }), _jsxs(Div, { layout: "grid", gap: "3", className: "grid-cols-2", children: [_jsx(Input, { label: "Start Date", type: "date", value: draft.startDate, onChange: (e) => set("startDate", e.target.value), required: true }), _jsx(Input, { label: "End Date", type: "date", value: draft.endDate, onChange: (e) => set("endDate", e.target.value), required: true })] })] }), _jsxs(Div, { children: [_jsx(Text, { size: "sm", className: CLS_SECTION_LABEL, children: "Applicability (optional)" }), _jsxs(Stack, { gap: "sm", children: [_jsxs(Div, { children: [_jsx(Text, { size: "xs", className: "mb-1 text-[var(--appkit-color-text-secondary)]", children: "Applicable products" }), _jsx(ProductInlineSelect, { scope: "store", multiple: true, value: draft.applicableProducts, onChange: (v) => set("applicableProducts", v), placeholder: "Restrict to specific products\u2026" })] }), _jsxs(Div, { children: [_jsx(Text, { size: "xs", className: "mb-1 text-[var(--appkit-color-text-secondary)]", children: "Applicable categories" }), _jsx(CategoryInlineSelect, { value: "", onChange: (id) => {
77
77
  if (!id || draft.applicableCategories.includes(id))
78
78
  return;
79
79
  set("applicableCategories", [...draft.applicableCategories, id]);
80
- }, placeholder: "Add a category\u2026" }), draft.applicableCategories.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", padding: "t-xs", children: draft.applicableCategories.map((cid) => (_jsxs(Span, { layout: "inline-flex", gap: "xs", border: "strong", padding: "pill-sm", rounded: "full", surface: "muted", color: "primary", size: "xs", children: [cid, _jsx(IconButton, { type: "button", "aria-label": `Remove ${cid}`, variant: "ghost", className: "text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]", onClick: () => set("applicableCategories", draft.applicableCategories.filter((c) => c !== cid)), children: "\u00D7" })] }, cid))) }))] }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: "Leave both empty to apply the coupon to every product in your store." })] })] }), _jsx(Row, { className: "border border-[var(--appkit-color-border)] dark:border-[var(--appkit-color-border-dark)]", padding: "inline", align: "center", gap: "3", rounded: "lg", children: _jsxs(Label, { layout: "flex", gap: "lg", className: "cursor-pointer w-full", children: [_jsx(Checkbox, { bare: true, checked: draft.isActive, onChange: (e) => set("isActive", e.target.checked), className: "h-4 w-4 rounded border-[var(--appkit-color-border)] text-[var(--appkit-color-primary)] focus:ring-[var(--appkit-color-primary)]" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-primary)]", weight: "medium", children: "Active" }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: "Customers can apply this coupon at checkout" })] })] }) })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)]", padding: "inlineLg", children: _jsxs(Row, { justify: "end", gap: "3", children: [onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: saving, children: "Cancel" })), _jsx(Button, { type: "submit", isLoading: saving, disabled: saving, children: isEdit ? "Save Changes" : "Create Coupon" })] }) })] }) }));
80
+ }, placeholder: "Add a category\u2026" }), draft.applicableCategories.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", padding: "t-xs", children: draft.applicableCategories.map((cid) => (_jsxs(Span, { layout: "inline-flex", gap: "xs", border: "strong", padding: "pill-sm", rounded: "full", surface: "muted", color: "primary", size: "xs", children: [cid, _jsx(IconButton, { type: "button", "aria-label": `Remove ${cid}`, variant: "ghost", className: "text-[var(--appkit-color-text-muted)] hover:text-[var(--appkit-color-text)]", onClick: () => set("applicableCategories", draft.applicableCategories.filter((c) => c !== cid)), children: "\u00D7" })] }, cid))) }))] }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: "Leave both empty to apply the coupon to every product in your store." })] })] }), _jsx(Row, { className: "border border-[var(--appkit-color-border)] dark:border-[var(--appkit-color-border-dark)]", padding: "inline", align: "center", gap: "3", rounded: "lg", children: _jsxs(Label, { layout: "flex", gap: "lg", className: "cursor-pointer w-full", children: [_jsx(Checkbox, { bare: true, checked: draft.isActive, onChange: (e) => set("isActive", e.target.checked), className: "h-4 w-4 rounded border-[var(--appkit-color-border)] text-[var(--appkit-color-primary)] focus:ring-[var(--appkit-color-primary)]" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-primary)]", weight: "medium", children: "Active" }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: "Customers can apply this coupon at checkout" })] })] }) })] }), _jsx(Div, { className: "border-t border-[var(--appkit-color-border)]", padding: "inlineLg", children: _jsxs(Row, { justify: "end", gap: "3", children: [onCancel && (_jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: saving, children: "Cancel" })), _jsx(Button, { type: "submit", isLoading: saving, disabled: saving, onClick: (e) => void handleSubmit(e, setFieldError), children: isEdit ? "Save Changes" : "Create Coupon" })] }) })] })) }));
81
81
  }