@mohasinac/appkit 4.62.0 → 4.63.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 (391) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -29
  3. package/dist/_internal/client/features/layout/filterNavItems.d.ts +28 -0
  4. package/dist/_internal/client/features/layout/filterNavItems.js +35 -0
  5. package/dist/_internal/server/features/ads/booking.d.ts +118 -0
  6. package/dist/_internal/server/features/ads/booking.js +326 -0
  7. package/dist/_internal/server/features/checkout/actions.d.ts +129 -9
  8. package/dist/_internal/server/features/checkout/actions.js +850 -79
  9. package/dist/_internal/server/features/checkout/expand-order-items.d.ts +1 -2
  10. package/dist/_internal/server/features/checkout/expand-order-items.js +32 -0
  11. package/dist/_internal/server/features/checkout/freeze-parts.d.ts +50 -0
  12. package/dist/_internal/server/features/checkout/freeze-parts.js +94 -0
  13. package/dist/_internal/server/features/checkout/value-otp-gate.d.ts +55 -0
  14. package/dist/_internal/server/features/checkout/value-otp-gate.js +60 -0
  15. package/dist/_internal/server/features/dashboard/attention.d.ts +128 -0
  16. package/dist/_internal/server/features/dashboard/attention.js +183 -0
  17. package/dist/_internal/server/features/dashboard/data.d.ts +47 -0
  18. package/dist/_internal/server/features/dashboard/data.js +82 -0
  19. package/dist/_internal/server/features/inventory/reservations.d.ts +99 -0
  20. package/dist/_internal/server/features/inventory/reservations.js +273 -0
  21. package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
  22. package/dist/_internal/server/features/invoicing/allocate.js +162 -0
  23. package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
  24. package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
  25. package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
  26. package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
  27. package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
  28. package/dist/_internal/server/features/invoicing/issue.js +202 -0
  29. package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
  30. package/dist/_internal/server/features/offers/destinations.js +65 -0
  31. package/dist/_internal/server/features/orders/actions.d.ts +8 -1
  32. package/dist/_internal/server/features/orders/actions.js +81 -8
  33. package/dist/_internal/server/features/orders/adapters.js +54 -0
  34. package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
  35. package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
  36. package/dist/_internal/server/features/orders/balance.d.ts +89 -0
  37. package/dist/_internal/server/features/orders/balance.js +256 -0
  38. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
  39. package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
  40. package/dist/_internal/server/features/points/award.js +46 -4
  41. package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
  42. package/dist/_internal/server/features/pricing/actions.js +144 -0
  43. package/dist/_internal/server/features/refunds/actions.js +26 -0
  44. package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
  45. package/dist/_internal/server/features/shipping/book-split.js +130 -0
  46. package/dist/_internal/server/features/shipping/book.d.ts +61 -0
  47. package/dist/_internal/server/features/shipping/book.js +176 -0
  48. package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
  49. package/dist/_internal/server/features/shipping/courier-block.js +264 -0
  50. package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
  51. package/dist/_internal/server/features/site-settings/adapters.js +29 -0
  52. package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
  53. package/dist/_internal/server/features/stores/adapters.js +65 -0
  54. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  55. package/dist/_internal/server/functions/scheduled.js +95 -2
  56. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +11 -2
  57. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
  58. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
  59. package/dist/_internal/server/jobs/core/index.d.ts +2 -1
  60. package/dist/_internal/server/jobs/core/index.js +2 -1
  61. package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
  62. package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
  63. package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
  64. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
  65. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +65 -0
  66. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
  67. package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
  68. package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
  69. package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
  70. package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
  71. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +25 -2
  72. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  73. package/dist/_internal/server/jobs/handlers/index.js +1 -0
  74. package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
  75. package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
  76. package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
  77. package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
  78. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  79. package/dist/_internal/shared/actions/action-registry.js +296 -0
  80. package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
  81. package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
  82. package/dist/_internal/shared/checkout/order-math.d.ts +174 -0
  83. package/dist/_internal/shared/checkout/order-math.js +159 -0
  84. package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
  85. package/dist/_internal/shared/checkout/refundable-base.js +13 -1
  86. package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
  87. package/dist/_internal/shared/checkout/rules/index.js +1 -1
  88. package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
  89. package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
  90. package/dist/_internal/shared/checkout/split.d.ts +75 -0
  91. package/dist/_internal/shared/checkout/split.js +120 -0
  92. package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
  93. package/dist/_internal/shared/content/disclaimers.js +217 -0
  94. package/dist/_internal/shared/content/published-values.d.ts +93 -0
  95. package/dist/_internal/shared/content/published-values.js +206 -0
  96. package/dist/_internal/shared/features/admin/integrations.d.ts +139 -0
  97. package/dist/_internal/shared/features/admin/integrations.js +263 -0
  98. package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
  99. package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
  100. package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
  101. package/dist/_internal/shared/features/emi/choice.js +69 -0
  102. package/dist/_internal/shared/features/emi/group.d.ts +84 -0
  103. package/dist/_internal/shared/features/emi/group.js +126 -0
  104. package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
  105. package/dist/_internal/shared/features/ledger/posting.js +165 -0
  106. package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
  107. package/dist/_internal/shared/features/ledger/reports.js +127 -0
  108. package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
  109. package/dist/_internal/shared/features/ledger/rules.js +221 -0
  110. package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
  111. package/dist/_internal/shared/features/offers/coordination.js +80 -0
  112. package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
  113. package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
  114. package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
  115. package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
  116. package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
  117. package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
  118. package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
  119. package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
  120. package/dist/_internal/shared/features/points/accepts.d.ts +29 -0
  121. package/dist/_internal/shared/features/points/accepts.js +49 -0
  122. package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
  123. package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
  124. package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
  125. package/dist/_internal/shared/features/shipping/parcel.js +139 -0
  126. package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
  127. package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
  128. package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
  129. package/dist/_internal/shared/features/site-settings/fees.js +12 -8
  130. package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
  131. package/dist/_internal/shared/fees/calculator.d.ts +33 -1
  132. package/dist/_internal/shared/fees/calculator.js +32 -2
  133. package/dist/_internal/shared/fees/explain.d.ts +81 -0
  134. package/dist/_internal/shared/fees/explain.js +101 -0
  135. package/dist/_internal/shared/history/index.d.ts +1 -1
  136. package/dist/_internal/shared/history/index.js +1 -1
  137. package/dist/_internal/shared/history/types.d.ts +7 -1
  138. package/dist/_internal/shared/history/types.js +25 -0
  139. package/dist/client.d.ts +21 -0
  140. package/dist/client.js +43 -0
  141. package/dist/constants/api-endpoints.d.ts +114 -0
  142. package/dist/constants/api-endpoints.js +55 -0
  143. package/dist/constants/field-names.d.ts +42 -0
  144. package/dist/constants/field-names.js +52 -0
  145. package/dist/contracts/index.d.ts +1 -1
  146. package/dist/contracts/shipping.d.ts +45 -0
  147. package/dist/contracts/shipping.js +15 -0
  148. package/dist/features/about/components/AboutView.js +1 -1
  149. package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
  150. package/dist/features/account/components/UserOrdersView.js +47 -35
  151. package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
  152. package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
  153. package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
  154. package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
  155. package/dist/features/admin/components/AdminInvoicingView.js +65 -0
  156. package/dist/features/admin/components/AdminOrderEditorView.d.ts +33 -1
  157. package/dist/features/admin/components/AdminOrderEditorView.js +81 -3
  158. package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
  159. package/dist/features/admin/components/AdminOrdersView.js +86 -5
  160. package/dist/features/admin/components/OwnerQuickActions.js +24 -2
  161. package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
  162. package/dist/features/admin/constants/filter-tabs.js +6 -0
  163. package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
  164. package/dist/features/admin/hooks/useAdminListingData.js +16 -0
  165. package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
  166. package/dist/features/admin/schemas/firestore.d.ts +62 -0
  167. package/dist/features/admin/schemas/firestore.js +5 -0
  168. package/dist/features/ads/schemas/booking-write.d.ts +120 -0
  169. package/dist/features/ads/schemas/booking-write.js +75 -0
  170. package/dist/features/ads/schemas/creative-form.d.ts +18 -0
  171. package/dist/features/ads/schemas/creative-form.js +51 -0
  172. package/dist/features/ads/schemas/firestore.d.ts +140 -0
  173. package/dist/features/ads/schemas/firestore.js +109 -0
  174. package/dist/features/auctions/repository/bid.repository.js +3 -1
  175. package/dist/features/audit-log/schemas/firestore.d.ts +140 -0
  176. package/dist/features/audit-log/schemas/firestore.js +89 -0
  177. package/dist/features/auth/actions/profile-actions.d.ts +69 -1
  178. package/dist/features/auth/actions/profile-actions.js +62 -1
  179. package/dist/features/auth/permissions/constants.js +13 -0
  180. package/dist/features/auth/schemas/firestore.d.ts +16 -2
  181. package/dist/features/auth/schemas/firestore.js +2 -1
  182. package/dist/features/blog/actions/blog-actions.d.ts +6 -0
  183. package/dist/features/blog/actions/blog-actions.js +17 -2
  184. package/dist/features/blog/api/route.js +4 -1
  185. package/dist/features/blog/columns/index.js +5 -0
  186. package/dist/features/blog/components/BlogIndexPageView.js +2 -1
  187. package/dist/features/blog/repository/blog.repository.d.ts +4 -0
  188. package/dist/features/blog/repository/blog.repository.js +5 -0
  189. package/dist/features/blog/schemas/blog-form.d.ts +3 -0
  190. package/dist/features/blog/schemas/blog-form.js +1 -0
  191. package/dist/features/blog/schemas/firestore.d.ts +38 -1
  192. package/dist/features/blog/schemas/firestore.js +23 -0
  193. package/dist/features/blog/schemas/index.d.ts +6 -0
  194. package/dist/features/blog/schemas/index.js +2 -0
  195. package/dist/features/blog/types/index.d.ts +2 -0
  196. package/dist/features/cart/components/CartPriceBreakdown.d.ts +25 -1
  197. package/dist/features/cart/components/CartPriceBreakdown.js +29 -6
  198. package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
  199. package/dist/features/cart/components/PointsTenderPicker.js +15 -0
  200. package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
  201. package/dist/features/cart/components/index.d.ts +2 -0
  202. package/dist/features/cart/components/index.js +1 -0
  203. package/dist/features/cart/repository/cart.repository.d.ts +33 -0
  204. package/dist/features/cart/repository/cart.repository.js +47 -0
  205. package/dist/features/cart/schemas/firestore.d.ts +116 -0
  206. package/dist/features/contact/email.d.ts +27 -10
  207. package/dist/features/contact/email.js +20 -3
  208. package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
  209. package/dist/features/dashboard/components/DashboardHubView.js +56 -0
  210. package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
  211. package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
  212. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
  213. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
  214. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +62 -0
  215. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +108 -0
  216. package/dist/features/dispute-reasons/schemas/firestore.d.ts +62 -0
  217. package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
  218. package/dist/features/events/components/EventSaleBanner.js +1 -1
  219. package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
  220. package/dist/features/faq/components/FAQAccordion.js +29 -4
  221. package/dist/features/faq/components/FAQPageContent.js +2 -2
  222. package/dist/features/gateway-events/schemas/index.d.ts +2 -2
  223. package/dist/features/homepage/schemas/index.d.ts +8 -8
  224. package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
  225. package/dist/features/inventory/schemas/stock-reservation.js +103 -0
  226. package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
  227. package/dist/features/invoicing/schemas/firestore.js +45 -0
  228. package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
  229. package/dist/features/invoicing/utils/financial-year.js +53 -0
  230. package/dist/features/jobs/actions/enqueue-job.js +18 -1
  231. package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
  232. package/dist/features/jobs/repository/jobs.repository.js +84 -1
  233. package/dist/features/jobs/schemas/firestore.d.ts +80 -0
  234. package/dist/features/jobs/schemas/firestore.js +37 -0
  235. package/dist/features/layout/AppLayoutShell.d.ts +9 -1
  236. package/dist/features/layout/AppLayoutShell.js +12 -4
  237. package/dist/features/layout/BottomActions.js +2 -1
  238. package/dist/features/layout/BottomNavbar.d.ts +10 -2
  239. package/dist/features/layout/SideActionDock.js +52 -2
  240. package/dist/features/layout/TitleBarLayout.js +13 -4
  241. package/dist/features/layout/TitleBarLayout.style.css +59 -0
  242. package/dist/features/layout/useCtaReveal.d.ts +16 -1
  243. package/dist/features/layout/useCtaReveal.js +17 -26
  244. package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
  245. package/dist/features/ledger/repository/journal.repository.js +312 -0
  246. package/dist/features/ledger/schemas/expenses.d.ts +87 -0
  247. package/dist/features/ledger/schemas/expenses.js +27 -0
  248. package/dist/features/ledger/schemas/firestore.d.ts +183 -0
  249. package/dist/features/ledger/schemas/firestore.js +69 -0
  250. package/dist/features/lottery/schemas/zod.d.ts +6 -6
  251. package/dist/features/media/MediaImage.js +22 -1
  252. package/dist/features/media/upload/ImageUpload.js +1 -1
  253. package/dist/features/obligations/constants/policies.js +24 -1
  254. package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
  255. package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
  256. package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
  257. package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
  258. package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
  259. package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
  260. package/dist/features/orders/components/RaiseDisputeForm.js +119 -0
  261. package/dist/features/orders/components/RefundRequestView.js +19 -1
  262. package/dist/features/orders/components/index.d.ts +4 -0
  263. package/dist/features/orders/components/index.js +2 -0
  264. package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
  265. package/dist/features/orders/constants/order-lane-tabs.js +88 -0
  266. package/dist/features/orders/repository/orders.repository.d.ts +85 -1
  267. package/dist/features/orders/repository/orders.repository.js +205 -6
  268. package/dist/features/orders/schemas/balance.d.ts +40 -0
  269. package/dist/features/orders/schemas/balance.js +47 -0
  270. package/dist/features/orders/schemas/firestore.d.ts +398 -1
  271. package/dist/features/orders/schemas/firestore.js +43 -0
  272. package/dist/features/orders/schemas/index.d.ts +56 -8
  273. package/dist/features/orders/schemas/index.js +12 -0
  274. package/dist/features/orders/types/index.d.ts +47 -0
  275. package/dist/features/orders/utils/courier-block.d.ts +25 -0
  276. package/dist/features/orders/utils/courier-block.js +23 -0
  277. package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
  278. package/dist/features/payment-cases/schemas/firestore.js +1 -0
  279. package/dist/features/payments/repository/payout.repository.js +3 -2
  280. package/dist/features/payments/schemas/firestore.d.ts +92 -1
  281. package/dist/features/payments/schemas/firestore.js +23 -0
  282. package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
  283. package/dist/features/pricing/components/AdminPricingView.js +157 -0
  284. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
  285. package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
  286. package/dist/features/products/components/ProductDetailPageView.js +2 -2
  287. package/dist/features/products/schemas/firestore.d.ts +26 -1
  288. package/dist/features/products/schemas/firestore.js +2 -0
  289. package/dist/features/products/schemas/index.d.ts +2 -2
  290. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  291. package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
  292. package/dist/features/promotions/actions/coupon-stacking.js +21 -2
  293. package/dist/features/promotions/components/CouponCard.js +13 -0
  294. package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
  295. package/dist/features/promotions/constants/coupon-help.js +9 -1
  296. package/dist/features/promotions/schemas/firestore.d.ts +24 -0
  297. package/dist/features/promotions/schemas/firestore.js +4 -0
  298. package/dist/features/promotions/types/index.d.ts +19 -1
  299. package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
  300. package/dist/features/reviews/repository/reviews.repository.js +16 -0
  301. package/dist/features/scams/components/ScamProfileView.js +2 -2
  302. package/dist/features/scams/components/ScamRegistryView.js +2 -2
  303. package/dist/features/scams/repository/scammer.repository.js +2 -1
  304. package/dist/features/scams/schemas/firestore.d.ts +2 -1
  305. package/dist/features/scams/schemas/firestore.js +2 -1
  306. package/dist/features/search/action-index/tab-entries.js +1 -1
  307. package/dist/features/seller/actions/offer-actions.js +56 -0
  308. package/dist/features/seller/actions/seller-actions.d.ts +1 -1
  309. package/dist/features/seller/actions/seller-actions.js +37 -3
  310. package/dist/features/seller/components/DispatchForm.d.ts +11 -0
  311. package/dist/features/seller/components/DispatchForm.js +190 -0
  312. package/dist/features/seller/components/SellerOrdersView.js +221 -27
  313. package/dist/features/seller/components/index.d.ts +1 -0
  314. package/dist/features/seller/components/index.js +1 -0
  315. package/dist/features/seller/hooks/useSellerListingData.js +25 -0
  316. package/dist/features/seller/repository/offer.repository.js +4 -3
  317. package/dist/features/seller/schemas/firestore.d.ts +23 -2
  318. package/dist/features/seller/schemas/firestore.js +3 -2
  319. package/dist/features/seller/schemas/index.d.ts +4 -4
  320. package/dist/features/seller/schemas/order-forms.d.ts +61 -4
  321. package/dist/features/seller/schemas/order-forms.js +41 -8
  322. package/dist/features/shipments/schemas/firestore.d.ts +25 -0
  323. package/dist/features/shipments/schemas/validation.d.ts +4 -4
  324. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +82 -0
  325. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +145 -0
  326. package/dist/features/shipping-boxes/schemas/firestore.d.ts +80 -0
  327. package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
  328. package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
  329. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
  330. package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
  331. package/dist/features/stores/repository/store.repository.d.ts +18 -0
  332. package/dist/features/stores/repository/store.repository.js +27 -1
  333. package/dist/features/stores/schemas/firestore.d.ts +112 -0
  334. package/dist/features/stores/schemas/firestore.js +20 -0
  335. package/dist/features/stores/schemas/index.d.ts +6 -6
  336. package/dist/features/stores/types/index.d.ts +19 -0
  337. package/dist/features/support/repository/support.repository.js +72 -6
  338. package/dist/features/support/schemas/firestore.d.ts +17 -0
  339. package/dist/features/support/schemas/index.d.ts +8 -8
  340. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  341. package/dist/index.d.ts +11 -1
  342. package/dist/index.js +33 -1
  343. package/dist/next/routing/route-map.d.ts +112 -0
  344. package/dist/next/routing/route-map.js +63 -0
  345. package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
  346. package/dist/providers/shipping-shiprocket/index.js +84 -1
  347. package/dist/react/contexts/SessionContext.d.ts +17 -1
  348. package/dist/schemas/registry.d.ts +69 -34
  349. package/dist/schemas/sieve.d.ts +2 -2
  350. package/dist/security/pii-schemas.d.ts +1 -1
  351. package/dist/security/pii-schemas.js +45 -0
  352. package/dist/server-entry.d.ts +2 -2
  353. package/dist/server-entry.js +8 -1
  354. package/dist/server.d.ts +36 -1
  355. package/dist/server.js +183 -1
  356. package/dist/styles.css +412 -80
  357. package/dist/tailwind-utilities.css +1 -1
  358. package/dist/tokens/themes/default-dark.js +1 -0
  359. package/dist/tokens/themes/default-light.js +1 -0
  360. package/dist/tokens/themes/required.d.ts +1 -1
  361. package/dist/tokens/themes/required.js +1 -0
  362. package/dist/tokens/tokens.css +15 -0
  363. package/dist/ui/components/Button.d.ts +10 -1
  364. package/dist/ui/components/Button.js +25 -3
  365. package/dist/ui/components/Chart.d.ts +63 -0
  366. package/dist/ui/components/Chart.js +169 -0
  367. package/dist/ui/components/Chart.style.css +214 -0
  368. package/dist/ui/components/Disclaimer.d.ts +19 -0
  369. package/dist/ui/components/Disclaimer.js +83 -0
  370. package/dist/ui/components/OverflowRow.js +54 -2
  371. package/dist/ui/components/OverflowRow.style.css +38 -0
  372. package/dist/ui/components/RichTextEditor.js +99 -8
  373. package/dist/ui/components/chart-spec.d.ts +68 -0
  374. package/dist/ui/components/chart-spec.js +140 -0
  375. package/dist/ui/components/index.style.css +1 -0
  376. package/dist/ui/components/surface-tokens.d.ts +1 -1
  377. package/dist/ui/components/surface-tokens.js +9 -0
  378. package/dist/ui/components/tabs-overflow.js +64 -11
  379. package/dist/ui/components/toolbar-collapse.d.ts +13 -0
  380. package/dist/ui/components/toolbar-collapse.js +10 -1
  381. package/dist/ui/index.d.ts +8 -0
  382. package/dist/ui/index.js +9 -0
  383. package/dist/ui/rich-text/RichText.d.ts +10 -1
  384. package/dist/ui/rich-text/RichText.js +33 -2
  385. package/dist/ui/rich-text/RichText.style.css +83 -79
  386. package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
  387. package/dist/ui/rich-text/RichTextRenderer.js +34 -2
  388. package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
  389. package/dist/ui/rich-text/chart-blocks.js +87 -0
  390. package/package.json +1 -1
  391. package/scripts/seed-cli.mjs +1834 -1822
