@mohasinac/appkit 4.62.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 (204) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/server/features/checkout/actions.d.ts +80 -0
  3. package/dist/_internal/server/features/checkout/actions.js +542 -26
  4. package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
  5. package/dist/_internal/server/features/invoicing/allocate.js +162 -0
  6. package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
  7. package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
  8. package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
  9. package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
  10. package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
  11. package/dist/_internal/server/features/invoicing/issue.js +202 -0
  12. package/dist/_internal/server/features/orders/actions.d.ts +8 -1
  13. package/dist/_internal/server/features/orders/actions.js +81 -8
  14. package/dist/_internal/server/features/orders/adapters.js +33 -0
  15. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
  16. package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
  17. package/dist/_internal/server/features/points/award.js +46 -4
  18. package/dist/_internal/server/features/refunds/actions.js +26 -0
  19. package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
  20. package/dist/_internal/server/features/shipping/courier-block.js +239 -0
  21. package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
  22. package/dist/_internal/server/features/site-settings/adapters.js +15 -0
  23. package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
  24. package/dist/_internal/server/features/stores/adapters.js +51 -0
  25. package/dist/_internal/server/functions/scheduled.js +38 -1
  26. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +9 -2
  27. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
  28. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
  29. package/dist/_internal/server/jobs/core/index.d.ts +2 -1
  30. package/dist/_internal/server/jobs/core/index.js +2 -1
  31. package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
  32. package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
  33. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
  34. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +62 -0
  35. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
  36. package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
  37. package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
  38. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +10 -2
  39. package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
  40. package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
  41. package/dist/_internal/shared/actions/action-registry.js +28 -0
  42. package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
  43. package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
  44. package/dist/_internal/shared/checkout/order-math.d.ts +145 -0
  45. package/dist/_internal/shared/checkout/order-math.js +173 -0
  46. package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
  47. package/dist/_internal/shared/checkout/refundable-base.js +13 -1
  48. package/dist/_internal/shared/checkout/split.d.ts +87 -0
  49. package/dist/_internal/shared/checkout/split.js +100 -0
  50. package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
  51. package/dist/_internal/shared/content/disclaimers.js +217 -0
  52. package/dist/_internal/shared/content/published-values.d.ts +88 -0
  53. package/dist/_internal/shared/content/published-values.js +198 -0
  54. package/dist/_internal/shared/features/admin/integrations.d.ts +109 -0
  55. package/dist/_internal/shared/features/admin/integrations.js +253 -0
  56. package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
  57. package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
  58. package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
  59. package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
  60. package/dist/_internal/shared/features/points/accepts.d.ts +36 -0
  61. package/dist/_internal/shared/features/points/accepts.js +43 -0
  62. package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
  63. package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
  64. package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
  65. package/dist/_internal/shared/fees/calculator.d.ts +33 -1
  66. package/dist/_internal/shared/fees/calculator.js +32 -2
  67. package/dist/client.d.ts +11 -0
  68. package/dist/client.js +30 -0
  69. package/dist/constants/api-endpoints.d.ts +42 -0
  70. package/dist/constants/api-endpoints.js +14 -0
  71. package/dist/constants/field-names.d.ts +1 -0
  72. package/dist/constants/field-names.js +5 -0
  73. package/dist/contracts/index.d.ts +1 -1
  74. package/dist/contracts/shipping.d.ts +45 -0
  75. package/dist/contracts/shipping.js +15 -0
  76. package/dist/features/about/components/AboutView.js +1 -1
  77. package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
  78. package/dist/features/account/components/UserOrdersView.js +19 -33
  79. package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
  80. package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
  81. package/dist/features/admin/components/AdminInvoicingView.js +65 -0
  82. package/dist/features/admin/components/AdminOrderEditorView.d.ts +18 -1
  83. package/dist/features/admin/components/AdminOrderEditorView.js +11 -2
  84. package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
  85. package/dist/features/admin/components/AdminOrdersView.js +67 -5
  86. package/dist/features/admin/components/OwnerQuickActions.js +24 -2
  87. package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
  88. package/dist/features/admin/constants/filter-tabs.js +6 -0
  89. package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
  90. package/dist/features/admin/hooks/useAdminListingData.js +16 -0
  91. package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
  92. package/dist/features/admin/schemas/firestore.d.ts +30 -0
  93. package/dist/features/admin/schemas/firestore.js +5 -0
  94. package/dist/features/audit-log/schemas/firestore.d.ts +30 -0
  95. package/dist/features/audit-log/schemas/firestore.js +15 -0
  96. package/dist/features/auth/permissions/constants.js +13 -0
  97. package/dist/features/cart/components/CartPriceBreakdown.d.ts +13 -0
  98. package/dist/features/cart/components/CartPriceBreakdown.js +6 -2
  99. package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
  100. package/dist/features/cart/components/PointsTenderPicker.js +15 -0
  101. package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
  102. package/dist/features/cart/components/index.d.ts +2 -0
  103. package/dist/features/cart/components/index.js +1 -0
  104. package/dist/features/cart/repository/cart.repository.d.ts +19 -0
  105. package/dist/features/cart/repository/cart.repository.js +27 -0
  106. package/dist/features/cart/schemas/firestore.d.ts +92 -0
  107. package/dist/features/contact/email.d.ts +27 -10
  108. package/dist/features/contact/email.js +20 -3
  109. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +38 -0
  110. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +80 -0
  111. package/dist/features/dispute-reasons/schemas/firestore.d.ts +64 -0
  112. package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
  113. package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
  114. package/dist/features/faq/components/FAQAccordion.js +29 -4
  115. package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
  116. package/dist/features/invoicing/schemas/firestore.js +45 -0
  117. package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
  118. package/dist/features/invoicing/utils/financial-year.js +53 -0
  119. package/dist/features/jobs/actions/enqueue-job.js +18 -1
  120. package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
  121. package/dist/features/jobs/repository/jobs.repository.js +84 -1
  122. package/dist/features/jobs/schemas/firestore.d.ts +80 -0
  123. package/dist/features/jobs/schemas/firestore.js +37 -0
  124. package/dist/features/lottery/schemas/zod.d.ts +6 -6
  125. package/dist/features/media/MediaImage.js +22 -1
  126. package/dist/features/media/upload/ImageUpload.js +1 -1
  127. package/dist/features/obligations/constants/policies.js +24 -1
  128. package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
  129. package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
  130. package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
  131. package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
  132. package/dist/features/orders/components/RaiseDisputeForm.js +111 -0
  133. package/dist/features/orders/components/RefundRequestView.js +19 -1
  134. package/dist/features/orders/components/index.d.ts +4 -0
  135. package/dist/features/orders/components/index.js +2 -0
  136. package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
  137. package/dist/features/orders/constants/order-lane-tabs.js +88 -0
  138. package/dist/features/orders/repository/orders.repository.d.ts +57 -0
  139. package/dist/features/orders/repository/orders.repository.js +145 -0
  140. package/dist/features/orders/schemas/firestore.d.ts +196 -1
  141. package/dist/features/orders/schemas/index.d.ts +54 -6
  142. package/dist/features/orders/schemas/index.js +12 -0
  143. package/dist/features/orders/types/index.d.ts +26 -0
  144. package/dist/features/orders/utils/courier-block.d.ts +25 -0
  145. package/dist/features/orders/utils/courier-block.js +23 -0
  146. package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
  147. package/dist/features/payment-cases/schemas/firestore.js +1 -0
  148. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
  149. package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
  150. package/dist/features/products/components/ProductDetailPageView.js +2 -2
  151. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  152. package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
  153. package/dist/features/promotions/actions/coupon-stacking.js +21 -2
  154. package/dist/features/promotions/components/CouponCard.js +13 -0
  155. package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
  156. package/dist/features/promotions/constants/coupon-help.js +9 -1
  157. package/dist/features/promotions/schemas/firestore.d.ts +24 -0
  158. package/dist/features/promotions/schemas/firestore.js +4 -0
  159. package/dist/features/promotions/types/index.d.ts +19 -1
  160. package/dist/features/scams/components/ScamProfileView.js +2 -2
  161. package/dist/features/scams/components/ScamRegistryView.js +2 -2
  162. package/dist/features/search/action-index/tab-entries.js +1 -1
  163. package/dist/features/seller/actions/seller-actions.js +33 -3
  164. package/dist/features/seller/components/SellerOrdersView.js +129 -27
  165. package/dist/features/seller/hooks/useSellerListingData.js +25 -0
  166. package/dist/features/shipments/schemas/validation.d.ts +4 -4
  167. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +43 -0
  168. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +90 -0
  169. package/dist/features/shipping-boxes/schemas/firestore.d.ts +82 -0
  170. package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
  171. package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
  172. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
  173. package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
  174. package/dist/features/stores/schemas/firestore.d.ts +71 -0
  175. package/dist/features/stores/types/index.d.ts +19 -0
  176. package/dist/features/support/schemas/index.d.ts +6 -6
  177. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  178. package/dist/index.d.ts +6 -1
  179. package/dist/index.js +14 -1
  180. package/dist/next/routing/route-map.d.ts +20 -0
  181. package/dist/next/routing/route-map.js +10 -0
  182. package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
  183. package/dist/providers/shipping-shiprocket/index.js +84 -1
  184. package/dist/schemas/registry.d.ts +47 -18
  185. package/dist/schemas/sieve.d.ts +2 -2
  186. package/dist/server-entry.d.ts +2 -2
  187. package/dist/server-entry.js +8 -1
  188. package/dist/server.d.ts +10 -1
  189. package/dist/server.js +39 -1
  190. package/dist/styles.css +64 -80
  191. package/dist/tailwind-utilities.css +1 -1
  192. package/dist/tokens/themes/default-dark.js +1 -0
  193. package/dist/tokens/themes/default-light.js +1 -0
  194. package/dist/tokens/themes/required.d.ts +1 -1
  195. package/dist/tokens/themes/required.js +1 -0
  196. package/dist/tokens/tokens.css +15 -0
  197. package/dist/ui/components/Disclaimer.d.ts +19 -0
  198. package/dist/ui/components/Disclaimer.js +74 -0
  199. package/dist/ui/components/tabs-overflow.js +64 -11
  200. package/dist/ui/index.d.ts +2 -0
  201. package/dist/ui/index.js +6 -0
  202. package/dist/ui/rich-text/RichText.style.css +48 -79
  203. package/package.json +1 -1
  204. 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
  */
