@mohasinac/appkit 4.61.0 → 4.62.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +19 -3
  3. package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +1 -1
  4. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +1 -1
  5. package/dist/_internal/server/features/checkout/actions.d.ts +80 -0
  6. package/dist/_internal/server/features/checkout/actions.js +542 -26
  7. package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
  8. package/dist/_internal/server/features/invoicing/allocate.js +162 -0
  9. package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
  10. package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
  11. package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
  12. package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
  13. package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
  14. package/dist/_internal/server/features/invoicing/issue.js +202 -0
  15. package/dist/_internal/server/features/orders/actions.d.ts +8 -1
  16. package/dist/_internal/server/features/orders/actions.js +81 -8
  17. package/dist/_internal/server/features/orders/adapters.js +33 -0
  18. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
  19. package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
  20. package/dist/_internal/server/features/points/award.js +46 -4
  21. package/dist/_internal/server/features/refunds/actions.js +26 -0
  22. package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
  23. package/dist/_internal/server/features/shipping/courier-block.js +239 -0
  24. package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
  25. package/dist/_internal/server/features/site-settings/adapters.js +15 -0
  26. package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
  27. package/dist/_internal/server/features/stores/adapters.js +51 -0
  28. package/dist/_internal/server/functions/scheduled.js +38 -1
  29. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +9 -2
  30. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
  31. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
  32. package/dist/_internal/server/jobs/core/index.d.ts +2 -1
  33. package/dist/_internal/server/jobs/core/index.js +2 -1
  34. package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
  35. package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
  36. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
  37. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +62 -0
  38. package/dist/_internal/server/jobs/core/paymentReconcile.js +87 -97
  39. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
  40. package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
  41. package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
  42. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +10 -2
  43. package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
  44. package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
  45. package/dist/_internal/shared/actions/action-registry.js +177 -9
  46. package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
  47. package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
  48. package/dist/_internal/shared/checkout/order-math.d.ts +145 -0
  49. package/dist/_internal/shared/checkout/order-math.js +173 -0
  50. package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
  51. package/dist/_internal/shared/checkout/refundable-base.js +13 -1
  52. package/dist/_internal/shared/checkout/split.d.ts +87 -0
  53. package/dist/_internal/shared/checkout/split.js +100 -0
  54. package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
  55. package/dist/_internal/shared/content/disclaimers.js +217 -0
  56. package/dist/_internal/shared/content/published-values.d.ts +88 -0
  57. package/dist/_internal/shared/content/published-values.js +198 -0
  58. package/dist/_internal/shared/features/admin/integrations.d.ts +109 -0
  59. package/dist/_internal/shared/features/admin/integrations.js +253 -0
  60. package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
  61. package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
  62. package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
  63. package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
  64. package/dist/_internal/shared/features/points/accepts.d.ts +36 -0
  65. package/dist/_internal/shared/features/points/accepts.js +43 -0
  66. package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
  67. package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
  68. package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
  69. package/dist/_internal/shared/fees/calculator.d.ts +33 -1
  70. package/dist/_internal/shared/fees/calculator.js +32 -2
  71. package/dist/_internal/shared/media/limits.d.ts +10 -0
  72. package/dist/_internal/shared/media/limits.js +10 -0
  73. package/dist/_internal/shared/types/action-result.js +15 -1
  74. package/dist/client/api/surface-error.js +16 -2
  75. package/dist/client/api/useApiMutation.js +17 -1
  76. package/dist/client.d.ts +12 -1
  77. package/dist/client.js +31 -1
  78. package/dist/constants/api-endpoints.d.ts +45 -0
  79. package/dist/constants/api-endpoints.js +15 -0
  80. package/dist/constants/field-names.d.ts +18 -0
  81. package/dist/constants/field-names.js +28 -0
  82. package/dist/contracts/index.d.ts +1 -1
  83. package/dist/contracts/shipping.d.ts +45 -0
  84. package/dist/contracts/shipping.js +15 -0
  85. package/dist/core/server-action.js +19 -3
  86. package/dist/errors/error-mapping.js +26 -1
  87. package/dist/features/about/components/AboutView.js +1 -1
  88. package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
  89. package/dist/features/account/components/UserOffersPanel.js +5 -3
  90. package/dist/features/account/components/UserOrdersView.js +19 -33
  91. package/dist/features/admin/components/AdminActionIndexView.js +100 -5
  92. package/dist/features/admin/components/AdminAddressClustersView.js +21 -2
  93. package/dist/features/admin/components/AdminBlogEditorView.js +8 -8
  94. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  95. package/dist/features/admin/components/AdminCarouselView.js +96 -4
  96. package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
  97. package/dist/features/admin/components/AdminFulfillmentView.js +3 -2
  98. package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
  99. package/dist/features/admin/components/AdminInvoicingView.js +65 -0
  100. package/dist/features/admin/components/AdminMediaView.js +36 -3
  101. package/dist/features/admin/components/AdminNavigationView.js +4 -1
  102. package/dist/features/admin/components/AdminOffersView.js +2 -1
  103. package/dist/features/admin/components/AdminOrderEditorView.d.ts +18 -1
  104. package/dist/features/admin/components/AdminOrderEditorView.js +11 -2
  105. package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
  106. package/dist/features/admin/components/AdminOrdersView.js +67 -5
  107. package/dist/features/admin/components/AdminPaymentClustersView.js +19 -2
  108. package/dist/features/admin/components/AdminProductEditorView.js +2 -2
  109. package/dist/features/admin/components/AdminSectionsView.d.ts +10 -0
  110. package/dist/features/admin/components/AdminSectionsView.js +486 -224
  111. package/dist/features/admin/components/OwnerQuickActions.js +24 -2
  112. package/dist/features/admin/components/ReferenceRecordEditor.js +1 -1
  113. package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
  114. package/dist/features/admin/constants/filter-tabs.js +6 -0
  115. package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
  116. package/dist/features/admin/hooks/useAdminListingData.js +16 -0
  117. package/dist/features/admin/hooks/useOptimisticRowFlag.d.ts +33 -0
  118. package/dist/features/admin/hooks/useOptimisticRowFlag.js +87 -0
  119. package/dist/features/admin/repository/notification.repository.d.ts +15 -0
  120. package/dist/features/admin/repository/notification.repository.js +32 -0
  121. package/dist/features/admin/schemas/action-index-form.d.ts +34 -0
  122. package/dist/features/admin/schemas/action-index-form.js +115 -0
  123. package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
  124. package/dist/features/admin/schemas/firestore.d.ts +30 -0
  125. package/dist/features/admin/schemas/firestore.js +5 -0
  126. package/dist/features/audit-log/schemas/firestore.d.ts +30 -0
  127. package/dist/features/audit-log/schemas/firestore.js +15 -0
  128. package/dist/features/auth/components/RegisterForm.js +2 -1
  129. package/dist/features/auth/permissions/constants.js +13 -0
  130. package/dist/features/cart/components/CartPriceBreakdown.d.ts +13 -0
  131. package/dist/features/cart/components/CartPriceBreakdown.js +6 -2
  132. package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
  133. package/dist/features/cart/components/PointsTenderPicker.js +15 -0
  134. package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
  135. package/dist/features/cart/components/index.d.ts +2 -0
  136. package/dist/features/cart/components/index.js +1 -0
  137. package/dist/features/cart/repository/cart.repository.d.ts +19 -0
  138. package/dist/features/cart/repository/cart.repository.js +27 -0
  139. package/dist/features/cart/schemas/firestore.d.ts +92 -0
  140. package/dist/features/catalogue/components/CatalogueItemEditorView.js +3 -3
  141. package/dist/features/contact/email.d.ts +27 -10
  142. package/dist/features/contact/email.js +20 -3
  143. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +38 -0
  144. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +80 -0
  145. package/dist/features/dispute-reasons/schemas/firestore.d.ts +64 -0
  146. package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
  147. package/dist/features/events/components/AdminEventEditorView.js +8 -6
  148. package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
  149. package/dist/features/faq/components/FAQAccordion.js +29 -4
  150. package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
  151. package/dist/features/invoicing/schemas/firestore.js +45 -0
  152. package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
  153. package/dist/features/invoicing/utils/financial-year.js +53 -0
  154. package/dist/features/jobs/actions/enqueue-job.js +18 -1
  155. package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
  156. package/dist/features/jobs/repository/jobs.repository.js +84 -1
  157. package/dist/features/jobs/schemas/firestore.d.ts +80 -0
  158. package/dist/features/jobs/schemas/firestore.js +37 -0
  159. package/dist/features/layout/hooks/useFormBottomActions.js +20 -1
  160. package/dist/features/lottery/schemas/zod.d.ts +6 -6
  161. package/dist/features/media/MediaImage.js +22 -1
  162. package/dist/features/media/upload/ImageUpload.js +1 -1
  163. package/dist/features/obligations/constants/policies.js +24 -1
  164. package/dist/features/orders/components/EmiInstallmentsPanel.js +25 -7
  165. package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
  166. package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
  167. package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
  168. package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
  169. package/dist/features/orders/components/RaiseDisputeForm.js +111 -0
  170. package/dist/features/orders/components/RefundRequestView.js +22 -4
  171. package/dist/features/orders/components/index.d.ts +4 -0
  172. package/dist/features/orders/components/index.js +2 -0
  173. package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
  174. package/dist/features/orders/constants/order-lane-tabs.js +88 -0
  175. package/dist/features/orders/repository/orders.repository.d.ts +57 -0
  176. package/dist/features/orders/repository/orders.repository.js +145 -0
  177. package/dist/features/orders/schemas/firestore.d.ts +196 -1
  178. package/dist/features/orders/schemas/index.d.ts +60 -12
  179. package/dist/features/orders/schemas/index.js +12 -0
  180. package/dist/features/orders/types/index.d.ts +26 -0
  181. package/dist/features/orders/utils/courier-block.d.ts +25 -0
  182. package/dist/features/orders/utils/courier-block.js +23 -0
  183. package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
  184. package/dist/features/payment-cases/schemas/firestore.js +1 -0
  185. package/dist/features/products/components/GroupMemberPicker.js +4 -1
  186. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
  187. package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
  188. package/dist/features/products/components/ProductDetailActions.js +2 -1
  189. package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
  190. package/dist/features/products/components/ProductDetailPageView.js +2 -2
  191. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  192. package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
  193. package/dist/features/promotions/actions/coupon-stacking.js +21 -2
  194. package/dist/features/promotions/components/CouponCard.js +13 -0
  195. package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
  196. package/dist/features/promotions/constants/coupon-help.js +9 -1
  197. package/dist/features/promotions/schemas/firestore.d.ts +24 -0
  198. package/dist/features/promotions/schemas/firestore.js +4 -0
  199. package/dist/features/promotions/types/index.d.ts +19 -1
  200. package/dist/features/scams/components/ScamProfileView.js +2 -2
  201. package/dist/features/scams/components/ScamRegistryView.js +2 -2
  202. package/dist/features/search/action-index/tab-entries.js +1 -1
  203. package/dist/features/seller/actions/seller-actions.js +33 -3
  204. package/dist/features/seller/components/FulfillmentView.js +3 -2
  205. package/dist/features/seller/components/SellerAddressesView.js +32 -4
  206. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +62 -3
  207. package/dist/features/seller/components/SellerBundlesView.js +53 -14
  208. package/dist/features/seller/components/SellerGroupedListingsView.js +23 -3
  209. package/dist/features/seller/components/SellerListingTypeView.js +56 -10
  210. package/dist/features/seller/components/SellerOrdersView.js +129 -27
  211. package/dist/features/seller/components/SellerPayoutMethodsView.js +33 -1
  212. package/dist/features/seller/components/SellerPayoutSettingsView.js +48 -9
  213. package/dist/features/seller/components/SellerProductsView.js +5 -1
  214. package/dist/features/seller/components/SellerShippingConfigsView.js +33 -1
  215. package/dist/features/seller/components/SellerShippingView.js +2 -1
  216. package/dist/features/seller/components/SellerStoreCategoriesView.js +33 -1
  217. package/dist/features/seller/components/SellerSupportView.js +69 -2
  218. package/dist/features/seller/hooks/useSellerListingData.js +25 -0
  219. package/dist/features/shell/SectionForm.js +20 -16
  220. package/dist/features/shell/build-sections.js +14 -1
  221. package/dist/features/shipments/components/AdminShipmentLotItemsView.js +54 -1
  222. package/dist/features/shipments/components/AdminShipmentProjectionsView.js +3 -2
  223. package/dist/features/shipments/schemas/validation.d.ts +4 -4
  224. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +43 -0
  225. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +90 -0
  226. package/dist/features/shipping-boxes/schemas/firestore.d.ts +82 -0
  227. package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
  228. package/dist/features/sticker-trace/components/StickerCommitPanel.js +2 -1
  229. package/dist/features/sticker-trace/components/StickerPrintPanel.js +2 -1
  230. package/dist/features/sticker-trace/components/StickerRetryPanel.js +24 -4
  231. package/dist/features/sticker-trace/components/StickerTraceStudio.js +2 -1
  232. package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
  233. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
  234. package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
  235. package/dist/features/stores/schemas/firestore.d.ts +71 -0
  236. package/dist/features/stores/types/index.d.ts +19 -0
  237. package/dist/features/support/schemas/index.d.ts +6 -6
  238. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +3 -2
  239. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  240. package/dist/index.d.ts +11 -2
  241. package/dist/index.js +17 -2
  242. package/dist/next/routing/route-map.d.ts +24 -0
  243. package/dist/next/routing/route-map.js +19 -0
  244. package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
  245. package/dist/providers/shipping-shiprocket/index.js +84 -1
  246. package/dist/schemas/registry.d.ts +51 -22
  247. package/dist/schemas/sieve.d.ts +5 -5
  248. package/dist/server-entry.d.ts +2 -2
  249. package/dist/server-entry.js +8 -1
  250. package/dist/server.d.ts +11 -2
  251. package/dist/server.js +40 -2
  252. package/dist/styles.css +64 -80
  253. package/dist/tailwind-utilities.css +1 -1
  254. package/dist/tokens/themes/default-dark.js +1 -0
  255. package/dist/tokens/themes/default-light.js +1 -0
  256. package/dist/tokens/themes/required.d.ts +1 -1
  257. package/dist/tokens/themes/required.js +1 -0
  258. package/dist/tokens/tokens.css +15 -0
  259. package/dist/ui/components/Checkbox.js +10 -2
  260. package/dist/ui/components/DateInput.js +8 -1
  261. package/dist/ui/components/Disclaimer.d.ts +19 -0
  262. package/dist/ui/components/Disclaimer.js +74 -0
  263. package/dist/ui/components/Input.js +22 -2
  264. package/dist/ui/components/PaginatedSelect.d.ts +8 -19
  265. package/dist/ui/components/PaginatedSelect.js +43 -9
  266. package/dist/ui/components/Radio.js +8 -1
  267. package/dist/ui/components/Select.js +10 -2
  268. package/dist/ui/components/TagInput.d.ts +8 -1
  269. package/dist/ui/components/TagInput.js +4 -3
  270. package/dist/ui/components/Textarea.js +10 -2
  271. package/dist/ui/components/tabs-overflow.js +64 -11
  272. package/dist/ui/forms/FieldCheckbox.js +1 -1
  273. package/dist/ui/forms/FormErrorList.d.ts +0 -16
  274. package/dist/ui/forms/FormErrorList.js +34 -1
  275. package/dist/ui/forms/apply-zod-issues.d.ts +19 -1
  276. package/dist/ui/forms/apply-zod-issues.js +23 -1
  277. package/dist/ui/index.d.ts +2 -0
  278. package/dist/ui/index.js +6 -0
  279. package/dist/ui/rich-text/RichText.style.css +48 -79
  280. package/package.json +1 -1
  281. package/scripts/seed-cli.mjs +1831 -1822