@@ -0,0 +1,74 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useMemo, useState } from "react";
4
+ import { Alert, Badge, Button, Div, Heading, Row, Stack, Text, } from "../../../ui/index.js";
5
+ import { useToast } from "../../../ui/components/Toast.js";
6
+ import { normalizeError } from "../../../errors/normalize.js";
7
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
8
+ import { INTEGRATIONS, INTEGRATION_ORDER, allIntegrationsPatch, integrationPatch, readIntegrationStates, } from "../../../_internal/shared/features/admin/integrations.js";
9
+ /** What the switch currently is, in the admin's vocabulary rather than a boolean. */
10
+ function stateLabel(id, enabled) {
11
+ /*
12
+ * 🛑 Shipping is NOT on/off, and this is the one row where saying so matters.
13
+ *
14
+ * "Off" selects `provider: "manual"` — the self-ship flow sellers already
15
+ * use and claim postage back through. Rendering that as "Disabled" beside
16
+ * three switches that genuinely stop things would invite an admin to read it
17
+ * as "shipping is broken", and the descriptor's own note says the switch
18
+ * "selects manual rather than stopping anything".
19
+ */
20
+ if (id === "shipping")
21
+ return enabled ? "We book couriers" : "Sellers self-ship";
22
+ return enabled ? "On" : "Off";
23
+ }
24
+ export function AdminIntegrationsView({ settings, onSave }) {
25
+ const { showToast } = useToast();
26
+ const [pending, setPending] = useState(null);
27
+ /*
28
+ * 🛑 Derived from the prop, NOT copied into state.
29
+ *
30
+ * A `useState(readIntegrationStates(settings))` would go stale the moment
31
+ * the page refetched, and the staleness would be invisible: the row would
32
+ * read "On" while the guard enforcing it read "off". This page's entire job
33
+ * is to agree with the guard, so it re-derives rather than remembers.
34
+ */
35
+ const states = useMemo(() => readIntegrationStates(settings), [settings]);
36
+ const apply = useCallback(async (key, patch, label) => {
37
+ setPending(key);
38
+ try {
39
+ await onSave(patch);
40
+ showToast(`${label} saved.`, "success");
41
+ }
42
+ catch (err) {
43
+ void normalizeError(err);
44
+ /*
45
+ * 🛑 No optimistic flip anywhere in this component, so a failure needs
46
+ * no rollback — the row still shows what the server last told us. On a
47
+ * kill switch the cost of the two mistakes is wildly asymmetric: a
48
+ * row that briefly lags is a nuisance, while a row claiming "payments
49
+ * off" over a live gateway is the failure the model's own header warns
50
+ * about.
51
+ */
52
+ showToast(`Couldn't save ${label.toLowerCase()}. Nothing was changed.`, "error");
53
+ }
54
+ finally {
55
+ setPending(null);
56
+ }
57
+ }, [onSave, showToast]);
58
+ return (_jsxs(Stack, { gap: "lg", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 1, size: "xl", weight: "semibold", color: "primary", children: "Integrations" }), _jsx(Text, { size: "sm", color: "muted", children: "What the platform talks to, and what happens when it stops. Each switch writes the same settings field the code enforcing it reads." })] }), _jsx(Alert, { variant: "warning", title: "Switch payments off only when nobody is checking out", children: "Turning online payments off also closes the routes that FINISH a payment, including the gateway webhook. A buyer part-way through the gateway can be charged without an order being placed, and the gateway will not retry. There is no safe mid-checkout moment." }), _jsx(Stack, { gap: "md", children: INTEGRATION_ORDER.map((id) => {
59
+ const d = INTEGRATIONS[id];
60
+ const enabled = states[id].enabled;
61
+ const busy = pending === id || pending === "all";
62
+ const action = enabled
63
+ ? ACTIONS.ADMIN["disable-integration"]
64
+ : ACTIONS.ADMIN["enable-integration"];
65
+ return (_jsx(Div, { surface: d.severe && enabled ? "warning-surface" : "card", padding: "md", rounded: "lg", border: "default", children: _jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { align: "start", justify: "between", gap: "md", className: "min-w-0", children: [_jsxs(Stack, { gap: "xs", className: "min-w-0", children: [_jsxs(Row, { align: "center", gap: "sm", className: "min-w-0", children: [_jsx(Text, { weight: "semibold", color: "primary", className: "min-w-0", children: d.label }), _jsx(Badge, { variant: enabled ? "success" : "inactive", children: stateLabel(id, enabled) }), d.severe && _jsx(Badge, { variant: "warning", children: "High impact" })] }), _jsx(Text, { size: "sm", color: "muted", children: d.summary })] }), _jsx(Button, { action: action,
66
+ /*
67
+ * 🛑 The dialog body is composed HERE from the descriptor.
68
+ * The ActionDef ships an empty body on purpose — see its
69
+ * note. A per-integration consequence copied into the
70
+ * registry would be a second source for one sentence.
71
+ */
72
+ confirmationBody: enabled ? d.offConsequence : undefined, isLoading: busy, disabled: busy, onClick: () => apply(id, integrationPatch(id, !enabled), `${d.label} ${enabled ? "off" : "on"}`) })] }), _jsxs(Text, { size: "xs", color: "muted", children: [_jsxs(Text, { as: "span", size: "xs", weight: "semibold", color: "muted", children: ["Off means:", " "] }), d.offConsequence] }), _jsxs(Row, { align: "center", gap: "sm", className: "min-w-0", children: [_jsx(Text, { size: "xs", color: "faint", numeric: true, className: "min-w-0", truncate: 1, children: d.field }), _jsx(Text, { size: "xs", color: "faint", children: "\u00B7" }), _jsx(Text, { size: "xs", color: "faint", className: "min-w-0", truncate: 1, children: d.routeGuard === "gated" ? "API routes close when off" : "no route gate" })] })] }) }, id));
73
+ }) }), _jsxs(Row, { align: "center", gap: "sm", border: "top-subtle", padding: "t-md", children: [_jsx(Button, { action: ACTIONS.ADMIN["enable-all-integrations"], isLoading: pending === "all", disabled: pending !== null, onClick: () => apply("all", allIntegrationsPatch(true), "All integrations on") }), _jsx(Button, { action: ACTIONS.ADMIN["disable-all-integrations"], isLoading: pending === "all", disabled: pending !== null, onClick: () => apply("all", allIntegrationsPatch(false), "All integrations off") })] })] }));
74
+ }
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare function AdminInvoicingView(): React.JSX.Element;
@@ -0,0 +1,65 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Button, Card, Disclaimer, Div, Heading, Row, Stack, Text } from "../../../ui/index.js";
5
+ import { FieldInput } from "../../../ui/forms/FieldInput.js";
6
+ import { Icon } from "../../../ui/components/Icon.js";
7
+ import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
8
+ /**
9
+ * Admin → Finance → Invoicing: the GSTR-1 draft export (Phase 7d).
10
+ *
11
+ * ## 🛑 Why this page exists at all
12
+ *
13
+ * The export is an API route, and an API route with no surface is a dead end —
14
+ * reachable only by someone who already knows the URL, which is Root Cause
15
+ * #56's shape (16 of 70 listing views had no way to open a row). The person
16
+ * who needs this is an accountant at quarter end, and they will not type a
17
+ * query string.
18
+ *
19
+ * ## 🛑 It downloads; it does not file
20
+ *
21
+ * Nothing here talks to the GST portal — there is no webhook facility in
22
+ * existence and no GSP contract. The copy says "draft" everywhere it can,
23
+ * because a page that implied a return had been FILED when it had not is the
24
+ * most consequential thing this feature could get wrong.
25
+ */
26
+ /** Indian FY quarters, which is the unit a GSTR-1 is filed in. */
27
+ const QUARTERS = [
28
+ { label: "Q1 — Apr to Jun", fromMonth: "04-01", toMonth: "06-30" },
29
+ { label: "Q2 — Jul to Sep", fromMonth: "07-01", toMonth: "09-30" },
30
+ { label: "Q3 — Oct to Dec", fromMonth: "10-01", toMonth: "12-31" },
31
+ /*
32
+ * 🛑 Q4 SPANS A CALENDAR YEAR BOUNDARY. Jan–Mar of FY 26-27 is calendar
33
+ * 2027, not 2026 — the same off-by-one the `financialYearLabel` helper
34
+ * exists to contain, surfacing here because a quarter picker has to know it
35
+ * too. `yearOffset` is what carries it; dropping it would export the wrong
36
+ * three months with no visible error.
37
+ */
38
+ { label: "Q4 — Jan to Mar", fromMonth: "01-01", toMonth: "03-31", yearOffset: 1 },
39
+ ];
40
+ function defaultFyStartYear() {
41
+ const now = new Date();
42
+ // Zero-based month: April is 3. Jan–Mar belongs to the FY that started last
43
+ // calendar year.
44
+ return now.getMonth() >= 3 ? now.getFullYear() : now.getFullYear() - 1;
45
+ }
46
+ export function AdminInvoicingView() {
47
+ const [fyStart, setFyStart] = useState(String(defaultFyStartYear()));
48
+ const [from, setFrom] = useState("");
49
+ const [to, setTo] = useState("");
50
+ const applyQuarter = (q) => {
51
+ const start = Number(fyStart);
52
+ if (!Number.isFinite(start))
53
+ return;
54
+ const offset = "yearOffset" in q ? q.yearOffset : 0;
55
+ setFrom(`${start + offset}-${q.fromMonth}`);
56
+ setTo(`${start + offset}-${q.toMonth}`);
57
+ };
58
+ const href = from && to
59
+ ? `${ADMIN_ENDPOINTS.INVOICING_GSTR1}?from=${from}&to=${to}`
60
+ : undefined;
61
+ return (_jsxs(Stack, { gap: "lg", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 1, children: "Invoicing" }), _jsx(Text, { color: "muted", size: "sm", children: "Export the invoice and credit-note register for a period, as a draft for your accountant." })] }), _jsx(Card, { padding: "md", children: _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, children: "GSTR-1 draft" }), _jsx(Disclaimer, { id: "accounting-output" }), _jsx(Row, { gap: "sm", wrap: true, children: _jsx(Div, { className: "w-40", children: _jsx(FieldInput, { name: "fyStart", label: "FY starting", value: fyStart, onChange: setFyStart, hint: "e.g. 2026 for FY 26-27" }) }) }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Quarter" }), _jsx(Row, { gap: "sm", wrap: true, children: QUARTERS.map((q) => (_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => applyQuarter(q), children: q.label }, q.label))) })] }), _jsxs(Row, { gap: "sm", wrap: true, children: [_jsx(Div, { className: "w-48", children: _jsx(FieldInput, { name: "from", label: "From", value: from, onChange: setFrom }) }), _jsx(Div, { className: "w-48", children: _jsx(FieldInput, { name: "to", label: "To", value: to, onChange: setTo }) })] }), _jsxs(Row, { gap: "sm", align: "center", children: [_jsxs(Button, { type: "button", variant: "primary", disabled: !href, onClick: () => {
62
+ if (href)
63
+ window.location.href = href;
64
+ }, children: [_jsx(Icon, { name: "download", size: "sm" }), "Download draft CSV"] }), !href && (_jsx(Text, { color: "muted", size: "xs", children: "Pick a quarter, or enter both dates as YYYY-MM-DD." }))] }), _jsx(Text, { color: "muted", size: "xs", children: "Credit notes appear as negative rows, so the TOTAL row is net of refunds. Any paid order in the window that carries no invoice number is listed at the bottom of the file as a warning \u2014 those are not included in the totals and need looking at." })] }) })] }));
65
+ }
@@ -1,4 +1,5 @@
1
1
  import { type OrderAddonBadgesOrder } from "../../orders/components/OrderAddonBadges";