@@ -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
@@ -733,6 +747,14 @@ export declare const API_ENDPOINTS: {
733
747
  readonly PAYOUTS_EXPORT: "/api/admin/payouts/export";
734
748
  readonly AUDIT_LOG: "/api/admin/audit-log";
735
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";
736
758
  readonly PAYMENT_CASES: "/api/admin/payment-cases";
737
759
  readonly PAYMENT_CASE_BY_ID: (id: string) => string;
738
760
  readonly PROTECTION_CLAIMS: "/api/admin/protection-claims";
@@ -855,6 +877,12 @@ export declare const API_ENDPOINTS: {
855
877
  readonly SELECTION: "/api/cart/selection";
856
878
  /** Per-store paid add-on selections — the source of truth for what's charged. */
857
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";
858
886
  readonly COUPON: "/api/cart/coupon";
859
887
  /**
860
888
  * Add a buyer-assembled group selection as ONE line. Separate from `GET`'s
@@ -1281,6 +1309,14 @@ export declare const API_ROUTES: {
1281
1309
  readonly PAYOUTS_EXPORT: "/api/admin/payouts/export";
1282
1310
  readonly AUDIT_LOG: "/api/admin/audit-log";
1283
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";
1284
1320
  readonly PAYMENT_CASES: "/api/admin/payment-cases";
1285
1321
  readonly PAYMENT_CASE_BY_ID: (id: string) => string;
1286
1322
  readonly PROTECTION_CLAIMS: "/api/admin/protection-claims";
@@ -1403,6 +1439,12 @@ export declare const API_ROUTES: {
1403
1439
  readonly SELECTION: "/api/cart/selection";
1404
1440
  /** Per-store paid add-on selections — the source of truth for what's charged. */
1405
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";
1406
1448
  readonly COUPON: "/api/cart/coupon";
1407
1449
  /**
1408
1450
  * Add a buyer-assembled group selection as ONE line. Separate from `GET`'s
@@ -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
@@ -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";
@@ -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",
@@ -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
  }
@@ -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",
@@ -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))) }));
@@ -22,11 +22,28 @@ const __P = {
22
22
  const __O = {
23
23
  yAuto: "overflow-y-auto",
24
24
  };
25
+ /*
26
+ * 🛑 Not derived from `COUPON_TYPE_LABELS` on purpose, and the reason is the
27
+ * opposite of what the duplication framework usually says.
28
+ *
29
+ * `points_earn` is OFFERED here, so an admin can create a points offer — but
30
+ * the editor's discount fields (`discount.value`, `maxDiscount`,
31
+ * `minPurchase`) are meaningless for it, and it carries `pointsPercent`
32
+ * instead. Deriving the list would silently add the option the moment the
33
+ * union grew, which is how `AdminEventsView` ended up offering `contest`,
34
+ * `giveaway` and `flash-sale` — three values `EventType` never had, each
35
+ * matching zero rows forever (Root Cause #72).
36
+ *
37
+ * So the list is explicit and the entry is deliberate.
38
+ * `audit-filter-tab-enums` covers the chip row, which is the surface where
39
+ * coverage actually matters.
40
+ */
25
41
  const TYPE_OPTIONS = [
26
42
  { label: "Percentage Discount", value: "percentage" },
27
43
  { label: "Fixed Amount Discount", value: "fixed" },
28
44
  { label: "Free Shipping", value: "free_shipping" },
29
45
  { label: "Buy X Get Y", value: "buy_x_get_y" },
46
+ { label: "Points Back (awards points, no discount)", value: "points_earn" },
30
47
  ];
31
48
  // --- Helpers -----------------------------------------------------------------
32
49
  function toCouponCode(str) {
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare function AdminInvoicingView(): React.JSX.Element;
@@ -0,0 +1,65 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Button, Card, Disclaimer, Div, Heading, Row, Stack, Text } from "../../../ui/index.js";
5
+ import { FieldInput } from "../../../ui/forms/FieldInput.js";
6
+ import { Icon } from "../../../ui/components/Icon.js";
7
+ import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
8
+ /**
9
+ * Admin → Finance → Invoicing: the GSTR-1 draft export (Phase 7d).
10
+ *
11
+ * ## 🛑 Why this page exists at all
12
+ *
13
+ * The export is an API route, and an API route with no surface is a dead end —
14
+ * reachable only by someone who already knows the URL, which is Root Cause
15
+ * #56's shape (16 of 70 listing views had no way to open a row). The person
16
+ * who needs this is an accountant at quarter end, and they will not type a
17
+ * query string.
18
+ *
19
+ * ## 🛑 It downloads; it does not file
20
+ *
21
+ * Nothing here talks to the GST portal — there is no webhook facility in
22
+ * existence and no GSP contract. The copy says "draft" everywhere it can,
23
+ * because a page that implied a return had been FILED when it had not is the
24
+ * most consequential thing this feature could get wrong.
25
+ */
26
+ /** Indian FY quarters, which is the unit a GSTR-1 is filed in. */
27
+ const QUARTERS = [
28
+ { label: "Q1 — Apr to Jun", fromMonth: "04-01", toMonth: "06-30" },
29
+ { label: "Q2 — Jul to Sep", fromMonth: "07-01", toMonth: "09-30" },
30
+ { label: "Q3 — Oct to Dec", fromMonth: "10-01", toMonth: "12-31" },
31
+ /*
32
+ * 🛑 Q4 SPANS A CALENDAR YEAR BOUNDARY. Jan–Mar of FY 26-27 is calendar
33
+ * 2027, not 2026 — the same off-by-one the `financialYearLabel` helper
34
+ * exists to contain, surfacing here because a quarter picker has to know it
35
+ * too. `yearOffset` is what carries it; dropping it would export the wrong
36
+ * three months with no visible error.
37
+ */
38
+ { label: "Q4 — Jan to Mar", fromMonth: "01-01", toMonth: "03-31", yearOffset: 1 },
39
+ ];
40
+ function defaultFyStartYear() {
41
+ const now = new Date();
42
+ // Zero-based month: April is 3. Jan–Mar belongs to the FY that started last
43
+ // calendar year.
44
+ return now.getMonth() >= 3 ? now.getFullYear() : now.getFullYear() - 1;
45
+ }
46
+ export function AdminInvoicingView() {
47
+ const [fyStart, setFyStart] = useState(String(defaultFyStartYear()));
48
+ const [from, setFrom] = useState("");
49
+ const [to, setTo] = useState("");
50
+ const applyQuarter = (q) => {
51
+ const start = Number(fyStart);
52
+ if (!Number.isFinite(start))
53
+ return;
54
+ const offset = "yearOffset" in q ? q.yearOffset : 0;
55
+ setFrom(`${start + offset}-${q.fromMonth}`);
56
+ setTo(`${start + offset}-${q.toMonth}`);
57
+ };
58
+ const href = from && to
59
+ ? `${ADMIN_ENDPOINTS.INVOICING_GSTR1}?from=${from}&to=${to}`
60
+ : undefined;
61
+ return (_jsxs(Stack, { gap: "lg", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 1, children: "Invoicing" }), _jsx(Text, { color: "muted", size: "sm", children: "Export the invoice and credit-note register for a period, as a draft for your accountant." })] }), _jsx(Card, { padding: "md", children: _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, children: "GSTR-1 draft" }), _jsx(Disclaimer, { id: "accounting-output" }), _jsx(Row, { gap: "sm", wrap: true, children: _jsx(Div, { className: "w-40", children: _jsx(FieldInput, { name: "fyStart", label: "FY starting", value: fyStart, onChange: setFyStart, hint: "e.g. 2026 for FY 26-27" }) }) }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Quarter" }), _jsx(Row, { gap: "sm", wrap: true, children: QUARTERS.map((q) => (_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => applyQuarter(q), children: q.label }, q.label))) })] }), _jsxs(Row, { gap: "sm", wrap: true, children: [_jsx(Div, { className: "w-48", children: _jsx(FieldInput, { name: "from", label: "From", value: from, onChange: setFrom }) }), _jsx(Div, { className: "w-48", children: _jsx(FieldInput, { name: "to", label: "To", value: to, onChange: setTo }) })] }), _jsxs(Row, { gap: "sm", align: "center", children: [_jsxs(Button, { type: "button", variant: "primary", disabled: !href, onClick: () => {
62
+ if (href)
63
+ window.location.href = href;
64
+ }, children: [_jsx(Icon, { name: "download", size: "sm" }), "Download draft CSV"] }), !href && (_jsx(Text, { color: "muted", size: "xs", children: "Pick a quarter, or enter both dates as YYYY-MM-DD." }))] }), _jsx(Text, { color: "muted", size: "xs", children: "Credit notes appear as negative rows, so the TOTAL row is net of refunds. Any paid order in the window that carries no invoice number is listed at the bottom of the file as a warning \u2014 those are not included in the totals and need looking at." })] }) })] }));
65
+ }
@@ -1,4 +1,5 @@
1
1
  import { type OrderAddonBadgesOrder } from "../../orders/components/OrderAddonBadges";
