@mohasinac/appkit 4.59.0 → 4.60.1

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 (532) 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.d.ts +20 -2
  27. package/dist/_internal/server/features/orders/adapters.js +44 -0
  28. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
  29. package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
  30. package/dist/_internal/server/features/orders/index.d.ts +2 -1
  31. package/dist/_internal/server/features/orders/index.js +1 -1
  32. package/dist/_internal/server/features/orders/service.d.ts +60 -0
  33. package/dist/_internal/server/features/orders/service.js +74 -0
  34. package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
  35. package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
  36. package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
  37. package/dist/_internal/server/features/payment-cases/index.js +9 -0
  38. package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
  39. package/dist/_internal/server/features/payouts/actions.js +84 -10
  40. package/dist/_internal/server/features/points/adjust.d.ts +61 -0
  41. package/dist/_internal/server/features/points/adjust.js +121 -0
  42. package/dist/_internal/server/features/points/award.d.ts +65 -0
  43. package/dist/_internal/server/features/points/award.js +145 -0
  44. package/dist/_internal/server/features/price-index/market.d.ts +33 -0
  45. package/dist/_internal/server/features/price-index/market.js +54 -1
  46. package/dist/_internal/server/features/products/data.js +40 -22
  47. package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
  48. package/dist/_internal/server/features/protection-claims/actions.js +228 -0
  49. package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
  50. package/dist/_internal/server/features/protection-claims/index.js +8 -0
  51. package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
  52. package/dist/_internal/server/features/refunds/actions.js +259 -17
  53. package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
  54. package/dist/_internal/server/features/refunds/capacity.js +37 -0
  55. package/dist/_internal/server/features/reviews/actions.js +3 -2
  56. package/dist/_internal/server/features/search/index-build/sources.js +20 -1
  57. package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
  58. package/dist/_internal/server/features/shipping/reconcile.js +34 -3
  59. package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
  60. package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
  61. package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
  62. package/dist/_internal/server/features/stores/tabs.js +24 -4
  63. package/dist/_internal/server/functions/https.d.ts +31 -1
  64. package/dist/_internal/server/functions/https.js +52 -1
  65. package/dist/_internal/server/functions/scheduled.d.ts +72 -1
  66. package/dist/_internal/server/functions/scheduled.js +124 -1
  67. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
  68. package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
  69. package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
  70. package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
  71. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
  72. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
  73. package/dist/_internal/server/jobs/core/index.d.ts +2 -0
  74. package/dist/_internal/server/jobs/core/index.js +2 -0
  75. package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
  76. package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
  77. package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
  78. package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
  79. package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
  80. package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
  81. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
  82. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
  83. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
  84. package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
  85. package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
  86. package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
  87. package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
  88. package/dist/_internal/server/jobs/handlers/index.js +5 -0
  89. package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
  90. package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
  91. package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
  92. package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
  93. package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
  94. package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
  95. package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
  96. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  97. package/dist/_internal/shared/actions/action-registry.js +71 -0
  98. package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
  99. package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
  100. package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
  101. package/dist/_internal/shared/checkout/lanes.js +46 -0
  102. package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
  103. package/dist/_internal/shared/checkout/order-math.js +251 -3
  104. package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
  105. package/dist/_internal/shared/checkout/refundable-base.js +122 -0
  106. package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
  107. package/dist/_internal/shared/features/auctions/config.js +30 -1
  108. package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
  109. package/dist/_internal/shared/features/brands/schema.js +18 -0
  110. package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
  111. package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
  112. package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
  113. package/dist/_internal/shared/features/print/label-profiles.js +350 -0
  114. package/dist/_internal/shared/features/products/to-product-item.js +33 -5
  115. package/dist/_internal/shared/fees/calculator.d.ts +31 -2
  116. package/dist/_internal/shared/fees/calculator.js +103 -5
  117. package/dist/_internal/shared/fees/india-states.d.ts +195 -0
  118. package/dist/_internal/shared/fees/india-states.js +190 -0
  119. package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
  120. package/dist/_internal/shared/fees/payout-hold.js +74 -0
  121. package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
  122. package/dist/_internal/shared/obligations/deadlines.js +155 -0
  123. package/dist/_internal/shared/shipping/quote.d.ts +96 -0
  124. package/dist/_internal/shared/shipping/quote.js +67 -0
  125. package/dist/_internal/shared/time/business-days.d.ts +91 -0
  126. package/dist/_internal/shared/time/business-days.js +185 -0
  127. package/dist/cli/index.js +0 -2
  128. package/dist/client.d.ts +10 -4
  129. package/dist/client.js +29 -3
  130. package/dist/configs/tailwind.d.ts +0 -12
  131. package/dist/configs/tailwind.js +28 -15
  132. package/dist/constants/api-endpoints.d.ts +51 -9
  133. package/dist/constants/api-endpoints.js +23 -7
  134. package/dist/constants/index.d.ts +1 -1
  135. package/dist/constants/index.js +1 -1
  136. package/dist/contracts/index.d.ts +1 -1
  137. package/dist/contracts/payment.d.ts +131 -0
  138. package/dist/features/about/components/AboutView.js +1 -1
  139. package/dist/features/about/components/DeveloperView.js +5 -2
  140. package/dist/features/about/components/FAQPageView.js +2 -1
  141. package/dist/features/about/components/FeesView.js +2 -1
  142. package/dist/features/about/components/HelpPageView.js +2 -1
  143. package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
  144. package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
  145. package/dist/features/about/components/HowEmiWorksView.js +2 -1
  146. package/dist/features/about/components/HowOffersWorkView.js +2 -1
  147. package/dist/features/about/components/HowOrdersWorkView.js +2 -1
  148. package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
  149. package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
  150. package/dist/features/about/components/HowReviewsWorkView.js +2 -1
  151. package/dist/features/about/components/PolicyPageView.js +2 -1
  152. package/dist/features/about/components/ProseHero.d.ts +73 -0
  153. package/dist/features/about/components/ProseHero.js +18 -0
  154. package/dist/features/about/components/SecurityPrivacyView.js +2 -1
  155. package/dist/features/about/components/ShippingPolicyView.js +2 -1
  156. package/dist/features/about/components/TrackOrderView.js +2 -1
  157. package/dist/features/about/components/UnauthorizedView.js +1 -1
  158. package/dist/features/about/index.d.ts +2 -0
  159. package/dist/features/about/index.js +5 -0
  160. package/dist/features/account/actions/address-actions.d.ts +26 -2
  161. package/dist/features/account/actions/address-actions.js +25 -1
  162. package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
  163. package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
  164. package/dist/features/account/components/UserOrdersView.js +17 -0
  165. package/dist/features/account/components/UserSidebar.js +1 -1
  166. package/dist/features/account/hooks/useNotifications.js +29 -0
  167. package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
  168. package/dist/features/admin/components/AdminBeysView.js +2 -2
  169. package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
  170. package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
  171. package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
  172. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  173. package/dist/features/admin/components/AdminCategoriesView.js +1 -1
  174. package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
  175. package/dist/features/admin/components/AdminContactEditorView.js +1 -1
  176. package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
  177. package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
  178. package/dist/features/admin/components/AdminFaqsView.js +2 -2
  179. package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
  180. package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
  181. package/dist/features/admin/components/AdminGuideHubView.js +1 -1
  182. package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
  183. package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
  184. package/dist/features/admin/components/AdminPartsView.js +2 -2
  185. package/dist/features/admin/components/AdminProductsView.js +75 -4
  186. package/dist/features/admin/components/AdminSidebar.js +2 -2
  187. package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
  188. package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
  189. package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
  190. package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
  191. package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
  192. package/dist/features/admin/components/AdminUsersView.js +72 -1
  193. package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
  194. package/dist/features/admin/components/AdminViewCards.js +4 -4
  195. package/dist/features/admin/components/DataListingView.d.ts +28 -0
  196. package/dist/features/admin/components/DataListingView.js +175 -14
  197. package/dist/features/admin/components/DataTable.d.ts +12 -1
  198. package/dist/features/admin/components/DataTable.js +3 -3
  199. package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
  200. package/dist/features/admin/components/OwnerQuickActions.js +60 -2
  201. package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
  202. package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
  203. package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
  204. package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
  205. package/dist/features/admin/components/index.d.ts +4 -0
  206. package/dist/features/admin/components/index.js +6 -0
  207. package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
  208. package/dist/features/admin/constants/filter-tabs.js +106 -0
  209. package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
  210. package/dist/features/admin/repository/site-settings.repository.js +82 -1
  211. package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
  212. package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
  213. package/dist/features/admin/schemas/firestore.js +5 -1
  214. package/dist/features/admin/schemas/site-settings-form.js +30 -1
  215. package/dist/features/auctions/actions/bid-actions.js +13 -1
  216. package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
  217. package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
  218. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
  219. package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
  220. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
  221. package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
  222. package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
  223. package/dist/features/audit-log/schemas/firestore.js +14 -0
  224. package/dist/features/auth/permissions/constants.d.ts +17 -1
  225. package/dist/features/auth/permissions/constants.js +18 -0
  226. package/dist/features/blog/components/BlogPostView.js +1 -1
  227. package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
  228. package/dist/features/categories/api/[id]/description-template/route.js +107 -1
  229. package/dist/features/categories/components/BrandDetailPageView.js +2 -2
  230. package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
  231. package/dist/features/categories/components/BundleDetailView.js +1 -1
  232. package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
  233. package/dist/features/categories/components/CategoryForm.js +12 -1
  234. package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
  235. package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
  236. package/dist/features/categories/repository/market-price-index.repository.js +105 -0
  237. package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
  238. package/dist/features/categories/repository/price-observation.repository.js +132 -0
  239. package/dist/features/events/components/EventSaleBanner.js +1 -1
  240. package/dist/features/events/components/EventStatusBadge.js +1 -1
  241. package/dist/features/faq/components/FAQAccordion.js +2 -1
  242. package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
  243. package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
  244. package/dist/features/faq/components/RelatedFAQs.js +1 -1
  245. package/dist/features/filters/FilterFacetSection.js +1 -1
  246. package/dist/features/filters/SwitchFilter.js +1 -1
  247. package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
  248. package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
  249. package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
  250. package/dist/features/gateway-events/schemas/firestore.js +125 -0
  251. package/dist/features/gateway-events/schemas/index.d.ts +81 -0
  252. package/dist/features/gateway-events/schemas/index.js +79 -0
  253. package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
  254. package/dist/features/homepage/components/AnnouncementBar.js +37 -1
  255. package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
  256. package/dist/features/homepage/components/BrandsSection.js +1 -1
  257. package/dist/features/homepage/components/CTABannerSection.js +1 -1
  258. package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
  259. package/dist/features/homepage/components/HeroCarousel.js +2 -2
  260. package/dist/features/homepage/components/SocialPostCard.js +1 -1
  261. package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
  262. package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
  263. package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
  264. package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
  265. package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
  266. package/dist/features/inventory/components/ReceiveStationView.js +413 -0
  267. package/dist/features/inventory/components/operator-message.d.ts +29 -0
  268. package/dist/features/inventory/components/operator-message.js +41 -0
  269. package/dist/features/inventory/index.d.ts +13 -0
  270. package/dist/features/inventory/index.js +28 -0
  271. package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
  272. package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
  273. package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
  274. package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
  275. package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
  276. package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
  277. package/dist/features/inventory/schemas/firestore.d.ts +197 -0
  278. package/dist/features/inventory/schemas/firestore.js +159 -0
  279. package/dist/features/layout/AppLayoutShell.d.ts +12 -1
  280. package/dist/features/layout/AppLayoutShell.js +25 -12
  281. package/dist/features/layout/AutoBreadcrumbs.js +162 -3
  282. package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
  283. package/dist/features/layout/BottomNavbar.d.ts +1 -1
  284. package/dist/features/layout/BottomNavbar.js +2 -2
  285. package/dist/features/media/MediaAvatar.js +1 -1
  286. package/dist/features/media/MediaImage.d.ts +24 -4
  287. package/dist/features/media/MediaImage.js +44 -4
  288. package/dist/features/media/MediaLightbox.js +1 -1
  289. package/dist/features/media/modals/ImageCropModal.js +1 -1
  290. package/dist/features/media/modals/ImageEditor.js +1 -1
  291. package/dist/features/media/upload/MediaUploadField.js +91 -3
  292. package/dist/features/obligations/constants/policies.d.ts +92 -0
  293. package/dist/features/obligations/constants/policies.js +378 -0
  294. package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
  295. package/dist/features/obligations/repository/obligations.repository.js +158 -0
  296. package/dist/features/obligations/schemas/firestore.d.ts +160 -0
  297. package/dist/features/obligations/schemas/firestore.js +87 -0
  298. package/dist/features/orders/actions/order-actions.d.ts +43 -4
  299. package/dist/features/orders/actions/order-actions.js +60 -3
  300. package/dist/features/orders/actions/refund-actions.js +70 -10
  301. package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
  302. package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
  303. package/dist/features/orders/components/OrdersList.js +2 -2
  304. package/dist/features/orders/components/RefundHistoryTable.js +3 -1
  305. package/dist/features/orders/components/index.d.ts +2 -0
  306. package/dist/features/orders/components/index.js +6 -0
  307. package/dist/features/orders/constants/transitions.d.ts +59 -0
  308. package/dist/features/orders/constants/transitions.js +104 -0
  309. package/dist/features/orders/repository/orders.repository.d.ts +109 -6
  310. package/dist/features/orders/repository/orders.repository.js +464 -21
  311. package/dist/features/orders/schemas/firestore.d.ts +270 -0
  312. package/dist/features/orders/schemas/firestore.js +24 -0
  313. package/dist/features/orders/schemas/index.d.ts +61 -0
  314. package/dist/features/orders/schemas/index.js +22 -0
  315. package/dist/features/orders/types/index.d.ts +79 -0
  316. package/dist/features/parts/components/PartsIndexPageView.js +8 -1
  317. package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
  318. package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
  319. package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
  320. package/dist/features/payment-cases/schemas/firestore.js +105 -0
  321. package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
  322. package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
  323. package/dist/features/payments/repository/payout.repository.d.ts +0 -1
  324. package/dist/features/payments/repository/payout.repository.js +0 -1
  325. package/dist/features/points/repository/points.repository.d.ts +122 -0
  326. package/dist/features/points/repository/points.repository.js +304 -1
  327. package/dist/features/points/schemas/adjust-form.d.ts +58 -0
  328. package/dist/features/points/schemas/adjust-form.js +63 -0
  329. package/dist/features/points/schemas/firestore.d.ts +108 -1
  330. package/dist/features/points/schemas/firestore.js +84 -0
  331. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
  332. package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
  333. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
  334. package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
  335. package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
  336. package/dist/features/price-index/market-stats.d.ts +82 -0
  337. package/dist/features/price-index/market-stats.js +230 -0
  338. package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
  339. package/dist/features/print/components/BoxLabelPrintView.js +66 -0
  340. package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
  341. package/dist/features/print/components/LabelProfilePicker.js +38 -0
  342. package/dist/features/print/components/labels.d.ts +117 -0
  343. package/dist/features/print/components/labels.js +258 -0
  344. package/dist/features/print/components/labels.style.css +46 -0
  345. package/dist/features/print/index.d.ts +6 -0
  346. package/dist/features/print/index.js +16 -0
  347. package/dist/features/products/columns/productTableColumns.js +5 -4
  348. package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
  349. package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
  350. package/dist/features/products/components/PrizeDrawCollage.js +1 -1
  351. package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
  352. package/dist/features/products/components/PrizeRevealModal.js +2 -2
  353. package/dist/features/products/components/ProductDetailPageView.js +18 -4
  354. package/dist/features/products/components/ProductForm.js +34 -23
  355. package/dist/features/products/components/ProductGrid.js +86 -62
  356. package/dist/features/products/constants/card-badges.d.ts +97 -0
  357. package/dist/features/products/constants/card-badges.js +100 -0
  358. package/dist/features/products/repository/products.repository.js +34 -1
  359. package/dist/features/products/schemas/firestore.d.ts +46 -3
  360. package/dist/features/products/schemas/firestore.js +21 -0
  361. package/dist/features/promotions/components/PromotionsView.js +1 -1
  362. package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
  363. package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
  364. package/dist/features/promotions/schemas/firestore.js +30 -3
  365. package/dist/features/promotions/schemas/index.d.ts +6 -6
  366. package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
  367. package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
  368. package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
  369. package/dist/features/protection-claims/schemas/firestore.js +130 -0
  370. package/dist/features/reviews/actions/review-actions.js +3 -2
  371. package/dist/features/reviews/components/ReviewModal.js +1 -1
  372. package/dist/features/reviews/components/ReviewsList.js +1 -1
  373. package/dist/features/reviews/schemas/firestore.d.ts +20 -1
  374. package/dist/features/scams/constants/scam-types.d.ts +2 -0
  375. package/dist/features/scams/constants/scam-types.js +18 -0
  376. package/dist/features/scams/schemas/report-form.d.ts +3 -3
  377. package/dist/features/scams/schemas/report-form.js +26 -1
  378. package/dist/features/search/components/Search.js +9 -9
  379. package/dist/features/search/components/SearchResultsClient.js +18 -3
  380. package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
  381. package/dist/features/search/components/SearchResultsPageView.js +2 -2
  382. package/dist/features/seller/actions/offer-actions.js +16 -1
  383. package/dist/features/seller/actions/seller-actions.js +14 -3
  384. package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
  385. package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
  386. package/dist/features/seller/components/PrintCenterView.js +41 -30
  387. package/dist/features/seller/components/SellerOrdersView.js +1 -1
  388. package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
  389. package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
  390. package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
  391. package/dist/features/seller/components/SellerProductShell.js +123 -13
  392. package/dist/features/seller/components/SellerReviewsView.js +4 -2
  393. package/dist/features/seller/components/SellerSidebar.js +1 -1
  394. package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
  395. package/dist/features/seller/schemas/coupon-form.js +27 -0
  396. package/dist/features/seller/schemas/product-form.d.ts +3 -0
  397. package/dist/features/seller/schemas/product-form.js +5 -0
  398. package/dist/features/shell/FormShell.js +2 -2
  399. package/dist/features/shell/QuickFormDrawer.js +14 -0
  400. package/dist/features/shell/SectionForm.d.ts +19 -0
  401. package/dist/features/shell/SectionForm.js +133 -9
  402. package/dist/features/shell/SectionForm.style.css +54 -0
  403. package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
  404. package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
  405. package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
  406. package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
  407. package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
  408. package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
  409. package/dist/features/stores/components/StoreGuideHubView.js +1 -1
  410. package/dist/features/stores/components/StoreHeader.js +1 -1
  411. package/dist/features/support/schemas/firestore.d.ts +73 -2
  412. package/dist/features/support/schemas/firestore.js +59 -0
  413. package/dist/index.d.ts +16 -24
  414. package/dist/index.js +38 -47
  415. package/dist/next/ErrorBoundary.js +1 -1
  416. package/dist/next/routing/route-map.d.ts +24 -0
  417. package/dist/next/routing/route-map.js +40 -0
  418. package/dist/providers/payment-manual/index.d.ts +15 -1
  419. package/dist/providers/payment-manual/index.js +37 -0
  420. package/dist/providers/payment-phonepe/index.d.ts +14 -1
  421. package/dist/providers/payment-phonepe/index.js +36 -0
  422. package/dist/providers/payment-razorpay/index.d.ts +44 -1
  423. package/dist/providers/payment-razorpay/index.js +99 -0
  424. package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
  425. package/dist/providers/shipping-shiprocket/index.js +18 -0
  426. package/dist/schemas/registry.d.ts +44 -11
  427. package/dist/server-entry.d.ts +2 -0
  428. package/dist/server-entry.js +27 -0
  429. package/dist/server.d.ts +43 -0
  430. package/dist/server.js +98 -0
  431. package/dist/styles.css +418 -149
  432. package/dist/tailwind-utilities.css +1 -1
  433. package/dist/tokens/themes/default-dark.js +15 -12
  434. package/dist/tokens/themes/default-light.js +14 -11
  435. package/dist/tokens/tokens.css +106 -23
  436. package/dist/ui/DataTable.style.css +4 -4
  437. package/dist/ui/columns/cell-renderers.d.ts +20 -0
  438. package/dist/ui/columns/cell-renderers.js +26 -1
  439. package/dist/ui/columns/index.d.ts +1 -1
  440. package/dist/ui/columns/index.js +4 -1
  441. package/dist/ui/components/Badge.style.css +1 -1
  442. package/dist/ui/components/Breadcrumb.js +19 -1
  443. package/dist/ui/components/BulkActionBar.style.css +4 -4
  444. package/dist/ui/components/Button.style.css +1 -1
  445. package/dist/ui/components/Checkbox.style.css +1 -1
  446. package/dist/ui/components/FallbackShell.js +1 -1
  447. package/dist/ui/components/Form.d.ts +14 -1
  448. package/dist/ui/components/Form.js +2 -2
  449. package/dist/ui/components/FormField.js +18 -4
  450. package/dist/ui/components/FormField.style.css +43 -3
  451. package/dist/ui/components/GatedPrice.style.css +1 -1
  452. package/dist/ui/components/IconButton.style.css +1 -1
  453. package/dist/ui/components/Input.js +22 -1
  454. package/dist/ui/components/Input.style.css +38 -22
  455. package/dist/ui/components/ListingFilterDrawer.js +1 -1
  456. package/dist/ui/components/ListingToolbar.js +1 -1
  457. package/dist/ui/components/PaginatedSelect.js +0 -0
  458. package/dist/ui/components/Radio.style.css +1 -1
  459. package/dist/ui/components/Repeater.d.ts +53 -0
  460. package/dist/ui/components/Repeater.js +7 -0
  461. package/dist/ui/components/RichTextEditor.d.ts +20 -1
  462. package/dist/ui/components/RichTextEditor.js +23 -2
  463. package/dist/ui/components/Select.style.css +7 -21
  464. package/dist/ui/components/Semantic.js +27 -3
  465. package/dist/ui/components/TagInput.style.css +4 -9
  466. package/dist/ui/components/Textarea.style.css +4 -18
  467. package/dist/ui/components/Toggle.style.css +1 -1
  468. package/dist/ui/components/Typography.d.ts +3 -3
  469. package/dist/ui/components/Typography.js +15 -15
  470. package/dist/ui/components/ViewToggle.style.css +4 -4
  471. package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
  472. package/dist/ui/forms/AddAnotherToggle.js +56 -0
  473. package/dist/ui/forms/ColorPickerField.js +15 -2
  474. package/dist/ui/forms/FieldCheckbox.js +15 -2
  475. package/dist/ui/forms/FieldInput.d.ts +22 -1
  476. package/dist/ui/forms/FieldInput.js +19 -5
  477. package/dist/ui/forms/FieldLabel.d.ts +35 -0
  478. package/dist/ui/forms/FieldLabel.js +70 -0
  479. package/dist/ui/forms/FieldSelect.d.ts +4 -1
  480. package/dist/ui/forms/FieldSelect.js +27 -5
  481. package/dist/ui/forms/FieldTextarea.d.ts +4 -1
  482. package/dist/ui/forms/FieldTextarea.js +22 -5
  483. package/dist/ui/forms/FormShell.d.ts +17 -1
  484. package/dist/ui/forms/FormShell.js +36 -2
  485. package/dist/ui/forms/FormShell.style.css +41 -33
  486. package/dist/ui/forms/index.d.ts +5 -1
  487. package/dist/ui/forms/index.js +2 -0
  488. package/dist/ui/icons/icon-registry.d.ts +1 -0
  489. package/dist/ui/icons/icon-registry.js +17 -1
  490. package/dist/utils/id-generators.d.ts +80 -0
  491. package/dist/utils/id-generators.js +93 -0
  492. package/dist/utils/search-txt-builders.d.ts +12 -0
  493. package/dist/utils/search-txt-builders.js +17 -0
  494. package/package.json +1 -1
  495. package/scripts/seed-cli.mjs +106 -5
  496. package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
  497. package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
  498. package/dist/features/loyalty/api/balance/route.d.ts +0 -12
  499. package/dist/features/loyalty/api/balance/route.js +0 -41
  500. package/dist/features/loyalty/api/earn/route.d.ts +0 -12
  501. package/dist/features/loyalty/api/earn/route.js +0 -38
  502. package/dist/features/loyalty/api/history/route.d.ts +0 -12
  503. package/dist/features/loyalty/api/history/route.js +0 -58
  504. package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
  505. package/dist/features/loyalty/api/redeem/route.js +0 -38
  506. package/dist/features/loyalty/columns/index.d.ts +0 -17
  507. package/dist/features/loyalty/columns/index.js +0 -40
  508. package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
  509. package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
  510. package/dist/features/loyalty/components/index.d.ts +0 -1
  511. package/dist/features/loyalty/components/index.js +0 -1
  512. package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
  513. package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
  514. package/dist/features/loyalty/index.d.ts +0 -6
  515. package/dist/features/loyalty/index.js +0 -6
  516. package/dist/features/loyalty/manifest.d.ts +0 -2
  517. package/dist/features/loyalty/manifest.js +0 -16
  518. package/dist/features/loyalty/messages/en.json +0 -19
  519. package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
  520. package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
  521. package/dist/features/loyalty/schemas/index.d.ts +0 -85
  522. package/dist/features/loyalty/schemas/index.js +0 -40
  523. package/dist/features/loyalty/server.d.ts +0 -6
  524. package/dist/features/loyalty/server.js +0 -6
  525. package/dist/features/loyalty/types/index.d.ts +0 -31
  526. package/dist/features/loyalty/types/index.js +0 -1
  527. package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
  528. package/dist/features/loyalty/types/loyalty-config.js +0 -1
  529. package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
  530. package/dist/features/loyalty/types/loyalty-math.js +0 -27
  531. package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
  532. package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
