@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
@@ -10,8 +10,14 @@ import { serverLogger } from "../../../monitoring/index.js";
10
10
  import { orderRepository } from "../../orders/repository/orders.repository.js";
11
11
  import { RefundStatusValues } from "../../orders/schemas/index.js";
12
12
  import { siteSettingsRepository } from "../../admin/repository/site-settings.repository.js";
13
+ /*
14
+ * One owner for "how much of the platform fee is withheld". Shared with the
15
+ * cumulative refund cap, so the figure SHOWN to the buyer and the money
16
+ * actually withheld cannot diverge — they did: this file showed a net figure
17
+ * while `cancelOrder` moved no money at all (D14).
18
+ */
19
+ import { platformFeeWithheld, refundCapacity, } from "../../../_internal/shared/checkout/refundable-base.js";
13
20
  import { sendNotification } from "../../admin/actions/notification-actions.js";
14
- const DEFAULT_PLATFORM_FEE_PERCENT = 5;
15
21
  const DEFAULT_GST_PERCENT = 18;
16
22
  export async function issuePartialRefund(adminUid, orderId, deductFees, refundNote) {
17
23
  const order = await orderRepository.findById(orderId);
@@ -22,13 +28,37 @@ export async function issuePartialRefund(adminUid, orderId, deductFees, refundNo
22
28
  if (order.refundStatus === RefundStatusValues.COMPLETED)
23
29
  throw new ValidationError("Order has already been fully refunded.");
24
30
  const settings = await siteSettingsRepository.getSingleton();
25
- const platformFeePercent = settings.commissions?.platformFeePercent ?? DEFAULT_PLATFORM_FEE_PERCENT;
26
31
  const gstPercent = settings.commissions?.gstPercent ?? DEFAULT_GST_PERCENT;
27
- const effectiveRate = platformFeePercent * (1 + gstPercent / 100);
32
+ /*
33
+ * ── D32: THE SINGLE WORST MONEY DEFECT IN THE AUDIT ─────────────────────
34
+ *
35
+ * This computed a PERCENTAGE OF THE REFUND:
36
+ *
37
+ * const effectiveRate = platformFeePercent * (1 + gstPercent / 100);
38
+ * const feeDeducted = (grossRefund * effectiveRate) / 100;
39
+ *
40
+ * which ignores `commissions.platformFeeMax` (default **₹10**) entirely, and
41
+ * ignores `order.platformFee` — the amount that was ACTUALLY COLLECTED and
42
+ * is stored on the order.
43
+ *
44
+ * Measured on a ₹5,000 order at 2% + 18% GST: it deducted **₹118** from a
45
+ * buyer who had paid **₹11.80**, keeping ~₹106.20 of their GOODS money and
46
+ * labelling it a platform fee. The error grows with order value, so it was
47
+ * largest exactly where a buyer would notice.
48
+ *
49
+ * 🛑 `platformFeeWithheld` returns the fee as CHARGED, never a recomputed
50
+ * rate, and it is shared with the cumulative refund cap — so the figure
51
+ * SHOWN to the buyer here and the money actually withheld now come from one
52
+ * place. They did not before: this function showed a net figure while
53
+ * `cancelOrder` moved no money at all (D14).
54
+ *
55
+ * `gstPercent` is passed only as the LEGACY fallback, for orders written
56
+ * before `platformFeeGst` was persisted (2026-10-21). An order that carries
57
+ * the field ignores it entirely — which is the point, since reading today's
58
+ * rate for a historical order is the mistake this replaces.
59
+ */
28
60
  const grossRefund = order.totalPrice;
29
- const feeDeducted = deductFees
30
- ? parseFloat(((grossRefund * effectiveRate) / 100).toFixed(2))
31
- : 0;
61
+ const feeDeducted = deductFees ? platformFeeWithheld(order, gstPercent) : 0;
32
62
  const netRefund = parseFloat((grossRefund - feeDeducted).toFixed(2));
33
63
  await orderRepository.update(orderId, {
34
64
  refundStatus: "processing",
@@ -75,12 +105,42 @@ export async function previewCancellationRefund(userId, orderId) {
75
105
  if (order.paymentStatus !== "paid")
76
106
  return null;
77
107
  const settings = await siteSettingsRepository.getSingleton();
78
- const platformFeePercent = settings.commissions?.platformFeePercent ?? DEFAULT_PLATFORM_FEE_PERCENT;
79
108
  const gstPercent = settings.commissions?.gstPercent ?? DEFAULT_GST_PERCENT;
80
- const effectiveRate = platformFeePercent * (1 + gstPercent / 100);
109
+ /*
110
+ * 🛑 A SECOND COPY of D32 lived here, and the plan only named one.
111
+ *
112
+ * Identical arithmetic to the function above — `platformFeePercent × (1 +
113
+ * gstPercent/100)` applied to the whole refund, ignoring the ₹10 cap and the
114
+ * stored `order.platformFee`. And this is the WORSE of the two, because this
115
+ * is the function whose output is shown to the BUYER before they confirm a
116
+ * cancellation: it quoted a net refund ₹106 short on a ₹5,000 order.
117
+ *
118
+ * Found by grepping for the shape rather than the name — the plan cited
119
+ * `refund-actions.ts:104-110`, which is the other one. Root Cause #75's
120
+ * lesson exactly: a rule reachable by copy-paste gets copied, and the copy
121
+ * is not found by searching for the function that was supposed to own it.
122
+ *
123
+ * ── 2026-10-21: and `refundCapacity`, not `totalPrice − withheld` ─────────
124
+ *
125
+ * 🛑 The net figure now subtracts refunds ALREADY COMMITTED on this order,
126
+ * because `cancelOrderWithRefund` moves exactly `refundCapacity().remaining`
127
+ * — so if these two expressions differ, the number shown to the buyer is not
128
+ * the number that moves, which is the whole defect D14 is about.
129
+ *
130
+ * They differ on precisely one order: a partially-refunded one. The old
131
+ * expression ignored prior refunds, so cancelling an order that had already
132
+ * had a line refunded quoted the buyer the full base a second time. That
133
+ * would not have over-paid — `reserveRefund`'s transactional cap refuses it
134
+ * — but it would have quoted a figure and then failed, which is a worse
135
+ * experience than quoting the right one.
136
+ *
137
+ * On an order with no prior refunds the two are identical, so nothing changes
138
+ * for the common case.
139
+ */
140
+ const capacity = refundCapacity(order, gstPercent);
81
141
  const grossRefund = order.totalPrice;
82
- const feeDeducted = parseFloat(((grossRefund * effectiveRate) / 100).toFixed(2));
83
- const netRefund = parseFloat((grossRefund - feeDeducted).toFixed(2));
142
+ const feeDeducted = capacity.withheld;
143
+ const netRefund = capacity.remaining;
84
144
  return {
85
145
  orderId,
86
146
  grossRefund,
@@ -0,0 +1,27 @@
1
+ export interface RateableLine {
2
+ orderItemId: string;
3
+ productId: string;
4
+ productTitle: string;
5
+ existingRating?: number;
6
+ }
7
+ export interface OrderCompletionPanelProps {
8
+ orderId: string;
9
+ /** `/api/user/orders/{id}/completion`. Injected so appkit owns no route map. */
10
+ endpoint: string;
11
+ /** Already complete — the panel then only confirms it. */
12
+ completedAt?: string | null;
13
+ /** Who closed it: the buyer's uid, or `"system"` for the 7-day sweep. */
14
+ completedBy?: string | null;
15
+ /** Delivered? Nothing can be rated or completed before the parcel arrives. */
16
+ isDelivered: boolean;
17
+ /**
18
+ * Anything contested — an open return, refund or dispute.
19
+ *
20
+ * The server refuses in that case regardless; this suppresses the panel so
21
+ * the buyer is not offered a button that would be refused, and is told why.
22
+ */
23
+ isContested?: boolean;
24
+ /** Called after a successful completion, so the page can refresh the order. */
25
+ onCompleted?: () => void;
26
+ }
27
+ export declare function OrderCompletionPanel({ orderId, endpoint, completedAt, completedBy, isDelivered, isContested, onCompleted, }: OrderCompletionPanelProps): import("react").JSX.Element | null;
@@ -0,0 +1,192 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * OrderCompletionPanel — rate the lines, then close the order.
5
+ *
6
+ * ## The owner's rule, and why this panel is the only way to use it
7
+ *
8
+ * *"points only get issued on successful purchases after buyer put reviews,
9
+ * then only… they are also assigned if the user [raises] no issues or updates
10
+ * after 7 days of delivery"*, and then: *"auto assigned after 7 days of
11
+ * delivery, when order is marked complete; if review is given and order marked
12
+ * complete by the buyer before — immediate disburse."*
13
+ *
14
+ * So completion has two writers converging on one fact (`completedAt`): the
15
+ * 7-day `order-complete` obligation, and this. Both were built — the sweep,
16
+ * the dispute cutoff, the per-item review link, `markOrderCompleteAction` — and
17
+ * **the buyer had no UI for any of it.** A grep for `markOrderCompleteAction`
18
+ * across the app returned two hits, both my own comments. This is the surface
19
+ * that makes the rule usable rather than merely implemented.
20
+ *
21
+ * ## 🛑 ≥1 rated line, never all of them
22
+ *
23
+ * Requiring every line makes a six-item order's completion hostage to the one
24
+ * item the buyer has no opinion about — and their alternative is to wait seven
25
+ * days and be completed automatically with **no** review, which is worse for
26
+ * everyone. One rating is the signal; demanding six is a toll. The server
27
+ * enforces the same rule, so this is the affordance, not the gate.
28
+ *
29
+ * ## 🛑 The button says what it DOES, all four consequences
30
+ *
31
+ * Completing closes the dispute window, releases the seller's payout and
32
+ * awards points. A buyer pressing "Mark complete" without being told that it
33
+ * ends their ability to raise a new dispute would be waiving a right by
34
+ * pressing a button labelled as housekeeping. `payout-release` is
35
+ * `blockedBy: ["order-complete"]`, so the early release is a CONSEQUENCE of
36
+ * this press rather than a second decision — which is precisely why it has to
37
+ * be disclosed here.
38
+ *
39
+ * ## Per LINE, not per product (the Zomato shape)
40
+ *
41
+ * `orderItemId` is `${orderId}__${productId}__${n}`, so the same product
42
+ * ordered twice is two ratings. `ReviewDocument` was already per-product; what
43
+ * it lacked was any link to the ORDER — no `orderId` anywhere, and
44
+ * `isVerifiedPurchase` was read in two places and written by nothing, so a
45
+ * review had never been attributable to a purchase.
46
+ *
47
+ * EXPORTS: OrderCompletionPanel, type OrderCompletionPanelProps
48
+ *
49
+ * @tag domain:orders,reviews
50
+ * @tag layer:component
51
+ * @tag pattern:form
52
+ * @tag access:client
53
+ * @tag consumers:user/orders/[id]
54
+ * @tag sideEffects:network
55
+ */
56
+ import { useCallback, useEffect, useState } from "react";
57
+ import { Alert } from "../../../ui/components/Alert.js";
58
+ import { Button } from "../../../ui/components/Button.js";
59
+ /*
60
+ * `Div` has its OWN module; `Row` and `Stack` are in `Layout`. Imported from
61
+ * the defining modules rather than the `ui` barrel — Root Cause #18, and the
62
+ * barrel is also what the build-weight work is unwinding.
63
+ */
64
+ import { Div } from "../../../ui/components/Div.js";
65
+ import { Icon } from "../../../ui/components/Icon.js";
66
+ import { Row, Stack } from "../../../ui/components/Layout.js";
67
+ import { Text } from "../../../ui/components/Typography.js";
68
+ import { Textarea } from "../../../ui/components/Textarea.js";
69
+ import { Skeleton } from "../../../ui/components/Skeleton.js";
70
+ import { normalizeError } from "../../../errors/normalize.js";
71
+ const MIN_COMMENT = 10;
72
+ /**
73
+ * Stars as buttons.
74
+ *
75
+ * 🛑 `aria-label` per star and `aria-pressed` on the selected one — a row of
76
+ * five unlabelled glyphs is unusable with a screen reader, and the rating is
77
+ * the only required field here. Colour is never the sole signal: the selected
78
+ * star is filled AND pressed.
79
+ */
80
+ function StarRow({ value, onChange, disabled, }) {
81
+ return (_jsx(Row, { gap: "xs", role: "group", "aria-label": "Rating out of 5", children: [1, 2, 3, 4, 5].map((n) => (_jsx(Button, { variant: n <= value ? "primary" : "outline", size: "sm", disabled: disabled, "aria-label": `${n} star${n === 1 ? "" : "s"}`, "aria-pressed": n === value, onClick: () => onChange(n), children: _jsx(Icon, { name: "review", filled: n <= value }) }, n))) }));
82
+ }
83
+ export function OrderCompletionPanel({ orderId, endpoint, completedAt, completedBy, isDelivered, isContested, onCompleted, }) {
84
+ const [lines, setLines] = useState([]);
85
+ const [loading, setLoading] = useState(true);
86
+ const [loadError, setLoadError] = useState(null);
87
+ const [openLine, setOpenLine] = useState(null);
88
+ const [rating, setRating] = useState(0);
89
+ const [comment, setComment] = useState("");
90
+ const [busy, setBusy] = useState(false);
91
+ const [actionError, setActionError] = useState(null);
92
+ const load = useCallback(() => {
93
+ setLoading(true);
94
+ setLoadError(null);
95
+ fetch(endpoint)
96
+ .then(async (r) => {
97
+ const body = await r.json().catch(() => null);
98
+ /*
99
+ * 🛑 The status is checked BEFORE the body is trusted. `errorResponse()`
100
+ * is valid JSON with a non-2xx status, so `body?.data?.items ?? []`
101
+ * would turn "the request failed" into "nothing to rate" — and this
102
+ * panel would then hide itself, which is the most convincing possible
103
+ * way to lose a feature.
104
+ */
105
+ if (!r.ok)
106
+ throw new Error(body?.error ?? `Could not load this order (${r.status})`);
107
+ return (body?.data?.items ?? []);
108
+ })
109
+ .then(setLines)
110
+ .catch((err) => setLoadError(normalizeError(err).message))
111
+ .finally(() => setLoading(false));
112
+ }, [endpoint]);
113
+ useEffect(() => {
114
+ if (!completedAt && isDelivered && !isContested)
115
+ load();
116
+ else
117
+ setLoading(false);
118
+ }, [completedAt, isDelivered, isContested, load]);
119
+ // ── Already complete: confirm it and say which path closed it ────────────
120
+ if (completedAt) {
121
+ const bySystem = completedBy === "system";
122
+ return (_jsx(Div, { surface: "card", padding: "md", children: _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "This order is complete" }), _jsxs(Text, { size: "xs", color: "muted", children: [bySystem
123
+ ? `Completed automatically on ${new Date(completedAt).toLocaleDateString()}, seven days after delivery with nothing raised against it.`
124
+ : `You marked this complete on ${new Date(completedAt).toLocaleDateString()}.`, " ", "The dispute window for it has closed."] })] }) }));
125
+ }
126
+ if (!isDelivered)
127
+ return null;
128
+ if (isContested) {
129
+ return (_jsx(Div, { surface: "card", padding: "md", children: _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Rating is paused on this order" }), _jsx(Text, { size: "xs", color: "muted", children: "There is an open return, refund or dispute. Once that is resolved you can rate what you bought and close the order." })] }) }));
130
+ }
131
+ const ratedCount = lines.filter((l) => l.existingRating != null).length;
132
+ const canComplete = ratedCount > 0;
133
+ const submitRating = async (orderItemId) => {
134
+ setBusy(true);
135
+ setActionError(null);
136
+ try {
137
+ const res = await fetch(endpoint, {
138
+ method: "POST",
139
+ headers: { "Content-Type": "application/json" },
140
+ body: JSON.stringify({ action: "rate", orderItemId, rating, comment }),
141
+ });
142
+ if (!res.ok) {
143
+ const body = await res.json().catch(() => null);
144
+ throw new Error(body?.error ?? `That rating was not saved (${res.status})`);
145
+ }
146
+ setOpenLine(null);
147
+ setRating(0);
148
+ setComment("");
149
+ load();
150
+ }
151
+ catch (err) {
152
+ setActionError(normalizeError(err).message);
153
+ }
154
+ finally {
155
+ setBusy(false);
156
+ }
157
+ };
158
+ const complete = async () => {
159
+ setBusy(true);
160
+ setActionError(null);
161
+ try {
162
+ const res = await fetch(endpoint, {
163
+ method: "POST",
164
+ headers: { "Content-Type": "application/json" },
165
+ body: JSON.stringify({ action: "complete" }),
166
+ });
167
+ if (!res.ok) {
168
+ const body = await res.json().catch(() => null);
169
+ throw new Error(body?.error ?? `The order was not completed (${res.status})`);
170
+ }
171
+ onCompleted?.();
172
+ }
173
+ catch (err) {
174
+ setActionError(normalizeError(err).message);
175
+ }
176
+ finally {
177
+ setBusy(false);
178
+ }
179
+ };
180
+ return (_jsx(Div, { surface: "card", padding: "md", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Rate what you bought" }), _jsx(Text, { size: "xs", color: "muted", children: "Rate each item separately. One rating is enough to close the order \u2014 or leave it and we will close it automatically seven days after delivery." })] }), actionError ? (_jsx(Alert, { variant: "error", title: "That did not save", children: actionError })) : null, loading ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Skeleton, { variant: "rectangular", height: "48px" }), _jsx(Skeleton, { variant: "rectangular", height: "48px" })] })) : loadError ? (_jsxs(Alert, { variant: "error", title: "Couldn't load the items", children: [loadError, " Refresh to retry."] })) : lines.length === 0 ? (_jsx(Text, { size: "xs", color: "muted", children: "There is nothing on this order to rate." })) : (_jsx(Stack, { gap: "sm", children: lines.map((line) => (_jsxs(Div, { border: "default", rounded: "default", padding: "sm", children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", wrap: true, children: [_jsx(Text, { size: "sm", className: "flex-1 line-clamp-2", color: "primary", children: line.productTitle }), line.existingRating != null ? (_jsxs(Text, { size: "xs", color: "success", children: ["★".repeat(line.existingRating), " rated"] })) : openLine === line.orderItemId ? null : (_jsx(Button, { variant: "outline", size: "sm", onClick: () => {
181
+ setOpenLine(line.orderItemId);
182
+ setRating(0);
183
+ setComment("");
184
+ }, children: "Rate this" }))] }), openLine === line.orderItemId ? (_jsxs(Stack, { gap: "sm", className: "mt-2", children: [_jsx(StarRow, { value: rating, onChange: setRating, disabled: busy }), _jsx(Textarea, { value: comment, onChange: (e) => setComment(e.target.value), rows: 3, placeholder: "What was it like? The next buyer reads this." }), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { variant: "primary", size: "sm", isLoading: busy,
185
+ /*
186
+ * Mirrors the route's own floor. A one-word review is
187
+ * indistinguishable from an empty box to the next
188
+ * buyer, which is why the server has the same minimum
189
+ * rather than this being a UI preference.
190
+ */
191
+ disabled: busy || rating === 0 || comment.trim().length < MIN_COMMENT, onClick: () => submitRating(line.orderItemId), children: "Save rating" }), _jsx(Button, { variant: "ghost", size: "sm", disabled: busy, onClick: () => setOpenLine(null), children: "Cancel" })] })] })) : null] }, line.orderItemId))) })), _jsx(Div, { border: "subtle", className: "border-t", padding: "t-sm", children: _jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "xs", color: "muted", children: "Marking this complete confirms you are happy with it. It closes the window for raising a new dispute on this order, releases the seller\u2019s payment, and credits your points." }), _jsxs(Row, { gap: "sm", align: "center", wrap: true, children: [_jsx(Button, { variant: "primary", size: "sm", isLoading: busy, disabled: busy || !canComplete, onClick: complete, children: "Mark complete" }), !canComplete ? (_jsx(Text, { size: "xs", color: "muted", children: "Rate at least one item first." })) : null] })] }) })] }) }));
192
+ }
@@ -14,7 +14,7 @@ const STATUS_COLORS = {
14
14
  cancelled: "bg-error-surface text-error",
15
15
  refunded: "bg-warning-surface text-warning",
16
16
  return_requested: "bg-warning-surface text-warning",
17
- returned: "bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text-muted)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)]",
17
+ returned: "bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)]",
18
18
  };