2
+ import type { OrderSourceContext } from "../../orders/schemas/firestore";
2
3
  import React from "react";
3
4
  export interface AdminOrderItemRow {
4
5
  productId: string;
@@ -7,6 +8,10 @@ export interface AdminOrderItemRow {
7
8
  quantity: number;
8
9
  unitPrice: number;
9
10
  totalPrice: number;
11
+ groupSlug?: string;
12
+ groupTitle?: string;
13
+ bundleCategorySlug?: string;
14
+ bundleProductIds?: string[];
10
15
  }
11
16
  export interface AdminOrderEditorViewProps {
12
17
  open: boolean;
@@ -36,5 +41,17 @@ export interface AdminOrderEditorViewProps {
36
41
  * coupon is what an admin needs when reconciling a discounted order.
37
42
  */
38
43
  addons?: OrderAddonBadgesOrder;
44
+ /**
45
+ * How the buyer earned this price — a won auction, a buyout, an agreed
46
+ * offer, a pre-order deposit, prize-draw entries.
47
+ *
48
+ * Support is the surface that needs this most: a buyer disputing "I agreed
49
+ * ₹X" is otherwise unanswerable from the admin side, because the negotiated
50
+ * amount lives only in `sourceContext` and nothing rendered it anywhere.
51
+ * `GET /api/admin/orders/[id]` already returns it via `maskedOrder`.
52
+ */
53
+ sourceContext?: OrderSourceContext;
54
+ /** For the provenance card's money formatting; falls back to the default. */
55
+ currency?: string;
39
56
  }
40
- export declare function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, }: AdminOrderEditorViewProps): React.JSX.Element;
57
+ export declare function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, sourceContext, currency, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, }: AdminOrderEditorViewProps): React.JSX.Element;
@@ -1,6 +1,8 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { OrderAddonBadges } from "../../orders/components/OrderAddonBadges.js";
4
+ import { OrderProvenanceCard } from "../../orders/components/OrderProvenanceCard.js";
5
+ import { groupOrderItemsByLine } from "../../orders/utils/bundle-grouping.js";
4
6
  import { isManualPaymentMethod } from "../../orders/constants/payment-window.js";
