@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,96 @@
1
+ /**
2
+ * Issuing an invoice: allocate the number, render, store the bytes (Phase 7).
3
+ *
4
+ * ## 🛑 WHERE the PDF goes, and why it is not the media path
5
+ *
6
+ * `private/invoices/{orderId}/{uuid}.pdf`, with **no `mediaAssets` row** and a
7
+ * **random** object name. An invoice carries the buyer's full name, address and
8
+ * phone.
9
+ *
10
+ * Three facts, each of which alone would be enough:
11
+ *
12
+ * 1. `/api/media/[...slug]` applies **no authentication at all** — it is a
13
+ * deliberately bare handler whose job is watermarking public product
14
+ * photography.
15
+ * 2. The bucket's storage rule is `allow read: if true`.
16
+ * 3. Media filenames in this codebase are content-derived and therefore
17
+ * GUESSABLE — `generateInvoiceFilename` is `invoice-{orderId}-{YYYYMMDD}`.
18
+ *
19
+ * So the random name is against the SEO-slug convention everywhere else *on
20
+ * purpose*: guessable is the one property this must not have. And the `private/`
21
+ * prefix is refused by the proxy's raw-path fallback — a check, not a naming
22
+ * convention, which is precisely the distinction the digital-content comment
23
+ * got wrong for weeks.
24
+ *
25
+ * ## 🛑 Store the bytes; never re-render to serve or to email
26
+ *
27
+ * Two renders can disagree — a changed template, a different library version,
28
+ * a settings read that moved — and the buyer's copy must be byte-identical to
29
+ * ours. So the bytes are written once, at issue time, and every later consumer
30
+ * reads that object. On-demand rendering survives only as a fallback for orders
31
+ * that predate this phase and have no stored path.
32
+ */
33
+ import type { OrderDocument } from "../../../../features/orders/schemas/firestore";
34
+ /** Storage prefix. Mirrors `private/digital-content/` — refused by the proxy. */
35
+ export declare const INVOICE_PREFIX_PATH = "private/invoices";
36
+ /**
37
+ * Renders the PDF for an order.
38
+ *
39
+ * Injected rather than imported, because the renderer lives in
40
+ * `functions/src/invoice-pdf.ts` — it pulls `pdfkit`, which is a Functions
41
+ * dependency and has no business in appkit's module graph (Rule #6's build
42
+ * weight, and Root Cause #18's import-chain trap).
43
+ */
44
+ export type InvoiceRenderer = (order: OrderDocument) => Promise<Buffer>;
45
+ export interface IssueInvoiceResult {
46
+ invoiceNumber: string;
47
+ invoiceStoragePath: string;
48
+ /** False when the order already had an invoice — the normal re-entry case. */
49
+ issued: boolean;
50
+ }
51
+ /**
52
+ * Give a paid order its invoice number and its stored PDF.
53
+ *
54
+ * 🛑 Idempotent through `allocateSeriesNumber`'s transactional claim.
55
+ * `onOrderStatusChange` can fire more than once, and two numbers for one order
56
+ * is the single unrecoverable failure in this phase — a number cannot be
57
+ * withdrawn without leaving a gap.
58
+ *
59
+ * 🛑 Called on `paymentStatus → paid`, never at creation. COD and manual orders
60
+ * are created `pending`.
61
+ */
62
+ export declare function issueInvoiceForOrder(order: OrderDocument, render: InvoiceRenderer): Promise<IssueInvoiceResult>;
63
+ /**
64
+ * Persist bytes rendered on demand, for an order that is already NUMBERED.
65
+ *
66
+ * ## Why the lazy path exists at all
67
+ *
68
+ * Numbering happens in `onOrderStatusChange`, which runs in appkit and cannot
69
+ * render — `pdfkit` is a Functions dependency and bringing it into appkit's
70
+ * module graph would add it to every consumer build (Rule #6's build weight,
71
+ * Root Cause #18's import chain). So the two halves are split: the number is
72
+ * allocated immediately and transactionally, the bytes are produced the first
73
+ * time anyone asks for them and then stored forever.
74
+ *
75
+ * That split is a deliberate trade and worth naming: the first download of a
76
+ * given invoice pays a render, and every later one does not. The alternative —
77
+ * a second Firestore trigger on `orders` living in `functions/` — is the shape
78
+ * that burned a million invocations a day in Root Cause #92.
79
+ *
80
+ * 🛑 Refuses to overwrite an existing path. A second numbered copy of one
81
+ * invoice is two documents claiming one number, and the one a buyer already
82
+ * downloaded is the one that counts.
83
+ *
84
+ * 🛑 Refuses an un-numbered order. Storing a PDF before the number exists
85
+ * would freeze a pre-payment snapshot as "the invoice", and the real issuance
86
+ * would then find a path already set.
87
+ */
88
+ export declare function storeRenderedInvoice(orderId: string, bytes: Buffer): Promise<string | null>;
89
+ /**
90
+ * The stored bytes for an already-issued invoice, or null.
91
+ *
92
+ * Returns null rather than throwing on a missing object, because the caller's
93
+ * correct response is to fall back to an on-demand render for a pre-Phase-7
94
+ * order — and a throw would turn a legacy order's invoice page into an error.
95
+ */
96
+ export declare function readStoredInvoice(storagePath: string | undefined): Promise<Buffer | null>;
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Issuing an invoice: allocate the number, render, store the bytes (Phase 7).
3
+ *
4
+ * ## 🛑 WHERE the PDF goes, and why it is not the media path
5
+ *
6
+ * `private/invoices/{orderId}/{uuid}.pdf`, with **no `mediaAssets` row** and a
7
+ * **random** object name. An invoice carries the buyer's full name, address and
8
+ * phone.
9
+ *
10
+ * Three facts, each of which alone would be enough:
11
+ *
12
+ * 1. `/api/media/[...slug]` applies **no authentication at all** — it is a
13
+ * deliberately bare handler whose job is watermarking public product
14
+ * photography.
15
+ * 2. The bucket's storage rule is `allow read: if true`.
16
+ * 3. Media filenames in this codebase are content-derived and therefore
17
+ * GUESSABLE — `generateInvoiceFilename` is `invoice-{orderId}-{YYYYMMDD}`.
18
+ *
19
+ * So the random name is against the SEO-slug convention everywhere else *on
20
+ * purpose*: guessable is the one property this must not have. And the `private/`
21
+ * prefix is refused by the proxy's raw-path fallback — a check, not a naming
22
+ * convention, which is precisely the distinction the digital-content comment
23
+ * got wrong for weeks.
24
+ *
25
+ * ## 🛑 Store the bytes; never re-render to serve or to email
26
+ *
27
+ * Two renders can disagree — a changed template, a different library version,
28
+ * a settings read that moved — and the buyer's copy must be byte-identical to
29
+ * ours. So the bytes are written once, at issue time, and every later consumer
30
+ * reads that object. On-demand rendering survives only as a fallback for orders
31
+ * that predate this phase and have no stored path.
32
+ */
33
+ import { randomUUID } from "node:crypto";
34
+ import { getAdminStorage } from "../../../../providers/db-firebase/index.js";
35
+ import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
36
+ import { serverLogger } from "../../../../monitoring/index.js";
37
+ import { normalizeError } from "../../../../errors/normalize.js";
38
+ import { allocateSeriesNumber } from "./allocate.js";
39
+ /** Storage prefix. Mirrors `private/digital-content/` — refused by the proxy. */
40
+ export const INVOICE_PREFIX_PATH = "private/invoices";
41
+ /**
42
+ * Give a paid order its invoice number and its stored PDF.
43
+ *
44
+ * 🛑 Idempotent through `allocateSeriesNumber`'s transactional claim.
45
+ * `onOrderStatusChange` can fire more than once, and two numbers for one order
46
+ * is the single unrecoverable failure in this phase — a number cannot be
47
+ * withdrawn without leaving a gap.
48
+ *
49
+ * 🛑 Called on `paymentStatus → paid`, never at creation. COD and manual orders
50
+ * are created `pending`.
51
+ */
52
+ export async function issueInvoiceForOrder(order, render) {
53
+ /*
54
+ * The fast path, and the common one: a second trigger on an order that is
55
+ * already invoiced does no work and touches no counter. Checked before the
56
+ * transaction purely to save the round trip — the transaction would reach
57
+ * the same answer, which is what makes this safe rather than a race.
58
+ */
59
+ if (order.invoiceNumber && order.invoiceStoragePath) {
60
+ return {
61
+ invoiceNumber: order.invoiceNumber,
62
+ invoiceStoragePath: order.invoiceStoragePath,
63
+ issued: false,
64
+ };
65
+ }
66
+ const issuedAt = new Date();
67
+ const allocation = await allocateSeriesNumber({
68
+ kind: "invoice",
69
+ subjectId: order.id,
70
+ orderId: order.id,
71
+ at: issuedAt,
72
+ });
73
+ /*
74
+ * 🛑 `randomUUID()`, and the ORDER ID is in the directory rather than the
75
+ * filename. The directory makes an operator's life possible (every document
76
+ * for one order is in one place); the random leaf is what makes the object
77
+ * unguessable. Putting the order id in the FILENAME would undo the second
78
+ * without improving the first.
79
+ */
80
+ const storagePath = `${INVOICE_PREFIX_PATH}/${order.id}/${randomUUID()}.pdf`;
81
+ /*
82
+ * Rendered with the number already on the order object, so the PDF carries
83
+ * the number it was allocated — the renderer reads `order.invoiceNumber`,
84
+ * and handing it the un-numbered document would produce an invoice with no
85
+ * number printed on it while the database said otherwise.
86
+ */
87
+ const buffer = await render({
88
+ ...order,
89
+ invoiceNumber: allocation.number,
90
+ invoiceIssuedAt: issuedAt,
91
+ });
92
+ await getAdminStorage()
93
+ .bucket()
94
+ .file(storagePath)
95
+ .save(buffer, {
96
+ contentType: "application/pdf",
97
+ /*
98
+ * 🛑 `private, no-store` on the OBJECT as well as on every response that
99
+ * serves it. The object is not reachable without a signed URL or the
100
+ * Admin SDK, but an intermediate cache that ever saw one must not keep
101
+ * it: an invoice is one buyer's address, not a product photo.
102
+ */
103
+ metadata: { cacheControl: "private, no-store" },
104
+ resumable: false,
105
+ });
106
+ /*
107
+ * 🛑 The order is updated AFTER the bytes exist. The reverse ordering would
108
+ * leave an order claiming an `invoiceStoragePath` that resolves to nothing,
109
+ * and the serve route would then 404 on a document the buyer is entitled to
110
+ * — while the number is already consumed, so retrying cannot re-issue it.
111
+ *
112
+ * A crash between the save and this update leaves an unreferenced object
113
+ * (a few KB) and an order with a number but no path, which the next trigger
114
+ * re-renders into a NEW object. Wasteful and harmless, which is the right
115
+ * side of this trade.
116
+ */
117
+ await orderRepository.update(order.id, {
118
+ invoiceNumber: allocation.number,
119
+ invoiceIssuedAt: issuedAt,
120
+ invoiceStoragePath: storagePath,
121
+ });
122
+ serverLogger.info("[invoicing] invoice issued", {
123
+ orderId: order.id,
124
+ invoiceNumber: allocation.number,
125
+ });
126
+ return {
127
+ invoiceNumber: allocation.number,
128
+ invoiceStoragePath: storagePath,
129
+ issued: allocation.allocated,
130
+ };
131
+ }
132
+ /**
133
+ * Persist bytes rendered on demand, for an order that is already NUMBERED.
134
+ *
135
+ * ## Why the lazy path exists at all
136
+ *
137
+ * Numbering happens in `onOrderStatusChange`, which runs in appkit and cannot
138
+ * render — `pdfkit` is a Functions dependency and bringing it into appkit's
139
+ * module graph would add it to every consumer build (Rule #6's build weight,
140
+ * Root Cause #18's import chain). So the two halves are split: the number is
141
+ * allocated immediately and transactionally, the bytes are produced the first
142
+ * time anyone asks for them and then stored forever.
143
+ *
144
+ * That split is a deliberate trade and worth naming: the first download of a
145
+ * given invoice pays a render, and every later one does not. The alternative —
146
+ * a second Firestore trigger on `orders` living in `functions/` — is the shape
147
+ * that burned a million invocations a day in Root Cause #92.
148
+ *
149
+ * 🛑 Refuses to overwrite an existing path. A second numbered copy of one
150
+ * invoice is two documents claiming one number, and the one a buyer already
151
+ * downloaded is the one that counts.
152
+ *
153
+ * 🛑 Refuses an un-numbered order. Storing a PDF before the number exists
154
+ * would freeze a pre-payment snapshot as "the invoice", and the real issuance
155
+ * would then find a path already set.
156
+ */
157
+ export async function storeRenderedInvoice(orderId, bytes) {
158
+ const order = await orderRepository.findById(orderId);
159
+ if (!order?.invoiceNumber)
160
+ return null;
161
+ if (order.invoiceStoragePath)
162
+ return order.invoiceStoragePath;
163
+ const storagePath = `${INVOICE_PREFIX_PATH}/${orderId}/${randomUUID()}.pdf`;
164
+ await getAdminStorage()
165
+ .bucket()
166
+ .file(storagePath)
167
+ .save(bytes, {
168
+ contentType: "application/pdf",
169
+ metadata: { cacheControl: "private, no-store" },
170
+ resumable: false,
171
+ });
172
+ // Path written only after the bytes exist — the reverse would point at
173
+ // nothing and 404 a document the buyer is entitled to.
174
+ await orderRepository.update(orderId, { invoiceStoragePath: storagePath });
175
+ serverLogger.info("[invoicing] invoice bytes stored on first download", {
176
+ orderId,
177
+ invoiceNumber: order.invoiceNumber,
178
+ });
179
+ return storagePath;
180
+ }
181
+ /**
182
+ * The stored bytes for an already-issued invoice, or null.
183
+ *
184
+ * Returns null rather than throwing on a missing object, because the caller's
185
+ * correct response is to fall back to an on-demand render for a pre-Phase-7
186
+ * order — and a throw would turn a legacy order's invoice page into an error.
187
+ */
188
+ export async function readStoredInvoice(storagePath) {
189
+ if (!storagePath)
190
+ return null;
191
+ try {
192
+ const [buffer] = await getAdminStorage().bucket().file(storagePath).download();
193
+ return buffer;
194
+ }
195
+ catch (err) {
196
+ serverLogger.warn("[invoicing] stored invoice unreadable — falling back to render", {
197
+ storagePath,
198
+ error: normalizeError(err).message,
199
+ });
200
+ return null;
201
+ }
202
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Server-side verification that a coordinated purchase's destinations are real
3
+ * and belong to the people naming them.
4
+ *
5
+ * ## 🛑 Why this file exists at all
6
+ *
7
+ * `CoordinationMember.destinationAddressId` is a REFERENCE, which is the right
8
+ * shape — copying the address inline would put a name, a phone number and a
9
+ * street into an offer document with no encryption story and no retention
10
+ * story. But a reference has a failure mode a copy does not:
11
+ *
12
+ * **An id is guessable in a way an address is not.** Without this check a
13
+ * member supplies any `addresses` document id and the parcel ships to a
14
+ * stranger's house, at the coordinator's expense, with every other part of
15
+ * the flow behaving correctly. Nothing errors; the order is valid; the goods
16
+ * simply go somewhere nobody chose.
17
+ *
18
+ * So the rule is: **an id that crosses the wire is never trusted, and is
19
+ * resolved against its claimed owner before anything is written.**
20
+ */
21
+ import type { CoordinationMember } from "../../../shared/features/offers/coordination";
22
+ /**
23
+ * Every named destination must exist and belong to the member who named it.
24
+ *
25
+ * 🛑 **Owned by THAT MEMBER, not merely by someone.** Checking only that the
26
+ * address exists and has `ownerType: "user"` would let a member ship to any
27
+ * other user's saved address — the weaker check reads as a real one and
28
+ * closes nothing.
29
+ *
30
+ * 🛑 **Fails the whole request, naming the member.** Dropping a bad
31
+ * destination and shipping that member's parcel to the coordinator instead
32
+ * would be a silent substitution of somebody's delivery address, which is
33
+ * exactly the class of quiet wrongness this check exists to prevent. Grouped
34
+ * cart lines take the same position for the same reason.
35
+ *
36
+ * One `getAll` over the distinct ids, not one read per member: five members
37
+ * sharing two destinations is two reads, and Rule #6's budget is per request
38
+ * rather than per member.
39
+ */
40
+ export declare function assertOwnedAddresses(members: readonly CoordinationMember[]): Promise<void>;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Server-side verification that a coordinated purchase's destinations are real
3
+ * and belong to the people naming them.
4
+ *
5
+ * ## 🛑 Why this file exists at all
6
+ *
7
+ * `CoordinationMember.destinationAddressId` is a REFERENCE, which is the right
8
+ * shape — copying the address inline would put a name, a phone number and a
9
+ * street into an offer document with no encryption story and no retention
10
+ * story. But a reference has a failure mode a copy does not:
11
+ *
12
+ * **An id is guessable in a way an address is not.** Without this check a
13
+ * member supplies any `addresses` document id and the parcel ships to a
14
+ * stranger's house, at the coordinator's expense, with every other part of
15
+ * the flow behaving correctly. Nothing errors; the order is valid; the goods
16
+ * simply go somewhere nobody chose.
17
+ *
18
+ * So the rule is: **an id that crosses the wire is never trusted, and is
19
+ * resolved against its claimed owner before anything is written.**
20
+ */
21
+ import { addressesRepository } from "../../../../features/addresses/repository/addresses.repository.js";
22
+ import { ValidationError } from "../../../../errors/validation-error.js";
23
+ /**
24
+ * Every named destination must exist and belong to the member who named it.
25
+ *
26
+ * 🛑 **Owned by THAT MEMBER, not merely by someone.** Checking only that the
27
+ * address exists and has `ownerType: "user"` would let a member ship to any
28
+ * other user's saved address — the weaker check reads as a real one and
29
+ * closes nothing.
30
+ *
31
+ * 🛑 **Fails the whole request, naming the member.** Dropping a bad
32
+ * destination and shipping that member's parcel to the coordinator instead
33
+ * would be a silent substitution of somebody's delivery address, which is
34
+ * exactly the class of quiet wrongness this check exists to prevent. Grouped
35
+ * cart lines take the same position for the same reason.
36
+ *
37
+ * One `getAll` over the distinct ids, not one read per member: five members
38
+ * sharing two destinations is two reads, and Rule #6's budget is per request
39
+ * rather than per member.
40
+ */
41
+ export async function assertOwnedAddresses(members) {
42
+ const named = members.filter((m) => !!m.destinationAddressId);
43
+ if (named.length === 0)
44
+ return;
45
+ const ids = [...new Set(named.map((m) => m.destinationAddressId))];
46
+ const docs = await Promise.all(ids.map((id) => addressesRepository.findById(id)));
47
+ const byId = new Map(ids.map((id, i) => [id, docs[i]]));
48
+ for (const member of named) {
49
+ const address = byId.get(member.destinationAddressId);
50
+ /*
51
+ * 🛑 ONE message for "does not exist" and "is not yours".
52
+ *
53
+ * Distinguishing them turns this endpoint into an oracle for which
54
+ * `addresses` ids are real — a caller probes ids and reads existence off
55
+ * the error text. The member who typed a genuinely stale id and the
56
+ * member probing somebody else's get the same answer, and only the first
57
+ * of those has a legitimate next step anyway (pick again).
58
+ */
59
+ if (!address || address.ownerType !== "user" || address.ownerId !== member.uid) {
60
+ throw new ValidationError("A chosen delivery address is not available.", {
61
+ destinationAddressId: `${member.uid}: that address cannot be used.`,
62
+ });
63
+ }
64
+ }
65
+ }
@@ -1,4 +1,5 @@
1
1
  import { type ActionResult } from "../../../shared/types/action-result";
2
+ import { type DisputeKind } from "../../../shared/features/orders/dispute-reasons";
2
3
  export declare function createOrderAction(input: unknown): Promise<ActionResult<unknown>>;
3
4
  /**
4
5
  * Cancel an order, and give the money back if any was taken (D14).
@@ -58,7 +59,13 @@ export declare function adminRejectPaymentAsFraudAction(orderId: string, note: s
58
59
  * the seller (store owner), or admin/moderator. Does not itself reverse
59
60
  * payment/order status — surfaces to the admin queue for investigation.
60
61
  */
61
- export declare function raiseOrderDisputeAction(orderId: string, reason: string): Promise<ActionResult<void>>;
62
+ export declare function raiseOrderDisputeAction(orderId: string, input: {
63
+ kind: DisputeKind;
64
+ /** `disputeReasons/{id}` — must exist and must match `kind`. */
65
+ reasonId: string;
66
+ /** The buyer's own words. Optional; the reason is the required part. */
67
+ detail?: string;
68
+ }): Promise<ActionResult<void>>;
62
69
  /**
63
70
  * A buyer submits payment for ONE of their own EMI instalments.
64
71
  *
@@ -15,6 +15,8 @@ import { adminNotificationsRepository, orderRepository, storeRepository } from "
15
15
  import { requireRoleUser } from "../../../../providers/auth-firebase/helpers.js";
16
16
  import { createOrderSchema, updateOrderStatusSchema, cancelOrderSchema, returnRequestSchema, } from "../../../shared/features/orders/schema.js";
17
17
  import { isFinalSaleExempt } from "../../../shared/features/orders/return-reasons.js";
18
+ import { DISPUTE_KIND_LABEL, checkDisputeGate, isDisputeKind, } from "../../../shared/features/orders/dispute-reasons.js";
19
+ import { disputeReasonsRepository } from "../../../../features/dispute-reasons/repository/dispute-reasons.repository.js";
18
20
  import { REFUND_COPY } from "../../../shared/features/orders/refund-copy.js";
19
21
  import { assertOrderCancellable, assertReturnWindowOpen } from "./service.js";
20
22
  import { cancelOrderWithRefund } from "./cancellation-refund.js";
@@ -390,10 +392,12 @@ export async function adminRejectPaymentAsFraudAction(orderId, note) {
390
392
  * the seller (store owner), or admin/moderator. Does not itself reverse
391
393
  * payment/order status — surfaces to the admin queue for investigation.
392
394
  */
393
- export async function raiseOrderDisputeAction(orderId, reason) {
395
+ export async function raiseOrderDisputeAction(orderId, input) {
394
396
  return wrapAction(async () => {
395
397
  const user = await requireRoleUser(["user", "buyer", "seller", "admin"]);
396
- if (!reason.trim())
398
+ if (!isDisputeKind(input.kind))
399
+ throw new ValidationError("Pick what kind of problem this is.");
400
+ if (!input.reasonId?.trim())
397
401
  throw new ValidationError(REASON_REQUIRED_MSG);
398
402
  const order = await orderRepository.findById(orderId);
399
403
  if (!order)
@@ -407,11 +411,50 @@ export async function raiseOrderDisputeAction(orderId, reason) {
407
411
  if (!isBuyer && !isSeller && !isAdminUser(user) && !isModeratorUser(user)) {
408
412
  throw new OrderOwnershipError(orderId);
409
413
  }
410
- if (!order.autoApproved) {
411
- throw new ValidationError("Disputes can only be raised on auto-approved orders. Use the return/refund flow for manually-reviewed orders.");
412
- }
414
+ /*
415
+ * 🛑 THE `autoApproved` RESTRICTION IS GONE, and that is the one
416
+ * behaviour change an existing path will notice.
417
+ *
418
+ * It used to refuse every order whose payment was not auto-approved,
419
+ * telling the caller to use the return/refund flow — which made this a
420
+ * narrow "the 2-hour auto-approval might have been wrong" escalation
421
+ * rather than a dispute mechanism. The owner's rules are about delivery
422
+ * and item problems on ANY order, so the scope had to widen.
423
+ *
424
+ * What replaces it is a REAL gate per kind, below: the old check refused
425
+ * 99% of orders for a reason unrelated to the complaint, while accepting
426
+ * "it never arrived" on an order delivered last week.
427
+ */
413
428
  if (order.disputeStatus === "open")
414
429
  return; // idempotent
430
+ /*
431
+ * The reason must exist AND belong to the kind being claimed.
432
+ *
433
+ * 🛑 Both halves matter. Without the kind check a caller could pass an
434
+ * `item` reason with `kind: "not_reached"` and pass the wrong gate — the
435
+ * kind is what the gate keys on, so an unvalidated pairing is the gate
436
+ * being bypassed by the request body.
437
+ */
438
+ const reasonDoc = await disputeReasonsRepository.findById(input.reasonId.trim());
439
+ if (!reasonDoc || !reasonDoc.isActive) {
440
+ throw new ValidationError("That reason is no longer available. Pick another.");
441
+ }
442
+ if (reasonDoc.kind !== input.kind) {
443
+ throw new ValidationError("That reason doesn't match the kind of problem selected.");
444
+ }
445
+ /*
446
+ * ── THE GATES ─────────────────────────────────────────────────────────
447
+ *
448
+ * Owner's rules: an item dispute only after the order was received; a
449
+ * "not reached" dispute only once the ETD is more than 3 days past.
450
+ *
451
+ * Enforced HERE, where the request arrives, for the same reason the
452
+ * 7-day cutoff below is: a refusal has to live where the refusing
453
+ * happens. The message is buyer-facing and names the next action.
454
+ */
455
+ const gate = checkDisputeGate(input.kind, order);
456
+ if (!gate.allowed)
457
+ throw new ValidationError(gate.message);
415
458
  /*
416
459
  * ── THE DISPUTE CUTOFF ────────────────────────────────────────────────
417
460
  *
@@ -436,15 +479,39 @@ export async function raiseOrderDisputeAction(orderId, reason) {
436
479
  * `disputeWindowClosed` boolean would need a backfill and would drift from
437
480
  * the policy the day the duration changed (Root Cause #42).
438
481
  */
482
+ /*
483
+ * 🛑 This correctly does NOT apply to a `not_reached` dispute, and that is
484
+ * not an oversight to "fix".
485
+ *
486
+ * `order-complete` is anchored on `deliveryDate`, so `deadlinesFor` yields
487
+ * no such deadline when nothing was delivered — and you cannot be past a
488
+ * delivery window that never happened. Making the cutoff refuse
489
+ * undelivered parcels would block exactly the buyers whose parcel is lost,
490
+ * which is the opposite of what a dispute window is for.
491
+ */
439
492
  const completion = deadlinesFor(order).find((d) => d.kind === "order-complete");
440
493
  if (completion && completion.dueAt.getTime() <= Date.now()) {
441
494
  throw new ValidationError(`The dispute window for this order closed on ${completion.dueAt.toLocaleDateString("en-IN")} — 7 days after delivery. Contact support if you believe this is wrong.`);
442
495
  }
496
+ /*
497
+ * No `as any`. Every field below is declared on `OrderDocument`, and the
498
+ * cast that used to be here is precisely how a misspelled field reaches
499
+ * Firestore unnoticed — it silences the check on all six at once.
500
+ *
501
+ * 🛑 `disputeReason` stores the reason's LABEL, not its id. It is a
502
+ * SNAPSHOT: staff can retire or reword a `disputeReasons` row, and neither
503
+ * may rewrite what a buyer said months ago. `disputeReasonId` beside it is
504
+ * the join key for "what are buyers reporting"; the label is the record.
505
+ * Same discipline as the fee terms and the box presets.
506
+ */
443
507
  await orderRepository.update(orderId, {
444
508
  disputeRaised: true,
445
509
  disputeRaisedBy: user.uid,
446
510
  disputeRaisedAt: new Date(),
447
- disputeReason: reason,
511
+ disputeKind: input.kind,
512
+ disputeReasonId: reasonDoc.id,
513
+ disputeReason: reasonDoc.label,
514
+ ...(input.detail?.trim() ? { disputeDetail: input.detail.trim() } : {}),
448
515
  disputeStatus: "open",
449
516
  });
450
517
  /*
@@ -463,8 +530,14 @@ export async function raiseOrderDisputeAction(orderId, reason) {
463
530
  await adminNotificationsRepository
464
531
  .create({
465
532
  category: "fraud",
466
- title: "Dispute raised on auto-approved order",
467
- body: `A dispute was raised on order "${order.productTitle}" (auto-approved payment). Reason: ${reason}.`,
533
+ /*
534
+ * 🛑 No longer "on auto-approved order". The title said that because
535
+ * the action refused everything else; now any order can be disputed,
536
+ * and a title naming a payment path the dispute has nothing to do with
537
+ * would send an operator looking in the wrong place.
538
+ */
539
+ title: `Dispute raised: ${DISPUTE_KIND_LABEL[input.kind]}`,
540
+ body: `A dispute was raised on order "${order.productTitle}". Reason: ${reasonDoc.label}.${input.detail?.trim() ? ` Buyer said: "${input.detail.trim()}"` : ""}`,
468
541
  severity: "error",
469
542
  isRead: false,
470
543
  entityType: "order",
@@ -203,6 +203,39 @@ export function orderDocumentToOrder(doc) {
203
203
  saleProtectionAddon: doc.saleProtectionAddon,
204
204
  saleProtectionFee: doc.saleProtectionFee,
205
205
  platformFee: doc.platformFee,
206
+ /*
207
+ * D46/D47 — the points tender, mapped for the same reason as everything
208
+ * above it and stated once more because this is the field most likely to
209
+ * be dropped: it is optional, it is absent on every historical order, and a
210
+ * surface that never receives it renders as though no points were used.
211
+ *
212
+ * That is Root Cause #57 exactly, and on this field it is not cosmetic —
213
+ * the order page would show a buyer "Total ₹1,000" and a card statement
214
+ * reading ₹750 with nothing on the page explaining the difference, which
215
+ * reads as an overcharge rather than as their own discount.
216
+ *
217
+ * Readers must use `amountDue ?? total`, never a `?? 0`: absent means "no
218
+ * points, the whole total was collected", and zero means free.
219
+ */
220
+ pointsApplied: doc.pointsApplied,
221
+ amountDue: doc.amountDue,
222
+ /*
223
+ * Phase 7 — the invoice number, mapped for the same Root Cause #57 reason
224
+ * as everything above. Unmapped, the buyer's invoice page would render a
225
+ * statutory document with no number on it while the database held one, and
226
+ * the only symptom would be a blank line where an auditor looks first.
227
+ *
228
+ * 🛑 `invoiceStoragePath` is deliberately NOT mapped. It is a raw Storage
229
+ * path, and the client has no use for one — the bytes are served by an
230
+ * authenticated route. Publishing the path into an RSC payload would hand
231
+ * every buyer a string pointing at a private object, which is the shape of
232
+ * Root Cause #70's leak even though the `private/` prefix is refused by
233
+ * the proxy.
234
+ */
235
+ invoiceNumber: doc.invoiceNumber,
236
+ invoiceIssuedAt: doc.invoiceIssuedAt
237
+ ? new Date(doc.invoiceIssuedAt).toISOString()
238
+ : undefined,
206
239
  /*
207
240
  * 🛑 `gstAmount` -> `tax`, and codHandlingFee alongside it.
208
241
  *
@@ -313,6 +346,27 @@ export function orderDocumentToOrder(doc) {
313
346
  timelineTruncated: doc.statusHistoryTruncated,
314
347
  /** How the buyer acquired the right to buy. Written once, at creation. */
315
348
  sourceContext: doc.sourceContext,
349
+ /*
350
+ * D55 — the pre-order balance, present only once a seller has opened it.
351
+ *
352
+ * 🛑 `reviewedBy` is deliberately NOT mapped. It is the reviewing staff
353
+ * member's uid and no buyer surface reads it, so it stays server-side by
354
+ * default (§ Public Data Projections — a field is private until someone
355
+ * names the client that reads it).
356
+ */
357
+ balance: doc.balance
358
+ ? {
359
+ amount: doc.balance.amount,
360
+ method: doc.balance.method,
361
+ status: doc.balance.status,
362
+ dueBy: toIsoOrUndefined(doc.balance.dueBy),
363
+ proofUrl: doc.balance.proofUrl,
364
+ transactionId: doc.balance.transactionId,
365
+ submittedAt: toIsoOrUndefined(doc.balance.submittedAt),
366
+ reviewedAt: toIsoOrUndefined(doc.balance.reviewedAt),
367
+ reviewNote: doc.balance.reviewNote,
368
+ }
369
+ : undefined,
316
370
  createdAt: doc.createdAt instanceof Date
317
371
  ? doc.createdAt.toISOString()
318
372
  : String(doc.createdAt),