@mohasinac/appkit 4.59.0 → 4.60.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 (531) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +62 -5
  3. package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +2 -2
  4. package/dist/_internal/server/features/brands/actions.js +78 -4
  5. package/dist/_internal/server/features/checkout/actions.d.ts +36 -13
  6. package/dist/_internal/server/features/checkout/actions.js +668 -183
  7. package/dist/_internal/server/features/checkout/supplier-identity.js +18 -1
  8. package/dist/_internal/server/features/gateway-events/index.d.ts +10 -0
  9. package/dist/_internal/server/features/gateway-events/index.js +10 -0
  10. package/dist/_internal/server/features/gateway-events/ingest.d.ts +127 -0
  11. package/dist/_internal/server/features/gateway-events/ingest.js +188 -0
  12. package/dist/_internal/server/features/gateway-events/redact.d.ts +66 -0
  13. package/dist/_internal/server/features/gateway-events/redact.js +151 -0
  14. package/dist/_internal/server/features/grievance/actions.d.ts +63 -0
  15. package/dist/_internal/server/features/grievance/actions.js +173 -0
  16. package/dist/_internal/server/features/grievance/index.d.ts +8 -0
  17. package/dist/_internal/server/features/grievance/index.js +8 -0
  18. package/dist/_internal/server/features/grouped/data.js +28 -3
  19. package/dist/_internal/server/features/lottery/actions.js +99 -4
  20. package/dist/_internal/server/features/order-completion/actions.d.ts +92 -0
  21. package/dist/_internal/server/features/order-completion/actions.js +336 -0
  22. package/dist/_internal/server/features/order-completion/index.d.ts +7 -0
  23. package/dist/_internal/server/features/order-completion/index.js +7 -0
  24. package/dist/_internal/server/features/orders/actions.d.ts +13 -0
  25. package/dist/_internal/server/features/orders/actions.js +56 -2
  26. package/dist/_internal/server/features/orders/adapters.js +26 -0
  27. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
  28. package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
  29. package/dist/_internal/server/features/orders/index.d.ts +2 -1
  30. package/dist/_internal/server/features/orders/index.js +1 -1
  31. package/dist/_internal/server/features/orders/service.d.ts +60 -0
  32. package/dist/_internal/server/features/orders/service.js +74 -0
  33. package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
  34. package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
  35. package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
  36. package/dist/_internal/server/features/payment-cases/index.js +9 -0
  37. package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
  38. package/dist/_internal/server/features/payouts/actions.js +84 -10
  39. package/dist/_internal/server/features/points/adjust.d.ts +61 -0
  40. package/dist/_internal/server/features/points/adjust.js +121 -0
  41. package/dist/_internal/server/features/points/award.d.ts +65 -0
  42. package/dist/_internal/server/features/points/award.js +145 -0
  43. package/dist/_internal/server/features/price-index/market.d.ts +33 -0
  44. package/dist/_internal/server/features/price-index/market.js +54 -1
  45. package/dist/_internal/server/features/products/data.js +40 -22
  46. package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
  47. package/dist/_internal/server/features/protection-claims/actions.js +228 -0
  48. package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
  49. package/dist/_internal/server/features/protection-claims/index.js +8 -0
  50. package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
  51. package/dist/_internal/server/features/refunds/actions.js +259 -17
  52. package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
  53. package/dist/_internal/server/features/refunds/capacity.js +37 -0
  54. package/dist/_internal/server/features/reviews/actions.js +3 -2
  55. package/dist/_internal/server/features/search/index-build/sources.js +20 -1
  56. package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
  57. package/dist/_internal/server/features/shipping/reconcile.js +34 -3
  58. package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
  59. package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
  60. package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
  61. package/dist/_internal/server/features/stores/tabs.js +24 -4
  62. package/dist/_internal/server/functions/https.d.ts +31 -1
  63. package/dist/_internal/server/functions/https.js +52 -1
  64. package/dist/_internal/server/functions/scheduled.d.ts +72 -1
  65. package/dist/_internal/server/functions/scheduled.js +124 -1
  66. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
  67. package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
  68. package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
  69. package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
  70. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
  71. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
  72. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  73. package/dist/_internal/server/jobs/core/index.js +2 -0
  74. package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
  75. package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
  76. package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
  77. package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
  78. package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
  79. package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
  80. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
  81. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
  82. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
  83. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
  84. package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
  85. package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
  86. package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
  87. package/dist/_internal/server/jobs/handlers/index.js +5 -0
  88. package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
  89. package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
  90. package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
  91. package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
  92. package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
  93. package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
  94. package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
  95. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  96. package/dist/_internal/shared/actions/action-registry.js +71 -0
  97. package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
  98. package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
  99. package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
  100. package/dist/_internal/shared/checkout/lanes.js +46 -0
  101. package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
  102. package/dist/_internal/shared/checkout/order-math.js +251 -3
  103. package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
  104. package/dist/_internal/shared/checkout/refundable-base.js +122 -0
  105. package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
  106. package/dist/_internal/shared/features/auctions/config.js +30 -1
  107. package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
  108. package/dist/_internal/shared/features/brands/schema.js +18 -0
  109. package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
  110. package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
  111. package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
  112. package/dist/_internal/shared/features/print/label-profiles.js +350 -0
  113. package/dist/_internal/shared/features/products/to-product-item.js +33 -5
  114. package/dist/_internal/shared/fees/calculator.d.ts +31 -2
  115. package/dist/_internal/shared/fees/calculator.js +103 -5
  116. package/dist/_internal/shared/fees/india-states.d.ts +195 -0
  117. package/dist/_internal/shared/fees/india-states.js +190 -0
  118. package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
  119. package/dist/_internal/shared/fees/payout-hold.js +74 -0
  120. package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
  121. package/dist/_internal/shared/obligations/deadlines.js +155 -0
  122. package/dist/_internal/shared/shipping/quote.d.ts +96 -0
  123. package/dist/_internal/shared/shipping/quote.js +67 -0
  124. package/dist/_internal/shared/time/business-days.d.ts +91 -0
  125. package/dist/_internal/shared/time/business-days.js +185 -0
  126. package/dist/cli/index.js +0 -2
  127. package/dist/client.d.ts +9 -4
  128. package/dist/client.js +26 -3
  129. package/dist/configs/tailwind.d.ts +0 -12
  130. package/dist/configs/tailwind.js +28 -15
  131. package/dist/constants/api-endpoints.d.ts +51 -9
  132. package/dist/constants/api-endpoints.js +23 -7
  133. package/dist/constants/index.d.ts +1 -1
  134. package/dist/constants/index.js +1 -1
  135. package/dist/contracts/index.d.ts +1 -1
  136. package/dist/contracts/payment.d.ts +131 -0
  137. package/dist/features/about/components/AboutView.js +1 -1
  138. package/dist/features/about/components/DeveloperView.js +5 -2
  139. package/dist/features/about/components/FAQPageView.js +2 -1
  140. package/dist/features/about/components/FeesView.js +2 -1
  141. package/dist/features/about/components/HelpPageView.js +2 -1
  142. package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
  143. package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
  144. package/dist/features/about/components/HowEmiWorksView.js +2 -1
  145. package/dist/features/about/components/HowOffersWorkView.js +2 -1
  146. package/dist/features/about/components/HowOrdersWorkView.js +2 -1
  147. package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
  148. package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
  149. package/dist/features/about/components/HowReviewsWorkView.js +2 -1
  150. package/dist/features/about/components/PolicyPageView.js +2 -1
  151. package/dist/features/about/components/ProseHero.d.ts +73 -0
  152. package/dist/features/about/components/ProseHero.js +18 -0
  153. package/dist/features/about/components/SecurityPrivacyView.js +2 -1
  154. package/dist/features/about/components/ShippingPolicyView.js +2 -1
  155. package/dist/features/about/components/TrackOrderView.js +2 -1
  156. package/dist/features/about/components/UnauthorizedView.js +1 -1
  157. package/dist/features/about/index.d.ts +2 -0
  158. package/dist/features/about/index.js +5 -0
  159. package/dist/features/account/actions/address-actions.d.ts +26 -2
  160. package/dist/features/account/actions/address-actions.js +25 -1
  161. package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
  162. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  163. package/dist/features/account/components/UserOrdersView.js +17 -0
  164. package/dist/features/account/components/UserSidebar.js +1 -1
  165. package/dist/features/account/hooks/useNotifications.js +29 -0
  166. package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
  167. package/dist/features/admin/components/AdminBeysView.js +2 -2
  168. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  169. package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
  170. package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
  171. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  172. package/dist/features/admin/components/AdminCategoriesView.js +1 -1
  173. package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
  174. package/dist/features/admin/components/AdminContactEditorView.js +1 -1
  175. package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
  176. package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
  177. package/dist/features/admin/components/AdminFaqsView.js +2 -2
  178. package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
  179. package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
  180. package/dist/features/admin/components/AdminGuideHubView.js +1 -1
  181. package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
  182. package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
  183. package/dist/features/admin/components/AdminPartsView.js +2 -2
  184. package/dist/features/admin/components/AdminProductsView.js +75 -4
  185. package/dist/features/admin/components/AdminSidebar.js +2 -2
  186. package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
  187. package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
  188. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  189. package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
  190. package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
  191. package/dist/features/admin/components/AdminUsersView.js +72 -1
  192. package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
  193. package/dist/features/admin/components/AdminViewCards.js +4 -4
  194. package/dist/features/admin/components/DataListingView.d.ts +28 -0
  195. package/dist/features/admin/components/DataListingView.js +175 -14
  196. package/dist/features/admin/components/DataTable.d.ts +12 -1
  197. package/dist/features/admin/components/DataTable.js +3 -3
  198. package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
  199. package/dist/features/admin/components/OwnerQuickActions.js +60 -2
  200. package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
  201. package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
  202. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
  203. package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
  204. package/dist/features/admin/components/index.d.ts +4 -0
  205. package/dist/features/admin/components/index.js +6 -0
  206. package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
  207. package/dist/features/admin/constants/filter-tabs.js +106 -0
  208. package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
  209. package/dist/features/admin/repository/site-settings.repository.js +82 -1
  210. package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
  211. package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
  212. package/dist/features/admin/schemas/firestore.js +5 -1
  213. package/dist/features/admin/schemas/site-settings-form.js +30 -1
  214. package/dist/features/auctions/actions/bid-actions.js +13 -1
  215. package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
  216. package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
  217. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
  218. package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
  219. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
  220. package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
  221. package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
  222. package/dist/features/audit-log/schemas/firestore.js +14 -0
  223. package/dist/features/auth/permissions/constants.d.ts +17 -1
  224. package/dist/features/auth/permissions/constants.js +18 -0
  225. package/dist/features/blog/components/BlogPostView.js +1 -1
  226. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
  227. package/dist/features/categories/api/[id]/description-template/route.js +107 -1
  228. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  229. package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
  230. package/dist/features/categories/components/BundleDetailView.js +1 -1
  231. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  232. package/dist/features/categories/components/CategoryForm.js +12 -1
  233. package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
  234. package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
  235. package/dist/features/categories/repository/market-price-index.repository.js +105 -0
  236. package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
  237. package/dist/features/categories/repository/price-observation.repository.js +132 -0
  238. package/dist/features/events/components/EventSaleBanner.js +1 -1
  239. package/dist/features/events/components/EventStatusBadge.js +1 -1
  240. package/dist/features/faq/components/FAQAccordion.js +2 -1
  241. package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
  242. package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
  243. package/dist/features/faq/components/RelatedFAQs.js +1 -1
  244. package/dist/features/filters/FilterFacetSection.js +1 -1
  245. package/dist/features/filters/SwitchFilter.js +1 -1
  246. package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
  247. package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
  248. package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
  249. package/dist/features/gateway-events/schemas/firestore.js +125 -0
  250. package/dist/features/gateway-events/schemas/index.d.ts +81 -0
  251. package/dist/features/gateway-events/schemas/index.js +79 -0
  252. package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
  253. package/dist/features/homepage/components/AnnouncementBar.js +37 -1
  254. package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
  255. package/dist/features/homepage/components/BrandsSection.js +1 -1
  256. package/dist/features/homepage/components/CTABannerSection.js +1 -1
  257. package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
  258. package/dist/features/homepage/components/HeroCarousel.js +2 -2
  259. package/dist/features/homepage/components/SocialPostCard.js +1 -1
  260. package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
  261. package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
  262. package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
  263. package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
  264. package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
  265. package/dist/features/inventory/components/ReceiveStationView.js +413 -0
  266. package/dist/features/inventory/components/operator-message.d.ts +29 -0
  267. package/dist/features/inventory/components/operator-message.js +41 -0
  268. package/dist/features/inventory/index.d.ts +13 -0
  269. package/dist/features/inventory/index.js +28 -0
  270. package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
  271. package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
  272. package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
  273. package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
  274. package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
  275. package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
  276. package/dist/features/inventory/schemas/firestore.d.ts +197 -0
  277. package/dist/features/inventory/schemas/firestore.js +159 -0
  278. package/dist/features/layout/AppLayoutShell.d.ts +12 -1
  279. package/dist/features/layout/AppLayoutShell.js +25 -12
  280. package/dist/features/layout/AutoBreadcrumbs.js +162 -3
  281. package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
  282. package/dist/features/layout/BottomNavbar.d.ts +1 -1
  283. package/dist/features/layout/BottomNavbar.js +2 -2
  284. package/dist/features/media/MediaAvatar.js +1 -1
  285. package/dist/features/media/MediaImage.d.ts +24 -4
  286. package/dist/features/media/MediaImage.js +44 -4
  287. package/dist/features/media/MediaLightbox.js +1 -1
  288. package/dist/features/media/modals/ImageCropModal.js +1 -1
  289. package/dist/features/media/modals/ImageEditor.js +1 -1
  290. package/dist/features/media/upload/MediaUploadField.js +91 -3
  291. package/dist/features/obligations/constants/policies.d.ts +92 -0
  292. package/dist/features/obligations/constants/policies.js +378 -0
  293. package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
  294. package/dist/features/obligations/repository/obligations.repository.js +158 -0
  295. package/dist/features/obligations/schemas/firestore.d.ts +160 -0
  296. package/dist/features/obligations/schemas/firestore.js +87 -0
  297. package/dist/features/orders/actions/order-actions.d.ts +43 -4
  298. package/dist/features/orders/actions/order-actions.js +60 -3
  299. package/dist/features/orders/actions/refund-actions.js +70 -10
  300. package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
  301. package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
  302. package/dist/features/orders/components/OrdersList.js +2 -2
  303. package/dist/features/orders/components/RefundHistoryTable.js +3 -1
  304. package/dist/features/orders/components/index.d.ts +2 -0
  305. package/dist/features/orders/components/index.js +6 -0
  306. package/dist/features/orders/constants/transitions.d.ts +59 -0
  307. package/dist/features/orders/constants/transitions.js +104 -0
  308. package/dist/features/orders/repository/orders.repository.d.ts +109 -6
  309. package/dist/features/orders/repository/orders.repository.js +464 -21
  310. package/dist/features/orders/schemas/firestore.d.ts +270 -0
  311. package/dist/features/orders/schemas/firestore.js +24 -0
  312. package/dist/features/orders/schemas/index.d.ts +61 -0
  313. package/dist/features/orders/schemas/index.js +22 -0
  314. package/dist/features/orders/types/index.d.ts +79 -0
  315. package/dist/features/parts/components/PartsIndexPageView.js +8 -1
  316. package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
  317. package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
  318. package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
  319. package/dist/features/payment-cases/schemas/firestore.js +105 -0
  320. package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
  321. package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
  322. package/dist/features/payments/repository/payout.repository.d.ts +0 -1
  323. package/dist/features/payments/repository/payout.repository.js +0 -1
  324. package/dist/features/points/repository/points.repository.d.ts +122 -0
  325. package/dist/features/points/repository/points.repository.js +304 -1
  326. package/dist/features/points/schemas/adjust-form.d.ts +58 -0
  327. package/dist/features/points/schemas/adjust-form.js +63 -0
  328. package/dist/features/points/schemas/firestore.d.ts +108 -1
  329. package/dist/features/points/schemas/firestore.js +84 -0
  330. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
  331. package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
  332. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
  333. package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
  334. package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
  335. package/dist/features/price-index/market-stats.d.ts +82 -0
  336. package/dist/features/price-index/market-stats.js +230 -0
  337. package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
  338. package/dist/features/print/components/BoxLabelPrintView.js +66 -0
  339. package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
  340. package/dist/features/print/components/LabelProfilePicker.js +38 -0
  341. package/dist/features/print/components/labels.d.ts +117 -0
  342. package/dist/features/print/components/labels.js +258 -0
  343. package/dist/features/print/components/labels.style.css +46 -0
  344. package/dist/features/print/index.d.ts +6 -0
  345. package/dist/features/print/index.js +16 -0
  346. package/dist/features/products/columns/productTableColumns.js +5 -4
  347. package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
  348. package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
  349. package/dist/features/products/components/PrizeDrawCollage.js +1 -1
  350. package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
  351. package/dist/features/products/components/PrizeRevealModal.js +2 -2
  352. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  353. package/dist/features/products/components/ProductForm.js +34 -23
  354. package/dist/features/products/components/ProductGrid.js +86 -62
  355. package/dist/features/products/constants/card-badges.d.ts +97 -0
  356. package/dist/features/products/constants/card-badges.js +100 -0
  357. package/dist/features/products/repository/products.repository.js +34 -1
  358. package/dist/features/products/schemas/firestore.d.ts +46 -3
  359. package/dist/features/products/schemas/firestore.js +21 -0
  360. package/dist/features/promotions/components/PromotionsView.js +1 -1
  361. package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
  362. package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
  363. package/dist/features/promotions/schemas/firestore.js +30 -3
  364. package/dist/features/promotions/schemas/index.d.ts +6 -6
  365. package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
  366. package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
  367. package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
  368. package/dist/features/protection-claims/schemas/firestore.js +130 -0
  369. package/dist/features/reviews/actions/review-actions.js +3 -2
  370. package/dist/features/reviews/components/ReviewModal.js +1 -1
  371. package/dist/features/reviews/components/ReviewsList.js +1 -1
  372. package/dist/features/reviews/schemas/firestore.d.ts +20 -1
  373. package/dist/features/scams/constants/scam-types.d.ts +2 -0
  374. package/dist/features/scams/constants/scam-types.js +18 -0
  375. package/dist/features/scams/schemas/report-form.d.ts +3 -3
  376. package/dist/features/scams/schemas/report-form.js +26 -1
  377. package/dist/features/search/components/Search.js +9 -9
  378. package/dist/features/search/components/SearchResultsClient.js +18 -3
  379. package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
  380. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  381. package/dist/features/seller/actions/offer-actions.js +16 -1
  382. package/dist/features/seller/actions/seller-actions.js +14 -3
  383. package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
  384. package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
  385. package/dist/features/seller/components/PrintCenterView.js +41 -30
  386. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  387. package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
  388. package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
  389. package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
  390. package/dist/features/seller/components/SellerProductShell.js +123 -13
  391. package/dist/features/seller/components/SellerReviewsView.js +4 -2
  392. package/dist/features/seller/components/SellerSidebar.js +1 -1
  393. package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
  394. package/dist/features/seller/schemas/coupon-form.js +27 -0
  395. package/dist/features/seller/schemas/product-form.d.ts +3 -0
  396. package/dist/features/seller/schemas/product-form.js +5 -0
  397. package/dist/features/shell/FormShell.js +2 -2
  398. package/dist/features/shell/QuickFormDrawer.js +14 -0
  399. package/dist/features/shell/SectionForm.d.ts +19 -0
  400. package/dist/features/shell/SectionForm.js +133 -9
  401. package/dist/features/shell/SectionForm.style.css +54 -0
  402. package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
  403. package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
  404. package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
  405. package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
  406. package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
  407. package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
  408. package/dist/features/stores/components/StoreGuideHubView.js +1 -1
  409. package/dist/features/stores/components/StoreHeader.js +1 -1
  410. package/dist/features/support/schemas/firestore.d.ts +73 -2
  411. package/dist/features/support/schemas/firestore.js +59 -0
  412. package/dist/index.d.ts +16 -24
  413. package/dist/index.js +38 -47
  414. package/dist/next/ErrorBoundary.js +1 -1
  415. package/dist/next/routing/route-map.d.ts +24 -0
  416. package/dist/next/routing/route-map.js +40 -0
  417. package/dist/providers/payment-manual/index.d.ts +15 -1
  418. package/dist/providers/payment-manual/index.js +37 -0
  419. package/dist/providers/payment-phonepe/index.d.ts +14 -1
  420. package/dist/providers/payment-phonepe/index.js +36 -0
  421. package/dist/providers/payment-razorpay/index.d.ts +44 -1
  422. package/dist/providers/payment-razorpay/index.js +99 -0
  423. package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
  424. package/dist/providers/shipping-shiprocket/index.js +18 -0
  425. package/dist/schemas/registry.d.ts +44 -11
  426. package/dist/server-entry.d.ts +2 -0
  427. package/dist/server-entry.js +27 -0
  428. package/dist/server.d.ts +42 -0
  429. package/dist/server.js +93 -0
  430. package/dist/styles.css +418 -149
  431. package/dist/tailwind-utilities.css +1 -1
  432. package/dist/tokens/themes/default-dark.js +15 -12
  433. package/dist/tokens/themes/default-light.js +14 -11
  434. package/dist/tokens/tokens.css +106 -23
  435. package/dist/ui/DataTable.style.css +4 -4
  436. package/dist/ui/columns/cell-renderers.d.ts +20 -0
  437. package/dist/ui/columns/cell-renderers.js +26 -1
  438. package/dist/ui/columns/index.d.ts +1 -1
  439. package/dist/ui/columns/index.js +4 -1
  440. package/dist/ui/components/Badge.style.css +1 -1
  441. package/dist/ui/components/Breadcrumb.js +19 -1
  442. package/dist/ui/components/BulkActionBar.style.css +4 -4
  443. package/dist/ui/components/Button.style.css +1 -1
  444. package/dist/ui/components/Checkbox.style.css +1 -1
  445. package/dist/ui/components/FallbackShell.js +1 -1
  446. package/dist/ui/components/Form.d.ts +14 -1
  447. package/dist/ui/components/Form.js +2 -2
  448. package/dist/ui/components/FormField.js +18 -4
  449. package/dist/ui/components/FormField.style.css +43 -3
  450. package/dist/ui/components/GatedPrice.style.css +1 -1
  451. package/dist/ui/components/IconButton.style.css +1 -1
  452. package/dist/ui/components/Input.js +22 -1
  453. package/dist/ui/components/Input.style.css +38 -22
  454. package/dist/ui/components/ListingFilterDrawer.js +1 -1
  455. package/dist/ui/components/ListingToolbar.js +1 -1
  456. package/dist/ui/components/PaginatedSelect.js +0 -0
  457. package/dist/ui/components/Radio.style.css +1 -1
  458. package/dist/ui/components/Repeater.d.ts +53 -0
  459. package/dist/ui/components/Repeater.js +7 -0
  460. package/dist/ui/components/RichTextEditor.d.ts +20 -1
  461. package/dist/ui/components/RichTextEditor.js +23 -2
  462. package/dist/ui/components/Select.style.css +7 -21
  463. package/dist/ui/components/Semantic.js +27 -3
  464. package/dist/ui/components/TagInput.style.css +4 -9
  465. package/dist/ui/components/Textarea.style.css +4 -18
  466. package/dist/ui/components/Toggle.style.css +1 -1
  467. package/dist/ui/components/Typography.d.ts +3 -3
  468. package/dist/ui/components/Typography.js +15 -15
  469. package/dist/ui/components/ViewToggle.style.css +4 -4
  470. package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
  471. package/dist/ui/forms/AddAnotherToggle.js +56 -0
  472. package/dist/ui/forms/ColorPickerField.js +15 -2
  473. package/dist/ui/forms/FieldCheckbox.js +15 -2
  474. package/dist/ui/forms/FieldInput.d.ts +22 -1
  475. package/dist/ui/forms/FieldInput.js +19 -5
  476. package/dist/ui/forms/FieldLabel.d.ts +35 -0
  477. package/dist/ui/forms/FieldLabel.js +70 -0
  478. package/dist/ui/forms/FieldSelect.d.ts +4 -1
  479. package/dist/ui/forms/FieldSelect.js +27 -5
  480. package/dist/ui/forms/FieldTextarea.d.ts +4 -1
  481. package/dist/ui/forms/FieldTextarea.js +22 -5
  482. package/dist/ui/forms/FormShell.d.ts +17 -1
  483. package/dist/ui/forms/FormShell.js +36 -2
  484. package/dist/ui/forms/FormShell.style.css +41 -33
  485. package/dist/ui/forms/index.d.ts +5 -1
  486. package/dist/ui/forms/index.js +2 -0
  487. package/dist/ui/icons/icon-registry.d.ts +1 -0
  488. package/dist/ui/icons/icon-registry.js +17 -1
  489. package/dist/utils/id-generators.d.ts +80 -0
  490. package/dist/utils/id-generators.js +93 -0
  491. package/dist/utils/search-txt-builders.d.ts +12 -0
  492. package/dist/utils/search-txt-builders.js +17 -0
  493. package/package.json +1 -1
  494. package/scripts/seed-cli.mjs +106 -5
  495. package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
  496. package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
  497. package/dist/features/loyalty/api/balance/route.d.ts +0 -12
  498. package/dist/features/loyalty/api/balance/route.js +0 -41
  499. package/dist/features/loyalty/api/earn/route.d.ts +0 -12
  500. package/dist/features/loyalty/api/earn/route.js +0 -38
  501. package/dist/features/loyalty/api/history/route.d.ts +0 -12
  502. package/dist/features/loyalty/api/history/route.js +0 -58
  503. package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
  504. package/dist/features/loyalty/api/redeem/route.js +0 -38
  505. package/dist/features/loyalty/columns/index.d.ts +0 -17
  506. package/dist/features/loyalty/columns/index.js +0 -40
  507. package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
  508. package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
  509. package/dist/features/loyalty/components/index.d.ts +0 -1
  510. package/dist/features/loyalty/components/index.js +0 -1
  511. package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
  512. package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
  513. package/dist/features/loyalty/index.d.ts +0 -6
  514. package/dist/features/loyalty/index.js +0 -6
  515. package/dist/features/loyalty/manifest.d.ts +0 -2
  516. package/dist/features/loyalty/manifest.js +0 -16
  517. package/dist/features/loyalty/messages/en.json +0 -19
  518. package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
  519. package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
  520. package/dist/features/loyalty/schemas/index.d.ts +0 -85
  521. package/dist/features/loyalty/schemas/index.js +0 -40
  522. package/dist/features/loyalty/server.d.ts +0 -6
  523. package/dist/features/loyalty/server.js +0 -6
  524. package/dist/features/loyalty/types/index.d.ts +0 -31
  525. package/dist/features/loyalty/types/index.js +0 -1
  526. package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
  527. package/dist/features/loyalty/types/loyalty-config.js +0 -1
  528. package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
  529. package/dist/features/loyalty/types/loyalty-math.js +0 -27
  530. package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
  531. package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