5
7
  import { useApiMutation } from "@mohasinac/appkit/client";
6
8
  import React, { useState } from "react";
@@ -18,6 +20,13 @@ import { useFormShellState, FormShellContext } from "../../../ui/forms/index.js"
18
20
  import { applyZodIssues } from "../../../ui/forms/apply-zod-issues.js";
19
21
  import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary.js";
20
22
  import { ValidationError } from "../../../errors/validation-error.js";
23
+ /**
24
+ * One item row. Extracted so the single and grouped render paths share exactly
25
+ * one piece of markup rather than two that can drift.
26
+ */
27
+ function adminOrderItemRow(item, key) {
28
+ return (_jsxs(Div, { layout: "flex", align: "center", gap: "3", padding: "sm", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) }), _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title }), _jsxs(Text, { size: "xs", color: "muted", children: ["Qty: ", item.quantity, " \u00D7 ", toCurrency(item.unitPrice)] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "semibold", children: toCurrency(item.totalPrice) })] }, key));
29
+ }
21
30
  const STATUS_OPTIONS = [
22
31
  { label: "Pending", value: "pending" },
23
32
  { label: "Processing", value: "processing" },
@@ -37,7 +46,7 @@ const CARRIER_OPTIONS = [
37
46
  { label: "Other", value: "Other" },
38
47
  ];
39
48
  // --- Component ---------------------------------------------------------------
40
- export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, }) {
49
+ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, sourceContext, currency, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, }) {
41
50
  const queryClient = useQueryClient();
42
51
  const { showToast } = useToast();
43
52
  const [draft, setDraft] = React.useState({
@@ -207,7 +216,7 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
207
216
  setIsRejectingFraud(false);
208
217
  }
209
218
  };
210
- return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: orderLabel ? `Order: ${orderLabel}` : "Update Order", children: _jsx(FormShellContext.Provider, { value: form.shellCtx, children: _jsxs(Stack, { gap: "md", padding: "md", children: [_jsx(FormErrorSummary, {}), addons && _jsx(OrderAddonBadges, { order: addons, variant: "detail" }), items && items.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Label, { size: "sm", weight: "medium", color: "primary", children: ["Items (", items.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: items.map((item, i) => (_jsxs(Div, { layout: "flex", align: "center", gap: "3", padding: "sm", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) }), _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title }), _jsxs(Text, { size: "xs", color: "muted", children: ["Qty: ", item.quantity, " \u00D7 ", toCurrency(item.unitPrice)] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "semibold", children: toCurrency(item.totalPrice) })] }, item.productId || i))) })] })), _jsx(SectionForm, { sections: sections, values: draft, onChange: (partial) => setDraft((d) => ({ ...d, ...partial })), onSubmit: () => {
219
+ return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: orderLabel ? `Order: ${orderLabel}` : "Update Order", children: _jsx(FormShellContext.Provider, { value: form.shellCtx, children: _jsxs(Stack, { gap: "md", padding: "md", children: [_jsx(FormErrorSummary, {}), addons && _jsx(OrderAddonBadges, { order: addons, variant: "detail" }), _jsx(OrderProvenanceCard, { sourceContext: sourceContext, currency: currency }), items && items.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Label, { size: "sm", weight: "medium", color: "primary", children: ["Items (", items.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: groupOrderItemsByLine(items).map((group, gi) => group.kind === "single" ? (adminOrderItemRow(group.item, `s-${group.index}`)) : (_jsxs(Div, { children: [_jsxs(Div, { layout: "flex", align: "center", gap: "2", padding: "x-sm", surface: "muted", children: [_jsx(Text, { size: "xs", weight: "semibold", children: group.groupTitle ?? "Grouped purchase" }), _jsx(Text, { size: "xs", color: "muted", children: `${group.memberCount} ${group.memberCount === 1 ? "item" : "items"} · one line` })] }), _jsx(Div, { className: "pl-4", children: group.items.map(({ item, index }) => adminOrderItemRow(item, `m-${index}`)) })] }, `g-${gi}`))) })] })), _jsx(SectionForm, { sections: sections, values: draft, onChange: (partial) => setDraft((d) => ({ ...d, ...partial })), onSubmit: () => {
211
220
  // SectionForm surfaces errors and then submits regardless, so the
212
221
  // guard is here.
213
222
  form.clearErrors();