2
+ import type { OrderSourceContext } from "../../orders/schemas/firestore";
2
3
  import React from "react";
3
4
  export interface AdminOrderItemRow {
4
5
  productId: string;
@@ -7,6 +8,10 @@ export interface AdminOrderItemRow {
7
8
  quantity: number;
8
9
  unitPrice: number;
9
10
  totalPrice: number;
11
+ groupSlug?: string;
12
+ groupTitle?: string;
13
+ bundleCategorySlug?: string;
14
+ bundleProductIds?: string[];
10
15
  }
11
16
  export interface AdminOrderEditorViewProps {
12
17
  open: boolean;
@@ -36,5 +41,32 @@ export interface AdminOrderEditorViewProps {
36
41
  * coupon is what an admin needs when reconciling a discounted order.
37
42
  */
38
43
  addons?: OrderAddonBadgesOrder;
44
+ /**
45
+ * How the buyer earned this price — a won auction, a buyout, an agreed
46
+ * offer, a pre-order deposit, prize-draw entries.
47
+ *
48
+ * Support is the surface that needs this most: a buyer disputing "I agreed
49
+ * ₹X" is otherwise unanswerable from the admin side, because the negotiated
50
+ * amount lives only in `sourceContext` and nothing rendered it anywhere.
51
+ * `GET /api/admin/orders/[id]` already returns it via `maskedOrder`.
52
+ */
53
+ sourceContext?: OrderSourceContext;
54
+ /** For the provenance card's money formatting; falls back to the default. */
55
+ currency?: string;
56
+ /**
57
+ * D55 — the pre-order balance block, present once a seller has opened it.
58
+ *
59
+ * 🛑 Admin is the ONLY role that sees `proofUrl` here, matching the deposit:
60
+ * the seller's own endpoint strips it, because a bank screenshot is the
61
+ * buyer's and verification is not the seller's job. Reviewing a payment to
62
+ * yourself is not a review.
63
+ */
64
+ balance?: {
65
+ amount: number;
66
+ status: "awaiting" | "submitted" | "paid" | "rejected";
67
+ proofUrl?: string;
68
+ transactionId?: string;
69
+ reviewNote?: string;
70
+ };
39
71
  }
40
- export declare function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, }: AdminOrderEditorViewProps): React.JSX.Element;
72
+ export declare function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, sourceContext, currency, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, balance, }: AdminOrderEditorViewProps): React.JSX.Element;
@@ -1,6 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { OrderAddonBadges } from "../../orders/components/OrderAddonBadges.js";
4
+ import { OrderProvenanceCard } from "../../orders/components/OrderProvenanceCard.js";
5
+ import { groupOrderItemsByLine } from "../../orders/utils/bundle-grouping.js";
4
6
  import { isManualPaymentMethod } from "../../orders/constants/payment-window.js";