package/dist/client.js CHANGED
@@ -335,6 +335,10 @@ export { ProtectedRoute, ForgotPasswordView, LoginForm, RegisterForm, ResetPassw
335
335
  export { useLogout, useLogin, useGoogleLogin, useLinkGoogleAccount, useRegister, useForgotPassword, useResetPassword, useVerifyEmail, useChangeEmail } from "./features/auth/index.js";
336
336
  export { CartView, CartItemRow, CartGroupLineRow, CartSummary, CartPriceBreakdown, CartDrawer, CheckoutView, CheckoutSuccessView, CheckoutAddressStep, useGuestCart, useCartCount, useAddToCart, useCart, useGuestCartMerge, useCartQuery } from "./features/cart/index.js";
337
337
  export { StoreAddonsPicker, hasAnyStoreAddon } from "./features/cart/index.js";
338
+ // D46/D47 — the cart's single "pay with my points" control. One per CART, not
339
+ // per store, unlike the add-ons picker above it: points are the buyer's balance
340
+ // rather than something bought from a seller.
341
+ export { PointsTenderPicker } from "./features/cart/index.js";
338
342
  export { clientLineTotal } from "./features/cart/utils/line-total.js";
339
343
  // `clearCartOps` / `removeCartOpsFor` are exported because the cart page has to
340
344
  // drain the queue after a removal succeeds server-side — without them a cleared
@@ -416,6 +420,9 @@ export { couponDraftToPayload } from "./features/seller/schemas/coupon-form.js";
416
420
  export { UserAccountHubView, UserOrdersView, UserBidsView, OrderDetailView, UserReturnsView, useNotifications } from "./features/account/index.js";
417
421
  export { useOrders, useOrder, OrdersList } from "./features/orders/index.js";
418
422
  export { OrderStatusTimeline } from "./features/orders/components/OrderStatusTimeline.js";
423
+ export { OrderProvenanceCard } from "./features/orders/components/OrderProvenanceCard.js";
424
+ // The buyer's dispute form: pick a kind, pick a reason, say what happened.
425
+ export { RaiseDisputeForm } from "./features/orders/components/RaiseDisputeForm.js";
419
426
  /*
420
427
  * The buyer's completion surface. Client entry only — it fetches and posts.
421
428
  */
@@ -493,7 +500,7 @@ export { YourPurchaseStrip } from "./features/orders/components/YourPurchaseStri
493
500
  export { ReviewFormDrawer, reviewFormSchema } from "./features/reviews/components/ReviewFormDrawer.js";
494
501
  export { PurchaseReviewAffordance } from "./features/reviews/components/PurchaseReviewAffordance.js";
495
502
  // Media upload limits — shared by client uploaders + server sign/finalize routes.
496
- export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits.js";
503
+ export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, REVIEW_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits.js";
497
504
  export { ScamAwarenessModal } from "./features/scams/components/ScamAwarenessModal.js";
498
505
  export { SellerTrustBadge } from "./features/scams/components/SellerTrustBadge.js";
499
506
  // [CLIENT-ONLY] — Admin panel components for action/nav permission management.
@@ -634,6 +641,29 @@ export { REFUND_COPY } from "./_internal/shared/features/orders/refund-copy.js";
634
641
  // See the module docstring for why the exempt set is "seller or carrier fault"
635
642
  // rather than "serious reasons".
636
643
  export { RETURN_REASON, FINAL_SALE_EXEMPT_REASONS, RETURN_REASON_LABEL, RETURN_REASON_GROUP_LABEL, isFinalSaleExempt, isReturnReason, selectableReturnReasons, } from "./_internal/shared/features/orders/return-reasons.js";
644
+ // Dispute KINDS and their gates. Exported for the same reason the return
645
+ // reasons above are: the buyer's form renders the labels and needs to show the
646
+ // date a `not_reached` dispute becomes filable from, and
647
+ // `@mohasinac/appkit/_internal/*` is NOT a subpath export — `_internal` is
648
+ // reachable only through a barrel.
649
+ export { DISPUTE_KIND, DISPUTE_KIND_LABEL, DISPUTE_GATE, DISPUTE_TRANSIT_WINDOW_DAYS, DISPUTE_NOT_REACHED_GRACE_DAYS, isDisputeKind, checkDisputeGate, resolveDisputeEtd, disputeNotReachedFilableFrom, } from "./_internal/shared/features/orders/dispute-reasons.js";
650
+ /*
651
+ * The integration switches. Exported from the CLIENT entry because the admin
652
+ * integrations page is a "use client" surface — it renders each row's label and
653
+ * `offConsequence` and needs `readIntegrationStates` to show the current
654
+ * position.
655
+ *
656
+ * 🛑 The SAME module is re-exported from `server.ts`, deliberately, because the
657
+ * route guards resolve their answer through `readIntegrationStates` too. One
658
+ * expression, two entries — which is the point: a hand-written copy of "is this
659
+ * integration on" in a guard is how the page and the enforcement drift apart,
660
+ * and that had already happened once inside the module itself (the payments OR
661
+ * across both gateways).
662
+ *
663
+ * Safe in a client bundle: the file's only import is `import type`, so its
664
+ * runtime graph is empty and it cannot reach firebase-admin.
665
+ */
666
+ export { INTEGRATIONS, INTEGRATION_ORDER, readIntegrationStates, integrationPatch, allIntegrationsPatch, } from "./_internal/shared/features/admin/integrations.js";
637
667
  // Buyer-facing return/refund request form and the window it is gated on.
638
668
  // Exported from the CLIENT entry because the page that renders it is a
639
669
  // "use client" route — importing it from the main entry there would drag the
@@ -150,6 +150,14 @@ export declare const ADMIN_ENDPOINTS: {
150
150
  readonly PAYOUTS_EXPORT: "/api/admin/payouts/export";
151
151
  readonly AUDIT_LOG: "/api/admin/audit-log";
152
152
  readonly AUDIT_LOG_BY_ID: (id: string) => string;
153
+ /**
154
+ * Phase 7 — the GSTR-1 DRAFT export. Takes `?from=&to=` as YYYY-MM-DD and
155
+ * returns a CSV.
156
+ *
157
+ * 🛑 "draft" is in the filename for a reason: nothing in this codebase talks
158
+ * to the GST portal, and the file is the input to a human filing.
159
+ */
160
+ readonly INVOICING_GSTR1: "/api/admin/invoicing/gstr1";
153
161
  readonly PAYMENT_CASES: "/api/admin/payment-cases";
154
162
  readonly PAYMENT_CASE_BY_ID: (id: string) => string;
155
163
  readonly PROTECTION_CLAIMS: "/api/admin/protection-claims";
@@ -272,6 +280,12 @@ export declare const CART_ENDPOINTS: {
272
280
  readonly SELECTION: "/api/cart/selection";
273
281
  /** Per-store paid add-on selections — the source of truth for what's charged. */
274
282
  readonly ADDONS: "/api/cart/addons";
283
+ /**
284
+ * Pay with points, on or off (D46/D47). A boolean and nothing else — the
285
+ * AMOUNT is derived server-side from the live balance and the 50% cap, which
286
+ * is why there is no `/api/cart/points/amount`.
287
+ */
288
+ readonly POINTS: "/api/cart/points";
275
289
  readonly COUPON: "/api/cart/coupon";
276
290
  /**
277
291
  * Add a buyer-assembled group selection as ONE line. Separate from `GET`'s
@@ -535,6 +549,7 @@ export declare const SELLER_ENDPOINTS: {
535
549
  readonly BUNDLES: "/api/store/bundles";
536
550
  /** Tickets raised ABOUT this store (W11). */
537
551
  readonly SUPPORT: "/api/store/support";
552
+ readonly SUPPORT_TICKET_BY_ID: (id: string) => string;
538
553
  readonly BUNDLE_BY_ID: (id: string) => string;
539
554
  readonly ORDERS_ASSIGN: (orderId: string) => string;
540
555
  readonly PRODUCT_BY_ID: (id: string) => string;
@@ -732,6 +747,14 @@ export declare const API_ENDPOINTS: {
732
747
  readonly PAYOUTS_EXPORT: "/api/admin/payouts/export";
733
748
  readonly AUDIT_LOG: "/api/admin/audit-log";
734
749
  readonly AUDIT_LOG_BY_ID: (id: string) => string;
750
+ /**
751
+ * Phase 7 — the GSTR-1 DRAFT export. Takes `?from=&to=` as YYYY-MM-DD and
752
+ * returns a CSV.
753
+ *
754
+ * 🛑 "draft" is in the filename for a reason: nothing in this codebase talks
755
+ * to the GST portal, and the file is the input to a human filing.
756
+ */
757
+ readonly INVOICING_GSTR1: "/api/admin/invoicing/gstr1";
735
758
  readonly PAYMENT_CASES: "/api/admin/payment-cases";
736
759
  readonly PAYMENT_CASE_BY_ID: (id: string) => string;
737
760
  readonly PROTECTION_CLAIMS: "/api/admin/protection-claims";
@@ -854,6 +877,12 @@ export declare const API_ENDPOINTS: {
854
877
  readonly SELECTION: "/api/cart/selection";
855
878
  /** Per-store paid add-on selections — the source of truth for what's charged. */
856
879
  readonly ADDONS: "/api/cart/addons";
880
+ /**
881
+ * Pay with points, on or off (D46/D47). A boolean and nothing else — the
882
+ * AMOUNT is derived server-side from the live balance and the 50% cap, which
883
+ * is why there is no `/api/cart/points/amount`.
884
+ */
885
+ readonly POINTS: "/api/cart/points";
857
886
  readonly COUPON: "/api/cart/coupon";
858
887
  /**
859
888
  * Add a buyer-assembled group selection as ONE line. Separate from `GET`'s
@@ -1061,6 +1090,7 @@ export declare const API_ENDPOINTS: {
1061
1090
  readonly BUNDLES: "/api/store/bundles";
1062
1091
  /** Tickets raised ABOUT this store (W11). */
1063
1092
  readonly SUPPORT: "/api/store/support";
1093
+ readonly SUPPORT_TICKET_BY_ID: (id: string) => string;
1064
1094
  readonly BUNDLE_BY_ID: (id: string) => string;
1065
1095
  readonly ORDERS_ASSIGN: (orderId: string) => string;
1066
1096
  readonly PRODUCT_BY_ID: (id: string) => string;
@@ -1279,6 +1309,14 @@ export declare const API_ROUTES: {
1279
1309
  readonly PAYOUTS_EXPORT: "/api/admin/payouts/export";
1280
1310
  readonly AUDIT_LOG: "/api/admin/audit-log";
1281
1311
  readonly AUDIT_LOG_BY_ID: (id: string) => string;
1312
+ /**
1313
+ * Phase 7 — the GSTR-1 DRAFT export. Takes `?from=&to=` as YYYY-MM-DD and
1314
+ * returns a CSV.
1315
+ *
1316
+ * 🛑 "draft" is in the filename for a reason: nothing in this codebase talks
1317
+ * to the GST portal, and the file is the input to a human filing.
1318
+ */
1319
+ readonly INVOICING_GSTR1: "/api/admin/invoicing/gstr1";
1282
1320
  readonly PAYMENT_CASES: "/api/admin/payment-cases";
1283
1321
  readonly PAYMENT_CASE_BY_ID: (id: string) => string;
1284
1322
  readonly PROTECTION_CLAIMS: "/api/admin/protection-claims";
@@ -1401,6 +1439,12 @@ export declare const API_ROUTES: {
1401
1439
  readonly SELECTION: "/api/cart/selection";
1402
1440
  /** Per-store paid add-on selections — the source of truth for what's charged. */
1403
1441
  readonly ADDONS: "/api/cart/addons";
1442
+ /**
1443
+ * Pay with points, on or off (D46/D47). A boolean and nothing else — the
1444
+ * AMOUNT is derived server-side from the live balance and the 50% cap, which
1445
+ * is why there is no `/api/cart/points/amount`.
1446
+ */
1447
+ readonly POINTS: "/api/cart/points";
1404
1448
  readonly COUPON: "/api/cart/coupon";
1405
1449
  /**
1406
1450
  * Add a buyer-assembled group selection as ONE line. Separate from `GET`'s
@@ -1608,6 +1652,7 @@ export declare const API_ROUTES: {
1608
1652
  readonly BUNDLES: "/api/store/bundles";
1609
1653
  /** Tickets raised ABOUT this store (W11). */
1610
1654
  readonly SUPPORT: "/api/store/support";
1655
+ readonly SUPPORT_TICKET_BY_ID: (id: string) => string;
1611
1656
  readonly BUNDLE_BY_ID: (id: string) => string;
1612
1657
  readonly ORDERS_ASSIGN: (orderId: string) => string;
1613
1658
  readonly PRODUCT_BY_ID: (id: string) => string;
@@ -187,6 +187,14 @@ export const ADMIN_ENDPOINTS = {
187
187
  PAYOUTS_EXPORT: "/api/admin/payouts/export",
188
188
  AUDIT_LOG: "/api/admin/audit-log",
189
189
  AUDIT_LOG_BY_ID: (id) => `/api/admin/audit-log/${id}`,
190
+ /**
191
+ * Phase 7 — the GSTR-1 DRAFT export. Takes `?from=&to=` as YYYY-MM-DD and
192
+ * returns a CSV.
193
+ *
194
+ * 🛑 "draft" is in the filename for a reason: nothing in this codebase talks
195
+ * to the GST portal, and the file is the input to a human filing.
196
+ */
197
+ INVOICING_GSTR1: "/api/admin/invoicing/gstr1",
190
198
  /*
191
199
  * Payment protection. `payment-cases` is NOT under `maintenance/` — see the
192
200
  * route-map entry for why: maintenance means "something broke, look when you
@@ -325,6 +333,12 @@ export const CART_ENDPOINTS = {
325
333
  SELECTION: "/api/cart/selection",
326
334
  /** Per-store paid add-on selections — the source of truth for what's charged. */
327
335
  ADDONS: "/api/cart/addons",
336
+ /**
337
+ * Pay with points, on or off (D46/D47). A boolean and nothing else — the
338
+ * AMOUNT is derived server-side from the live balance and the 50% cap, which
339
+ * is why there is no `/api/cart/points/amount`.
340
+ */
341
+ POINTS: "/api/cart/points",
328
342
  COUPON: "/api/cart/coupon",
329
343
  /**
330
344
  * Add a buyer-assembled group selection as ONE line. Separate from `GET`'s
@@ -690,6 +704,7 @@ export const SELLER_ENDPOINTS = {
690
704
  BUNDLES: "/api/store/bundles",
691
705
  /** Tickets raised ABOUT this store (W11). */
692
706
  SUPPORT: "/api/store/support",
707
+ SUPPORT_TICKET_BY_ID: (id) => `/api/store/support/${id}`,
693
708
  BUNDLE_BY_ID: (id) => `/api/store/bundles/${id}`,
694
709
  ORDERS_ASSIGN: (orderId) => `/api/store/orders/${orderId}/assign`,
695
710
  PRODUCT_BY_ID: (id) => `/api/store/products/${id}`,
@@ -835,6 +835,7 @@ export declare const COUPON_FIELDS: {
835
835
  readonly FIXED: "fixed";
836
836
  readonly FREE_SHIPPING: "free_shipping";
837
837
  readonly BUY_X_GET_Y: "buy_x_get_y";
838
+ readonly POINTS_EARN: "points_earn";
838
839
  };
839
840
  readonly SCOPE_VALUES: {
840
841
  readonly ADMIN: "admin";
@@ -982,6 +983,23 @@ export declare const COMMON_FIELDS: {
982
983
  readonly IS_ACTIVE: "isActive";
983
984
  readonly ORDER: "order";
984
985
  };
986
+ /**
987
+ * The idempotency rows behind a refund.
988
+ *
989
+ * 🛑 `STATE` is the claim's own lifecycle ("claimed" → settled), NOT a
990
+ * postal state. `audit-route-nav-field-constants` matches a raw field
991
+ * string against every constant that happens to share its VALUE, so it
992
+ * suggested `ADDRESS_FIELDS.STATE` here — right string, wrong concept, and
993
+ * taking the suggestion would have made the query read as address filtering.
994
+ */
995
+ export declare const REFUND_CLAIM_FIELDS: {
996
+ readonly STATE: "state";
997
+ readonly CLAIMED_AT: "claimedAt";
998
+ };
999
+ /** Observed market prices, keyed by the taxonomy node they were seen under. */
1000
+ export declare const PRICE_OBSERVATION_FIELDS: {
1001
+ readonly CATEGORY_ID: "categoryId";
1002
+ };
985
1003
  export declare const OAUTH_STATE_VALUES: {
986
1004
  readonly PENDING: "pending";
987
1005
  readonly USED: "used";
@@ -1003,6 +1003,11 @@ export const COUPON_FIELDS = {
1003
1003
  FIXED: "fixed",
1004
1004
  FREE_SHIPPING: "free_shipping",
1005
1005
  BUY_X_GET_Y: "buy_x_get_y",
1006
+ // D51. 🛑 This file calls itself canonical and has been stale twice
1007
+ // (EVENT_FIELDS invented a "published" status, SCAMMER_FIELDS lost
1008
+ // "removed") — Root Cause #34. The authority is `CouponType` in
1009
+ // features/promotions/types; this mirror has to follow it.
1010
+ POINTS_EARN: "points_earn",
1006
1011
  },
1007
1012
  SCOPE_VALUES: {
1008
1013
  ADMIN: "admin",
@@ -1175,6 +1180,29 @@ export const COMMON_FIELDS = {
1175
1180
  ORDER: "order",
1176
1181
  };
1177
1182
  // ============================================================================
1183
+ // REFUND CLAIMS
1184
+ // ============================================================================
1185
+ /**
1186
+ * The idempotency rows behind a refund.
1187
+ *
1188
+ * 🛑 `STATE` is the claim's own lifecycle ("claimed" → settled), NOT a
1189
+ * postal state. `audit-route-nav-field-constants` matches a raw field
1190
+ * string against every constant that happens to share its VALUE, so it
1191
+ * suggested `ADDRESS_FIELDS.STATE` here — right string, wrong concept, and
1192
+ * taking the suggestion would have made the query read as address filtering.
1193
+ */
1194
+ export const REFUND_CLAIM_FIELDS = {
1195
+ STATE: "state",
1196
+ CLAIMED_AT: "claimedAt",
1197
+ };
1198
+ // ============================================================================
1199
+ // PRICE OBSERVATIONS
1200
+ // ============================================================================
1201
+ /** Observed market prices, keyed by the taxonomy node they were seen under. */
1202
+ export const PRICE_OBSERVATION_FIELDS = {
1203
+ CATEGORY_ID: "categoryId",
1204
+ };
1205
+ // ============================================================================
1178
1206
  // OAUTH STATE VALUES
1179
1207
  // ============================================================================
1180
1208
  export const OAUTH_STATE_VALUES = {
@@ -4,7 +4,7 @@ export type { EmailAttachment, EmailOptions, EmailResult, IEmailProvider, } from
4
4
  export type { UploadOptions, StorageFile, IStorageProvider } from "./storage";
5
5
  export type { PaymentOrder, PaymentCapture, Refund, Dispute, GatewayPaymentRecord, } from "./payment";
6
6
  export { IPaymentProvider } from "./payment";
7
- export type { ShippingAddress, CreateShipmentInput, Shipment, TrackingEvent, TrackingInfo, ServiceabilityResult, } from "./shipping";
7
+ export type { ShippingAddress, CreateShipmentInput, Shipment, TrackingEvent, TrackingInfo, ServiceabilityResult, WalletBalance, } from "./shipping";
8
8
  export { IShippingProvider } from "./shipping";
9
9
  export type { SearchOptions, SearchHit, SearchResult, SuggestOptions, ISearchProvider, } from "./search";
10
10
  export type { ICacheProvider, QueueJob, IQueueProvider, EventHandler, IEventBus, } from "./infra";
@@ -50,6 +50,38 @@ export interface ServiceabilityResult {
50
50
  rate: number;
51
51
  }>;
52
52
  }
53
+ /**
54
+ * A courier provider's prepaid balance.
55
+ *
56
+ * 🛑 THREE states, not two, and collapsing them is the whole hazard:
57
+ *
58
+ * - `{ known: false, supported: false }` — this provider has no wallet at all
59
+ * (manual shipping). Nothing to check, ever.
60
+ * - `{ known: false, supported: true }` — it HAS one and we could not read it
61
+ * right now (network, malformed payload, auth). **Treat as "attempt the
62
+ * booking"**: the attempt's own failure is the authority, and refusing on an
63
+ * unreadable balance would degrade every order on a transient blip.
64
+ * - `{ known: true, amount }` — a real figure.
65
+ *
66
+ * A balance of `0` is the LAST of those, and is a decision to skip. "I could
67
+ * not read it" is not "there is no money".
68
+ *
69
+ * 🛑 There is deliberately NO `creditLimit` field. Measured against the live
70
+ * Shiprocket API 2026-10-22: `GET /account/details/wallet-balance` returns
71
+ * `{ data: { balance_amount: string } }` and nothing else, and
72
+ * `/account/details` is a 404. So postpaid credit is not readable, and a field
73
+ * with no producer is the speculative-infra pattern this codebase refuses.
74
+ * When postpaid is granted, re-probe and add it then.
75
+ */
76
+ export type WalletBalance = {
77
+ known: true;
78
+ amount: number;
79
+ currency?: string;
80
+ } | {
81
+ known: false;
82
+ supported: boolean;
83
+ reason?: string;
84
+ };
53
85
  /**
54
86
  * Shipping carrier adapter contract. An abstract base class rather than a
55
87
  * plain interface so every provider (manual, and any future carrier
@@ -66,4 +98,17 @@ export declare abstract class IShippingProvider {
66
98
  abstract cancelShipment(shipmentId: string): Promise<void>;
67
99
  abstract checkServiceability(pincode: string, weight: number): Promise<ServiceabilityResult>;
68
100
  abstract generateLabel(shipmentId: string): Promise<ArrayBuffer>;
101
+ /**
102
+ * The provider's prepaid balance, when it HAS one.
103
+ *
104
+ * 🛑 Deliberately NOT abstract. Manual shipping has no wallet — a seller
105
+ * posts the parcel and claims the cost back — so an abstract member would
106
+ * force every implementation to invent an answer. A non-abstract default is
107
+ * the established shape here for a capability a provider lacks (the dispute
108
+ * contract's `listDisputes() => []` is the precedent).
109
+ *
110
+ * The default says "I do not have one", which is TRUE for manual and is not
111
+ * the same statement as "I have one and it is empty".
112
+ */
113
+ getWalletBalance(): Promise<WalletBalance>;
69
114
  }
@@ -9,4 +9,19 @@
9
9
  * Implemented by the manual provider (default) and its mock.
10
10
  */
11
11
  export class IShippingProvider {
12
+ /**
13
+ * The provider's prepaid balance, when it HAS one.
14
+ *
15
+ * 🛑 Deliberately NOT abstract. Manual shipping has no wallet — a seller
16
+ * posts the parcel and claims the cost back — so an abstract member would
17
+ * force every implementation to invent an answer. A non-abstract default is
18
+ * the established shape here for a capability a provider lacks (the dispute
19
+ * contract's `listDisputes() => []` is the precedent).
20
+ *
21
+ * The default says "I do not have one", which is TRUE for manual and is not
22
+ * the same statement as "I have one and it is empty".
23
+ */
24
+ async getWalletBalance() {
25
+ return { known: false, supported: false };
26
+ }
12
27
  }
@@ -1,6 +1,6 @@
1
1
  import { normalizeError } from "../errors/normalize.js";
2
2
  import { toApiIssues } from "../errors/error-envelope.js";
3
- import { HTTP_ERROR_CODES } from "../errors/error-mapping.js";
3
+ import { HTTP_ERROR_CODES, mapToHttpError } from "../errors/error-mapping.js";
4
4
  /**
5
5
  * createServerAction — @mohasinac/appkit/core
6
6
  *
@@ -97,11 +97,27 @@ export function createServerAction(options) {
97
97
  }
98
98
  catch (err) {
99
99
  void normalizeError(err);
100
- const message = err instanceof Error ? err.message : "An unexpected error occurred";
100
+ /*
101
+ * 🛑 This never got the Rule #9 fix its sibling `wrapAction` did.
102
+ *
103
+ * It took `err.message` verbatim — unscrubbed for 5xx, so a thrown
104
+ * Node/bundler error's internals reached the client — and emitted NO
105
+ * `code`, so `toUserMessage(result.code)` downstream could only ever
106
+ * resolve to the generic sentence.
107
+ *
108
+ * It has no in-repo callers, but it is exported from the public barrel,
109
+ * so it is a trap for the next consumer rather than a live bug. Route it
110
+ * through the same mapper `wrapAction` uses: `mapToHttpError` scrubs 5xx
111
+ * messages in production and carries the real one in `internalMessage`
112
+ * for recorders only.
113
+ */
114
+ const mapped = mapToHttpError(err, {
115
+ isProduction: process.env.NODE_ENV === "production",
116
+ });
101
117
  logger.error(`[createServerAction] handler error in "${actionName}"`, {
102
118
  err,
103
119
  });
104
- result = { ok: false, error: message };
120
+ result = { ok: false, code: mapped.code, error: mapped.message };
105
121
  }
106
122
  // -- 4. After hooks (per-action → global) ------------------------------
107
123
  try {
@@ -42,6 +42,31 @@ function isZodLikeError(err) {
42
42
  "issues" in err &&
43
43
  Array.isArray(err.issues));
44
44
  }
45
+ /**
46
+ * The issues carried by a `ValidationError`, whichever of the two shapes the
47
+ * thrower used.
48
+ *
49
+ * 🛑 `new ValidationError(msg, parsed.error.issues)` silently lost everything.
50
+ * `ValidationError` forwards its second argument to `AppError.data`, and the
51
+ * old test was `isZodLikeError(err.data)` — which requires `"issues" in data`.
52
+ * An ARRAY has no `.issues` property (`"issues" in [] === false`), so passing
53
+ * `parsed.error.issues` (the obvious thing to pass, and what four routes do)
54
+ * produced a 400 with no field information at all. Only passing the whole
55
+ * `ZodError` worked.
56
+ *
57
+ * `media/finalize/route.ts` even carried a comment asserting the opposite —
58
+ * "carries `issues` through to the client, so the caller learns WHICH field it
59
+ * got wrong". It did not.
60
+ *
61
+ * Accept both, so the next caller cannot get it wrong either way.
62
+ */
63
+ function issuesFromErrorData(data) {
64
+ if (Array.isArray(data))
65
+ return data.length > 0 ? data : undefined;
66
+ if (isZodLikeError(data))
67
+ return data.issues;
68
+ return undefined;
69
+ }
45
70
  function isFirestoreLikeError(err) {
46
71
  if (typeof err !== "object" || err === null)
47
72
  return false;
@@ -110,7 +135,7 @@ function classifyError(err, opts) {
110
135
  status: 400,
111
136
  code: HTTP_ERROR_CODES.VALIDATION_FAILED,
112
137
  message: err.message,
113
- issues: isZodLikeError(err.data) ? err.data.issues : undefined,
138
+ issues: issuesFromErrorData(err.data),
114
139
  };
115
140
  }
116
141
  if (err instanceof AuthenticationError) {
@@ -2,5 +2,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Anchor, Avatar, Badge, Card, Div, Heading, Row, Section, Span, Stack, Text, TextLink } from "../../../ui/index.js";
3
3
  import { ROUTES } from "../../../constants/index.js";
4
4
  export function AboutView({ labels = {}, howItems = [], valueItems = [], milestones = [], teamMembers = [], ctaBannerClass = "", renderCtaButtons, }) {
5
- return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "y-4xl", children: _jsxs(Div, { paddingX: "x-page", className: "max-w-4xl mx-auto text-center", children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-6", mdSize: "5xl", size: "4xl", weight: "bold", children: labels.title }), _jsx(Text, { color: "inverse-muted", variant: "none", className: "max-w-2xl mx-auto", size: "xl", children: labels.subtitle })] }) }), _jsxs(Div, { paddingX: "x-page", className: "max-w-5xl mx-auto", padding: "y-4xl", children: [_jsxs(Section, { className: "text-center max-w-3xl mx-auto", children: [_jsx(Heading, { level: 2, className: "mb-4", children: labels.missionTitle }), _jsx(Text, { size: "lg", variant: "secondary", className: "leading-relaxed", children: labels.missionText })] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-12", align: "center", children: labels.howItWorksTitle }), _jsx(Div, { className: "grid md:grid-cols-3", gap: "6", children: howItems.map(({ title, text, icon, tone }) => (_jsxs(Card, { variant: `gradient-${tone ?? "indigo"}`, padding: "md", spacing: "sm", children: [_jsx(Div, { children: _jsx(Span, { size: "4xl", children: icon }) }), _jsx(Heading, { level: 3, children: title }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: text })] }, title))) })] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: labels.valuesSubtitle ? "mb-3" : "mb-12", align: "center", children: labels.valuesTitle }), labels.valuesSubtitle && (_jsx(Text, { size: "base", variant: "secondary", align: "center", className: "mb-12 max-w-2xl mx-auto leading-relaxed", children: labels.valuesSubtitle })), _jsx(Div, { className: "grid md:grid-cols-3", gap: "5", children: valueItems.map(({ title, text, icon, detail }) => (_jsxs(Stack, { gap: "3", surface: "default", padding: "lg", rounded: "xl", border: "default", children: [_jsx(Div, { children: _jsx(Span, { size: "3xl", children: icon }) }), _jsx(Heading, { level: 3, children: title }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: text }), detail && (_jsx(Text, { size: "sm", color: "muted", className: "leading-relaxed", children: detail }))] }, title))) }), labels.valuesLinkLabel && (_jsx(Div, { className: "mt-8 text-center", children: _jsx(TextLink, { href: String(ROUTES.PUBLIC.ETHICS), size: "sm", children: labels.valuesLinkLabel }) }))] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-10", align: "center", children: labels.milestonesTitle }), _jsx(Stack, { className: "relative border-l-2 border-primary/30 max-w-2xl mx-auto pl-[2rem]", gap: "xl", children: milestones.map(({ year, text }) => (_jsxs(Div, { className: "relative", children: [_jsx(Div, { className: "absolute -left-10 top-1 w-4 h-4 bg-primary border-2 border-white", rounded: "full" }), _jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: year }), _jsx(Text, { className: "mt-1", children: text })] }, year))) })] }), teamMembers.length > 0 && (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-2", align: "center", children: labels.teamTitle }), labels.teamSubtitle && (_jsx(Text, { size: "lg", variant: "secondary", align: "center", className: "mb-12 max-w-2xl mx-auto", children: labels.teamSubtitle })), _jsx(Div, { className: "grid md:grid-cols-3", gap: "6", children: teamMembers.map((member) => (_jsxs(Stack, { gap: "3", surface: "default", padding: "lg", rounded: "xl", border: "default", align: "center", className: "text-center", children: [_jsx(Avatar, { src: member.photoUrl, name: member.name, alt: member.name, size: "lg" }), _jsx(Heading, { level: 3, children: member.name }), _jsxs(Row, { gap: "xs", wrap: true, justify: "center", children: [_jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: member.role }), member.isFounder && _jsx(Badge, { variant: "primary", size: "xs", children: "Founder" }), member.isDeveloper && _jsx(Badge, { variant: "secondary", size: "xs", children: "Developer" })] }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: member.bio }), member.githubUrl && (_jsx(Anchor, { href: member.githubUrl, tone: "muted", size: "sm", underline: "hover", children: "GitHub \u2197" }))] }, member.name))) })] })), _jsxs(Section, { color: "inverse", tone: "accent-banner", className: `text-center ${ctaBannerClass}`, padding: "xl", rounded: "2xl", children: [_jsx(Heading, { color: "inverse", level: 2, variant: "none", className: "mb-8", size: "3xl", weight: "bold", children: labels.ctaTitle }), renderCtaButtons ? (renderCtaButtons()) : (_jsx(Row, { justify: "center", gap: "3", wrap: true, children: _jsxs(Span, { color: "inverse-muted", size: "sm", children: [labels.ctaSell, " \u00B7 ", labels.ctaShop] }) }))] })] })] }));
5
+ return (_jsxs(Div, { className: "-mx-4 md:-mx-6 lg:-mx-8 -mt-6 sm:-mt-8 lg:-mt-10", children: [_jsx(Section, { color: "inverse", tone: "accent-banner", padding: "y-4xl", children: _jsxs(Div, { paddingX: "x-page", className: "max-w-4xl mx-auto text-center", children: [_jsx(Heading, { color: "inverse", level: 1, variant: "none", className: "mb-6", mdSize: "5xl", size: "4xl", weight: "bold", children: labels.title }), _jsx(Text, { color: "inverse-muted", variant: "none", className: "max-w-2xl mx-auto", size: "xl", children: labels.subtitle })] }) }), _jsxs(Stack, { gap: "section", paddingX: "x-page", className: "max-w-5xl mx-auto", padding: "y-4xl", children: [_jsxs(Section, { className: "text-center max-w-3xl mx-auto", children: [_jsx(Heading, { level: 2, className: "mb-4", children: labels.missionTitle }), _jsx(Text, { size: "lg", variant: "secondary", className: "leading-relaxed", children: labels.missionText })] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-12", align: "center", children: labels.howItWorksTitle }), _jsx(Div, { className: "grid md:grid-cols-3", gap: "6", children: howItems.map(({ title, text, icon, tone }) => (_jsxs(Card, { variant: `gradient-${tone ?? "indigo"}`, padding: "md", spacing: "sm", children: [_jsx(Div, { children: _jsx(Span, { size: "4xl", children: icon }) }), _jsx(Heading, { level: 3, color: "inverse", variant: "none", children: title }), _jsx(Text, { size: "sm", color: "inverse-muted", variant: "none", className: "leading-relaxed", children: text })] }, title))) })] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: labels.valuesSubtitle ? "mb-3" : "mb-12", align: "center", children: labels.valuesTitle }), labels.valuesSubtitle && (_jsx(Text, { size: "base", variant: "secondary", align: "center", className: "mb-12 max-w-2xl mx-auto leading-relaxed", children: labels.valuesSubtitle })), _jsx(Div, { className: "grid md:grid-cols-3", gap: "5", children: valueItems.map(({ title, text, icon, detail }) => (_jsxs(Stack, { gap: "3", surface: "default", padding: "lg", rounded: "xl", border: "default", children: [_jsx(Div, { children: _jsx(Span, { size: "3xl", children: icon }) }), _jsx(Heading, { level: 3, children: title }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: text }), detail && (_jsx(Text, { size: "sm", color: "muted", className: "leading-relaxed", children: detail }))] }, title))) }), labels.valuesLinkLabel && (_jsx(Div, { className: "mt-8 text-center", children: _jsx(TextLink, { href: String(ROUTES.PUBLIC.ETHICS), size: "sm", children: labels.valuesLinkLabel }) }))] }), _jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-10", align: "center", children: labels.milestonesTitle }), _jsx(Stack, { className: "relative border-l-2 border-primary/30 max-w-2xl mx-auto pl-[2rem]", gap: "xl", children: milestones.map(({ year, text }) => (_jsxs(Div, { className: "relative", children: [_jsx(Div, { className: "absolute -left-10 top-1 w-4 h-4 bg-primary border-2 border-white", rounded: "full" }), _jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: year }), _jsx(Text, { className: "mt-1", children: text })] }, year))) })] }), teamMembers.length > 0 && (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "mb-2", align: "center", children: labels.teamTitle }), labels.teamSubtitle && (_jsx(Text, { size: "lg", variant: "secondary", align: "center", className: "mb-12 max-w-2xl mx-auto", children: labels.teamSubtitle })), _jsx(Div, { className: "grid md:grid-cols-3", gap: "6", children: teamMembers.map((member) => (_jsxs(Stack, { gap: "3", surface: "default", padding: "lg", rounded: "xl", border: "default", align: "center", className: "text-center", children: [_jsx(Avatar, { src: member.photoUrl, name: member.name, alt: member.name, size: "lg" }), _jsx(Heading, { level: 3, children: member.name }), _jsxs(Row, { gap: "xs", wrap: true, justify: "center", children: [_jsx(Span, { size: "xs", weight: "bold", className: "text-primary tracking-wide", transform: "uppercase", children: member.role }), member.isFounder && _jsx(Badge, { variant: "primary", size: "xs", children: "Founder" }), member.isDeveloper && _jsx(Badge, { variant: "secondary", size: "xs", children: "Developer" })] }), _jsx(Text, { size: "sm", variant: "secondary", className: "leading-relaxed", children: member.bio }), member.githubUrl && (_jsx(Anchor, { href: member.githubUrl, tone: "muted", size: "sm", underline: "hover", children: "GitHub \u2197" }))] }, member.name))) })] })), _jsxs(Section, { color: "inverse", tone: "accent-banner", className: `text-center ${ctaBannerClass}`, padding: "xl", rounded: "2xl", children: [_jsx(Heading, { color: "inverse", level: 2, variant: "none", className: "mb-8", size: "3xl", weight: "bold", children: labels.ctaTitle }), renderCtaButtons ? (renderCtaButtons()) : (_jsx(Row, { justify: "center", gap: "3", wrap: true, children: _jsxs(Span, { color: "inverse-muted", size: "sm", children: [labels.ctaSell, " \u00B7 ", labels.ctaShop] }) }))] })] })] }));
6
6
  }