@@ -17,6 +17,53 @@ import { cacheManager } from "../../../core/CacheManager.js";
17
17
  import { SITE_SETTINGS_COLLECTION, DEFAULT_SITE_SETTINGS_DATA, } from "../schemas/index.js";
18
18
  import { DatabaseError } from "../../../errors/index.js";
19
19
  import { decryptSecret, encryptSecret, maskSecret } from "../../../security/index.js";
20
+ /**
21
+ * Flatten a nested update into Firestore dotted field paths, so a partial
22
+ * group update MERGES into the stored map instead of replacing it.
23
+ *
24
+ * `update({ commissions: { gstPercent: 18 } })` deletes every other key under
25
+ * `commissions`; `update({ "commissions.gstPercent": 18 })` touches one leaf.
26
+ * See the call site for the five keys that were silently destroyed by the
27
+ * former (D13).
28
+ *
29
+ * Three cases are deliberately NOT recursed into:
30
+ *
31
+ * · **Arrays** — an array is a VALUE, not a map. Flattening
32
+ * `legalPages.sections.0.title` would make a shorter replacement array leave
33
+ * the old tail in place, which is a worse bug than the one being fixed.
34
+ * · **`undefined`** — kept at its own path so a caller can still delete a
35
+ * field. Recursing would turn it into a path to nothing.
36
+ * · **`Date` and any other class instance** — Firestore stores a `Date` as a
37
+ * Timestamp; walking its properties would write `{}`. Tested by rejecting
38
+ * anything whose prototype is not `Object.prototype`, which also covers
39
+ * `FieldValue` sentinels and `Timestamp` itself.
40
+ */
41
+ export function toDottedPaths(input, prefix = "") {
42
+ const out = {};
43
+ for (const [key, value] of Object.entries(input)) {
44
+ const path = prefix ? `${prefix}.${key}` : key;
45
+ const isPlainObject = typeof value === "object" &&
46
+ value !== null &&
47
+ !Array.isArray(value) &&
48
+ Object.getPrototypeOf(value) === Object.prototype;
49
+ if (isPlainObject) {
50
+ const nested = toDottedPaths(value, path);
51
+ /*
52
+ * An EMPTY object is written at its own path rather than vanishing.
53
+ * `{ watermark: {} }` is a caller saying "clear this group"; dropping it
54
+ * because it produced no leaves would silently ignore the instruction.
55
+ */
56
+ if (Object.keys(nested).length === 0)
57
+ out[path] = value;
58
+ else
59
+ Object.assign(out, nested);
60
+ }
61
+ else {
62
+ out[path] = value;
63
+ }
64
+ }
65
+ return out;
66
+ }
20
67
  /**
21
68
  * Repository for site settings management
22
69
  *
@@ -113,10 +160,44 @@ export class SiteSettingsRepository extends BaseRepository {
113
160
  ...finalUpdates,
114
161
  updatedAt: new Date(),
115
162
  });
163
+ /*
164
+ * ── D13: DOTTED PATHS, not a wholesale nested-map replace ────────────
165
+ *
166
+ * 🛑 This was `.update(updateData)` with nested objects, and Firestore's
167
+ * `update()` REPLACES a nested map wholesale rather than merging into
168
+ * it. So saving `{ commissions: { platformFeePercent: 2 } }` deleted
169
+ * every other key under `commissions`.
170
+ *
171
+ * That is not hypothetical. `AdminSiteSettingsView`'s commissions
172
+ * payload omitted FIVE keys — `saleProtectionFeeEnabled`,
173
+ * `saleProtectionFeePercent`, `saleProtectionFeeMin`,
174
+ * `codHandlingFeeCap`, `codHandlingFeePercent` — with no state, no field
175
+ * and no control for any of them. So the FIRST save of any commissions
176
+ * field silently:
177
+ *
178
+ * · stopped sale protection being sold, a product we charge 3% (min
179
+ * ₹49) for, with no error and nothing in the UI to notice; and
180
+ * · reverted COD handling to code defaults.
181
+ *
182
+ * It also made CLAUDE.md's stated remediation — "enable them once at
183
+ * Admin → Site Settings → Commissions" — impossible, because there was
184
+ * no control there to enable them with.
185
+ *
186
+ * Flattening to dotted paths is the structural fix: `commissions.gstPercent`
187
+ * touches exactly that leaf. The alternative (adding the five missing
188
+ * fields to the form) fixes TODAY'S instance and leaves the next group
189
+ * that grows a field to hit the same wall — and `legalPages` had already
190
+ * hit it independently (D19), which is the evidence that this recurs.
191
+ *
192
+ * 🛑 ARRAYS AND `undefined` ARE NOT FLATTENED. An array is a value, not a
193
+ * map: flattening `legalPages.sections.0.title` would make a shorter
194
+ * replacement array leave the old tail behind. And `undefined` must stay
195
+ * a top-level delete rather than becoming a dotted path to nothing.
196
+ */
116
197
  await this.db