5
7
  import { useApiMutation } from "@mohasinac/appkit/client";
6
8
  import React, { useState } from "react";
@@ -18,6 +20,13 @@ import { useFormShellState, FormShellContext } from "../../../ui/forms/index.js"
18
20
  import { applyZodIssues } from "../../../ui/forms/apply-zod-issues.js";
19
21
  import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary.js";
20
22
  import { ValidationError } from "../../../errors/validation-error.js";
23
+ /**
24
+ * One item row. Extracted so the single and grouped render paths share exactly
25
+ * one piece of markup rather than two that can drift.
26
+ */
27
+ function adminOrderItemRow(item, key) {
28
+ return (_jsxs(Div, { layout: "flex", align: "center", gap: "3", padding: "sm", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) }), _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title }), _jsxs(Text, { size: "xs", color: "muted", children: ["Qty: ", item.quantity, " \u00D7 ", toCurrency(item.unitPrice)] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "semibold", children: toCurrency(item.totalPrice) })] }, key));
29
+ }
21
30
  const STATUS_OPTIONS = [
22
31
  { label: "Pending", value: "pending" },
23
32
  { label: "Processing", value: "processing" },
@@ -37,7 +46,7 @@ const CARRIER_OPTIONS = [
37
46
  { label: "Other", value: "Other" },
38
47
  ];
39
48
  // --- Component ---------------------------------------------------------------
40
- export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, }) {
49
+ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, sourceContext, currency, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, balance, }) {
41
50
  const queryClient = useQueryClient();
42
51
  const { showToast } = useToast();
43
52
  const [draft, setDraft] = React.useState({
@@ -74,6 +83,19 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
74
83
  const [reviewNoteError, setReviewNoteError] = React.useState(null);
75
84
  const [isRequestingReupload, setIsRequestingReupload] = useState(false);
76
85
  const [isRejectingFraud, setIsRejectingFraud] = useState(false);
86
+ /*
87
+ * D55 — the balance review's OWN note and its own pending flags.
88
+ *
89
+ * 🛑 Not shared with `reviewNote` above, which belongs to the deposit's
90
+ * three actions. One note box feeding two reviews is the bug where an admin
91
+ * types "blurry screenshot" meaning the balance and sends it as a
92
+ * fraud-rejection reason on the deposit — and the deposit's rejection bans
93
+ * the account for 7 days.
94
+ */
95
+ const [balanceNote, setBalanceNote] = React.useState("");
96
+ const [balanceNoteError, setBalanceNoteError] = React.useState(null);
97
+ const [isAcceptingBalance, setIsAcceptingBalance] = useState(false);
98
+ const [isRejectingBalance, setIsRejectingBalance] = useState(false);
77
99
  // This used to inline `paymentMethod === "cash" || === "upi_manual"`, which
78
100
  // omitted `emi` — so the whole proof panel never rendered for an EMI order and
79
101
  // an admin had no way to verify one, even though the list row said "Awaiting
@@ -96,6 +118,10 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
96
118
  notes: "",
97
119
  });
98
120
  setReviewNote("");
121
+ // Reset with its sibling — a note typed for one order must not reappear
122
+ // under the next one the drawer opens.
123
+ setBalanceNote("");
124
+ setBalanceNoteError(null);
99
125
  }