@@ -81,6 +81,75 @@ function isImage(url) {
81
81
  function isPdf(url) {
82
82
  return /\.pdf(\?|$)/i.test(url);
83
83
  }
84
+ /**
85
+ * 🛑 AN EXTENSION REGEX CANNOT CLASSIFY ANYTHING THIS APP UPLOADED.
86
+ *
87
+ * `POST /api/media/finalize` returns `/media/{shortId}` where the shortId is the
88
+ * filename with its extension deliberately STRIPPED:
89
+ *
90
+ * const baseShortId = filename.replace(/\.[^.]+$/, ""); // finalize route
91
+ *
92
+ * so `isImage` / `isVideo` / `isPdf` above were ALL false for every freshly
93
+ * uploaded file. `MediaPreviewPanel` fell through its three branches to the bare
94
+ * `<Anchor>` at the bottom — a text link where the preview belongs — and the
95
+ * "Edit video" button, gated on `isVideo(value)`, never appeared either. The
96
+ * upload itself always worked, which is why this read as a missing feature
97
+ * rather than a broken one.
98
+ *
99
+ * Three signals, in order of how much each actually knows:
100
+ *
101
+ * 1. a real extension — seed fixtures and `source: "external"` URLs, which
102
+ * never pass through finalize. Unchanged behaviour for those.
103
+ * 2. the slug's own kind token, read off `MEDIA_FILENAME_PATTERNS` in
104
+ * `id-generators.ts` — stripping the extension leaves the token intact.
105
+ * (`deriveContextTypeFromFilename` is NOT usable here even though it owns
106
+ * that table: every one of its patterns is anchored with `\.<ext>$`, so it
107
+ * returns undefined for precisely the refs this has to classify.)
108
+ * 3. nothing recognisable — `"file"`, i.e. the bare link it renders today. A
109
+ * wrong guess is worse than the status quo, so this never guesses.
110
+ *
111
+ * 🛑 The token is a SUFFIX for most families and a PREFIX for three of them, so
112
+ * both are tested. A suffix-only rule silently misses `catalogue-image-{slug}-3`
113
+ * (the word is at the front, the number at the back) and
114
+ * `tester-screenshot-{case}-{tester}-{date}` — verified by running the real
115
+ * table's outputs through it.
116
+ *
117
+ * The three `*-proof` families are deliberately absent: their generators accept
118
+ * any extension, so a proof is as likely to be a PDF as a screenshot and there
119
+ * is nothing in the name to separate them. They keep the existing link.
120
+ *
121
+ * The authoritative answer is the uploaded file's own MIME, which `afterUpload`
122
+ * has and now remembers; this resolver is for a value that arrives as a prop
123
+ * (reopening an editor on a saved record), where no MIME is available.
124
+ */
125
+ function previewKindFor(url) {
126
+ if (isVideo(url))
127
+ return "video";
128
+ if (isPdf(url))
129
+ return "pdf";
130
+ if (isImage(url))
131
+ return "image";
132
+ const slug = url.split("?")[0].split("/").pop() ?? "";
133
+ if (/-video(-\d+)?$/i.test(slug))
134
+ return "video";
135
+ if (/-(image|logo|banner|avatar)(-\d+)?$/i.test(slug))
136
+ return "image";
137
+ if (/^(catalogue-image|tester-screenshot)-/i.test(slug))
138
+ return "image";
139
+ if (/^(invoice|payout-doc)-/i.test(slug))
140
+ return "pdf";
141
+ return "file";
142
+ }
143
+ /** The uploaded MIME wins over any name-based guess, for the url it was seen on. */
144
+ function previewKindFromMime(mimeType) {
145
+ if (mimeType.startsWith("video/"))
146
+ return "video";
147
+ if (mimeType.startsWith("image/"))
148
+ return "image";
149
+ if (mimeType === "application/pdf")
150
+ return "pdf";
151
+ return "file";
152
+ }
84
153
  function isPdfAccept(accept) {
85
154
  return accept
86
155
  .toLowerCase()
@@ -120,8 +189,8 @@ function YoutubeTabPanel({ ytInput, setYtInput, ytError, setYtError, onApply, va
120
189
  function ExternalUrlTabPanel({ extInput, setExtInput, extError, setExtError, onApply, }) {
121
190
  return (_jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { gap: "sm", children: [_jsx("input", { type: "url", value: extInput, onChange: (e) => { setExtInput(e.target.value); setExtError(""); }, placeholder: "https://example.com/image.jpg", className: "appkit-input flex-1" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: onApply, children: "Apply" })] }), extError && _jsx(Alert, { variant: "error", children: extError }), _jsx(Text, { size: "xs", variant: "secondary", children: "External URLs are stored as-is and are not watermarked." })] }));
122
191
  }