19
19
  export function OrderCard({ order, onClick, labels = {}, renderActions }) {
20
20
  const date = order.createdAt
@@ -24,7 +24,7 @@ export function OrderCard({ order, onClick, labels = {}, renderActions }) {
24
24
  day: "numeric",
25
25
  })
26
26
  : "";
27
- const statusColor = STATUS_COLORS[order.orderStatus] ?? "bg-[var(--appkit-color-surface)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)]";
27
+ const statusColor = STATUS_COLORS[order.orderStatus] ?? "bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text-muted)]";
28
28
  // SB8-F — count unrevealed prize-draw entries to surface a "reveals pending" badge.
29
29
  const unrevealedPrizeDraws = order.items.filter((it) => it.listingType === "prize-draw" &&
30
30
  it.prizeRevealStatus !== undefined &&
@@ -10,7 +10,9 @@ function formatDate(d) {
10
10
  return dt.toLocaleDateString("en-IN", { day: "numeric", month: "short", year: "numeric" });
11
11
  }
12
12
  function RefundRow({ event, currency }) {
13
- return (_jsxs(Row, { border: "subtle", justify: "between", align: "start", gap: "sm", className: "border-b last:border-0", padding: "y-sm", children: [_jsxs(Stack, { gap: "xs", className: "flex-1", children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Badge, { variant: event.type === "full" ? "danger" : "warning", children: event.type === "full" ? REFUND_COPY.history.badgeFull : REFUND_COPY.history.badgePartial }), _jsx(Text, { size: "xs", color: "muted", children: formatDate(event.refundedAt) })] }), _jsx(Text, { size: "sm", children: event.reason }), event.manualTransactionId && (_jsxs(Text, { size: "xs", color: "muted", children: [REFUND_COPY.history.labelTxn, " ", event.manualTransactionId] })), event.phonepeRefundId && (_jsxs(Text, { size: "xs", color: "muted", children: [REFUND_COPY.history.labelPhonePe, " ", event.phonepeRefundId] }))] }), _jsx(Text, { size: "sm", weight: "semibold", className: "shrink-0", children: formatCurrency(event.amount, currency) })] }));
13
+ return (_jsxs(Row, { border: "subtle", justify: "between", align: "start", gap: "sm", className: "border-b last:border-0", padding: "y-sm", children: [_jsxs(Stack, { gap: "xs", className: "flex-1", children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Badge, { variant: event.type === "full" ? "danger" : "warning", children: event.type === "full" ? REFUND_COPY.history.badgeFull : REFUND_COPY.history.badgePartial }), _jsx(Text, { size: "xs", color: "muted", children: formatDate(event.refundedAt) })] }), _jsx(Text, { size: "sm", children: event.reason }), event.manualTransactionId && (_jsxs(Text, { size: "xs", color: "muted", children: [REFUND_COPY.history.labelTxn, " ", event.manualTransactionId] })), (event.gatewayRefundId ?? event.phonepeRefundId) && (_jsxs(Text, { size: "xs", color: "muted", children: [event.gateway
14
+ ? `${event.gateway} refund:`
15
+ : REFUND_COPY.history.labelPhonePe, " ", event.gatewayRefundId ?? event.phonepeRefundId] })), event.status === "failed" && (_jsxs(Text, { size: "xs", color: "error", children: ["Failed", event.failureReason ? ` — ${event.failureReason}` : ""] })), event.status === "reserved" && (_jsx(Text, { size: "xs", color: "muted", children: "In progress \u2014 awaiting the gateway" }))] }), _jsx(Text, { size: "sm", weight: "semibold", className: "shrink-0", children: formatCurrency(event.amount, currency) })] }));
14
16
  }