100
126
  }, [open, currentStatus]);
101
127
  const saveMutation = useApiMutation({
@@ -207,7 +233,49 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
207
233
  setIsRejectingFraud(false);
208
234
  }
209
235
  };
210
- return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: orderLabel ? `Order: ${orderLabel}` : "Update Order", children: _jsx(FormShellContext.Provider, { value: form.shellCtx, children: _jsxs(Stack, { gap: "md", padding: "md", children: [_jsx(FormErrorSummary, {}), addons && _jsx(OrderAddonBadges, { order: addons, variant: "detail" }), items && items.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Label, { size: "sm", weight: "medium", color: "primary", children: ["Items (", items.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: items.map((item, i) => (_jsxs(Div, { layout: "flex", align: "center", gap: "3", padding: "sm", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) }), _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title }), _jsxs(Text, { size: "xs", color: "muted", children: ["Qty: ", item.quantity, " \u00D7 ", toCurrency(item.unitPrice)] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "semibold", children: toCurrency(item.totalPrice) })] }, item.productId || i))) })] })), _jsx(SectionForm, { sections: sections, values: draft, onChange: (partial) => setDraft((d) => ({ ...d, ...partial })), onSubmit: () => {
236
+ /**
237
+ * D55 — accept or reject the pre-order BALANCE proof.
238
+ *
239
+ * 🛑 One handler, because the two verdicts differ by a boolean and a note
240
+ * and nothing else. The deposit has three handlers because its three
241
+ * outcomes are genuinely different operations (verify / re-ask / cancel +
242
+ * ban); mirroring that shape here would be three copies of one request.
243
+ *
244
+ * The note requirement is checked in BOTH places on purpose: here so the
245
+ * admin is told before the round trip, and in `reviewBalanceProof` because
246
+ * the route is reachable without this component.
247
+ */
248
+ const handleBalanceReview = async (approve) => {
249
+ if (!orderId)
250
+ return;
251
+ if (!approve && !balanceNote.trim()) {
252
+ setBalanceNoteError("Say what was wrong — without it the buyer will send the same proof again.");
253
+ return;
254
+ }
255
+ setBalanceNoteError(null);
256
+ const setPending = approve ? setIsAcceptingBalance : setIsRejectingBalance;
257
+ setPending(true);
258
+ try {
259
+ await apiClient.post(ADMIN_ENDPOINTS.ORDER_BALANCE_REVIEW(orderId), {
260
+ approve,
261
+ ...(balanceNote.trim() ? { note: balanceNote.trim() } : {}),
262
+ });
263
+ showToast(approve
264
+ ? "Balance accepted. The order is clear to ship."
265
+ : "Proof returned to the buyer with your note.", "success");
266
+ queryClient.invalidateQueries({ queryKey: ["admin", "orders"] });
267
+ onClose();
268
+ }
269
+ catch (err) {
270
+ void normalizeError(err);
271
+ // Authored copy — `err.message` is server text (Root Cause #86).
272
+ showToast("Could not record that decision.", "error");
273
+ }
274
+ finally {
275
+ setPending(false);
276
+ }
277
+ };
278
+ return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: orderLabel ? `Order: ${orderLabel}` : "Update Order", children: _jsx(FormShellContext.Provider, { value: form.shellCtx, children: _jsxs(Stack, { gap: "md", padding: "md", children: [_jsx(FormErrorSummary, {}), addons && _jsx(OrderAddonBadges, { order: addons, variant: "detail" }), _jsx(OrderProvenanceCard, { sourceContext: sourceContext, currency: currency }), items && items.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Label, { size: "sm", weight: "medium", color: "primary", children: ["Items (", items.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: groupOrderItemsByLine(items).map((group, gi) => group.kind === "single" ? (adminOrderItemRow(group.item, `s-${group.index}`)) : (_jsxs(Div, { children: [_jsxs(Div, { layout: "flex", align: "center", gap: "2", padding: "x-sm", surface: "muted", children: [_jsx(Text, { size: "xs", weight: "semibold", children: group.groupTitle ?? "Grouped purchase" }), _jsx(Text, { size: "xs", color: "muted", children: `${group.memberCount} ${group.memberCount === 1 ? "item" : "items"} · one line` })] }), _jsx(Div, { paddingX: "l-md", children: group.items.map(({ item, index }) => adminOrderItemRow(item, `m-${index}`)) })] }, `g-${gi}`))) })] })), _jsx(SectionForm, { sections: sections, values: draft, onChange: (partial) => setDraft((d) => ({ ...d, ...partial })), onSubmit: () => {
211
279
  // SectionForm surfaces errors and then submits regardless, so the
212
280
  // guard is here.
213
281
  form.clearErrors();
@@ -217,5 +285,15 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
217
285
  return;
218
286
  }
219
287
  saveMutation.mutate();
220
- }, onValidationChange: () => form.validate(draft), schema: adminOrderUpdateSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, submitLabel: "Save changes", cancelLabel: "Cancel", onCancel: onClose, isLoading: saveMutation.isPending }), isManualPayment && (_jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Payment Proof" }), isRejected && (_jsx(Text, { size: "xs", className: "text-error", weight: "medium", children: "Payment rejected as fraud \u2014 no further action available here." })), isReuploadRequested && !isVerified && (_jsx(Text, { size: "xs", className: "text-warning", weight: "medium", children: "Re-upload requested \u2014 waiting for the buyer to submit a new proof." })), paymentProofUrl ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Div, { border: "default", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: paymentProofUrl, alt: "Payment screenshot", size: "card" }) }), paymentTransactionId && (_jsxs(Text, { size: "xs", color: "muted", children: ["UTR: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: paymentTransactionId })] })), needsVerification && (_jsxs(Stack, { gap: "xs", children: [(displayedUpiId || buyerReportedUpiId) && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Expected UPI: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: displayedUpiId || "—" }), " · ", "Buyer reported: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerReportedUpiId || "—" })] }), paymentUpiMismatch && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-error/20", surface: "danger-surface", children: _jsx(Text, { size: "xs", className: "text-error", weight: "semibold", children: "UPI mismatch \u2014 buyer-reported ID doesn't match the one shown for this order." }) }))] })), _jsxs(Text, { size: "xs", color: "muted", children: ["Buyer marked as paid: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerMarkedPaid ? "Yes" : "No" }), " · ", "Fraud agreement accepted: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerFraudAgreementAccepted ? "Yes" : "No" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Review note" }), _jsxs(Text, { size: "xs", color: "muted", children: ["Sent with ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: "Request re-upload" }), " or", " ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: "Reject as fraud" }), " below. Not saved by \u201CSave changes\u201D."] }), _jsx(Textarea, { value: reviewNote, onChange: (e) => handleReviewNoteChange(e.target.value), rows: 2, "aria-invalid": reviewNoteError ? true : undefined, placeholder: "e.g. Screenshot is blurry \u2014 amount not readable / UPI ID doesn't match order" }), reviewNoteError && (_jsx(Text, { size: "xs", color: "error", role: "alert", children: reviewNoteError }))] }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["verify-payment"], onClick: handleVerifyPayment, isLoading: isVerifyingPayment, disabled: isVerifyingPayment, variant: "primary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["request-payment-reupload"], onClick: handleRequestReupload, isLoading: isRequestingReupload, disabled: isRequestingReupload, variant: "secondary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["reject-payment-fraud"], onClick: handleRejectFraud, isLoading: isRejectingFraud, disabled: isRejectingFraud, variant: "danger", className: "w-full" })] })), isVerified && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-success/20", surface: "success-surface", children: _jsx(Text, { size: "xs", className: "text-success", weight: "medium", children: "Payment verified" }) }))] })) : (_jsx(Text, { size: "xs", color: "faint", children: "No proof uploaded yet." }))] }))] }) }) }));
288
+ }, onValidationChange: () => form.validate(draft), schema: adminOrderUpdateSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, submitLabel: "Save changes", cancelLabel: "Cancel", onCancel: onClose, isLoading: saveMutation.isPending }), isManualPayment && (_jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Payment Proof" }), isRejected && (_jsx(Text, { size: "xs", className: "text-error", weight: "medium", children: "Payment rejected as fraud \u2014 no further action available here." })), isReuploadRequested && !isVerified && (_jsx(Text, { size: "xs", className: "text-warning", weight: "medium", children: "Re-upload requested \u2014 waiting for the buyer to submit a new proof." })), paymentProofUrl ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Div, { border: "default", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: paymentProofUrl, alt: "Payment screenshot", size: "card" }) }), paymentTransactionId && (_jsxs(Text, { size: "xs", color: "muted", children: ["UTR: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: paymentTransactionId })] })), needsVerification && (_jsxs(Stack, { gap: "xs", children: [(displayedUpiId || buyerReportedUpiId) && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Expected UPI: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: displayedUpiId || "—" }), " · ", "Buyer reported: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerReportedUpiId || "—" })] }), paymentUpiMismatch && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-error/20", surface: "danger-surface", children: _jsx(Text, { size: "xs", className: "text-error", weight: "semibold", children: "UPI mismatch \u2014 buyer-reported ID doesn't match the one shown for this order." }) }))] })), _jsxs(Text, { size: "xs", color: "muted", children: ["Buyer marked as paid: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerMarkedPaid ? "Yes" : "No" }), " · ", "Fraud agreement accepted: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerFraudAgreementAccepted ? "Yes" : "No" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Review note" }), _jsxs(Text, { size: "xs", color: "muted", children: ["Sent with ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: "Request re-upload" }), " or", " ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: "Reject as fraud" }), " below. Not saved by \u201CSave changes\u201D."] }), _jsx(Textarea, { value: reviewNote, onChange: (e) => handleReviewNoteChange(e.target.value), rows: 2, "aria-invalid": reviewNoteError ? true : undefined, placeholder: "e.g. Screenshot is blurry \u2014 amount not readable / UPI ID doesn't match order" }), reviewNoteError && (_jsx(Text, { size: "xs", color: "error", role: "alert", children: reviewNoteError }))] }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["verify-payment"], onClick: handleVerifyPayment, isLoading: isVerifyingPayment, disabled: isVerifyingPayment, variant: "primary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["request-payment-reupload"], onClick: handleRequestReupload, isLoading: isRequestingReupload, disabled: isRequestingReupload, variant: "secondary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["reject-payment-fraud"], onClick: handleRejectFraud, isLoading: isRejectingFraud, disabled: isRejectingFraud, variant: "danger", className: "w-full" })] })), isVerified && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-success/20", surface: "success-surface", children: _jsx(Text, { size: "xs", className: "text-success", weight: "medium", children: "Payment verified" }) }))] })) : (_jsx(Text, { size: "xs", color: "faint", children: "No proof uploaded yet." }))] })), balance && (_jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Pre-order balance" }), _jsxs(Text, { size: "xs", color: "muted", children: [toCurrency(balance.amount), " \u00B7", " ", balance.status === "paid"
289
+ ? "accepted"
290
+ : balance.status === "submitted"
291
+ ? "proof submitted, awaiting your decision"
292
+ : balance.status === "rejected"
293
+ ? "returned to the buyer"
294
+ : "open — the buyer has not paid yet", balance.transactionId ? ` · Ref ${balance.transactionId}` : ""] }), balance.status === "rejected" && balance.reviewNote && (_jsxs(Text, { size: "xs", color: "muted", children: ["Last note: ", balance.reviewNote] })), balance.status === "submitted" && (_jsxs(Stack, { gap: "xs", children: [balance.proofUrl && (_jsx(Div, { border: "default", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: balance.proofUrl, alt: "Balance payment screenshot", size: "card" }) })), _jsx(Text, { size: "xs", color: "muted", children: "Check the reference against the bank before accepting. Accepting does NOT post a ledger entry \u2014 the sale was recognised in full when the order was created, so a second entry would double it." }), _jsx(Textarea, { value: balanceNote, onChange: (e) => {
295
+ setBalanceNote(e.target.value);
296
+ if (balanceNoteError)
297
+ setBalanceNoteError(null);
298
+ }, rows: 2, "aria-invalid": balanceNoteError ? true : undefined, placeholder: "Required when rejecting \u2014 e.g. reference does not match any credit we received" }), balanceNoteError && (_jsx(Text, { size: "xs", color: "error", role: "alert", children: balanceNoteError })), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["verify-balance"], onClick: () => void handleBalanceReview(true), isLoading: isAcceptingBalance, disabled: isAcceptingBalance || isRejectingBalance, variant: "primary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["reject-balance"], onClick: () => void handleBalanceReview(false), isLoading: isRejectingBalance, disabled: isAcceptingBalance || isRejectingBalance, variant: "secondary", className: "w-full" })] }))] }))] }) }) }));
221
299
  }
@@ -2,6 +2,16 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { ShoppingBag, Hash, Scale, Wallet, RotateCcw, DollarSign, Gavel } from "lucide-react";
3
3
  import { Code, Div, Heading, Li, Row, Section, Span, Stack, Text, Ul } from "../../../ui/index.js";
4
4
  import { GC } from "../../_guide-cls.js";
5
+ import { resolvePublishedValues } from "../../../_internal/shared/content/published-values.js";
6
+ /*
7
+ * Phase 9 — read from the published-value registry rather than restated.
8
+ *
9
+ * 🛑 No settings argument, deliberately: this is the OBLIGATION-derived window
10
+ * and it has no settings override, so passing settings would imply an
11
+ * admin-editable number that does not exist (the 18-dead-settings pattern,
12
+ * from the "control that appears to do something the code ignores" direction).
13
+ */
14
+ const { returnRespondHours: RETURN_RESPOND_HOURS, refundBusinessDaysMin: REFUND_DAYS_MIN, refundBusinessDaysMax: REFUND_DAYS_MAX, } = resolvePublishedValues();
5
15
  export function AdminOrdersGuideView() {
6
16
  return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(ShoppingBag, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Orders & Finance" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Order statuses, payouts, disputes, returns, and commission math on LetItRip." })] }), [
7
17
  {
@@ -11,7 +21,18 @@ export function AdminOrdersGuideView() {
11
21
  ["PROCESSING", "Seller has acknowledged and is packing."],
12
22
  ["SHIPPED", "Dispatched. Tracking number provided by seller."],
13
23
  ["DELIVERED", "Carrier confirmed delivery."],
14
- ["RETURN_REQUESTED", "Buyer opened a return. Seller has 3 days to respond."],
24
+ /*
25
+ * 🛑 Phase 9, decision 23 — this said "3 days" while the FAQ
26
+ * said 48 hours, and the FAQ WINS: it is the buyer-facing
27
+ * promise and the one a buyer has already read. The number now
28
+ * comes from `OBLIGATION_POLICIES["return-respond"]`, which is
29
+ * what actually closes the window — so a staff-facing guide can
30
+ * no longer quietly contradict what the buyer was told.
31
+ */
32
+ [
33
+ "RETURN_REQUESTED",
34
+ `Buyer opened a return. Seller has ${RETURN_RESPOND_HOURS} hours to respond.`,
35
+ ],
15
36
  ["REFUNDED", "Return accepted and refund issued via Razorpay."],
16
37
  ["CANCELLED", "Cancelled before shipping. Payment returned."],
17
38
  ].map(([status, note]) => (_jsxs(Row, { gap: "3", children: [_jsx(Text, { className: "flex-shrink-0 font-mono text-[var(--appkit-color-primary)] w-40", size: "xs", weight: "semibold", children: status }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: note })] }, status))), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] pt-[0.5rem]", size: "sm", children: "Admin-only transitions: force-cancel (any status before DELIVERED), force-refund (post-DELIVERED dispute)." })] })),