123
- function MediaPreviewPanel({ value, label, disabled, enableTrim, enableThumbnail, onEditVideo, onRemove, tUpload, tMediaEditor, }) {
124
- return (_jsxs(Div, { padding: "sm", rounded: "lg", surface: "muted", border: "default", children: [isVideo(value) ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Div, { className: "relative aspect-video", overflow: "hidden", rounded: "lg", children: _jsx(MediaVideo, { src: value, alt: label, controls: true, objectFit: "contain" }) }), _jsx(Text, { size: "xs", variant: "secondary", children: "Video uploaded successfully. Large videos may take longer to process." })] })) : isImage(value) ? (_jsx(Div, { className: "relative aspect-video", overflow: "hidden", rounded: "lg", children: _jsx(MediaImage, { src: value, alt: label, size: "card", objectFit: "contain" }) })) : isPdf(value) ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Div, { className: "relative w-full", overflow: "hidden", rounded: "lg", border: "default", style: { height: 280 }, children: _jsx("iframe", { src: value, title: filenameFromUrl(value), className: "w-full h-full border-0" }) }), _jsxs(Row, { gap: "md", align: "center", children: [_jsx(Div, { "aria-hidden": true, className: CLS_PDF_BADGE, children: _jsx(Span, { size: "xs", weight: "bold", children: "PDF" }) }), _jsx(Anchor, { href: value, className: CLS_PDF_NAME, children: filenameFromUrl(value) })] })] })) : (_jsx(Anchor, { href: value, className: CLS_PDF_LINK, children: filenameFromUrl(value) })), !disabled && (_jsxs(Row, { wrap: true, gap: "sm", className: "mt-2", children: [isVideo(value) && (enableTrim || enableThumbnail) && (_jsx(Button, { type: "button", onClick: () => onEditVideo(value), variant: "secondary", size: "sm", children: tMediaEditor("editVideo") })), _jsx(Button, { type: "button", onClick: onRemove, variant: "danger", size: "sm", children: tUpload("remove") })] }))] }));
192
+ function MediaPreviewPanel({ value, kind, label, disabled, enableTrim, enableThumbnail, onEditVideo, onRemove, tUpload, tMediaEditor, }) {
193
+ return (_jsxs(Div, { padding: "sm", rounded: "lg", surface: "muted", border: "default", children: [kind === "video" ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Div, { className: "relative aspect-video", overflow: "hidden", rounded: "lg", children: _jsx(MediaVideo, { src: value, alt: label, controls: true, objectFit: "contain" }) }), _jsx(Text, { size: "xs", variant: "secondary", children: "Video uploaded successfully. Large videos may take longer to process." })] })) : kind === "image" ? (_jsx(Div, { className: "relative aspect-video", overflow: "hidden", rounded: "lg", children: _jsx(MediaImage, { src: value, alt: label, size: "card", objectFit: "contain", fill: true }) })) : kind === "pdf" ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Div, { className: "relative w-full", overflow: "hidden", rounded: "lg", border: "default", style: { height: 280 }, children: _jsx("iframe", { src: value, title: filenameFromUrl(value), className: "w-full h-full border-0" }) }), _jsxs(Row, { gap: "md", align: "center", children: [_jsx(Div, { "aria-hidden": true, className: CLS_PDF_BADGE, children: _jsx(Span, { size: "xs", weight: "bold", children: "PDF" }) }), _jsx(Anchor, { href: value, className: CLS_PDF_NAME, children: filenameFromUrl(value) })] })] })) : (_jsx(Anchor, { href: value, className: CLS_PDF_LINK, children: filenameFromUrl(value) })), !disabled && (_jsxs(Row, { wrap: true, gap: "sm", className: "mt-2", children: [kind === "video" && (enableTrim || enableThumbnail) && (_jsx(Button, { type: "button", onClick: () => onEditVideo(value), variant: "secondary", size: "sm", children: tMediaEditor("editVideo") })), _jsx(Button, { type: "button", onClick: onRemove, variant: "danger", size: "sm", children: tUpload("remove") })] }))] }));
125
194
  }