@@ -2,6 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { Gavel, TrendingUp, Trophy, AlertTriangle, Package, XCircle, ShieldCheck } from "lucide-react";
3
3
  import { Alert, 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
+ /* Phase 9 — one definition of the refund timeline. See the 🛑 at the call site. */
7
+ const { refundBusinessDaysMin: REFUND_DAYS_MIN, refundBusinessDaysMax: REFUND_DAYS_MAX } = resolvePublishedValues();
5
8
  const SECTIONS = [
6
9
  {
7
10
  Icon: Gavel, title: "How Auctions Work",
@@ -25,7 +28,7 @@ const SECTIONS = [
25
28
  },
26
29
  {
27
30
  Icon: XCircle, iconCls: "w-5 h-5 text-rose-500", title: "Pre-order Cancellations",
28
- content: (_jsxs(_Fragment, { children: [_jsxs(Text, { className: `${GC.textMuted} mb-3`, children: ["To cancel a pre-order before the item is shipped, open a support ticket with category ", _jsx(Span, { weight: "bold", children: "Billing & Payment" }), " or ", _jsx(Span, { weight: "bold", children: "Order Issue" }), " and reference your order ID."] }), _jsx(Text, { className: GC.textMuted, children: "After cancellation is accepted, refunds follow the standard timeline: 3\u20137 business days to your original payment method." })] })),
31
+ content: (_jsxs(_Fragment, { children: [_jsxs(Text, { className: `${GC.textMuted} mb-3`, children: ["To cancel a pre-order before the item is shipped, open a support ticket with category ", _jsx(Span, { weight: "bold", children: "Billing & Payment" }), " or ", _jsx(Span, { weight: "bold", children: "Order Issue" }), " and reference your order ID."] }), _jsxs(Text, { className: GC.textMuted, children: ["After cancellation is accepted, refunds follow the standard timeline:", " ", REFUND_DAYS_MIN, "\u2013", REFUND_DAYS_MAX, " business days to your original payment method."] })] })),
29
32
  },
30
33
  {
31
34
  Icon: ShieldCheck, iconCls: "w-5 h-5 text-emerald-500", title: "Bidding Safety Tips",
@@ -1,11 +1,13 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useCallback, useEffect, useState, useTransition } from "react";
4
- import { Alert, Badge, Button, Div, Heading, LoginRequiredModal, Row, Spinner, Stack, Text } from "../../../ui/index.js";
4
+ import { Alert, Badge, Button, Div, Heading, LoginRequiredModal, Row, Spinner, Stack, Text, TextLink } from "../../../ui/index.js";
5
+ import { ROUTES } from "../../../next/routing/route-map.js";
5
6
  import { isAuthError } from "../../../utils/auth-error.js";
6
7
  import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
7
8
  import { ACCOUNT_ENDPOINTS } from "../../../constants/api-endpoints.js";
8
9
  import { normalizeError } from "../../../errors/normalize.js";
10
+ import { toUserMessage } from "../../../errors/error-display-map.js";
9
11
  const CLS_COUNTER_AMOUNT = "text-[length:var(--appkit-text-sm)] font-semibold text-info dark:text-info";
10
12
  function formatRupees(amount) {
11
13
  if (amount === undefined || amount === null)
@@ -60,7 +62,7 @@ function BuyerOfferCard({ offer, onAcceptCounter, onWithdraw, onCheckout, onUpda
60
62
  }
61
63
  });
62
64
  }
63
- return (_jsxs(Stack, { surface: "card", padding: "sm", gap: "3", children: [_jsxs(Row, { align: "start", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { className: "truncate", color: "primary", size: "sm", weight: "semibold", children: offer.productTitle ?? "Unknown Product" }), _jsx(Text, { className: "mt-0.5", color: "muted", size: "xs", children: relativeTime(offer.createdAt) })] }), _jsx(Badge, { variant: statusVariant(offer.status), size: "xs", className: "shrink-0 capitalize", children: offer.status })] }), _jsxs(Row, { wrap: true, gap: "md", children: [_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Listed" }), _jsx(Text, { size: "sm", weight: "medium", color: "muted", children: formatRupees(offer.listedPrice) })] }), _jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Your Offer" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: formatRupees(offer.offerAmount) })] }), offer.counterAmount != null && (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Seller Counter" }), _jsx(Text, { className: CLS_COUNTER_AMOUNT, children: formatRupees(offer.counterAmount) })] })), offer.lockedPrice != null && (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Agreed Price" }), _jsx(Text, { className: "text-success", size: "sm", weight: "semibold", children: formatRupees(offer.lockedPrice) })] }))] }), offer.sellerNote && (_jsxs(Text, { className: "italic", color: "muted", size: "xs", children: ["Seller note: \"", offer.sellerNote, "\""] })), offer.status === "countered" && (_jsxs(Stack, { gap: "sm", children: [_jsxs(Text, { className: "text-info", size: "xs", weight: "medium", children: ["Seller countered at ", formatRupees(offer.counterAmount), ". Accept or withdraw your offer."] }), _jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Button, { size: "sm", variant: "primary", onClick: () => act(() => onAcceptCounter(offer.id), { status: "accepted", lockedPrice: offer.counterAmount }), disabled: isPending, children: isPending ? "Accepting…" : `Accept ${formatRupees(offer.counterAmount)}` }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setConfirming("withdraw"), disabled: isPending, className: "text-error border border-error/20", children: "Withdraw" })] })] })), offer.status === "accepted" && onCheckout && (_jsx(Button, { size: "sm", variant: "primary", onClick: () => act(() => onCheckout(offer.id), { status: "paid" }), disabled: isPending, children: isPending ? "Processing…" : "Checkout at Agreed Price" })), offer.status === "pending" && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setConfirming("withdraw"), disabled: isPending, className: "text-error border border-error/20 text-[length:var(--appkit-text-xs)]", children: "Withdraw Offer" })), confirming === "withdraw" && (_jsxs(Row, { gap: "sm", children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: () => act(() => onWithdraw(offer.id), { status: "withdrawn" }), disabled: isPending, className: "text-error border border-error/20", children: isPending ? "Withdrawing…" : "Confirm Withdraw" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setConfirming(null), disabled: isPending, children: "Cancel" })] })), error && _jsx(Alert, { variant: "error", children: _jsx(Text, { size: "xs", children: error }) })] }));
65
+ return (_jsxs(Stack, { surface: "card", padding: "sm", gap: "3", children: [_jsxs(Row, { align: "start", justify: "between", gap: "sm", wrap: true, children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(TextLink, { href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(offer.productSlug ?? offer.productId)), size: "sm", weight: "semibold", truncate: true, children: offer.productTitle ?? "Unknown Product" }), _jsx(Text, { className: "mt-0.5", color: "muted", size: "xs", children: relativeTime(offer.createdAt) })] }), _jsx(Badge, { variant: statusVariant(offer.status), size: "xs", className: "shrink-0 capitalize", children: offer.status })] }), _jsxs(Row, { wrap: true, gap: "md", children: [_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Listed" }), _jsx(Text, { size: "sm", weight: "medium", color: "muted", children: formatRupees(offer.listedPrice) })] }), _jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Your Offer" }), _jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: formatRupees(offer.offerAmount) })] }), offer.counterAmount != null && (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Seller Counter" }), _jsx(Text, { className: CLS_COUNTER_AMOUNT, children: formatRupees(offer.counterAmount) })] })), offer.lockedPrice != null && (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", color: "faint", size: "xs", transform: "uppercase", children: "Agreed Price" }), _jsx(Text, { className: "text-success", size: "sm", weight: "semibold", children: formatRupees(offer.lockedPrice) })] }))] }), offer.sellerNote && (_jsxs(Text, { className: "italic", color: "muted", size: "xs", children: ["Seller note: \"", offer.sellerNote, "\""] })), offer.status === "countered" && (_jsxs(Stack, { gap: "sm", children: [_jsxs(Text, { className: "text-info", size: "xs", weight: "medium", children: ["Seller countered at ", formatRupees(offer.counterAmount), ". Accept or withdraw your offer."] }), _jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Button, { size: "sm", variant: "primary", onClick: () => act(() => onAcceptCounter(offer.id), { status: "accepted", lockedPrice: offer.counterAmount }), disabled: isPending, children: isPending ? "Accepting…" : `Accept ${formatRupees(offer.counterAmount)}` }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setConfirming("withdraw"), disabled: isPending, className: "text-error border border-error/20", children: "Withdraw" })] })] })), offer.status === "accepted" && onCheckout && (_jsx(Button, { size: "sm", variant: "primary", onClick: () => act(() => onCheckout(offer.id), { status: "paid" }), disabled: isPending, children: isPending ? "Processing…" : "Checkout at Agreed Price" })), offer.status === "pending" && (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setConfirming("withdraw"), disabled: isPending, className: "text-error border border-error/20 text-[length:var(--appkit-text-xs)]", children: "Withdraw Offer" })), confirming === "withdraw" && (_jsxs(Row, { gap: "sm", children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: () => act(() => onWithdraw(offer.id), { status: "withdrawn" }), disabled: isPending, className: "text-error border border-error/20", children: isPending ? "Withdrawing…" : "Confirm Withdraw" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setConfirming(null), disabled: isPending, children: "Cancel" })] })), error && _jsx(Alert, { variant: "error", children: _jsx(Text, { size: "xs", children: error }) })] }));
64
66
  }