@@ -30,7 +51,7 @@ export function AdminOrdersGuideView() {
30
51
  },
31
52
  {
32
53
  Icon: RotateCcw, title: "Return Requests",
33
- content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsx(Li, { children: "Admin can view the return reason and attached photos on the order detail page." }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "When to override a seller rejection" }), ": Only when photographic evidence clearly supports the buyer's claim of a significant not-as-described defect. Do not override for buyer's remorse."] }), _jsx(Li, { children: "After admin override: refund is issued automatically via Razorpay (3\u20137 business days for card, 1\u20132 for UPI). Seller payout for that order is withheld." })] })),
54
+ content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsx(Li, { children: "Admin can view the return reason and attached photos on the order detail page." }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "When to override a seller rejection" }), ": Only when photographic evidence clearly supports the buyer's claim of a significant not-as-described defect. Do not override for buyer's remorse."] }), _jsxs(Li, { children: ["After admin override: the refund is issued automatically (", REFUND_DAYS_MIN, "\u2013", REFUND_DAYS_MAX, " business days for card, 1\u20132 for UPI). Seller payout for that order is withheld."] })] })),
34
55
  },
35
56
  {
36
57
  Icon: DollarSign, title: "Commission & Settlement Math",
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { groupOrderItemsByLine } from "../../orders/utils/bundle-grouping.js";
3
4
  import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit/client";
4
5
  import { sortBy } from "@mohasinac/appkit/client";
5
6
  import React, { useState, useCallback } from "react";
@@ -61,14 +62,70 @@ function toOrderItemRows(item) {
61
62
  quantity: typeof entry.quantity === "number" ? entry.quantity : 1,
62
63
  unitPrice: typeof entry.unitPrice === "number" ? entry.unitPrice : 0,
63
64
  totalPrice: typeof entry.totalPrice === "number" ? entry.totalPrice : 0,
65
+ /*
66
+ * The collapse keys, carried so the editor can group a bundle or
67
+ * buyer-assembled group back into one line the way the buyer's receipt
68
+ * does. Narrowed by `typeof` like every field above rather than cast —
69
+ * this function's whole job is turning a loose record into a typed row,
70
+ * and a cast here is how `title`/`price` once slipped through (RC#45).
71
+ */
72
+ groupSlug: typeof entry.groupSlug === "string" ? entry.groupSlug : undefined,
73
+ groupTitle: typeof entry.groupTitle === "string" ? entry.groupTitle : undefined,
74
+ bundleCategorySlug: typeof entry.bundleCategorySlug === "string" ? entry.bundleCategorySlug : undefined,
75
+ bundleProductIds: Array.isArray(entry.bundleProductIds)
76
+ ? entry.bundleProductIds.filter((v) => typeof v === "string")
77
+ : undefined,
64
78
  }));