126
195
  export function MediaUploadField({ label, value, onChange, onChangeField, onUpload,
127
196
  // SB-UNI-Z5 2026-05-13 — `kind` derives accept/maxSizeMB when not set
@@ -139,6 +208,16 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
139
208
  const [ytError, setYtError] = useState("");
140
209
  const [extInput, setExtInput] = useState("");
141
210
  const [extError, setExtError] = useState("");
211
+ /*
212
+ * The kind of the file we just uploaded, remembered WITH the url it belongs to.
213
+ *
214
+ * Keyed on the url rather than held as a bare kind so it cannot go stale: a
215
+ * parent that swaps `value` out from under us (switching records, a reset, the
216
+ * trim flow replacing the url) simply stops matching and the resolver takes
217
+ * over. An effect clearing it on `value` change would be a second mechanism
218
+ * that has to agree with this one.
219
+ */
220
+ const [uploadedKind, setUploadedKind] = useState(null);
142
221
  const hasAlternateSources = showYoutube || showExternal;
143
222
  /*
144
223
  * PDF mode: derived from the `accept` prop. It forces file-only capture
@@ -256,6 +335,10 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
256
335
  ? "image/*,video/*"
257
336
  : "image/*";
258
337
  const afterUpload = (url, fileType, autoPosterUrl, duration) => {
338
+ // The one authoritative signal there is: the file's real MIME. Recorded
339
+ // before anything else so the preview is correct on the very first render
340
+ // after an upload, including for a video that is about to be trimmed.
341
+ setUploadedKind({ url, kind: previewKindFromMime(fileType) });
259
342
  onChangeField?.({
260
343
  url,
261
344
  type: inferMediaTypeFromMime(fileType, url),
@@ -281,6 +364,10 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
281
364
  onChange(url);
282
365
  };
283
366
  const handleTrimSave = (trimmedUrl) => {
367
+ // A trim mints a NEW url, so the kind recorded against the original no
368
+ // longer matches. It is a video by construction — the trim modal only opens
369
+ // for one — so carry that across rather than letting the resolver re-guess.
370
+ setUploadedKind({ url: trimmedUrl, kind: "video" });
284
371
  setShowTrimModal(false);
285
372
  if (enableThumbnail) {
286
373
  setPendingVideoUrl(trimmedUrl);
@@ -411,6 +498,7 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
411
498
  }
412
499
  onChange("");
413
500
  onChangeField?.(null);
501
+ setUploadedKind(null);
414
502
  setError(null);
415
503
  };
416
504
  const handleCameraCapture = async (blob, type) => {
@@ -445,5 +533,5 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
445
533
  return (_jsxs(Stack, { gap: "sm", children: [_jsx(Label, { className: "block", color: "muted", size: "sm", weight: "medium", children: label }), hasAlternateSources && !disabled && (_jsx(Row, { gap: "none", className: "appkit-media-upload__source-tabs", children: ["upload", ...(showYoutube ? ["youtube"] : []), ...(showExternal ? ["external"] : [])].map((tab) => (_jsx("button", { type: "button", onClick: () => setSourceTab(tab), className: [
446
534
  "appkit-media-upload__source-tab",
447
535
  sourceTab === tab ? "appkit-media-upload__source-tab--active" : "",
448
- ].filter(Boolean).join(" "), children: tab === "upload" ? "Upload" : tab === "youtube" ? "YouTube" : "External URL" }, tab))) })), hasAlternateSources && sourceTab === "youtube" && !disabled && (_jsx(YoutubeTabPanel, { ytInput: ytInput, setYtInput: setYtInput, ytError: ytError, setYtError: setYtError, onApply: handleYtApply, value: value })), hasAlternateSources && sourceTab === "external" && !disabled && (_jsx(ExternalUrlTabPanel, { extInput: extInput, setExtInput: setExtInput, extError: extError, setExtError: setExtError, onApply: handleExtApply })), (!hasAlternateSources || sourceTab === "upload") && value && !isLoading && (_jsx(MediaPreviewPanel, { value: value, label: label, disabled: disabled, enableTrim: enableTrim, enableThumbnail: enableThumbnail, onEditVideo: handleEditVideo, onRemove: handleRemove, tUpload: tUpload, tMediaEditor: tMediaEditor })), !disabled && !isLoading && (!hasAlternateSources || sourceTab === "upload") && (_jsxs(_Fragment, { children: [captureSource === "both" && isCameraSupported && (_jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: inputMode === "file" ? "primary" : "outline", size: "sm", onClick: () => setInputMode("file"), children: t("switchToUpload") }), _jsx(Button, { type: "button", variant: inputMode === "camera" ? "primary" : "outline", size: "sm", onClick: () => setInputMode("camera"), children: t("switchToCamera") })] })), showCamera && isCameraSupported && (_jsx(CameraCapture, { mode: captureMode, facingMode: "environment", onCapture: handleCameraCapture, onError: (msg) => setError(msg) })), showCamera && !isCameraSupported && (_jsx(Button, { rounded: "xl", type: "button", onClick: () => mobileCaptureRef.current?.click(), variant: "ghost", className: "w-full py-[var(--appkit-space-3)] border-2 border-dashed border-[var(--appkit-color-border)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)]", children: t("switchToCamera") })), showFileInput && (_jsx(Button, { rounded: "xl", type: "button", onClick: () => fileInputRef.current?.click(), variant: "ghost", className: "w-full py-[var(--appkit-space-3)] border-2 border-dashed border-[var(--appkit-color-border)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)]", children: value ? tUpload("replaceFile") : tUpload("chooseFile") }))] })), isLoading && (_jsxs(Row, { gap: "sm", children: [_jsx(Spinner, { size: "sm" }), _jsx(Text, { size: "sm", variant: "secondary", children: tUpload("uploading") })] })), _jsx("input", { ref: fileInputRef, type: "file", accept: accept, multiple: multiple, onChange: handleFileChange, className: "hidden", "aria-hidden": "true", "data-testid": "media-upload-input" }), showCamera && !isCameraSupported && (_jsx("input", { ref: mobileCaptureRef, type: "file", accept: captureModeAccept, capture: "environment", onChange: handleFileChange, className: "hidden", "aria-hidden": "true", "data-testid": "media-upload-capture-input" })), helperText && !error && (_jsx(Text, { variant: "secondary", size: "xs", children: helperText })), error && _jsx(Alert, { variant: "error", children: error }), pendingVideoUrl && (_jsx(VideoTrimModal, { isOpen: showTrimModal, videoUrl: pendingVideoUrl, onClose: handleTrimClose, onSave: handleTrimSave })), pendingVideoUrl && (_jsx(VideoThumbnailSelector, { isOpen: showThumbnailModal, videoUrl: pendingVideoUrl, onClose: handleThumbnailClose, onSelect: handleThumbnailSelect, onUpload: onUpload }))] }));
536
+ ].filter(Boolean).join(" "), children: tab === "upload" ? "Upload" : tab === "youtube" ? "YouTube" : "External URL" }, tab))) })), hasAlternateSources && sourceTab === "youtube" && !disabled && (_jsx(YoutubeTabPanel, { ytInput: ytInput, setYtInput: setYtInput, ytError: ytError, setYtError: setYtError, onApply: handleYtApply, value: value })), hasAlternateSources && sourceTab === "external" && !disabled && (_jsx(ExternalUrlTabPanel, { extInput: extInput, setExtInput: setExtInput, extError: extError, setExtError: setExtError, onApply: handleExtApply })), (!hasAlternateSources || sourceTab === "upload") && value && !isLoading && (_jsx(MediaPreviewPanel, { value: value, kind: uploadedKind?.url === value ? uploadedKind.kind : previewKindFor(value), label: label, disabled: disabled, enableTrim: enableTrim, enableThumbnail: enableThumbnail, onEditVideo: handleEditVideo, onRemove: handleRemove, tUpload: tUpload, tMediaEditor: tMediaEditor })), !disabled && !isLoading && (!hasAlternateSources || sourceTab === "upload") && (_jsxs(_Fragment, { children: [captureSource === "both" && isCameraSupported && (_jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: inputMode === "file" ? "primary" : "outline", size: "sm", onClick: () => setInputMode("file"), children: t("switchToUpload") }), _jsx(Button, { type: "button", variant: inputMode === "camera" ? "primary" : "outline", size: "sm", onClick: () => setInputMode("camera"), children: t("switchToCamera") })] })), showCamera && isCameraSupported && (_jsx(CameraCapture, { mode: captureMode, facingMode: "environment", onCapture: handleCameraCapture, onError: (msg) => setError(msg) })), showCamera && !isCameraSupported && (_jsx(Button, { rounded: "xl", type: "button", onClick: () => mobileCaptureRef.current?.click(), variant: "ghost", className: "w-full py-[var(--appkit-space-3)] border-2 border-dashed border-[var(--appkit-color-border)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)]", children: t("switchToCamera") })), showFileInput && (_jsx(Button, { rounded: "xl", type: "button", onClick: () => fileInputRef.current?.click(), variant: "ghost", className: "w-full py-[var(--appkit-space-3)] border-2 border-dashed border-[var(--appkit-color-border)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)]", children: value ? tUpload("replaceFile") : tUpload("chooseFile") }))] })), isLoading && (_jsxs(Row, { gap: "sm", children: [_jsx(Spinner, { size: "sm" }), _jsx(Text, { size: "sm", variant: "secondary", children: tUpload("uploading") })] })), _jsx("input", { ref: fileInputRef, type: "file", accept: accept, multiple: multiple, onChange: handleFileChange, className: "hidden", "aria-hidden": "true", "data-testid": "media-upload-input" }), showCamera && !isCameraSupported && (_jsx("input", { ref: mobileCaptureRef, type: "file", accept: captureModeAccept, capture: "environment", onChange: handleFileChange, className: "hidden", "aria-hidden": "true", "data-testid": "media-upload-capture-input" })), helperText && !error && (_jsx(Text, { variant: "secondary", size: "xs", children: helperText })), error && _jsx(Alert, { variant: "error", children: error }), pendingVideoUrl && (_jsx(VideoTrimModal, { isOpen: showTrimModal, videoUrl: pendingVideoUrl, onClose: handleTrimClose, onSave: handleTrimSave })), pendingVideoUrl && (_jsx(VideoThumbnailSelector, { isOpen: showThumbnailModal, videoUrl: pendingVideoUrl, onClose: handleThumbnailClose, onSelect: handleThumbnailSelect, onUpload: onUpload }))] }));
449
537
  }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * `OBLIGATION_POLICIES` — the ONE place a lifecycle duration lives.
3
+ *
4
+ * ## What this replaces
5
+ *
6
+ * Durations were scattered across hardcoded constants, dead `siteSettings`
7
+ * fields, and published FAQ prose that disagreed with all of them. The plan's
8
+ * own audit of it found eighteen dead timer settings (D28/D44/D57/D60) and
9
+ * these live contradictions:
10
+ *
11
+ * | fact | code said | the FAQ said | a guide said |
12
+ * |---|---|---|---|
13
+ * | seller return response | nothing enforced | 48 hours | 3 days |
14
+ * | offer checkout deadline | 48h | 24h | — |
15
+ * | return window | `ORDER_RETURN_WINDOW_DAYS` = 7 | 7 days / 2 days / 48h | — |
16
+ *
17
+ * `publishedCopyKey` is the half that fixes that: Phase 9 renders the number
18
+ * from HERE, so published copy **cannot** disagree with the code. A duration
19
+ * changed in this file changes the policy page, the FAQ and the sweep together.
20
+ *
21
+ * ## `deadlinesFor(order)` is a PURE FUNCTION over this registry
22
+ *
23
+ * So a buyer or seller page can render "Dispatch by Thu 3 Nov" with **zero
24
+ * extra reads**. That is strictly better than storing deadline fields on the
25
+ * order, which would need a backfill and would drift from the policy the day
26
+ * somebody edited one — the mirror trap (Root Cause #42).
27
+ *
28
+ * ## 🛑 Every duration here is a DEFAULT, and `settingPath` says which admin
29
+ * field overrides it
30
+ *
31
+ * A duration with no reader is a dead setting; a setting with no duration is a
32
+ * control that silently does nothing. `platformLimits`' own comment states the
33
+ * rule its three deleted siblings were removed under: *"Do not re-add them
34
+ * without a reader: a control that silently does nothing is worse than an
35
+ * absent one."* This registry is the reader.
36
+ */
37
+ import type { OrderDocument } from "../../orders/schemas/firestore";
38
+ import type { ObligationDueAction, ObligationKind } from "../schemas/firestore";
39
+ export type DurationUnit = "minutes" | "hours" | "days";
40
+ export interface Duration {
41
+ value: number;
42
+ unit: DurationUnit;
43
+ /**
44
+ * `business` counts weekends and the Indian holiday calendar; `calendar` is
45
+ * wall-clock.
46
+ *
47
+ * 🛑 These are NOT interchangeable and the distinction was a live bug:
48
+ * `getBusinessDayCutoff` subtracted CALENDAR days and called them business
49
+ * days, so the "7-business-day" payout hold was really about 5 — four
50
+ * calendar days short of what it promised. A duration that says `business`
51
+ * must go through `addBusinessDays`.
52
+ */
53
+ calendar: "calendar" | "business";
54
+ }
55
+ export interface ObligationPolicy {
56
+ /** Which fact starts the clock. `null` means the clock is not running yet. */
57
+ anchor: (order: OrderDocument) => Date | null | undefined;
58
+ duration: Duration;
59
+ /** Warn this long BEFORE it fires, so the deadline is visible in advance. */
60
+ warnAt?: readonly Duration[];
61
+ /**
62
+ * Re-checked at FIRE time, against a freshly-read subject.
63
+ *
64
+ * 🛑 This is what makes the collection an index rather than truth. A
65
+ * `seller-accept` obligation whose order has since been paid-and-accepted
66
+ * returns `false` here and closes `superseded` — no action, no case, no
67
+ * noise.
68
+ */
69
+ precondition: (order: OrderDocument) => boolean;
70
+ /** Kinds that must be closed before this one may fire. */
71
+ blockedBy?: readonly ObligationKind[];
72
+ /** The guardrail tier. See `ObligationDueAction`. */
73
+ onDue: ObligationDueAction;
74
+ /** The `siteSettings` path an admin can override the duration with. */
75
+ settingPath?: string;
76
+ /** The copy key Phase 9 renders this duration into. */
77
+ publishedCopyKey: string;
78
+ /** One line for an operator reading a case opened from this kind. */
79
+ describe: string;
80
+ }
81
+ export declare const OBLIGATION_POLICIES: Record<ObligationKind, ObligationPolicy>;
82
+ /**
83
+ * Minutes a duration represents, for the non-business case.
84
+ *
85
+ * Exported so the sweep and `deadlinesFor` share one conversion rather than
86
+ * two — and `PAYMENT_WINDOW_MINUTES` is imported here purely so the one
87
+ * pre-existing minute-scale window in the system is expressible in the same
88
+ * vocabulary if it is ever folded in.
89
+ */
90
+ export declare function durationToMinutes(d: Duration): number;
91
+ /** The payment window, in this registry's vocabulary. Not yet an obligation kind. */
92
+ export declare const PAYMENT_WINDOW_AS_DURATION: Duration;
@@ -0,0 +1,378 @@
1
+ /**
2
+ * `OBLIGATION_POLICIES` — the ONE place a lifecycle duration lives.
3
+ *
4
+ * ## What this replaces
5
+ *
6
+ * Durations were scattered across hardcoded constants, dead `siteSettings`
7
+ * fields, and published FAQ prose that disagreed with all of them. The plan's
8
+ * own audit of it found eighteen dead timer settings (D28/D44/D57/D60) and
9
+ * these live contradictions:
10
+ *
11
+ * | fact | code said | the FAQ said | a guide said |
12
+ * |---|---|---|---|
13
+ * | seller return response | nothing enforced | 48 hours | 3 days |
14
+ * | offer checkout deadline | 48h | 24h | — |
15
+ * | return window | `ORDER_RETURN_WINDOW_DAYS` = 7 | 7 days / 2 days / 48h | — |
16
+ *
17
+ * `publishedCopyKey` is the half that fixes that: Phase 9 renders the number
18
+ * from HERE, so published copy **cannot** disagree with the code. A duration
19
+ * changed in this file changes the policy page, the FAQ and the sweep together.
20
+ *
21
+ * ## `deadlinesFor(order)` is a PURE FUNCTION over this registry
22
+ *
23
+ * So a buyer or seller page can render "Dispatch by Thu 3 Nov" with **zero
24
+ * extra reads**. That is strictly better than storing deadline fields on the
25
+ * order, which would need a backfill and would drift from the policy the day
26
+ * somebody edited one — the mirror trap (Root Cause #42).
27
+ *
28
+ * ## 🛑 Every duration here is a DEFAULT, and `settingPath` says which admin
29
+ * field overrides it
30
+ *
31
+ * A duration with no reader is a dead setting; a setting with no duration is a
32
+ * control that silently does nothing. `platformLimits`' own comment states the
33
+ * rule its three deleted siblings were removed under: *"Do not re-add them
34
+ * without a reader: a control that silently does nothing is worse than an
35
+ * absent one."* This registry is the reader.
36
+ */
37
+ import { ORDER_RETURN_WINDOW_DAYS, ORDER_AUTO_CONFIRM_DAYS, } from "../../../_internal/shared/features/orders/config.js";
38
+ import { PROTECTION_CLAIM_WINDOW_HOURS } from "../../../_internal/shared/features/shipping/protections.js";
39
+ import { PAYMENT_WINDOW_MINUTES } from "../../orders/constants/payment-window.js";
40
+ const isPaid = (o) => o.paymentStatus === "paid";
41
+ const notTerminal = (o) => o.status !== "cancelled" && o.status !== "refunded";
42
+ /**
43
+ * 🛑 `shippingDate != null` IS THE DISCRIMINATOR for before/after dispatch,
44
+ * not the status set — and this is the single highest-value line in the whole
45
+ * obligation design.
46
+ *
47
+ * "Cancel before dispatch" and "cancel after dispatch" are different events
48
+ * with different money: before, the buyer gets everything back; after, the
49
+ * buyer bears outbound shipping and it becomes a return. Today
50
+ * `assertOrderCancellable` just throws *"Only pending or confirmed orders can
51
+ * be cancelled"* and offers the buyer nothing — because it discriminates on
52
+ * STATUS, which cannot express "has the parcel physically left".
53
+ */
54
+ const hasDispatched = (o) => Boolean(o.shippingDate);
55
+ export const OBLIGATION_POLICIES = {
56
+ /*
57
+ * ── PRE-DISPATCH ─────────────────────────────────────────────────────────
58
+ */
59
+ /**
60
+ * The seller never accepts. **3 calendar days**, the figure the owner gave.
61
+ *
62
+ * 🛑 D30: there IS no seller-acceptance step today. `customShipOrder`
63
+ * requires `status === "confirmed"`, and **nothing ever sets `confirmed` on a
64
+ * paid order** except an admin PATCH — `onOrderCreate` writes one admin
65
+ * notification and stops. So this obligation is the first thing in the
66
+ * system that notices a seller ignoring an order.
67
+ *
68
+ * `act`: the ceiling is what we captured, the recipient is the payer, and
69
+ * the seller is not debited — they simply forfeit the sale. The platform
70
+ * absorbs the gateway fee, which is the cost of the seller's inaction and
71
+ * not the buyer's problem.
72
+ */
73
+ "seller-accept": {
74
+ anchor: (o) => o.orderDate ?? o.createdAt,
75
+ duration: { value: 3, unit: "days", calendar: "calendar" },
76
+ warnAt: [{ value: 24, unit: "hours", calendar: "calendar" }],
77
+ precondition: (o) => isPaid(o) && notTerminal(o) && !o.acceptedAt && !hasDispatched(o),
78
+ onDue: "act",
79
+ publishedCopyKey: "obligations.sellerAccept",
80
+ describe: "The seller did not accept this order within the acceptance window.",
81
+ },
82
+ /**
83
+ * Accepted but never dispatched. **2 business days**, because a weekend is
84
+ * not a seller ignoring you.
85
+ */
86
+ "seller-dispatch": {
87
+ anchor: (o) => o.acceptedAt,
88
+ duration: { value: 2, unit: "days", calendar: "business" },
89
+ warnAt: [{ value: 8, unit: "hours", calendar: "calendar" }],
90
+ precondition: (o) => isPaid(o) && notTerminal(o) && !hasDispatched(o),
91
+ blockedBy: ["seller-accept"],
92
+ onDue: "act",
93
+ publishedCopyKey: "obligations.sellerDispatch",
94
+ describe: "The seller accepted this order but did not dispatch it in time.",
95
+ },
96
+ /**
97
+ * A rejected payment proof the buyer never replaced.
98
+ *
99
+ * 🛑 THIS IS THE D27 FIX. An order in `reupload_requested` was caught by
100
+ * ZERO sweeps — excluded by all three because it has a proof, has an
101
+ * outcome, and has a deadline — so it held stock forever with no terminal
102
+ * state. One policy row replaces the reasoning that failed.
103
+ */
104
+ "proof-reupload": {
105
+ anchor: (o) => o.paymentReviewedAt,
106
+ duration: { value: 24, unit: "hours", calendar: "calendar" },
107
+ warnAt: [{ value: 4, unit: "hours", calendar: "calendar" }],
108
+ precondition: (o) => o.paymentReviewOutcome === "reupload_requested" && notTerminal(o) && !isPaid(o),
109
+ onDue: "act",
110
+ settingPath: "platformLimits.proofReuploadWindowHours",
111
+ publishedCopyKey: "obligations.proofReupload",
112
+ describe: "The buyer did not re-upload a payment proof after one was rejected.",
113
+ },
114
+ /*
115
+ * ── IN TRANSIT ───────────────────────────────────────────────────────────
116
+ */
117
+ /**
118
+ * Nobody ever confirms delivery. **14 days** — which is what the dead
119
+ * `ORDER_AUTO_CONFIRM_DAYS` constant was for, and this is its first reader.
120
+ *
121
+ * 🛑 Ordered BEFORE everything post-delivery, because it PRODUCES the anchor
122
+ * those depend on (`deliveryDate`). And `blockedBy` is load-bearing here: a
123
+ * parcel lost in transit must not auto-confirm, open a return window, and
124
+ * start the seller's payout clock. A transit claim blocks it.
125
+ */
126
+ "delivery-confirm": {
127
+ anchor: (o) => o.shippingDate,
128
+ duration: { value: ORDER_AUTO_CONFIRM_DAYS, unit: "days", calendar: "calendar" },
129
+ precondition: (o) => hasDispatched(o) && !o.deliveryDate && notTerminal(o),
130
+ blockedBy: ["claim-shipping"],
131
+ onDue: "act",
132
+ settingPath: "platformLimits.autoConfirmDeliveryDays",
133
+ publishedCopyKey: "obligations.deliveryConfirm",
134
+ describe: "Delivery was never confirmed, so it is being recorded as delivered.",
135
+ },
136
+ /*
137
+ * ── RETURNS ──────────────────────────────────────────────────────────────
138
+ */
139
+ /**
140
+ * The seller ignores a return request.
141
+ *
142
+ * 🛑 **48 hours, and this number settles a live contradiction**: the FAQ
143
+ * says the seller has 48 hours, `AdminOrdersGuideView` says 3 days, and
144
+ * nothing enforced either. Picking one and rendering it everywhere via
145
+ * `publishedCopyKey` is the point — 48h is the shorter, so it is the one
146
+ * that cannot make us look like we broke a published promise.
147
+ *
148
+ * `act`: auto-approving a return the seller ignored refunds the BUYER from
149
+ * money we hold. The seller-side consequence is a separate decision and is
150
+ * NOT taken here.
151
+ */
152
+ "return-respond": {
153
+ anchor: (o) => o.returnRequestedAt,
154
+ duration: { value: 48, unit: "hours", calendar: "calendar" },
155
+ warnAt: [{ value: 12, unit: "hours", calendar: "calendar" }],
156
+ precondition: (o) => o.status === "return_requested" && !o.returnPostedAt,
157
+ onDue: "act",
158
+ settingPath: "returns.sellerResponseHours",
159
+ publishedCopyKey: "obligations.returnRespond",
160
+ describe: "The seller did not respond to this return request in time.",
161
+ },
162
+ /** Return approved, buyer never posts it. No refund; the window does not reopen. */
163
+ "return-post": {
164
+ anchor: (o) => o.returnApprovedAt,
165
+ duration: { value: ORDER_RETURN_WINDOW_DAYS, unit: "days", calendar: "calendar" },
166
+ warnAt: [{ value: 2, unit: "days", calendar: "calendar" }],
167
+ precondition: (o) => Boolean(o.returnApprovedAt) && !o.returnPostedAt,
168
+ blockedBy: ["return-respond"],
169
+ onDue: "act",
170
+ settingPath: "returns.windowDays",
171
+ publishedCopyKey: "obligations.returnPost",
172
+ describe: "The buyer did not post the return within the window.",
173
+ },
174
+ /** Posted but never arrives — a transit problem, so a human looks. */
175
+ "return-receive": {
176
+ anchor: (o) => o.returnPostedAt,
177
+ duration: { value: 10, unit: "days", calendar: "calendar" },
178
+ precondition: (o) => Boolean(o.returnPostedAt) && !o.returnReceivedAt,
179
+ blockedBy: ["return-post"],
180
+ onDue: "escalate",
181
+ publishedCopyKey: "obligations.returnReceive",
182
+ describe: "A posted return never arrived at the seller.",
183
+ },
184
+ /**
185
+ * Received but never inspected. **3 business days, then the buyer wins.**
186
+ *
187
+ * 🛑 `act` on the BUYER side only. The buyer is refunded automatically
188
+ * because the seller had three business days and did nothing; the seller is
189
+ * **charged only by a human**, which is why there is no seller-debit here.
190
+ * Those are two different decisions and the plan's guardrail table keeps
191
+ * them apart deliberately.
192
+ */
193
+ "return-inspect": {
194
+ anchor: (o) => o.returnReceivedAt,
195
+ duration: { value: 3, unit: "days", calendar: "business" },
196
+ warnAt: [{ value: 1, unit: "days", calendar: "business" }],
197
+ precondition: (o) => Boolean(o.returnReceivedAt) && !o.returnInspection?.inspectedAt,
198
+ blockedBy: ["return-receive"],
199
+ onDue: "act",
200
+ settingPath: "returns.inspectionBusinessDays",
201
+ publishedCopyKey: "obligations.returnInspect",
202
+ describe: "A received return was never inspected, so it resolves in the buyer's favour.",
203
+ },
204
+ /*
205
+ * ── PROTECTION CLAIM WINDOWS ─────────────────────────────────────────────
206
+ *
207
+ * 🛑 These make the add-on REAL for the first time. We charge 2% (min ₹30)
208
+ * shipment protection and 3% (min ₹49) sale protection, the flags are seeded
209
+ * `true`, and `adjudicateClaim()` has existed with zero callers — so the fee
210
+ * was collected against a promise with no mechanism (D11).
211
+ *
212
+ * Closing the window is what makes the premium EARNED, which is also what
213
+ * the payout hold waits for. `act`, because closing a window moves no money.
214
+ */
215
+ "claim-shipping": {
216
+ anchor: (o) => o.deliveryDate,
217
+ duration: {
218
+ value: PROTECTION_CLAIM_WINDOW_HOURS.shipping,
219
+ unit: "hours",
220
+ calendar: "calendar",
221
+ },
222
+ precondition: (o) => Boolean(o.deliveryDate) && Boolean(o.shipmentProtectionFee),
223
+ onDue: "act",
224
+ publishedCopyKey: "obligations.claimShipping",
225
+ describe: "The shipment-protection claim window has closed.",
226
+ },
227
+ "claim-sale": {
228
+ anchor: (o) => o.deliveryDate,
229
+ duration: {
230
+ value: PROTECTION_CLAIM_WINDOW_HOURS.sale,
231
+ unit: "hours",
232
+ calendar: "calendar",
233
+ },
234
+ precondition: (o) => Boolean(o.deliveryDate) && Boolean(o.saleProtectionFee),
235
+ onDue: "act",
236
+ publishedCopyKey: "obligations.claimSale",
237
+ describe: "The sale-protection claim window has closed.",
238
+ },
239
+ /*
240
+ * ── COMPLETION ───────────────────────────────────────────────────────────
241
+ */
242
+ /**
243
+ * 7 days after delivery with no issue raised: the order is COMPLETE, points
244
+ * are awarded, and no NEW dispute may be opened.
245
+ *
246
+ * 🛑 Owner's requirement, 2026-10-21. Two things about it need stating
247
+ * because getting either wrong changes the product:
248
+ *
249
+ * **1. "No further disputes entertained" means NO NEW DISPUTE MAY BE RAISED
250
+ * — it cannot mean "everything closes at 7 days".** A return requested on
251
+ * day 6 still has to be approved, posted, received and inspected, which takes
252
+ * weeks; and `claim-sale`'s window is itself 168h = exactly 7 days. Reading
253
+ * it as a hard close would cancel in-flight returns the buyer raised in time,
254
+ * which is the opposite of what a dispute window is for. So this fires a
255
+ * CUTOFF for new claims and leaves open ones running — and `precondition`
256
+ * below is what makes that true, by refusing to fire while anything is still
257
+ * in flight.
258
+ *
259
+ * **2. The award must be idempotent, because completion has two triggers.**
260
+ * The other is the buyer posting a review, which is event-driven and needs no
261
+ * timer. Whichever happens first completes the order. A ledger entry keyed on
262
+ * the order is what stops a review on day 3 and this sweep on day 7 awarding
263
+ * twice — `pointsLedger` is append-only, so an un-keyed second append would
264
+ * be a permanent double-credit with no way to net it off.
265
+ *
266
+ * 🛑 **`act`, but the points award is NOT built yet** — Phase P is step 16.
267
+ * Firing this today closes the dispute window and records the completion;
268
+ * the ledger write is added when Phase P lands, and the sweep's own narrowing
269
+ * (see `performAction`) keeps anything monetary escalating until then.
270
+ *
271
+ * 🛑 **One open question I am NOT guessing at**, recorded in the plan: the
272
+ * requirement says *"if no review no points"* AND *"they are also assigned if
273
+ * the user [raises] no issues… after 7 days"*. Those two cannot both hold —
274
+ * the second awards points to a buyer who never reviewed. Implemented as
275
+ * "review OR 7-day silence", which is what *"they are ALSO assigned"* says,
276
+ * and flagged for confirmation.
277
+ */
278
+ "order-complete": {
279
+ anchor: (o) => o.deliveryDate,
280
+ duration: { value: 7, unit: "days", calendar: "calendar" },
281
+ warnAt: [{ value: 2, unit: "days", calendar: "calendar" }],
282
+ /*
283
+ * Refuses to fire while ANY issue is live: a return in any state, a refund
284
+ * already posted, or a buyer-raised dispute. That is what makes the cutoff
285
+ * safe — it can only close a window that nothing is currently using.
286
+ */
287
+ precondition: (o) => Boolean(o.deliveryDate) &&
288
+ /*
289
+ * 🛑 `!o.completedAt` — the BUYER may have completed it already.
290
+ *
291
+ * The owner's fast path (`markOrderCompleteAction`: rate an item, mark
292
+ * complete, immediate disburse) sets `completedAt` and closes this
293
+ * obligation as `met`. Without this clause the sweep would still consider
294
+ * the order due at day 7 and fire a second completion — re-closing a
295
+ * closed window and, once Phase P lands, awarding points a second time
296
+ * against an append-only ledger.
297
+ *
298
+ * Belt and braces with the obligation already being closed, deliberately:
299
+ * the row is an index and the order is the truth, so the truth gets a say.
300
+ */
301
+ !o.completedAt &&
302
+ notTerminal(o) &&
303
+ o.status !== "return_requested" &&
304
+ o.status !== "returned" &&
305
+ !o.returnRequestedAt &&
306
+ !o.disputeRaised &&
307
+ (o.refunds?.length ?? 0) === 0,
308
+ onDue: "act",
309
+ settingPath: "returns.disputeWindowDays",
310
+ publishedCopyKey: "obligations.orderComplete",
311
+ describe: "7 days passed since delivery with no issue raised — the order is complete and the dispute window has closed.",
312
+ },
313
+ /*
314
+ * ── MONEY OUT ────────────────────────────────────────────────────────────
315
+ */
316
+ /**
317
+ * When the seller's funds become payable.
318
+ *
319
+ * 🛑 `escalate`, NOT `act`, and the asymmetry is the rule: **withholding a
320
+ * payout is always safe; releasing one never is.** This obligation's firing
321
+ * means "the quiet period has elapsed" — it does not itself pay anybody.
322
+ * The payout job reads the same cutoff and decides.
323
+ *
324
+ * `blockedBy` is the quiet-period invariant in declarative form: an order's
325
+ * funds are payable only when every obligation that could still move money
326
+ * OUT of it is closed.
327
+ */
328
+ "payout-release": {
329
+ anchor: (o) => o.deliveryDate,
330
+ duration: { value: 7, unit: "days", calendar: "business" },
331
+ precondition: (o) => Boolean(o.deliveryDate) && notTerminal(o),
332
+ /*
333
+ * 🛑 `order-complete` is in this list, and it is the one that makes the
334
+ * quiet period mean what it says. The others close individual windows; the
335
+ * dispute cutoff is the fact that NO NEW claim can arrive. Releasing a
336
+ * payout before it has fired means paying a seller while a buyer can still
337
+ * raise a dispute against that order — which is how a chargeback lands
338
+ * after the money has left.
339
+ */
340
+ blockedBy: [
341
+ "return-respond",
342
+ "return-post",
343
+ "return-receive",
344
+ "return-inspect",
345
+ "claim-shipping",
346
+ "claim-sale",
347
+ "order-complete",
348
+ ],
349
+ onDue: "escalate",
350
+ settingPath: "commissions.payoutHoldDays",
351
+ publishedCopyKey: "obligations.payoutRelease",
352
+ describe: "The payout quiet period has elapsed for this order.",
353
+ },
354
+ };
355
+ /**
356
+ * Minutes a duration represents, for the non-business case.
357
+ *
358
+ * Exported so the sweep and `deadlinesFor` share one conversion rather than
359
+ * two — and `PAYMENT_WINDOW_MINUTES` is imported here purely so the one
360
+ * pre-existing minute-scale window in the system is expressible in the same
361
+ * vocabulary if it is ever folded in.
362
+ */
363
+ export function durationToMinutes(d) {
364
+ switch (d.unit) {
365
+ case "minutes":
366
+ return d.value;
367
+ case "hours":
368
+ return d.value * 60;
369
+ case "days":
370
+ return d.value * 24 * 60;
371
+ }
372
+ }
373
+ /** The payment window, in this registry's vocabulary. Not yet an obligation kind. */
374
+ export const PAYMENT_WINDOW_AS_DURATION = {
375
+ value: PAYMENT_WINDOW_MINUTES,
376
+ unit: "minutes",
377
+ calendar: "calendar",
378
+ };