15
17
  /** Warning triangle icon — inline SVG kept here since it's a single-use decorative element. */
16
18
  function WarningIcon() {
@@ -15,3 +15,5 @@ export type { RefundRequestViewProps } from "./RefundRequestView";
15
15
  export { YourPurchaseStrip } from "./YourPurchaseStrip";
16
16
  export type { YourPurchaseStripProps, PurchaseContext } from "./YourPurchaseStrip";
17
17
  export { EmiInstallmentsPanel, type EmiInstallmentsPanelProps, } from "./EmiInstallmentsPanel";
18
+ export { OrderCompletionPanel } from "./OrderCompletionPanel";
19
+ export type { OrderCompletionPanelProps, RateableLine } from "./OrderCompletionPanel";
@@ -8,3 +8,9 @@ export { RefundHistoryTable } from "./RefundHistoryTable.js";
8
8
  export { RefundRequestView } from "./RefundRequestView.js";
9
9
  export { YourPurchaseStrip } from "./YourPurchaseStrip.js";
10
10
  export { EmiInstallmentsPanel, } from "./EmiInstallmentsPanel.js";
11
+ /*
12
+ * The buyer's completion surface — per-line ratings plus Mark complete. The
13
+ * owner's completion rule had no UI at all before this: a grep for
14
+ * `markOrderCompleteAction` across the app returned two hits, both comments.
15
+ */
16
+ export { OrderCompletionPanel } from "./OrderCompletionPanel.js";
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The order status machine — which transitions are legal.
3
+ *
4
+ * ## 🛑 THIS MAP WAS BUILT FROM A CENSUS, NOT FROM A DESIGN
5
+ *
6
+ * The plan that called for it said "~32 writers and no `from` check anywhere,
7
+ * so inventing the map silently breaks working flows — the failure mode is a
8
+ * legitimate admin action that now throws." That is the right fear and it is
9
+ * why a read-only census came first. **What the census found contradicted the
10
+ * plan's own number**, which is worth recording:
11
+ *
12
+ * `orderRepository.updateStatus` has **four** callers, not thirty-two:
13
+ *
14
+ * 1. `_internal/server/features/orders/actions.ts:99` — hardcodes
15
+ * `"return_requested"`, cast `as any`.
16
+ * 2. `_internal/server/features/orders/actions.ts:113` — passes
17
+ * `parsed.data.status` straight through, cast `as any`.
18
+ * 3. `src/app/api/store/orders/[id]/route.ts:242` — passes `data.status`
19
+ * from the SELLER's request body.
20
+ * 4. `features/admin/actions/admin-actions.ts:152`.
21
+ *
22
+ * 🛑 **And the finding that matters: two of those four accept an arbitrary
23
+ * status from a request body, both with `as any` silencing the one type-level
24
+ * signal there was.** So a seller PATCHing their own order could set
25
+ * `delivered` on an unpaid order, or regress `delivered` → `pending`, and
26
+ * nothing anywhere would refuse it. The "~32 writers" framing made this look
27
+ * like a sprawling refactor; it is four call sites and a guard.
28
+ *
29
+ * (The other ~30 hits a naive grep returns are `offer.repository.ts`'s OWN
30
+ * `updateStatus` — a different method on a different collection. Conflating
31
+ * them is how the estimate got to 32, and acting on it would have meant
32
+ * "fixing" the offer lifecycle by accident.)
33
+ *
34
+ * ## Three entries look wrong and are not
35
+ *
36
+ * - **`return_requested` → `delivered`.** A REJECTED return must go back to
37
+ * its prior terminal state, not strand the order in a requested-return limbo
38
+ * forever. Without this the seller's "no, this is not a valid return" has no
39
+ * expressible outcome.
40
+ * - **`cancelled` → `refunded` and `returned` → `refunded`.** Money moves
41
+ * AFTER the goods decision. `refunded` is the only genuinely terminal state;
42
+ * `cancelled` is a goods outcome that may still owe money.
43
+ * - **`pending` → `refunded` is ABSENT.** A pending order was never captured,
44
+ * so there is nothing to refund. If a writer is found doing this, the writer
45
+ * is the bug — do not widen the map to accommodate it.
46
+ */
47
+ import type { OrderStatus } from "../types/index";
48
+ /**
49
+ * Legal next states per status. A `Record<OrderStatus, …>` so a tenth status
50
+ * cannot be added to the union without declaring where it can go — an array or
51
+ * a `Record<string, …>` catches neither mistake, which is how ten independent
52
+ * enumerations of `ListingType` came to disagree (Root Cause #61).
53
+ */
54
+ export declare const ORDER_STATUS_TRANSITIONS: Record<OrderStatus, readonly OrderStatus[]>;
55
+ /** Statuses from which nothing may follow. */
56
+ export declare const ORDER_TERMINAL_STATUSES: readonly OrderStatus[];
57
+ export declare function isLegalOrderTransition(from: OrderStatus, to: OrderStatus): boolean;
58
+ /** Human-readable refusal, for the error a caller sees. */
59
+ export declare function describeIllegalTransition(from: OrderStatus, to: OrderStatus): string;
@@ -0,0 +1,104 @@
1
+ /**
2
+ * The order status machine — which transitions are legal.
3
+ *
4
+ * ## 🛑 THIS MAP WAS BUILT FROM A CENSUS, NOT FROM A DESIGN
5
+ *
6
+ * The plan that called for it said "~32 writers and no `from` check anywhere,
7
+ * so inventing the map silently breaks working flows — the failure mode is a
8
+ * legitimate admin action that now throws." That is the right fear and it is
9
+ * why a read-only census came first. **What the census found contradicted the
10
+ * plan's own number**, which is worth recording:
11
+ *
12
+ * `orderRepository.updateStatus` has **four** callers, not thirty-two:
13
+ *
14
+ * 1. `_internal/server/features/orders/actions.ts:99` — hardcodes
15
+ * `"return_requested"`, cast `as any`.
16
+ * 2. `_internal/server/features/orders/actions.ts:113` — passes
17
+ * `parsed.data.status` straight through, cast `as any`.
18
+ * 3. `src/app/api/store/orders/[id]/route.ts:242` — passes `data.status`
19
+ * from the SELLER's request body.
20
+ * 4. `features/admin/actions/admin-actions.ts:152`.
21
+ *
22
+ * 🛑 **And the finding that matters: two of those four accept an arbitrary
23
+ * status from a request body, both with `as any` silencing the one type-level
24
+ * signal there was.** So a seller PATCHing their own order could set
25
+ * `delivered` on an unpaid order, or regress `delivered` → `pending`, and
26
+ * nothing anywhere would refuse it. The "~32 writers" framing made this look
27
+ * like a sprawling refactor; it is four call sites and a guard.
28
+ *
29
+ * (The other ~30 hits a naive grep returns are `offer.repository.ts`'s OWN
30
+ * `updateStatus` — a different method on a different collection. Conflating
31
+ * them is how the estimate got to 32, and acting on it would have meant
32
+ * "fixing" the offer lifecycle by accident.)
33
+ *
34
+ * ## Three entries look wrong and are not
35
+ *
36
+ * - **`return_requested` → `delivered`.** A REJECTED return must go back to
37
+ * its prior terminal state, not strand the order in a requested-return limbo
38
+ * forever. Without this the seller's "no, this is not a valid return" has no
39
+ * expressible outcome.
40
+ * - **`cancelled` → `refunded` and `returned` → `refunded`.** Money moves
41
+ * AFTER the goods decision. `refunded` is the only genuinely terminal state;
42
+ * `cancelled` is a goods outcome that may still owe money.
43
+ * - **`pending` → `refunded` is ABSENT.** A pending order was never captured,
44
+ * so there is nothing to refund. If a writer is found doing this, the writer
45
+ * is the bug — do not widen the map to accommodate it.
46
+ */
47
+ /**
48
+ * Legal next states per status. A `Record<OrderStatus, …>` so a tenth status
49
+ * cannot be added to the union without declaring where it can go — an array or
50
+ * a `Record<string, …>` catches neither mistake, which is how ten independent
51
+ * enumerations of `ListingType` came to disagree (Root Cause #61).
52
+ */
53
+ export const ORDER_STATUS_TRANSITIONS = {
54
+ pending: ["confirmed", "processing", "cancelled"],
55
+ confirmed: ["processing", "shipped", "cancelled", "refunded"],
56
+ processing: ["shipped", "cancelled", "refunded"],
57
+ shipped: ["delivered", "return_requested", "cancelled", "refunded"],
58
+ delivered: ["return_requested", "refunded"],
59
+ return_requested: ["returned", "delivered", "refunded", "cancelled"],
60
+ returned: ["refunded"],
61
+ cancelled: ["refunded"],
62
+ refunded: [],
63
+ };
64
+ /*
65
+ * Two entries beyond the plan's proposed map, and they have DIFFERENT
66
+ * justifications — which is worth separating, because one is evidence and the
67
+ * other is caution.
68
+ *
69
+ * **`pending → processing` — VERIFIED.** `paymentReviewAutoApprove.ts:66`
70
+ * writes `status: "processing"` on a pending order, and
71
+ * `checkout/actions.ts:1210` creates one directly as `PROCESSING` under the
72
+ * admin bypass. The plan's proposed map omitted this hop, so adopting the plan
73
+ * verbatim would have made the 2-hour auto-approve sweep start throwing — the
74
+ * exact "a legitimate flow now fails" regression the census existed to prevent.
75
+ * The census wins, as the plan instructed.
76
+ *
77
+ * 🛑 **`shipped → cancelled` — NOT verified, permissive by choice.** I wrote a
78
+ * comment claiming the census justified this via an RTO flow, then checked: the
79
+ * only grep hits were that comment and an unused `RTO_ENTRY_CUSTODY` constant.
80
+ * No writer produces it today. It is kept because the asymmetry of harm is
81
+ * one-sided — being too permissive here allows a transition nothing currently
82
+ * makes, while being too strict would break a dispatch-then-cancel flow the
83
+ * moment one is built — but it is a JUDGEMENT, not a measurement, and the next
84
+ * person should know which is which.
85
+ */
86
+ /** Statuses from which nothing may follow. */
87
+ export const ORDER_TERMINAL_STATUSES = ["refunded"];
88
+ export function isLegalOrderTransition(from, to) {
89
+ /*
90
+ * A no-op write is always legal. Re-writing the same status happens all the
91
+ * time — a patch that touches `trackingNumber` and re-sends the status it
92
+ * already had — and refusing it would break those writers for no gain.
93
+ */
94
+ if (from === to)
95
+ return true;
96
+ return (ORDER_STATUS_TRANSITIONS[from] ?? []).includes(to);
97
+ }
98
+ /** Human-readable refusal, for the error a caller sees. */
99
+ export function describeIllegalTransition(from, to) {
100
+ const allowed = ORDER_STATUS_TRANSITIONS[from] ?? [];
101
+ return allowed.length === 0
102
+ ? `An order in "${from}" is terminal — it cannot move to "${to}".`
103
+ : `An order in "${from}" cannot move to "${to}". Allowed: ${allowed.join(", ")}.`;
104
+ }