65
79
  }
80
+ /*
81
+ * The row's headline, counted in LINES rather than item rows.
82
+ *
83
+ * 🛑 `extraCount` deliberately changed meaning when grouping landed: it was
84
+ * `items.length - 1`, so a bundle of four read "Dranzer S +3 more" — naming an
85
+ * arbitrary member and implying four separate things were bought. It is now
86
+ * `groups.length - 1`, so the same order reads "Starter Bundle +0" and a
87
+ * bundle plus one standalone reads "+1 more". The number a buyer or admin
88
+ * reads on a row should match the number of lines they will see inside it.
89
+ */
90
+ /*
91
+ * `sourceContext` arrives as JsonValue off the raw admin-list doc, so it can
92
+ * be a string, an array or null — none of which `OrderProvenanceCard` can
93
+ * switch on. This narrows to "an object carrying a `path` tag" and nothing
94
+ * further: that is deliberately NOT the card's own switch duplicated, it only
95
+ * establishes that there is a tag to switch on, which is what makes the single
96
+ * cast true instead of hopeful. `as unknown as OrderSourceContext` would
97
+ * assert exactly the same thing while checking nothing (Root Cause #98).
98
+ */
99
+ function toSourceContext(value) {
100
+ if (!value || typeof value !== "object" || Array.isArray(value))
101
+ return undefined;
102
+ /*
103
+ * Via `object` rather than straight off the JsonValue union, for two
104
+ * reasons that pull the same way. `Array.isArray` does not narrow
105
+ * `JsonArray | JsonObject` apart, so `value.path` does not resolve; and
106
+ * `OrderSourceContext` is not assignable to `JsonObject` (its optional
107
+ * members admit `undefined`, which is not a JsonValue), so TS refuses that
108
+ * conversion outright. Probing as `{ path?: string }` keeps both the probe
109
+ * and the assertion legal without an `unknown` anywhere.
110
+ */
111
+ const obj = value;
112
+ return typeof obj.path === "string"
113
+ ? obj
114
+ : undefined;
115
+ }
66
116
  function firstOrderItem(item) {
67
- const items = toOrderItemRows(item);
117
+ const groups = groupOrderItemsByLine(toOrderItemRows(item));
118
+ const first = groups[0];
119
+ const extraCount = Math.max(0, groups.length - 1);
120
+ if (!first)
121
+ return { extraCount: 0 };
122
+ if (first.kind === "single") {
123
+ return { image: first.item.image, title: first.item.title, extraCount };
124
+ }
68
125
  return {
69
- image: items[0]?.image,
70
- title: items[0]?.title,
71
- extraCount: Math.max(0, items.length - 1),
126
+ image: first.items[0]?.item.image,
127
+ title: first.groupTitle ?? "Grouped purchase",
128
+ extraCount,
72
129
  };
73
130
  }