65
67
  export function UserOffersPanel({ fetchEndpoint = ACCOUNT_ENDPOINTS.OFFERS, onAcceptCounter, onWithdraw, onCheckout, className = "", }) {
66
68
  const [offers, setOffers] = useState([]);
@@ -92,7 +94,7 @@ export function UserOffersPanel({ fetchEndpoint = ACCOUNT_ENDPOINTS.OFFERS, onAc
92
94
  }
93
95
  catch (err) {
94
96
  void normalizeError(err);
95
- setFetchError(err instanceof Error ? err.message : "Failed to load offers.");
97
+ setFetchError(toUserMessage(err?.code, undefined, { fallback: "Failed to load offers." }));
96
98
  }
97
99
  finally {
98
100
  setLoading(false);
@@ -9,6 +9,7 @@ import { PaginatedSelect } from "../../../ui/components/PaginatedSelect.js";
9
9
  import { DataListingView } from "../../admin/components/DataListingView.js";
10
10
  import { OrderCard } from "../../orders/components/OrdersList.js";
11
11
  import { useOrderScope } from "../../orders/components/OrderScopeTabs.js";
12
+ import { ORDER_LANE_TABS } from "../../orders/constants/order-lane-tabs.js";
12
13
  import { useSearchParams } from "next/navigation.js";
13
14
  const CANCELLABLE_STATUSES = new Set(["pending", "confirmed", "processing"]);
14
15
  /*
@@ -34,40 +35,20 @@ const SORT_OPTIONS = [
34
35
  { value: sortBy("totalPrice", "DESC"), label: "Highest total" },
35
36
  { value: sortBy("totalPrice", "ASC"), label: "Lowest total" },
36
37
  ];
37
- /**
38
- * Lane tabs — the same auction > offer > standard split the cart and checkout
39
- * use, viewed after the fact. Every `id` is EXACTLY a stored
40
- * `OrderDocument.orderType` value (or "" for All): a tab id that doesn't
41
- * match a real stored value returns zero rows forever with no error anywhere
42
- * (CLAUDE.md Root Cause #33).
38
+ /*
39
+ * Lane tabs now come from `features/orders/constants/order-lane-tabs`, shared
40
+ * with the seller's order list.
43
41
  *
44
- * "standard" is filtered in memory server-side, because orders written before
45
- * `orderType` existed have no such field and a Firestore `==` would exclude
46
- * every one of them.
42
+ * They were defined here as a local array literal, which meant the seller's
43
+ * list could not have them without a tenth hand-written copy of one union
44
+ * (Root Cause #61). The shared version derives from a
45
+ * `Record<OrderType, string>`, so a sixth lane is a compile error rather than
46
+ * a tab that silently never appears — which is the bug this array already had
47
+ * once, shipping four tabs for five lanes.
48
+ *
49
+ * Every id-correctness note that lived here (byte-exact stored values, the
50
+ * unhyphenated `preorder`, and the in-memory `standard` filter) moved with it.
47
51
  */
48
- const ORDER_LANE_TABS = [
49
- { id: "", label: "All" },
50
- { id: "standard", label: "Normal" },
51
- { id: "auction", label: "Auction wins" },
52
- { id: "offer", label: "Offer wins" },
53
- /*
54
- * 🛑 `"preorder"` WITHOUT A HYPHEN, and this is not a typo to tidy up.
55
- *
56
- * `OrderType` (`orders/utils/order-splitter.ts`) spells it `"preorder"`,
57
- * while `ListingType` spells the same concept `"pre-order"` WITH a hyphen.
58
- * Two unions, two spellings, and the tab must match the one actually stored
59
- * on `OrderDocument.orderType` — a tab id matching no stored value returns
60
- * zero rows forever with no error anywhere (Root Cause #33), which is
61
- * exactly the failure this file's own comment above warns about.
62
- *
63
- * These two lanes existed in the union and had no tab, so pre-order and
64
- * prize-draw orders were reachable only by landing on "All" and scrolling.
65
- * Five lanes, four tabs — the eighth instance this session of a
66
- * hand-written enumeration drifting from the union it filters.
67
- */
68
- { id: "preorder", label: "Pre-orders" },
69
- { id: "prize-draw", label: "Prize draws" },
70
- ];
71
52
  /**
72
53
  * Payment-method tabs — a THIRD axis, independent of the lane tabs above and
73
54
  * the lifecycle scope beside them.
@@ -162,7 +143,12 @@ export function UserOrdersView({ onOrderClick }) {
162
143
  // in flight" without those two questions fighting over one control.
163
144
  buildExtraParams: () => orderScope.extraParams,
164
145
  renderAboveContent: orderScope.renderAboveContent,
165
- renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FilterChipGroup, { label: "Type", tabs: ORDER_LANE_TABS, value: pendingFilters.orderType ?? "", onChange: (id) => setPendingFilters((prev) => ({ ...prev, orderType: id })) }), _jsx(FilterChipGroup, { label: "Payment", tabs: PAYMENT_METHOD_TABS, value: pendingFilters.paymentMethod ?? "", onChange: (id) => setPendingFilters((prev) => ({ ...prev, paymentMethod: id })) }), _jsx(Div, { children: _jsx(PaginatedSelect, { value: pendingFilters.status || null, onChange: (v) => setPendingFilters((p) => ({ ...p, status: v ?? "" })), options: STATUS_OPTIONS, placeholder: "All statuses", ariaLabel: "Filter by order status" }) })] })),
146
+ renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FilterChipGroup, { label: "Type",
147
+ // No cast: `FilterChipGroup.tabs` is `readonly FilterChipGroupTab[]`,
148
+ // which the shared constant already satisfies. The `as unknown as`
149
+ // here was working around the local `as const` literal, not a real
150
+ // mismatch.
151
+ tabs: ORDER_LANE_TABS, value: pendingFilters.orderType ?? "", onChange: (id) => setPendingFilters((prev) => ({ ...prev, orderType: id })) }), _jsx(FilterChipGroup, { label: "Payment", tabs: PAYMENT_METHOD_TABS, value: pendingFilters.paymentMethod ?? "", onChange: (id) => setPendingFilters((prev) => ({ ...prev, paymentMethod: id })) }), _jsx(Div, { children: _jsx(PaginatedSelect, { value: pendingFilters.status || null, onChange: (v) => setPendingFilters((p) => ({ ...p, status: v ?? "" })), options: STATUS_OPTIONS, placeholder: "All statuses", ariaLabel: "Filter by order status" }) })] })),
166
152
  renderCards: (rows, _view, _selection, isLoading) => {
167
153
  if (isLoading) {
168
154
  return (_jsx(Stack, { gap: "md", children: Array.from({ length: 3 }).map((_, i) => (_jsx(Div, { className: "h-24 animate-pulse border border-[var(--appkit-color-border)]", rounded: "xl" }, i))) }));