117
198
  .collection(this.collection)
118
199
  .doc(this.SINGLETON_ID)
119
- .update(updateData);
200
+ .update(toDottedPaths(updateData));
120
201
  const latest = await this.getSingleton();
121
202
  cacheManager.set(this.cacheKey(), latest, {
122
203
  ttl: SiteSettingsRepository.CACHE_TTL_MS,
@@ -92,6 +92,17 @@ export declare const brandFormSchema: z.ZodObject<{
92
92
  website: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
93
93
  country: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
94
94
  founded: z.ZodOptional<z.ZodNumber>;
95
+ highlightsText: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
96
+ faqs: z.ZodOptional<z.ZodArray<z.ZodObject<{
97
+ question: z.ZodString;
98
+ answer: z.ZodString;
99
+ }, "strip", z.ZodTypeAny, {
100
+ question: string;
101
+ answer: string;
102
+ }, {
103
+ question: string;
104
+ answer: string;
105
+ }>, "many">>;
95
106
  isActive: z.ZodBoolean;
96
107
  displayOrder: z.ZodOptional<z.ZodNumber>;
97
108
  }, "strip", z.ZodTypeAny, {
@@ -99,20 +110,30 @@ export declare const brandFormSchema: z.ZodObject<{
99
110
  isActive: boolean;
100
111
  country?: string | undefined;
101
112
  slug?: string | undefined;
113
+ faqs?: {
114
+ question: string;
115
+ answer: string;
116
+ }[] | undefined;
102
117
  description?: string | undefined;
103
118
  displayOrder?: number | undefined;
104
119
  logoURL?: string | undefined;
105
120
  website?: string | undefined;
106
121
  founded?: number | undefined;
122
+ highlightsText?: string | undefined;
107
123
  }, {
108
124
  name: string;
109
125
  isActive: boolean;
110
126
  country?: string | undefined;
111
127
  slug?: string | undefined;
128
+ faqs?: {
129
+ question: string;
130
+ answer: string;
131
+ }[] | undefined;
112
132
  description?: string | undefined;
113
133
  displayOrder?: number | undefined;
114
134
  logoURL?: string | undefined;
115
135
  website?: string | undefined;
116
136
  founded?: number | undefined;
137
+ highlightsText?: string | undefined;
117
138
  }>;
118
139
  export type BrandFormValues = z.infer<typeof brandFormSchema>;
@@ -177,6 +177,50 @@ export const brandFormSchema = z.object({
177
177
  section: "identity", order: 2, row: "pair", label: "Country of origin",
178
178
  }),
179
179
  founded: annotate(z.number().int().min(1800, "That is earlier than any brand on record.").max(new Date().getFullYear(), "A founding year cannot be in the future.").optional(), { section: "identity", order: 3, row: "pair", kind: "number", label: "Founded year" }),
180
+ /*
181
+ * ── Editorial content ────────────────────────────────────────────────────
182
+ *
183
+ * 🛑 BOTH OF THESE HAVE RENDERED ON THE BRAND PAGE SINCE THEY SHIPPED, with
184
+ * no way to author them. `BrandDetailPageView:418` mounts
185
+ * `<CategoryHighlightsAndFaqSection highlights={brand?.highlights}
186
+ * faqs={brand?.faqs} />`, `CategoryDocument` declares both with a comment
187
+ * saying they render on "category and BRAND detail pages", and
188
+ * `CATEGORIES_UPDATABLE_FIELDS` has always permitted the write. The only
189
+ * missing piece was a form — the category editor had one, the brand editor
190
+ * did not, so brand highlights and FAQs were unreachable.
191
+ *
192
+ * `highlightsText` is a newline-separated textarea rather than a repeater,
193
+ * matching the category editor exactly: these are a flat `string[]`, an
194
+ * admin pasting five bullets wants to paste five lines, and order is
195
+ * preserved by the text itself.
196
+ */
197
+ highlightsText: annotate(z.string().optional().or(z.literal("")), {
198
+ section: "content", sectionLabel: "Content", order: 1, row: "full",
199
+ kind: "textarea", label: "Highlights — one per line",
200
+ help: "Short \"why shop this brand\" bullets, shown above the FAQs.",
201
+ }),
202
+ /*
203
+ * 🛑 DECLARED HERE SO THE PARSE COVERS IT, rendered by a custom `renderers`
204
+ * entry in the editor. `buildSectionsFromSchema` has no `kind` for a
205
+ * per-row shape, and inventing one for a single consumer would be the
206
+ * speculative-infra move — `logoURL` already establishes the pattern of a
207
+ * schema field with a bespoke renderer.
208
+ *
209
+ * It lives in the DRAFT rather than its own `useState`, unlike the category
210
+ * editor's three repeaters. Those predate this form being schema-driven;
211
+ * here the draft can hold the array, so validation, dirty-tracking and the
212
+ * error summary all see it, and there is one source of truth instead of two.
213
+ */
214
+ faqs: annotate(z
215
+ .array(z.object({
216
+ question: z.string().min(1, "A question is required").max(300),
217
+ answer: z.string().min(1, "An answer is required").max(2000),
218
+ }))
219
+ .max(8, "At most 8 questions")
220
+ .optional(), {
221
+ section: "content", order: 2, row: "full", label: "Brand FAQs",
222
+ help: "Rendered beneath the highlights on the brand page.",
223
+ }),
180
224
  isActive: annotate(z.boolean(), {
181
225
  section: "visibility", sectionLabel: "Visibility", order: 1, row: "quarter",
182
226
  label: "Active",
@@ -292,7 +292,11 @@ export const DEFAULT_SITE_SETTINGS_DATA = {
292
292
  platformShippingPercent: 10,
293
293
  platformShippingFixedMin: 0,
294
294
  autoPayoutWindowDays: 7,
295
- payoutHoldDays: 2,
295
+ // 7, not 2 — see `PAYOUT_HOLD_FLOOR_DAYS`. A 2-day hold releases the
296
+ // seller's money inside both the 7-day return window and the 7-day
297
+ // sale-protection claim window, so it was never a legal value; it was
298
+ // merely an unread one (D28).
299
+ payoutHoldDays: 7,
296
300
  minPayoutAmount: 100,
297
301
  auctionListingFee: 0,
298
302
  preOrderListingFee: 0,
@@ -41,6 +41,13 @@
41
41
  * @tag sideEffects:none
42
42
  */
43
43
  import { z } from "zod";
44
+ /*
45
+ * The floor lives with the payout-hold resolver that enforces it at read
46
+ * time, so the validation message and the honoured behaviour cannot drift —
47
+ * two copies of "7" is how `payoutHoldDays: 2` coexisted with a hardcoded 7
48
+ * for months (D28).
49
+ */
50
+ import { PAYOUT_HOLD_FLOOR_DAYS } from "../../../_internal/shared/fees/payout-hold.js";
44
51
  /** A percentage a human types into a settings box. */
45
52
  const percent = (label) => z.coerce
46
53
  .number({ invalid_type_error: `${label} must be a number.` })
@@ -102,7 +109,29 @@ export const siteSettingsFormSchema = z
102
109
  gatewayFeePercent: percent("Gateway fee"),
103
110
  platformFeeMax: money("Platform fee cap"),
104
111
  minPayoutAmount: money("Minimum payout"),
105
- payoutHoldDays: z.coerce.number().int().min(0).max(365).optional(),
112
+ /*
113
+ * 🛑 `.min(PAYOUT_HOLD_FLOOR_DAYS)`, not `.min(0)` — and the floor is
114
+ * not arbitrary.
115
+ *
116
+ * The hold must outlast every window during which money can still
117
+ * flow back OUT of the order: the 7-day return window
118
+ * (`returns.windowDays`) and the 7-day sale-protection claim window
119
+ * (`PROTECTION_CLAIM_WINDOW_HOURS.sale` = 168h). Allowing 0 let an
120
+ * admin release a seller's money before the buyer could even claim
121
+ * against protection WE had already sold and would have to fund —
122
+ * and the stored value was seeded **2**, i.e. inside that window.
123
+ *
124
+ * `resolvePayoutHoldDays` already clamps up to the floor at read
125
+ * time, so behaviour is correct either way. This exists so the admin
126
+ * is TOLD, at the moment they try it, instead of setting 2 and
127
+ * believing it took effect.
128
+ */
129
+ payoutHoldDays: z.coerce
130
+ .number()
131
+ .int()
132
+ .min(PAYOUT_HOLD_FLOOR_DAYS, `Payout hold must be at least ${PAYOUT_HOLD_FLOOR_DAYS} days — it has to outlast the return window and the protection claim window, or a seller is paid before a buyer can claim.`)
133
+ .max(365)
134
+ .optional(),
106
135
  codHandlingFee: money("COD handling fee"),
107
136
  codDepositPercent: percent("COD deposit"),
108
137
  })
@@ -312,7 +312,19 @@ export async function buyNowAuction(userId, userName, userEmail, input) {
312
312
  // Replaces the old `bidsHaveStarted` gate. Buy Now used to vanish the instant
313
313
  // anybody bid, eBay-style, which hid it on almost every real auction. What
314
314
  // actually matters is whether it is still a better deal than the bidding.
315
- if (!isBuyNowAvailable({ buyNowPrice, currentBid: standingBid, isEnded: false, isSold: product.isSold })) {
315
+ /*
316
+ * 🛑 `reservePrice` is passed HERE above all: this is the server-side money
317
+ * gate for a buyout. Omitting it is what let a Buy-Now below the seller's
318
+ * floor complete (D53) — the reserve was enforced only in
319
+ * `auctionSettlement`, i.e. only on the competitive path.
320
+ */
321
+ if (!isBuyNowAvailable({
322
+ buyNowPrice,
323
+ currentBid: standingBid,
324
+ isEnded: false,
325
+ isSold: product.isSold,
326
+ reservePrice: product.reservePrice,
327
+ })) {
316
328
  throw new ValidationError(ERROR_MESSAGES.BID.BUY_NOW_UNAVAILABLE, {
317
329
  code: BID_ERROR_CODES.BUY_NOW_UNAVAILABLE,
318
330
  });
@@ -39,7 +39,7 @@ function formatCountdownLabel(remaining) {
39
39
  * button can report an error or route to `/checkout?lane=auction`.
40
40
  */
41
41
  export function AuctionBottomActions(props) {
42
- const { currentBid, currency, bidCount, isEnded: ssrIsEnded, auctionEndDate, buyNowPrice } = props;
42
+ const { currentBid, currency, bidCount, isEnded: ssrIsEnded, auctionEndDate, buyNowPrice, reservePrice } = props;
43
43
  /*
44
44
  * Derived client-side — see `useIsAuctionEnded`. This bar is a FIXED, always-
45
45
  * visible row on mobile, so a stale "Place bid" here is the most prominent
@@ -55,7 +55,7 @@ export function AuctionBottomActions(props) {
55
55
  const buyNowMeta = ACTION_META[ACTION_ID.BUY_NOW_AUCTION];
56
56
  // Same single predicate the form and the card use. `!!props.onBuyNow` matters
57
57
  // because the preview/demo mount passes no action.
58
- const buyNowAvailable = isBuyNowAvailable({ buyNowPrice, currentBid, isEnded }) && !!props.onBuyNow;
58
+ const buyNowAvailable = isBuyNowAvailable({ buyNowPrice, currentBid, isEnded, reservePrice }) && !!props.onBuyNow;
59
59
  useBottomActions(isEnded
60
60
  ? {}
61
61
  : {
@@ -52,7 +52,7 @@ function toDescriptionHtml(raw) {
52
52
  }
53
53
  function renderAuctionInfoPanel(props) {
54
54
  const { productId, storeId, title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, buyNowAvailable, featured, freeShipping, condition, finalSale, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref } = props;
55
- return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_LIVE_BADGE, children: "\uD83C\uDFF7\uFE0F Live Auction" }), _jsx(AuctionLiveStatusBadge, { auctionEndDate: endDate, initialIsEnded: isEnded })] }), _jsx(OwnerQuickActions, { resource: "PRODUCT", placement: "detail", recordLabel: title, storeId: storeId, editHref: String(ROUTES.STORE.PRODUCTS_EDIT(productId)) }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title })] }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-0.5", color: "muted", size: "xs", children: "Current bid" }), _jsx(LiveBidPrice, { productId: productId, currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "2xl", badgeSize: "sm" }), endDate && !isEnded && (_jsxs(Text, { className: "mt-1.5", color: "muted", size: "sm", children: ["Ends in ", _jsx(Span, { weight: "medium", color: "muted", children: _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" }) })] })), endDate && _jsxs(Text, { className: "mt-0.5", color: "faint", size: "xs", children: [isEnded ? "Ended" : "Ends", " ", endDate.toLocaleString()] })] }), buyNowAvailable && buyNowPrice !== null && (_jsxs(Row, { align: "center", gap: "sm", className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)] bg-primary-50 dark:bg-primary-900/20", padding: "inlineSm", rounded: "lg", children: [_jsx(Span, { size: "xs", color: "muted", children: "Buy Now:" }), _jsx(Span, { size: "base", weight: "bold", className: "text-primary-700 dark:text-primary-300", children: _jsx(GatedPrice, { label: "Sign in to see the Buy Now price", children: formatCurrency(buyNowPrice, currency) }) }), _jsx(Span, { size: "xs", color: "faint", children: "\u2014 in \u201CPlace a bid\u201D" })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, finalSale: finalSale, labels: { featured: "Featured", fasterDelivery: "Faster Delivery", ratedSeller: "Rated Seller", condition: "Condition", conditionNew: "New", conditionUsed: "Used", conditionBroken: "For Parts", conditionRefurbished: "Refurbished", returnable: "Returnable", finalSale: "Final Sale", freeShipping: "Free Shipping", codAvailable: "Cash on Delivery", emiAvailable: "EMI Available", wishlistCount: (n) => `${n} wishlisted`, categoryProductCount: (n, cat) => `${n} in ${cat}` } }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-surface hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:text-primary-400", children: categoryName || category }), !category && categoryName && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: categoryName }), brand && brandSlug && _jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-surface hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:text-primary-400", children: brand }), brand && !brandSlug && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand })] })), productFeatures && features.length > 0 && _jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures }), !productFeatures && features.length > 0 && (_jsxs(Div, { border: "subtle", surface: "muted", padding: "inline", rounded: "xl", children: [_jsx(Text, { className: "mb-2 tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "About this item" }), _jsx(Ul, { spacing: "comfortable", size: "sm", color: "primary", children: features.map((f, i) => _jsxs(Li, { layout: "flex-start", gap: "2", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i)) })] })), descriptionHtml && _jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" }), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Listed by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && _jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" })] }) }))] }));
55
+ return (_jsxs(Stack, { gap: "md", children: [_jsxs(Div, { children: [_jsxs(Row, { gap: "xs", wrap: true, className: "mb-2", children: [_jsx(Span, { size: "xs", weight: "semibold", className: CLS_LIVE_BADGE, children: "\uD83C\uDFF7\uFE0F Live Auction" }), _jsx(AuctionLiveStatusBadge, { auctionEndDate: endDate, initialIsEnded: isEnded })] }), _jsx(Heading, { level: 1, className: "leading-snug", smSize: "2xl", color: "primary", size: "xl", weight: "bold", children: title }), _jsx(OwnerQuickActions, { resource: "PRODUCT", placement: "detail", recordLabel: title, storeId: storeId, editHref: String(ROUTES.STORE.PRODUCTS_EDIT(productId)) })] }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-0.5", color: "muted", size: "xs", children: "Current bid" }), _jsx(LiveBidPrice, { productId: productId, currentBid: currentBid, bidCount: bidCount, currency: currency, isEnded: isEnded, priceSize: "2xl", badgeSize: "sm" }), endDate && !isEnded && (_jsxs(Text, { className: "mt-1.5", color: "muted", size: "sm", children: ["Ends in ", _jsx(Span, { weight: "medium", color: "muted", children: _jsx(CountdownDisplay, { targetDate: endDate, format: "auto", expiredLabel: "Ended" }) })] })), endDate && _jsxs(Text, { className: "mt-0.5", color: "faint", size: "xs", children: [isEnded ? "Ended" : "Ends", " ", endDate.toLocaleString()] })] }), buyNowAvailable && buyNowPrice !== null && (_jsxs(Row, { align: "center", gap: "sm", className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)] bg-primary-50 dark:bg-primary-900/20", padding: "inlineSm", rounded: "lg", children: [_jsx(Span, { size: "xs", color: "muted", children: "Buy Now:" }), _jsx(Span, { size: "base", weight: "bold", className: "text-primary-700 dark:text-primary-300", children: _jsx(GatedPrice, { label: "Sign in to see the Buy Now price", children: formatCurrency(buyNowPrice, currency) }) }), _jsx(Span, { size: "xs", color: "faint", children: "\u2014 in \u201CPlace a bid\u201D" })] })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, finalSale: finalSale, labels: { featured: "Featured", fasterDelivery: "Faster Delivery", ratedSeller: "Rated Seller", condition: "Condition", conditionNew: "New", conditionUsed: "Used", conditionBroken: "For Parts", conditionRefurbished: "Refurbished", returnable: "Returnable", finalSale: "Final Sale", freeShipping: "Free Shipping", codAvailable: "Cash on Delivery", emiAvailable: "EMI Available", wishlistCount: (n) => `${n} wishlisted`, categoryProductCount: (n, cat) => `${n} in ${cat}` } }), (categoryName || category || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [category && _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-surface hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:text-primary-400", children: categoryName || category }), !category && categoryName && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: categoryName }), brand && brandSlug && _jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1)] text-[length:var(--appkit-text-xs)] font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-surface hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:text-primary-400", children: brand }), brand && !brandSlug && _jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand })] })), productFeatures && features.length > 0 && _jsx(FeatureBadgeList, { productFeatureIds: features, features: productFeatures }), !productFeatures && features.length > 0 && (_jsxs(Div, { border: "subtle", surface: "muted", padding: "inline", rounded: "xl", children: [_jsx(Text, { className: "mb-2 tracking-wide", color: "muted", size: "xs", weight: "semibold", transform: "uppercase", children: "About this item" }), _jsx(Ul, { spacing: "comfortable", size: "sm", color: "primary", children: features.map((f, i) => _jsxs(Li, { layout: "flex-start", gap: "2", children: [_jsx(Span, { className: "mt-0.5 flex-shrink-0 text-primary-500", children: "\u2022" }), f] }, i)) })] })), descriptionHtml && _jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "text-[length:var(--appkit-text-sm)] leading-relaxed text-[var(--appkit-color-text-muted)] line-clamp-4" }), safeSeller && (_jsx(Div, { className: `${__P.p3}`, border: "subtle", rounded: "xl", surface: "muted", children: _jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Div, { children: [_jsx(Text, { className: "text-[10px] tracking-wide mb-0.5", color: "faint", transform: "uppercase", children: "Listed by" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: safeSeller })] }), storeHref && _jsx(Link, { href: storeHref, className: "shrink-0 rounded-lg bg-primary/10 dark:bg-primary/20 px-[var(--appkit-space-3)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-xs)] font-semibold text-primary-700 dark:text-primary-300 hover:bg-primary/20 dark:hover:bg-primary/30 transition-colors", children: "Visit Store \u2192" })] }) }))] }));
56
56
  }
57
57
  /**
58
58
  * Store reviews for the auction's seller.
@@ -171,6 +171,8 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, on
171
171
  currentBid,
172
172
  isEnded,
173
173
  isSold: p.isSold === true,
174
+ // D53 — so the button is not offered for a buyout the server will refuse.
175
+ reservePrice: typeof p.reservePrice === "number" ? p.reservePrice : null,
174
176
  });
175
177
  const condition = typeof p.condition === "string" ? p.condition : null;
176
178
  const featured = p.featured === true;
@@ -29,6 +29,17 @@ export interface MarketplaceAuctionCardData {
29
29
  * (Root Cause #48).
30
30
  */
31
31
  isSold?: boolean;
32
+ /**
33
+ * D53 — the reserve, read by `isBuyNowAvailable` so a card cannot advertise
34
+ * a buyout the server will refuse.
35
+ *
36
+ * 🛑 Declared late: the predicate already read `product.reservePrice` while
37
+ * this type did not carry it, so the card was a type error AND every
38
+ * doc→card mapper was free to drop it. That is the exact failure the
39
+ * `isSold` note directly above warns about — a shared predicate reading a
40
+ * field the card shape does not guarantee.
41
+ */
42
+ reservePrice?: number | null;
32
43
  /**
33
44
  * The seller. Absent from this hand-rolled type entirely until now, so the
34
45
  * auction card could not show a seller even where every other card did —
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { ProductOwnerQuickActions } from "../../products/components/ProductOwnerQuickActions.js";
3
+ import { RecordOwnerQuickActions } from "../../admin/components/RecordOwnerQuickActions.js";
4
4
  import { normalizeError } from "../../../errors/normalize.js";
5
5
  import { useCallback, useEffect, useRef, useState } from "react";
6
6
  import { useRouter } from "next/navigation.js";
@@ -123,6 +123,8 @@ export function MarketplaceAuctionCard({ product, buyoutPrice, className = "", v
123
123
  currentBid: product.currentBid,
124
124
  isEnded,
125
125
  isSold: product.isSold === true,
126
+ // D53 — a card must not advertise a buyout price the server refuses.
127
+ reservePrice: typeof product.reservePrice === "number" ? product.reservePrice : null,
126
128
  })
127
129
  ? rawBuyoutPrice
128
130
  : undefined;
@@ -192,7 +194,7 @@ export function MarketplaceAuctionCard({ product, buyoutPrice, className = "", v
192
194
  const ownerActions = renderOwnerActions
193
195
  ? renderOwnerActions()
194
196
  : product.id
195
- ? (_jsx(ProductOwnerQuickActions, { productId: String(product.id), storeId: product.storeId ? String(product.storeId) : undefined, recordLabel: product.title, placement: "card" }))
197
+ ? (_jsx(RecordOwnerQuickActions, { resource: "PRODUCT", recordId: String(product.id), storeId: product.storeId ? String(product.storeId) : undefined, recordLabel: product.title, placement: "card" }))
196
198
  : null;
197
199
  if (!ownerActions)
198
200
  return card;
@@ -200,5 +202,5 @@ export function MarketplaceAuctionCard({ product, buyoutPrice, className = "", v
200
202
  * Sibling of the card, never a child — see the prop's docstring. `z-20`
201
203
  * clears this card's own overlays, which all sit at `z-10`.
202
204
  */
203
- return (_jsxs(Div, { className: "relative h-full", children: [card, _jsx(Div, { className: "absolute right-2 bottom-2 z-20", children: ownerActions })] }));
205
+ return (_jsxs(Div, { className: "flex h-full flex-col", children: [card, _jsx(Div, { className: "flex justify-end px-[var(--appkit-space-3)] pb-[var(--appkit-space-3)]", children: ownerActions })] }));
204
206
  }
@@ -40,12 +40,19 @@ export interface PlaceBidFormClientProps {
40
40
  isEnded: boolean;
41
41
  auctionEndDate: Date | null;
42
42
  buyNowPrice: number | null;
43
+ /**
44
+ * The seller's floor (D53). Threaded so the Buy-Now control is not offered
45
+ * for a buyout the server will refuse — `isBuyNowAvailable` now rejects
46
+ * `buyNowPrice < reservePrice`, and without this prop the button would
47
+ * render and then error on click.
48
+ */
49
+ reservePrice?: number | null;
43
50
  bidCount: number;
44
51
  tags?: string[];
45
52
  onPlaceBid: (input: PlaceBidInput) => Promise<BidActionEnvelope<unknown>>;
46
53
  onBuyNow?: () => Promise<BidActionEnvelope<BuyNowSuccess>>;
47
54
  }
48
- export declare function PlaceBidFormClient({ productId, currentBid: ssrCurrentBid, startingBid, tiers, minBidIncrementOverride, currency, isEnded: ssrIsEnded, auctionEndDate, buyNowPrice, bidCount: ssrBidCount, tags, onPlaceBid, onBuyNow, }: PlaceBidFormClientProps): React.JSX.Element;
55
+ export declare function PlaceBidFormClient({ productId, currentBid: ssrCurrentBid, startingBid, tiers, minBidIncrementOverride, currency, isEnded: ssrIsEnded, auctionEndDate, buyNowPrice, reservePrice, bidCount: ssrBidCount, tags, onPlaceBid, onBuyNow, }: PlaceBidFormClientProps): React.JSX.Element;
49
56
  /**
50
57
  * `PlaceBidModalButton` — opens a modal that hosts the same PlaceBidFormClient.
51
58
  *
@@ -37,7 +37,7 @@ const __P = {
37
37
  /** Feature-specific last resort. Authored copy, never derived from an error. */
38
38
  const BID_FALLBACK = "Failed to place bid. Please try again.";
39
39
  const BUY_NOW_FALLBACK = "Buy Now failed. Please try again.";
40
- export function PlaceBidFormClient({ productId, currentBid: ssrCurrentBid, startingBid, tiers, minBidIncrementOverride, currency, isEnded: ssrIsEnded, auctionEndDate, buyNowPrice, bidCount: ssrBidCount, tags = [], onPlaceBid, onBuyNow, }) {
40
+ export function PlaceBidFormClient({ productId, currentBid: ssrCurrentBid, startingBid, tiers, minBidIncrementOverride, currency, isEnded: ssrIsEnded, auctionEndDate, buyNowPrice, reservePrice, bidCount: ssrBidCount, tags = [], onPlaceBid, onBuyNow, }) {
41
41
  /*
42
42
  * 🛑 Derived here, not trusted from the server.
43
43
  *
@@ -142,7 +142,7 @@ export function PlaceBidFormClient({ productId, currentBid: ssrCurrentBid, start
142
142
  // `currentBid` here is the LIVE SSE price, so the button hides itself the
143
143
  // moment another bidder passes the buyout price — without a page reload, and
144
144
  // without this component owning a second copy of the rule.
145
- const buyNowAvailable = isBuyNowAvailable({ buyNowPrice, currentBid, isEnded }) && !!onBuyNow;
145
+ const buyNowAvailable = isBuyNowAvailable({ buyNowPrice, currentBid, isEnded, reservePrice }) && !!onBuyNow;
146
146
  const schema = placeBidSchema(minBid, formatCurrency(minBid, currency));
147
147
  async function submitBid(amount, setFieldError) {
148
148
  try {
@@ -30,6 +30,20 @@ export declare const AdminAuditActionValues: {
30
30
  * offer's own `statusHistory`.
31
31
  */
32
32
  readonly OFFER_CANCEL: "offer_cancel";
33
+ /**
34
+ * Admin credited or debited a buyer's points by hand.
35
+ *
36
+ * 🛑 Belongs here rather than only in the ledger, even though the ledger is
37
+ * already append-only and already records `actorUid` and `adminReason`. The
38
+ * two answer different questions: the ledger answers "why does THIS ACCOUNT
39
+ * have 53 points", this answers "what have admins done lately" — and the
40
+ * second is the one you need when the account is not yet suspected.
41
+ *
42
+ * 1 point = ₹1 of spendable discount, so a credit is a liability the platform
43
+ * must honour and a debit removes value a buyer already holds. That is the
44
+ * same class as `payout_mark_paid` and `coupon_update`, which are both here.
45
+ */
46
+ readonly POINTS_ADJUST: "points_adjust";
33
47
  };
34
48
  export type AdminAuditAction = (typeof AdminAuditActionValues)[keyof typeof AdminAuditActionValues];
35
49
  export interface AdminAuditLogDocument extends BaseDocument {
@@ -69,5 +83,19 @@ export declare const ADMIN_AUDIT_LOG_FIELDS: {
69
83
  * offer's own `statusHistory`.
70
84
  */
71
85
  readonly OFFER_CANCEL: "offer_cancel";
86
+ /**
87
+ * Admin credited or debited a buyer's points by hand.
88
+ *
89
+ * 🛑 Belongs here rather than only in the ledger, even though the ledger is
90
+ * already append-only and already records `actorUid` and `adminReason`. The
91
+ * two answer different questions: the ledger answers "why does THIS ACCOUNT
92
+ * have 53 points", this answers "what have admins done lately" — and the
93
+ * second is the one you need when the account is not yet suspected.
94
+ *
95
+ * 1 point = ₹1 of spendable discount, so a credit is a liability the platform
96
+ * must honour and a debit removes value a buyer already holds. That is the
97
+ * same class as `payout_mark_paid` and `coupon_update`, which are both here.
98
+ */
99
+ readonly POINTS_ADJUST: "points_adjust";
72
100
  };
73
101
  };
@@ -28,6 +28,20 @@ export const AdminAuditActionValues = {
28
28
  * offer's own `statusHistory`.
29
29
  */
30
30
  OFFER_CANCEL: "offer_cancel",
31
+ /**
32
+ * Admin credited or debited a buyer's points by hand.
33
+ *
34
+ * 🛑 Belongs here rather than only in the ledger, even though the ledger is
35
+ * already append-only and already records `actorUid` and `adminReason`. The
36
+ * two answer different questions: the ledger answers "why does THIS ACCOUNT
37
+ * have 53 points", this answers "what have admins done lately" — and the
38
+ * second is the one you need when the account is not yet suspected.
39
+ *
40
+ * 1 point = ₹1 of spendable discount, so a credit is a liability the platform
41
+ * must honour and a debit removes value a buyer already holds. That is the
42
+ * same class as `payout_mark_paid` and `coupon_update`, which are both here.
43
+ */
44
+ POINTS_ADJUST: "points_adjust",
31
45
  };
32
46
  export const ADMIN_AUDIT_LOG_FIELDS = {
33
47
  ACTOR_UID: "actorUid",
@@ -37,7 +37,23 @@ export type Permission = "admin:dashboard:view" | "admin:users:read" | "admin:us
37
37
  * mechanism — and revoking it is a permission change, which the admin audit
38
38
  * log already records.
39
39
  */
40
- | "tester:admin-surfaces" | "admin:events:read" | "admin:events:write" | "admin:events:delete" | "admin:event-entries:read" | "admin:event-entries:write" | "admin:support-tickets:read" | "admin:support-tickets:write" | "admin:shipments:read" | "admin:shipments:write" | "admin:catalogue:read" | "admin:catalogue:write" | "admin:grouped-listings:read" | "admin:grouped-listings:write" | "admin:support-tickets:assign" | "admin:support-tickets:close" | "admin:scammers:read" | "admin:scammers:write" | "admin:scammers:verify" | "admin:scammers:delete" | "admin:sessions:read" | "admin:sessions:delete" | "admin:notifications:read" | "admin:notifications:write" | "admin:carts:read" | "admin:wishlists:read" | "admin:feature-flags:read" | "admin:feature-flags:write" | "admin:team:read" | "admin:team:write" | "admin:maintenance:view-server-errors" | "admin:maintenance:view-client-errors" | "admin:maintenance:view-function-errors" | "admin:maintenance:view-cloud-logs" | "admin:maintenance:view-payment-rollbacks" | "admin:maintenance:run-analysis" | "admin:maintenance:purge-errors";
40
+ | "tester:admin-surfaces" | "admin:events:read" | "admin:events:write" | "admin:events:delete" | "admin:event-entries:read" | "admin:event-entries:write" | "admin:support-tickets:read" | "admin:support-tickets:write" | "admin:shipments:read" | "admin:shipments:write" | "admin:catalogue:read" | "admin:catalogue:write" | "admin:inventory:read" | "admin:inventory:write" | "admin:grouped-listings:read" | "admin:grouped-listings:write" | "admin:support-tickets:assign" | "admin:support-tickets:close" | "admin:scammers:read" | "admin:scammers:write" | "admin:scammers:verify" | "admin:scammers:delete" | "admin:sessions:read" | "admin:sessions:delete" | "admin:notifications:read" | "admin:notifications:write" | "admin:carts:read" | "admin:wishlists:read" | "admin:feature-flags:read" | "admin:feature-flags:write" | "admin:team:read" | "admin:team:write" | "admin:maintenance:view-server-errors" | "admin:maintenance:view-client-errors" | "admin:maintenance:view-function-errors" | "admin:maintenance:view-cloud-logs" | "admin:maintenance:view-payment-rollbacks" | "admin:maintenance:run-analysis" | "admin:maintenance:purge-errors" | "admin:maintenance:view-gateway-events" | "admin:maintenance:view-obligations" | "admin:payment-cases:read" | "admin:payment-cases:write" | "admin:protection-claims:read" | "admin:protection-claims:write"
41
+ /**
42
+ * Credit or debit a buyer's points by hand.
43
+ *
44
+ * 🛑 WRITE ONLY, with no matching `:read`, and that asymmetry is deliberate:
45
+ * a buyer's balance and ledger are already visible on the admin user detail
46
+ * page under the permissions that get you there. What needs its own gate is
47
+ * the act of CREATING SPENDABLE MONEY — 1 point = ₹1 against a future order,
48
+ * so a credit is a liability the platform must honour.
49
+ *
50
+ * Granted to no employee bundle. `createRouteHandler` lets an effective admin
51
+ * through without consulting the array at all, so this exists to be the thing
52
+ * an employee bundle would have to name explicitly — and nothing names it.
53
+ * Same reasoning as `admin:payment-cases:*`: a queue an analyst may read is
54
+ * different from a decision that moves money.
55
+ */
56
+ | "admin:points:write";
41
57
  /**
42
58
  * Granular user actions that can be soft-banned individually.
43
59
  * A user may have multiple simultaneous soft bans of different types.
@@ -262,6 +262,16 @@ export const PERMISSION_GROUPS = {
262
262
  "admin:maintenance:view-cloud-logs",
263
263
  "admin:maintenance:view-payment-rollbacks",
264
264
  "admin:maintenance:run-analysis",
265
+ /*
266
+ * The two new diagnostics surfaces DO belong to this role — a stuck
267
+ * gateway event and an overdue obligation are exactly what a maintenance
268
+ * employee should be able to look at.
269
+ *
270
+ * 🛑 `admin:payment-cases:*` and `admin:protection-claims:*` deliberately
271
+ * are NOT here. Those decide money and carry external deadlines.
272
+ */
273
+ "admin:maintenance:view-gateway-events",
274
+ "admin:maintenance:view-obligations",
265
275
  ],
266
276
  };
267
277
  // ============================================================================
@@ -284,6 +294,9 @@ export const ROUTE_PERMISSION_MAP = {
284
294
  "/admin/payouts": "admin:payouts:read",
285
295
  "/admin/audit-log": "admin:audit-log:read",
286
296
  "/admin/shipments": "admin:shipments:read",
297
+ "/admin/inventory": "admin:inventory:read",
298
+ "/admin/inventory/boxes": "admin:inventory:read",
299
+ "/admin/inventory/movements": "admin:inventory:read",
287
300
  "/admin/catalogue-approvals": "admin:catalogue:read",
288
301
  "/admin/grouped-listings": "admin:grouped-listings:read",
289
302
  "/admin/categories": "admin:categories:read",
@@ -326,6 +339,11 @@ export const ROUTE_PERMISSION_MAP = {
326
339
  "/admin/maintenance/cloud-logs": "admin:maintenance:view-cloud-logs",
327
340
  "/admin/maintenance/payment-rollbacks": "admin:maintenance:view-payment-rollbacks",
328
341
  "/admin/maintenance/analysis": "admin:maintenance:run-analysis",
342
+ "/admin/maintenance/gateway-events": "admin:maintenance:view-gateway-events",
343
+ "/admin/maintenance/obligations": "admin:maintenance:view-obligations",
344
+ // Payment protection — admin-only, see the permission union's own 🛑 note.
345
+ "/admin/money-cases": "admin:payment-cases:read",
346
+ "/admin/protection-claims": "admin:protection-claims:read",
329
347
  };
330
348
  /**
331
349
  * Every capability, as a runtime value.
@@ -56,5 +56,5 @@ export function BlogPostView({ slug, initialData, labels = {}, renderImage, rend
56
56
  })
57
57
  : "";
58
58
  const coverImageUrl = getMediaUrl(post.coverImage);
59
- return (_jsxs(Div, { className: `min-h-screen ${className}`, children: [coverImageUrl && (_jsxs(Div, { className: `relative h-72 md:h-96 ${__O.hidden}`, children: [renderImage ? (renderImage(post)) : (_jsx(MediaImage, { src: coverImageUrl, alt: post.title, size: "hero" })), _jsx(Scrim, { direction: "bottom-up", intensity: "subtle", className: "absolute inset-0" })] })), _jsxs(Div, { className: "max-w-3xl mx-auto", paddingY: "y-3xl", paddingX: "x-md", children: [renderBlogPostHeader(post, date, labels), post.youtubeId && /^[A-Za-z0-9_-]{6,20}$/.test(post.youtubeId) && (_jsx(Div, { className: "mb-8", children: _jsx(Iframe, { src: `https://www.youtube-nocookie.com/embed/${post.youtubeId}`, title: `${post.title} — video`, aspect: "video", rounded: "lg" }) })), post.tags && post.tags.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mb-8", children: post.tags.map((tag) => (_jsxs(Link, { href: `${String(ROUTES.BLOG.LIST)}?tags=${encodeURIComponent(tag)}`, className: "inline-block px-[var(--appkit-space-3)] py-[var(--appkit-space-1)] rounded-full bg-[var(--appkit-color-surface)] bg-[var(--appkit-color-surface-elevated)] hover:bg-primary/10 text-[var(--appkit-color-text-muted)] hover:text-primary text-[length:var(--appkit-text-xs)] font-medium transition-colors", children: ["#", tag] }, tag))) })), renderAuthorBio && _jsx(Div, { className: "mb-6", children: renderAuthorBio(post) }), _jsx(Div, { surface: "card", padding: "xl", className: "mb-12", children: renderContent ? renderContent(post) : (_jsx(RichText, { html: normalizeRichTextHtml(post.content ?? ""), proseClass: "prose max-w-none dark:prose-invert" })) }), _jsxs(Stack, { gap: "xl", children: [renderBlogPostRelated(related, labels?.relatedTitle ?? "Related Posts", renderRelatedCard), renderBlogPostRelated(relatedByTags, labels?.relatedByTagsTitle ?? "You might also like", renderRelatedCard), renderBlogPostRelated(relatedByAuthor, labels?.relatedByAuthorTitle ?? `More from ${post.authorName}`, renderRelatedCard)] }), renderBackButton && _jsx(Div, { border: "default", className: "mt-10 border-t", padding: "t-xl", children: renderBackButton() })] })] }));
59
+ return (_jsxs(Div, { className: `min-h-screen ${className}`, children: [coverImageUrl && (_jsxs(Div, { className: `relative h-72 md:h-96 ${__O.hidden}`, children: [renderImage ? (renderImage(post)) : (_jsx(MediaImage, { src: coverImageUrl, alt: post.title, size: "hero" })), _jsx(Scrim, { direction: "bottom-up", intensity: "subtle", className: "absolute inset-0" })] })), _jsxs(Div, { className: "max-w-3xl mx-auto", paddingY: "y-3xl", paddingX: "x-md", children: [renderBlogPostHeader(post, date, labels), post.youtubeId && /^[A-Za-z0-9_-]{6,20}$/.test(post.youtubeId) && (_jsx(Div, { className: "mb-8", children: _jsx(Iframe, { src: `https://www.youtube-nocookie.com/embed/${post.youtubeId}`, title: `${post.title} — video`, aspect: "video", rounded: "lg" }) })), post.tags && post.tags.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mb-8", children: post.tags.map((tag) => (_jsxs(Link, { href: `${String(ROUTES.BLOG.LIST)}?tags=${encodeURIComponent(tag)}`, className: "inline-block px-[var(--appkit-space-3)] py-[var(--appkit-space-1)] rounded-full bg-[var(--appkit-color-surface-elevated)] hover:bg-primary/10 text-[var(--appkit-color-text-muted)] hover:text-primary text-[length:var(--appkit-text-xs)] font-medium transition-colors", children: ["#", tag] }, tag))) })), renderAuthorBio && _jsx(Div, { className: "mb-6", children: renderAuthorBio(post) }), _jsx(Div, { surface: "card", padding: "xl", className: "mb-12", children: renderContent ? renderContent(post) : (_jsx(RichText, { html: normalizeRichTextHtml(post.content ?? ""), proseClass: "prose max-w-none dark:prose-invert" })) }), _jsxs(Stack, { gap: "xl", children: [renderBlogPostRelated(related, labels?.relatedTitle ?? "Related Posts", renderRelatedCard), renderBlogPostRelated(relatedByTags, labels?.relatedByTagsTitle ?? "You might also like", renderRelatedCard), renderBlogPostRelated(relatedByAuthor, labels?.relatedByAuthorTitle ?? `More from ${post.authorName}`, renderRelatedCard)] }), renderBackButton && _jsx(Div, { border: "default", className: "mt-10 border-t", padding: "t-xl", children: renderBackButton() })] })] }));
60
60
  }
@@ -81,5 +81,5 @@ export function AdminCatalogueApprovalsView() {
81
81
  }, children: "Reject" }), _jsx(Button, { isLoading: approveMutation.isPending, onClick: () => {
82
82
  approveMutation.mutate(selected.id);
83
83
  setSelected(null);
84
- }, children: "Approve" })] })) : null }), _jsx(Modal, { isOpen: !!rejectTarget, onClose: () => setRejectTarget(null), title: "Reject this catalogue listing?", size: "sm", children: _jsxs(Stack, { gap: "md", children: [_jsx(Text, { variant: "secondary", children: "The owner will see this reason." }), _jsx(Textarea, { value: rejectReason, onChange: (e) => setRejectReason(e.target.value), rows: 3, placeholder: "Reason for rejection" }), _jsx(Button, { isLoading: rejectMutation.isPending, disabled: !rejectReason.trim(), variant: "danger", onClick: () => rejectMutation.mutate(), children: "Reject" })] }) })] }));
84
+ }, children: "Approve" })] })) : null }), _jsx(Modal, { isOpen: !!rejectTarget, onClose: () => setRejectTarget(null), title: "Reject this catalogue listing?", size: "sm", children: _jsxs(Stack, { gap: "md", children: [_jsx(Text, { variant: "secondary", children: "The owner will see this reason." }), _jsx(Textarea, { label: "Reason for rejection", value: rejectReason, onChange: (e) => setRejectReason(e.target.value), rows: 3, placeholder: "Reason for rejection" }), _jsx(Button, { isLoading: rejectMutation.isPending, disabled: !rejectReason.trim(), variant: "danger", onClick: () => rejectMutation.mutate(), children: "Reject" })] }) })] }));
85
85
  }