74
131
  /**
@@ -206,6 +263,30 @@ export function AdminOrdersView({ children, ...props }) {
206
263
  couponCode: toStringValue(item.couponCode) || undefined,
207
264
  couponDiscount: Number(item.couponDiscount ?? 0) || undefined,
208
265
  },
266
+ // Raw doc in, so no serializer entry — but unlike `addons` above,
267
+ // which coerces every field by hand, this one needs a tag check
268
+ // before it can be trusted. See `toSourceContext`.
269
+ sourceContext: toSourceContext(item.sourceContext),
270
+ /*
271
+ * D55. Coerced field by field like `addons` above, deliberately
272
+ * NOT `as AdminOrderEditorViewProps["balance"]` — a cast at a
273
+ * raw-document boundary is where Root Cause #98 lives, and main
274
+ * had just removed the same cast from `sourceContext` one line up.
275
+ *
276
+ * `proofUrl` is omitted ON PURPOSE: this is the LIST payload, and
277
+ * the screenshot is admin-detail only. A list row never needs it.
278
+ */
279
+ balance: item.balance
280
+ ? {
281
+ amount: Number(item.balance.amount ?? 0),
282
+ status: toStringValue(item.balance.status, "awaiting"),
283
+ transactionId: toStringValue(item.balance.transactionId) ||
284
+ undefined,
285
+ reviewNote: toStringValue(item.balance.reviewNote) ||
286
+ undefined,
287
+ }
288
+ : undefined,
289
+ currency: toStringValue(item.currency, "") || undefined,
209
290
  };
210
291
  }),
211
292
  getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
@@ -289,5 +370,5 @@ export function AdminOrdersView({ children, ...props }) {
289
370
  ] })),
290
371
  renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_ORDER_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id, paymentReview: "" })) }), _jsx(FilterChipGroup, { label: "Manual payment", tabs: ADMIN_ORDER_PAYMENT_REVIEW_TABS, value: pendingFilters.paymentReview ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, paymentReview: id, status: "" })) })] })),
291
372
  };
292
- return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminOrderEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), orderId: selectedRow?.id, orderLabel: selectedRow?.primary, currentStatus: selectedRow?.status, items: selectedRow?.items, paymentProofUrl: selectedRow?.paymentProofUrl, paymentTransactionId: selectedRow?.paymentTransactionId, paymentMethod: selectedRow?.paymentMethod, paymentStatus: selectedRow?.paymentStatus, displayedUpiId: selectedRow?.displayedUpiId, buyerReportedUpiId: selectedRow?.buyerReportedUpiId, paymentUpiMismatch: selectedRow?.paymentUpiMismatch, buyerMarkedPaid: selectedRow?.buyerMarkedPaid, buyerFraudAgreementAccepted: selectedRow?.buyerFraudAgreementAccepted, paymentReviewOutcome: selectedRow?.paymentReviewOutcome, addons: selectedRow?.addons })] }));
373
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminOrderEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), orderId: selectedRow?.id, orderLabel: selectedRow?.primary, currentStatus: selectedRow?.status, items: selectedRow?.items, paymentProofUrl: selectedRow?.paymentProofUrl, paymentTransactionId: selectedRow?.paymentTransactionId, paymentMethod: selectedRow?.paymentMethod, paymentStatus: selectedRow?.paymentStatus, displayedUpiId: selectedRow?.displayedUpiId, buyerReportedUpiId: selectedRow?.buyerReportedUpiId, paymentUpiMismatch: selectedRow?.paymentUpiMismatch, buyerMarkedPaid: selectedRow?.buyerMarkedPaid, buyerFraudAgreementAccepted: selectedRow?.buyerFraudAgreementAccepted, paymentReviewOutcome: selectedRow?.paymentReviewOutcome, addons: selectedRow?.addons, sourceContext: selectedRow?.sourceContext, balance: selectedRow?.balance, currency: selectedRow?.currency })] }));
293
374
  }
@@ -236,6 +236,28 @@ export function OwnerQuickActions({ resource, editHref, onDelete, onClone, owner
236
236
  * 🛑 `inline-flex`, never `w-full`. In normal flow this must shrink to its
237
237
  * own content — a full-width bar would read as a section divider and would
238
238
  * stretch across a card body it is only a corner of.
239
+ *
240
+ * 🛑 `shrink-0` IS THE MOBILE FIX, and it is one word because the bug was
241
+ * one missing word.
242
+ *
243
+ * A flex item's default is `flex-shrink: 1`, so in the title/actions row
244
+ * this toolbar lives in — `<Row justify="between">` with an `<h1>` on the
245
+ * left — the heading and the toolbar compete for width, and at ~390px the
246
+ * toolbar LOSES. It compresses below its own content: the icons slide under
247
+ * each other, the padding collapses, and the 44px tap targets stop being
248
+ * 44px wide. That is the "works incorrectly on mobile" report.
249
+ *
250
+ * With `shrink-0` the toolbar keeps its intrinsic size and the HEADING gives
251
+ * way instead — which is right, because a heading wraps gracefully onto a
252
+ * second line and a row of hit targets does not.
253
+ *
254
+ * 🛑 Note what this is NOT: the desktop fix (`d94496c42`) gave the toolbar a
255
+ * container and added an audit whose two rules are about DOM ORDER — must
256
+ * come after the `<h1>`, must not share the heading's row unless that row is
257
+ * `justify="between"`. Both pass here. R2's exemption actively WHITELISTS
258
+ * the layout that breaks, so the surfaces that satisfy that audit are
259
+ * precisely the ones most likely to be wrong at phone width. A source-order
260
+ * rule cannot see a width problem.
239
261
  */
240
262
  const boxClass = onDark
241
263
  ? /*
@@ -244,12 +266,12 @@ export function OwnerQuickActions({ resource, editHref, onDelete, onClone, owner
244
266
  * LIGHT background in every light theme, so a themed box on a dark hero
245
267
  * photo is a white rectangle over the artwork.
246
268
  */
247
- "inline-flex rounded-[var(--appkit-radius-lg)] bg-black/55 backdrop-blur-sm p-[var(--appkit-space-1)] shadow-md"
269
+ "inline-flex shrink-0 rounded-[var(--appkit-radius-lg)] bg-black/55 backdrop-blur-sm p-[var(--appkit-space-1)] shadow-md"
248
270
  : /*
249
271
  * Off media: the real surface tokens, so it inverts with the theme
250
272
  * instead of being a fixed near-white that disappears in dark mode.
251
273
  */
252
- "inline-flex rounded-[var(--appkit-radius-lg)] bg-[var(--appkit-color-surface-elevated)] border border-[var(--appkit-color-border)] p-[var(--appkit-space-1)] shadow-sm";
274
+ "inline-flex shrink-0 rounded-[var(--appkit-radius-lg)] bg-[var(--appkit-color-surface-elevated)] border border-[var(--appkit-color-border)] p-[var(--appkit-space-1)] shadow-sm";
253
275
  return (_jsxs(_Fragment, { children: [_jsxs(Row, { gap: "xs", align: "center", className: `${boxClass} ${className}`.trim(),
254
276
  /*
255
277
  * Stops the click reaching an ancestor `<Link>` or the card's own
@@ -457,6 +457,9 @@ export declare const ADMIN_COUPON_TYPE_TABS: readonly [{
457
457
  }, {
458
458
  readonly id: "buy_x_get_y";
459
459
  readonly label: "Buy X Get Y";
460
+ }, {
461
+ readonly id: "points_earn";
462
+ readonly label: "Points Back";
460
463
  }];
461
464
  /** Empty-sentinel variant for views (like SellerBidsView) that drive the
462
465
  * filter URL with `""` instead of `"All"` so the `"" == no filter` invariant
@@ -757,6 +760,9 @@ export declare const ADMIN_PAYMENT_CASE_KIND_TABS: readonly [{
757
760
  }, {
758
761
  readonly id: "shipping_unpriced";
759
762
  readonly label: "Shipping unpriced";
763
+ }, {
764
+ readonly id: "courier_unavailable";
765
+ readonly label: "Courier unavailable";
760
766
  }, {
761
767
  readonly id: "dead_job";
762
768
  readonly label: "Dead job";
@@ -245,6 +245,11 @@ export const ADMIN_COUPON_TYPE_TABS = [
245
245
  { id: "fixed", label: "Fixed" },
246
246
  { id: "free_shipping", label: "Free Shipping" },
247
247
  { id: "buy_x_get_y", label: "Buy X Get Y" },
248
+ // D51. `audit-filter-tab-enums`' COVERAGE rule fails on a real union value
249
+ // with no chip — which is what it was extended to catch after ten such gaps
250
+ // were found at once (Root Cause #72), including sellers being unable to
251
+ // filter their own `in_review` listings.
252
+ { id: "points_earn", label: "Points Back" },
248
253
  ];
249
254
  // ---------------------------------------------------------------------------
250
255
  // Seller-dashboard filter-chip tab sets
@@ -441,6 +446,7 @@ export const ADMIN_PAYMENT_CASE_KIND_TABS = [
441
446
  { id: "protection_claim", label: "Protection claim" },
442
447
  { id: "grievance", label: "Grievance" },
443
448
  { id: "shipping_unpriced", label: "Shipping unpriced" },
449
+ { id: "courier_unavailable", label: "Courier unavailable" },
444
450
  { id: "dead_job", label: "Dead job" },
445
451
  { id: "ledger_drift", label: "Points drift" },
446
452
  ];