@mohasinac/appkit 4.62.1 → 4.63.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 (263) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -29
  3. package/dist/_internal/client/features/layout/filterNavItems.d.ts +28 -0
  4. package/dist/_internal/client/features/layout/filterNavItems.js +35 -0
  5. package/dist/_internal/server/features/ads/booking.d.ts +118 -0
  6. package/dist/_internal/server/features/ads/booking.js +326 -0
  7. package/dist/_internal/server/features/checkout/actions.d.ts +51 -11
  8. package/dist/_internal/server/features/checkout/actions.js +335 -80
  9. package/dist/_internal/server/features/checkout/expand-order-items.d.ts +1 -2
  10. package/dist/_internal/server/features/checkout/expand-order-items.js +32 -0
  11. package/dist/_internal/server/features/checkout/freeze-parts.d.ts +50 -0
  12. package/dist/_internal/server/features/checkout/freeze-parts.js +94 -0
  13. package/dist/_internal/server/features/checkout/value-otp-gate.d.ts +55 -0
  14. package/dist/_internal/server/features/checkout/value-otp-gate.js +60 -0
  15. package/dist/_internal/server/features/dashboard/attention.d.ts +128 -0
  16. package/dist/_internal/server/features/dashboard/attention.js +183 -0
  17. package/dist/_internal/server/features/dashboard/data.d.ts +47 -0
  18. package/dist/_internal/server/features/dashboard/data.js +82 -0
  19. package/dist/_internal/server/features/inventory/reservations.d.ts +99 -0
  20. package/dist/_internal/server/features/inventory/reservations.js +273 -0
  21. package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
  22. package/dist/_internal/server/features/offers/destinations.js +65 -0
  23. package/dist/_internal/server/features/orders/adapters.js +21 -0
  24. package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
  25. package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
  26. package/dist/_internal/server/features/orders/balance.d.ts +89 -0
  27. package/dist/_internal/server/features/orders/balance.js +256 -0
  28. package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
  29. package/dist/_internal/server/features/pricing/actions.js +144 -0
  30. package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
  31. package/dist/_internal/server/features/shipping/book-split.js +130 -0
  32. package/dist/_internal/server/features/shipping/book.d.ts +61 -0
  33. package/dist/_internal/server/features/shipping/book.js +176 -0
  34. package/dist/_internal/server/features/shipping/courier-block.js +26 -1
  35. package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
  36. package/dist/_internal/server/features/site-settings/adapters.js +14 -0
  37. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  38. package/dist/_internal/server/features/stores/adapters.js +14 -0
  39. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  40. package/dist/_internal/server/functions/scheduled.js +57 -1
  41. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +2 -0
  42. package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
  43. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +4 -1
  44. package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
  45. package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
  46. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +15 -0
  47. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  48. package/dist/_internal/server/jobs/handlers/index.js +1 -0
  49. package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
  50. package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
  51. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  52. package/dist/_internal/shared/actions/action-registry.js +268 -0
  53. package/dist/_internal/shared/checkout/order-math.d.ts +31 -2
  54. package/dist/_internal/shared/checkout/order-math.js +22 -36
  55. package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
  56. package/dist/_internal/shared/checkout/rules/index.js +1 -1
  57. package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
  58. package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
  59. package/dist/_internal/shared/checkout/split.d.ts +0 -12
  60. package/dist/_internal/shared/checkout/split.js +22 -2
  61. package/dist/_internal/shared/content/disclaimers.js +7 -7
  62. package/dist/_internal/shared/content/published-values.d.ts +6 -1
  63. package/dist/_internal/shared/content/published-values.js +8 -0
  64. package/dist/_internal/shared/features/admin/integrations.d.ts +32 -2
  65. package/dist/_internal/shared/features/admin/integrations.js +19 -9
  66. package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
  67. package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
  68. package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
  69. package/dist/_internal/shared/features/emi/choice.js +69 -0
  70. package/dist/_internal/shared/features/emi/group.d.ts +84 -0
  71. package/dist/_internal/shared/features/emi/group.js +126 -0
  72. package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
  73. package/dist/_internal/shared/features/ledger/posting.js +165 -0
  74. package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
  75. package/dist/_internal/shared/features/ledger/reports.js +127 -0
  76. package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
  77. package/dist/_internal/shared/features/ledger/rules.js +221 -0
  78. package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
  79. package/dist/_internal/shared/features/offers/coordination.js +80 -0
  80. package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
  81. package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
  82. package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
  83. package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
  84. package/dist/_internal/shared/features/points/accepts.d.ts +1 -8
  85. package/dist/_internal/shared/features/points/accepts.js +12 -6
  86. package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
  87. package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
  88. package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
  89. package/dist/_internal/shared/features/shipping/parcel.js +139 -0
  90. package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
  91. package/dist/_internal/shared/features/site-settings/fees.js +12 -8
  92. package/dist/_internal/shared/fees/explain.d.ts +81 -0
  93. package/dist/_internal/shared/fees/explain.js +101 -0
  94. package/dist/_internal/shared/history/index.d.ts +1 -1
  95. package/dist/_internal/shared/history/index.js +1 -1
  96. package/dist/_internal/shared/history/types.d.ts +7 -1
  97. package/dist/_internal/shared/history/types.js +25 -0
  98. package/dist/client.d.ts +11 -1
  99. package/dist/client.js +13 -0
  100. package/dist/constants/api-endpoints.d.ts +72 -0
  101. package/dist/constants/api-endpoints.js +41 -0
  102. package/dist/constants/field-names.d.ts +41 -0
  103. package/dist/constants/field-names.js +47 -0
  104. package/dist/features/account/components/UserOrdersView.js +28 -2
  105. package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
  106. package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
  107. package/dist/features/admin/components/AdminOrderEditorView.d.ts +16 -1
  108. package/dist/features/admin/components/AdminOrderEditorView.js +72 -3
  109. package/dist/features/admin/components/AdminOrdersView.js +20 -1
  110. package/dist/features/admin/schemas/firestore.d.ts +32 -0
  111. package/dist/features/ads/schemas/booking-write.d.ts +157 -0
  112. package/dist/features/ads/schemas/booking-write.js +88 -0
  113. package/dist/features/ads/schemas/creative-form.d.ts +18 -0
  114. package/dist/features/ads/schemas/creative-form.js +51 -0
  115. package/dist/features/ads/schemas/firestore.d.ts +140 -0
  116. package/dist/features/ads/schemas/firestore.js +109 -0
  117. package/dist/features/auctions/repository/bid.repository.js +3 -1
  118. package/dist/features/audit-log/schemas/firestore.d.ts +110 -0
  119. package/dist/features/audit-log/schemas/firestore.js +74 -0
  120. package/dist/features/auth/actions/profile-actions.d.ts +69 -1
  121. package/dist/features/auth/actions/profile-actions.js +62 -1
  122. package/dist/features/auth/schemas/firestore.d.ts +16 -2
  123. package/dist/features/auth/schemas/firestore.js +2 -1
  124. package/dist/features/blog/actions/blog-actions.d.ts +6 -0
  125. package/dist/features/blog/actions/blog-actions.js +17 -2
  126. package/dist/features/blog/api/route.js +4 -1
  127. package/dist/features/blog/columns/index.js +5 -0
  128. package/dist/features/blog/components/BlogIndexPageView.js +2 -1
  129. package/dist/features/blog/repository/blog.repository.d.ts +4 -0
  130. package/dist/features/blog/repository/blog.repository.js +5 -0
  131. package/dist/features/blog/schemas/blog-form.d.ts +3 -0
  132. package/dist/features/blog/schemas/blog-form.js +1 -0
  133. package/dist/features/blog/schemas/firestore.d.ts +38 -1
  134. package/dist/features/blog/schemas/firestore.js +23 -0
  135. package/dist/features/blog/schemas/index.d.ts +6 -0
  136. package/dist/features/blog/schemas/index.js +2 -0
  137. package/dist/features/blog/types/index.d.ts +2 -0
  138. package/dist/features/cart/components/CartPriceBreakdown.d.ts +12 -1
  139. package/dist/features/cart/components/CartPriceBreakdown.js +24 -5
  140. package/dist/features/cart/repository/cart.repository.d.ts +14 -0
  141. package/dist/features/cart/repository/cart.repository.js +20 -0
  142. package/dist/features/cart/schemas/firestore.d.ts +24 -0
  143. package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
  144. package/dist/features/dashboard/components/DashboardHubView.js +56 -0
  145. package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
  146. package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
  147. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
  148. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
  149. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +24 -0
  150. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +28 -0
  151. package/dist/features/dispute-reasons/schemas/firestore.d.ts +3 -5
  152. package/dist/features/events/components/EventSaleBanner.js +1 -1
  153. package/dist/features/faq/components/FAQPageContent.js +2 -2
  154. package/dist/features/gateway-events/schemas/index.d.ts +2 -2
  155. package/dist/features/homepage/schemas/index.d.ts +8 -8
  156. package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
  157. package/dist/features/inventory/schemas/stock-reservation.js +103 -0
  158. package/dist/features/layout/AppLayoutShell.d.ts +9 -1
  159. package/dist/features/layout/AppLayoutShell.js +12 -4
  160. package/dist/features/layout/BottomActions.js +2 -1
  161. package/dist/features/layout/BottomNavbar.d.ts +10 -2
  162. package/dist/features/layout/SideActionDock.js +52 -2
  163. package/dist/features/layout/TitleBarLayout.js +13 -4
  164. package/dist/features/layout/TitleBarLayout.style.css +59 -0
  165. package/dist/features/layout/useCtaReveal.d.ts +16 -1
  166. package/dist/features/layout/useCtaReveal.js +17 -26
  167. package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
  168. package/dist/features/ledger/repository/journal.repository.js +312 -0
  169. package/dist/features/ledger/schemas/expenses.d.ts +87 -0
  170. package/dist/features/ledger/schemas/expenses.js +27 -0
  171. package/dist/features/ledger/schemas/firestore.d.ts +183 -0
  172. package/dist/features/ledger/schemas/firestore.js +69 -0
  173. package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
  174. package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
  175. package/dist/features/orders/components/RaiseDisputeForm.js +19 -11
  176. package/dist/features/orders/repository/orders.repository.d.ts +28 -1
  177. package/dist/features/orders/repository/orders.repository.js +60 -6
  178. package/dist/features/orders/schemas/balance.d.ts +40 -0
  179. package/dist/features/orders/schemas/balance.js +47 -0
  180. package/dist/features/orders/schemas/firestore.d.ts +202 -0
  181. package/dist/features/orders/schemas/firestore.js +43 -0
  182. package/dist/features/orders/schemas/index.d.ts +2 -2
  183. package/dist/features/orders/types/index.d.ts +21 -0
  184. package/dist/features/payments/repository/payout.repository.js +3 -2
  185. package/dist/features/payments/schemas/firestore.d.ts +92 -1
  186. package/dist/features/payments/schemas/firestore.js +23 -0
  187. package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
  188. package/dist/features/pricing/components/AdminPricingView.js +157 -0
  189. package/dist/features/products/schemas/firestore.d.ts +26 -1
  190. package/dist/features/products/schemas/firestore.js +2 -0
  191. package/dist/features/products/schemas/index.d.ts +2 -2
  192. package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
  193. package/dist/features/reviews/repository/reviews.repository.js +16 -0
  194. package/dist/features/scams/repository/scammer.repository.js +2 -1
  195. package/dist/features/scams/schemas/firestore.d.ts +2 -1
  196. package/dist/features/scams/schemas/firestore.js +2 -1
  197. package/dist/features/seller/actions/offer-actions.js +56 -0
  198. package/dist/features/seller/actions/seller-actions.d.ts +1 -1
  199. package/dist/features/seller/actions/seller-actions.js +4 -0
  200. package/dist/features/seller/components/DispatchForm.d.ts +11 -0
  201. package/dist/features/seller/components/DispatchForm.js +190 -0
  202. package/dist/features/seller/components/SellerOrdersView.js +94 -2
  203. package/dist/features/seller/components/index.d.ts +1 -0
  204. package/dist/features/seller/components/index.js +1 -0
  205. package/dist/features/seller/repository/offer.repository.js +4 -3
  206. package/dist/features/seller/schemas/firestore.d.ts +23 -2
  207. package/dist/features/seller/schemas/firestore.js +3 -2
  208. package/dist/features/seller/schemas/index.d.ts +4 -4
  209. package/dist/features/seller/schemas/order-forms.d.ts +61 -4
  210. package/dist/features/seller/schemas/order-forms.js +41 -8
  211. package/dist/features/shipments/schemas/firestore.d.ts +25 -0
  212. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +39 -0
  213. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +55 -0
  214. package/dist/features/shipping-boxes/schemas/firestore.d.ts +2 -4
  215. package/dist/features/stores/repository/store.repository.d.ts +18 -0
  216. package/dist/features/stores/repository/store.repository.js +27 -1
  217. package/dist/features/stores/schemas/firestore.d.ts +41 -0
  218. package/dist/features/stores/schemas/firestore.js +20 -0
  219. package/dist/features/stores/schemas/index.d.ts +6 -6
  220. package/dist/features/support/repository/support.repository.js +72 -6
  221. package/dist/features/support/schemas/firestore.d.ts +17 -0
  222. package/dist/features/support/schemas/index.d.ts +2 -2
  223. package/dist/index.d.ts +5 -0
  224. package/dist/index.js +19 -0
  225. package/dist/next/routing/route-map.d.ts +92 -0
  226. package/dist/next/routing/route-map.js +53 -0
  227. package/dist/react/contexts/SessionContext.d.ts +17 -1
  228. package/dist/schemas/registry.d.ts +22 -16
  229. package/dist/security/pii-schemas.d.ts +1 -1
  230. package/dist/security/pii-schemas.js +45 -0
  231. package/dist/server-entry.d.ts +1 -1
  232. package/dist/server-entry.js +10 -1
  233. package/dist/server.d.ts +29 -1
  234. package/dist/server.js +158 -0
  235. package/dist/styles.css +349 -1
  236. package/dist/tailwind-utilities.css +1 -1
  237. package/dist/ui/components/Button.d.ts +10 -1
  238. package/dist/ui/components/Button.js +25 -3
  239. package/dist/ui/components/Chart.d.ts +63 -0
  240. package/dist/ui/components/Chart.js +169 -0
  241. package/dist/ui/components/Chart.style.css +214 -0
  242. package/dist/ui/components/Disclaimer.js +10 -1
  243. package/dist/ui/components/OverflowRow.js +54 -2
  244. package/dist/ui/components/OverflowRow.style.css +38 -0
  245. package/dist/ui/components/RichTextEditor.js +99 -8
  246. package/dist/ui/components/chart-spec.d.ts +68 -0
  247. package/dist/ui/components/chart-spec.js +140 -0
  248. package/dist/ui/components/index.style.css +1 -0
  249. package/dist/ui/components/surface-tokens.d.ts +1 -1
  250. package/dist/ui/components/surface-tokens.js +9 -0
  251. package/dist/ui/components/toolbar-collapse.d.ts +13 -0
  252. package/dist/ui/components/toolbar-collapse.js +10 -1
  253. package/dist/ui/index.d.ts +6 -0
  254. package/dist/ui/index.js +3 -0
  255. package/dist/ui/rich-text/RichText.d.ts +10 -1
  256. package/dist/ui/rich-text/RichText.js +33 -2
  257. package/dist/ui/rich-text/RichText.style.css +35 -0
  258. package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
  259. package/dist/ui/rich-text/RichTextRenderer.js +34 -2
  260. package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
  261. package/dist/ui/rich-text/chart-blocks.js +87 -0
  262. package/package.json +1 -1
  263. package/scripts/seed-cli.mjs +3 -0
@@ -5,6 +5,7 @@ import { OrderAddonBadges } from "../../orders/components/OrderAddonBadges.js";
5
5
  import { OrderProvenanceCard } from "../../orders/components/OrderProvenanceCard.js";
6
6
  import { groupOrderItemsByLine } from "../../orders/utils/bundle-grouping.js";
7
7
  import { normalizeError } from "../../../errors/normalize.js";
8
+ import { formatCurrency } from "../../../utils/number.formatter.js";
8
9
  // The defining module, not a barrel (Root Cause #18). Maps a server error CODE
9
10
  // to user-facing copy; it can never return server text, which is the point.
10
11
  import { toUserMessage } from "../../../errors/error-display-map.js";
@@ -27,6 +28,7 @@ import { SectionForm, useSectionFormNav, buildSectionsFromSchema, visibleValues,
27
28
  import { useFormShellState, FormShellContext } from "../../../ui/forms/index.js";
28
29
  import { applyZodIssues } from "../../../ui/forms/apply-zod-issues.js";
29
30
  import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary.js";
31
+ import { DispatchForm } from "./DispatchForm.js";
30
32
  import { PhysicalLocationModal } from "./PhysicalLocationModal.js";
31
33
  import { ROUTES } from "../../../constants/index.js";
32
34
  import { toRecordArray, toCurrency, toStringValue } from "../hooks/useSellerListingData.js";
@@ -37,6 +39,13 @@ import { MediaImage } from "../../media/MediaImage.js";
37
39
  const __O = {
38
40
  yAuto: "overflow-y-auto",
39
41
  };
42
+ /*
43
+ * The hairline that separates each panel section inside the order drawer.
44
+ * Named because the drawer gained its fourth section with D55's balance
45
+ * collector, and four copies of one border declaration is four places a
46
+ * future token rename has to find.
47
+ */
48
+ const SECTION_DIVIDER = "border-t border-[var(--appkit-color-border)]";
40
49
  const DEFAULT_SORT = "-createdAt";
41
50
  const SORT_OPTIONS = [
42
51
  { value: sortBy("createdAt", "DESC"), label: "Newest" },
@@ -96,6 +105,69 @@ const EMI_INSTALLMENT_BADGE_VARIANT = {
96
105
  pending: "warning",
97
106
  overdue: "danger",
98
107
  };
108
+ /**
109
+ * D55 — the seller's half of the pre-order balance.
110
+ *
111
+ * ## 🛑 Opening is an EVENT, and the control has to say so
112
+ *
113
+ * The balance has existed on the order since checkout. What the seller is
114
+ * declaring here is that the GOODS HAVE ARRIVED — a pre-order's premise is
115
+ * that the item does not exist yet, so asking for the balance before then is
116
+ * asking a buyer to fund inventory. The button's label and its blurb both
117
+ * name that, rather than saying "request payment", which would read as a
118
+ * nudge the seller could send at any time.
119
+ *
120
+ * ## Why no Cancel and no confirm dialog
121
+ *
122
+ * Opening takes money NOWHERE and reverses by simply not chasing it — the
123
+ * worst case is a buyer told a week early. A confirmation step on a
124
+ * non-destructive, idempotent action is the dialog people learn to click
125
+ * through, which is what makes the ones on real deletions worthless.
126
+ *
127
+ * ## The states are the seller's, not the buyer's
128
+ *
129
+ * Once opened the seller cannot act again, so every later state is read-only
130
+ * here: "with the buyer", "proof submitted, we are checking it", "paid". The
131
+ * verification is staff-only and deliberately NOT offered — a seller
132
+ * approving payment to themselves is not a review.
133
+ */
134
+ function SellerBalanceBlock({ orderId, apiBase, balance, balanceDue, currency, onOpened, }) {
135
+ const [opening, setOpening] = React.useState(false);
136
+ const [error, setError] = React.useState(null);
137
+ if (balance) {
138
+ const label = balance.status === "paid"
139
+ ? "Balance paid"
140
+ : balance.status === "submitted"
141
+ ? "Balance proof under review"
142
+ : balance.status === "rejected"
143
+ ? "Balance proof rejected — the buyer has been asked to resend"
144
+ : "Balance open — awaiting the buyer";
145
+ return (_jsxs(Div, { children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Pre-order balance" }), _jsxs(Text, { className: "mt-1", color: "muted", size: "xs", children: [formatCurrency(balance.amount, currency), " \u2014 ", label, balance.transactionId ? ` · Ref ${balance.transactionId}` : ""] })] }));
146
+ }
147
+ const handleOpen = async () => {
148
+ setOpening(true);
149
+ setError(null);
150
+ try {
151
+ const res = await fetch(`${apiBase}/${orderId}/open-balance`, {
152
+ method: "POST",
153
+ credentials: "include",
154
+ });
155
+ if (!res.ok) {
156
+ setError("Could not open the balance. Try again, or contact support if it persists.");
157
+ return;
158
+ }
159
+ onOpened();
160
+ }
161
+ catch (err) {
162
+ void normalizeError(err);
163
+ setError("Could not reach the server. Check your connection and try again.");
164
+ }
165
+ finally {
166
+ setOpening(false);
167
+ }
168
+ };
169
+ return (_jsxs(Div, { children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: "Pre-order balance" }), _jsxs(Text, { className: "mt-1", color: "muted", size: "xs", children: [formatCurrency(balanceDue, currency), " is still owed on this order. Open it once the stock is physically with you \u2014 the buyer is notified and gets a week to pay, and the parcel ships when it clears."] }), _jsx(Row, { className: "mt-2", gap: "3", children: _jsx(Button, { variant: "primary", size: "sm", disabled: opening, onClick: handleOpen, children: opening ? "Opening…" : "Stock has arrived — open balance" }) }), error && (_jsx(Div, { textSize: "xs", className: "mt-2 border border-error/20", color: "error", surface: "danger-surface", padding: "inlineSm", rounded: "lg", children: error }))] }));
170
+ }
99
171
  /**
100
172
  * Fetches + renders one order's full detail content (items, address,
101
173
  * payment, EMI, status/tracking update form). Shared by the seller orders
@@ -299,11 +371,31 @@ export function SellerOrderDetailPanel({ orderId, apiBase, onClose, }) {
299
371
  const addrLine = typeof rawAddr === "string" && rawAddr.trim()
300
372
  ? rawAddr.trim()
301
373
  : [addr.addressLine1, addr.city, addr.state, addr.pincode].filter(Boolean).join(", ");
302
- return (_jsxs(_Fragment, { children: [loading && (_jsx(Row, { align: "center", justify: "center", padding: "y-4xl", children: _jsx(Div, { className: "h-6 w-6 animate-spin border-2 border-[var(--appkit-color-primary)] border-t-transparent", rounded: "full" }) })), fetchError && (_jsx(Div, { textSize: "sm", className: "mx-4 mt-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "lg", children: fetchError })), order && !loading && (_jsx(Stack, { gap: "none", children: _jsxs(Stack, { className: `flex-1 ${__O.yAuto}`, gap: "5", padding: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Badge, { variant: STATUS_BADGE_VARIANT[order.status ?? ""] ?? "default", children: order.status ?? "Unknown" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-muted)]", children: toRelativeDate(order.createdAt) })] }), _jsx(OrderProvenanceCard, { sourceContext: order.sourceContext, currency: order.currency }), (order.items ?? []).length > 0 && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text)] mb-2", weight: "semibold", children: "Items" }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: groupOrderItemsByLine(order.items ?? []).map((group, gi) => group.kind === "single" ? (sellerOrderItemRow(group.item, `s-${group.index}`)) : (_jsxs(Div, { children: [_jsxs(Row, { paddingY: "y-xs", padding: "x-sm", align: "center", gap: "sm", surface: "muted", children: [_jsx(Text, { size: "xs", weight: "semibold", children: group.groupTitle ?? "Grouped purchase" }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-muted)]", children: `${group.memberCount} ${group.memberCount === 1 ? "item" : "items"} · one line` })] }), _jsx(Div, { className: "pl-4", children: group.items.map(({ item, index }) => sellerOrderItemRow(item, `m-${index}`)) })] }, `g-${gi}`))) })] })), _jsx(OrderAddonBadges, { order: order, variant: "detail" }), order.giftWrapAddon && (_jsxs(Div, { className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)]", surface: "subtle", padding: "inline", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "\uD83C\uDF81 Gift wrap requested" }), order.giftWrapMessage && (_jsxs(Text, { size: "sm", className: "mt-1 text-[var(--appkit-color-text-muted)]", children: ["\u201C", order.giftWrapMessage, "\u201D"] }))] })), _jsxs(Row, { surface: "muted", padding: "inline", align: "center", justify: "between", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Total" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-primary)]", weight: "bold", children: toCurrency(order.totalAmount ?? order.totalPrice ?? 0) })] }), addrLine && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "mb-1", weight: "semibold", children: "Shipping address" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-muted)]", children: [String(addr.fullName ?? ""), addrLine].filter(Boolean).join(" · ") })] })), order.paymentMethod && (_jsxs(Div, { children: [_jsxs(Row, { align: "center", justify: "between", className: "mb-1", gap: "3", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Payment" }), isManualPaymentMethod(order.paymentMethod) && (_jsx(Badge, { variant: sellerPaymentBadge(order).variant, children: sellerPaymentBadge(order).label }))] }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-muted)]", transform: "capitalize", children: order.paymentMethod }), isManualPaymentMethod(order.paymentMethod) && order.paymentTransactionId && (_jsxs(Text, { size: "xs", color: "muted", className: "mt-1", children: ["UTR: ", order.paymentTransactionId] }))] })), order.emiEnabled && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", padding: "t-md", children: [_jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsxs(Text, { size: "sm", weight: "semibold", children: ["EMI plan ", order.emiTenureMonths ? `· ${order.emiTenureMonths} months` : ""] }), _jsx(Badge, { variant: order.emiComplete ? "success" : "warning", children: order.emiComplete ? "Fully paid" : "In progress" })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsx(Text, { size: "xs", color: "muted", children: "Token collected" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiTokenAmount ?? 0) })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-3", children: [_jsx(Text, { size: "xs", color: "muted", children: "Remaining balance" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiRemainingBalance ?? 0) })] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: (order.emiInstallments ?? []).map((inst) => (_jsxs(Row, { paddingY: "y-xs-tall", padding: "x-sm", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { className: "min-w-0", children: [_jsxs(Text, { size: "sm", weight: "medium", children: ["Installment ", inst.index, " \u00B7 ", toCurrency(inst.amount)] }), _jsx(Text, { size: "xs", color: "muted", children: inst.status === "paid" && inst.paidAt
374
+ return (_jsxs(_Fragment, { children: [loading && (_jsx(Row, { align: "center", justify: "center", padding: "y-4xl", children: _jsx(Div, { className: "h-6 w-6 animate-spin border-2 border-[var(--appkit-color-primary)] border-t-transparent", rounded: "full" }) })), fetchError && (_jsx(Div, { textSize: "sm", className: "mx-4 mt-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "lg", children: fetchError })), order && !loading && (_jsx(Stack, { gap: "none", children: _jsxs(Stack, { className: `flex-1 ${__O.yAuto}`, gap: "5", padding: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Badge, { variant: STATUS_BADGE_VARIANT[order.status ?? ""] ?? "default", children: order.status ?? "Unknown" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-muted)]", children: toRelativeDate(order.createdAt) })] }), _jsx(OrderProvenanceCard, { sourceContext: order.sourceContext, currency: order.currency }), (order.items ?? []).length > 0 && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text)] mb-2", weight: "semibold", children: "Items" }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: groupOrderItemsByLine(order.items ?? []).map((group, gi) => group.kind === "single" ? (sellerOrderItemRow(group.item, `s-${group.index}`)) : (_jsxs(Div, { children: [_jsxs(Row, { paddingY: "y-xs", padding: "x-sm", align: "center", gap: "sm", surface: "muted", children: [_jsx(Text, { size: "xs", weight: "semibold", children: group.groupTitle ?? "Grouped purchase" }), _jsx(Text, { size: "xs", className: "text-[var(--appkit-color-text-muted)]", children: `${group.memberCount} ${group.memberCount === 1 ? "item" : "items"} · one line` })] }), _jsx(Div, { paddingX: "l-md", children: group.items.map(({ item, index }) => sellerOrderItemRow(item, `m-${index}`)) })] }, `g-${gi}`))) })] })), _jsx(OrderAddonBadges, { order: order, variant: "detail" }), order.giftWrapAddon && (_jsxs(Div, { className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)]", surface: "subtle", padding: "inline", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "\uD83C\uDF81 Gift wrap requested" }), order.giftWrapMessage && (_jsxs(Text, { size: "sm", className: "mt-1 text-[var(--appkit-color-text-muted)]", children: ["\u201C", order.giftWrapMessage, "\u201D"] }))] })), _jsxs(Row, { surface: "muted", padding: "inline", align: "center", justify: "between", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Total" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-primary)]", weight: "bold", children: toCurrency(order.totalAmount ?? order.totalPrice ?? 0) })] }), addrLine && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "mb-1", weight: "semibold", children: "Shipping address" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-muted)]", children: [String(addr.fullName ?? ""), addrLine].filter(Boolean).join(" · ") })] })), order.paymentMethod && (_jsxs(Div, { children: [_jsxs(Row, { align: "center", justify: "between", className: "mb-1", gap: "3", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Payment" }), isManualPaymentMethod(order.paymentMethod) && (_jsx(Badge, { variant: sellerPaymentBadge(order).variant, children: sellerPaymentBadge(order).label }))] }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-muted)]", transform: "capitalize", children: order.paymentMethod }), isManualPaymentMethod(order.paymentMethod) && order.paymentTransactionId && (_jsxs(Text, { size: "xs", color: "muted", className: "mt-1", children: ["UTR: ", order.paymentTransactionId] }))] })), order.emiEnabled && (_jsxs(Div, { className: SECTION_DIVIDER, padding: "t-md", children: [_jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsxs(Text, { size: "sm", weight: "semibold", children: ["EMI plan ", order.emiTenureMonths ? `· ${order.emiTenureMonths} months` : ""] }), _jsx(Badge, { variant: order.emiComplete ? "success" : "warning", children: order.emiComplete ? "Fully paid" : "In progress" })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsx(Text, { size: "xs", color: "muted", children: "Token collected" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiTokenAmount ?? 0) })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-3", children: [_jsx(Text, { size: "xs", color: "muted", children: "Remaining balance" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiRemainingBalance ?? 0) })] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: (order.emiInstallments ?? []).map((inst) => (_jsxs(Row, { paddingY: "y-xs-tall", padding: "x-sm", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { className: "min-w-0", children: [_jsxs(Text, { size: "sm", weight: "medium", children: ["Installment ", inst.index, " \u00B7 ", toCurrency(inst.amount)] }), _jsx(Text, { size: "xs", color: "muted", children: inst.status === "paid" && inst.paidAt
303
375
  ? `Paid ${toRelativeDate(inst.paidAt)}`
304
376
  : inst.dueDate
305
377
  ? `Due ${toRelativeDate(inst.dueDate)}`
306
- : "" })] }), _jsxs(Row, { align: "center", gap: "sm", className: "shrink-0", children: [_jsx(Badge, { variant: EMI_INSTALLMENT_BADGE_VARIANT[inst.status] ?? "default", children: inst.status }), inst.status !== "paid" && (_jsx(Button, { action: ACTIONS.STORE["mark-installment-paid"], size: "sm", variant: "outline", isLoading: markingPaidIndex === inst.index, disabled: markingPaidIndex !== null, onClick: () => handleMarkInstallmentPaid(inst.index), children: "Mark paid" }))] })] }, inst.index))) }), emiError && (_jsx(Div, { textSize: "xs", className: "mt-2 border border-error/20", color: "error", surface: "danger-surface", padding: "inlineSm", rounded: "lg", children: emiError }))] })), _jsxs(Stack, { className: "border-t border-[var(--appkit-color-border)]", padding: "t-md", gap: "3", children: [_jsxs(FormShellContext.Provider, { value: form.shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: draft, onChange: (partial) => setDraft((d) => ({ ...d, ...partial })), onSubmit: () => {
378
+ : "" })] }), _jsxs(Row, { align: "center", gap: "sm", className: "shrink-0", children: [_jsx(Badge, { variant: EMI_INSTALLMENT_BADGE_VARIANT[inst.status] ?? "default", children: inst.status }), inst.status !== "paid" && (_jsx(Button, { action: ACTIONS.STORE["mark-installment-paid"], size: "sm", variant: "outline", isLoading: markingPaidIndex === inst.index, disabled: markingPaidIndex !== null, onClick: () => handleMarkInstallmentPaid(inst.index), children: "Mark paid" }))] })] }, inst.index))) }), emiError && (_jsx(Div, { textSize: "xs", className: "mt-2 border border-error/20", color: "error", surface: "danger-surface", padding: "inlineSm", rounded: "lg", children: emiError }))] })), order.sourceContext?.path === "pre-order" &&
379
+ order.sourceContext.balanceDue > 0 && (_jsx(Stack, { className: SECTION_DIVIDER, padding: "t-md", gap: "3", children: _jsx(SellerBalanceBlock, { orderId: orderId, apiBase: apiBase, balance: order.balance, balanceDue: order.sourceContext.balanceDue, currency: order.currency, onOpened: () => {
380
+ showToast("Balance opened — the buyer has been notified", "success");
381
+ void fetch(`${apiBase}/${orderId}`)
382
+ .then((r) => r.json())
383
+ .then((json) => setOrder((json?.data ?? json)))
384
+ .catch((err) => void normalizeError(err));
385
+ } }) })), _jsx(Stack, { className: SECTION_DIVIDER, padding: "t-md", gap: "3", children: _jsx(DispatchForm, { orderId: orderId, trackingNumber: order.trackingNumber, platformBooked: order.platformBookedShipment, onDispatched: () => {
386
+ showToast("Dispatch recorded", "success");
387
+ /*
388
+ * Re-read rather than patching local state: the platform
389
+ * branch's tracking id is produced by the CARRIER, so there
390
+ * is no value here to optimistically write. On that branch
391
+ * the job is still running, so this first re-read correctly
392
+ * shows no tracking number yet.
393
+ */
394
+ void fetch(`${apiBase}/${orderId}`)
395
+ .then((r) => r.json())
396
+ .then((json) => setOrder((json?.data ?? json)))
397
+ .catch((err) => void normalizeError(err));
398
+ } }) }), _jsxs(Stack, { className: SECTION_DIVIDER, padding: "t-md", gap: "3", children: [_jsxs(FormShellContext.Provider, { value: form.shellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(SectionForm, { sections: sections, values: draft, onChange: (partial) => setDraft((d) => ({ ...d, ...partial })), onSubmit: () => {
307
399
  // SectionForm scrolls to the first erroring section and then
308
400
  // submits regardless, so the guard lives here.
309
401
  form.clearErrors();
@@ -5,6 +5,7 @@ export { SellerListingTypeView, SELLER_LISTING_TYPE_SPECS } from "./SellerListin
5
5
  export type { SellerListingTypeViewProps, SellerListingTypeSpec, SellerListingTypeKey, SellerListingRow } from "./SellerListingTypeView";
6
6
  export type { SellerProductsViewProps } from "./SellerProductsView";
7
7
  export { PrintCenterView } from "./PrintCenterView";
8
+ export { DispatchForm } from "./DispatchForm";
8
9
  export { SellerOrdersView, SellerOrderDetailPanel } from "./SellerOrdersView";
9
10
  export type { SellerOrdersViewProps } from "./SellerOrdersView";
10
11
  export { SellerOffersView } from "./SellerOffersView";
@@ -2,6 +2,7 @@ export { SellerDashboardView } from "./SellerDashboardView.js";
2
2
  export { SellerProductsView } from "./SellerProductsView.js";
3
3
  export { SellerListingTypeView, SELLER_LISTING_TYPE_SPECS } from "./SellerListingTypeView.js";
4
4
  export { PrintCenterView } from "./PrintCenterView.js";
5
+ export { DispatchForm } from "./DispatchForm.js";
5
6
  export { SellerOrdersView, SellerOrderDetailPanel } from "./SellerOrdersView.js";
6
7
  export { SellerOffersView } from "./SellerOffersView.js";
7
8
  export { SellerCouponsView } from "./SellerCouponsView.js";
@@ -247,9 +247,10 @@ class OfferRepository extends BaseRepository {
247
247
  trigger: ctx?.trigger ?? "offerRepository.updateStatus",
248
248
  reason: ctx?.reason,
249
249
  note: ctx?.note,
250
- // buyerName/buyerEmail live on this document. `encryptPiiFields` is a
251
- // flat top-level loop and never descends into arrays, so without this
252
- // they would persist in PLAINTEXT inside statusHistory.
250
+ // buyerName/buyerEmail live on this document. `encryptPiiFields` covers
251
+ // only NAMED paths, and nothing declares them under
252
+ // `statusHistory[].changes` — so without this scrub they persist in
253
+ // PLAINTEXT inside the history.
253
254
  piiFields: OFFER_PII_FIELDS,
254
255
  historyField: OFFER_FIELDS.STATUS_HISTORY,
255
256
  truncatedField: OFFER_FIELDS.STATUS_HISTORY_TRUNCATED,
@@ -5,6 +5,7 @@ import { type GenerateOfferIdInput } from "../../../utils/id-generators";
5
5
  import type { BaseDocument } from "../../../_internal/shared/types/base-document";
6
6
  import type { StatusChangeEntry } from "../../../_internal/shared/history/index";
7
7
  import type { Encrypted, Plaintext } from "../../../security/encrypted";
8
+ import type { OfferCoordination } from "../../../_internal/shared/features/offers/coordination";
8
9
  export type OfferStatus = "pending" | "accepted" | "declined" | "countered" | "expired" | "withdrawn" | "paid";
9
10
  /** Runtime-accessible offer status values — use instead of bare string literals. */
10
11
  export declare const OfferStatusValues: {
@@ -50,6 +51,25 @@ export interface OfferDocument extends BaseDocument {
50
51
  acceptedAt?: Date;
51
52
  checkoutDeadline?: Date;
52
53
  respondedAt?: Date;
54
+ /**
55
+ * Present when this offer is a COORDINATED purchase — several people
56
+ * chipping in on one listing.
57
+ *
58
+ * 🛑 A field on the offer, not a new document and not a new lifecycle.
59
+ * Acceptance produces an ordinary `offer-accepted` order; `unitPriceFor`
60
+ * already honours `lockedPrice`, so tax, payout base, refund ceiling and
61
+ * the invoice all follow with no new money term. See
62
+ * `_internal/shared/features/offers/coordination.ts` for the full list of
63
+ * things this deliberately does NOT add.
64
+ *
65
+ * 🛑 No member NAME is stored in here. `encryptPiiFields` CAN reach into an
66
+ * array (`coordination.members[].name`) — the claim that it cannot was
67
+ * wrong — but it covers only what is NAMED, and the near-miss `"name"`
68
+ * reads as covering it while storing plaintext. Members are uids; names
69
+ * resolve at render time, which removes the field rather than classifying
70
+ * it. See `coordination.ts`.
71
+ */
72
+ coordination?: OfferCoordination;
53
73
  /** Set by `markPaid` once the buyer's order exists — makes "paid" auditable. */
54
74
  paidOrderId?: string;
55
75
  paidAt?: Date;
@@ -93,8 +113,9 @@ export interface OfferDocument extends BaseDocument {
93
113
  /**
94
114
  * Fields whose changes are recorded in an offer's `statusHistory`.
95
115
  *
96
- * Excludes `buyerName`/`buyerEmail` (PII — `encryptPiiFields` never descends
97
- * into arrays, so they would persist in plaintext inside the history),
116
+ * Excludes `buyerName`/`buyerEmail` (PII — nothing declares them under
117
+ * `statusHistory[].changes`, and `encryptPiiFields` covers only NAMED paths,
118
+ * so they would persist in plaintext inside the history),
98
119
  * `buyerNote`/`sellerNote` (they ride in the entry's `note`), and
99
120
  * `updatedAt`/`respondedAt` (noise that would burn the FIFO cap).
100
121
  */
@@ -15,8 +15,9 @@ export const OfferStatusValues = {
15
15
  /**
16
16
  * Fields whose changes are recorded in an offer's `statusHistory`.
17
17
  *
18
- * Excludes `buyerName`/`buyerEmail` (PII — `encryptPiiFields` never descends
19
- * into arrays, so they would persist in plaintext inside the history),
18
+ * Excludes `buyerName`/`buyerEmail` (PII — nothing declares them under
19
+ * `statusHistory[].changes`, and `encryptPiiFields` covers only NAMED paths,
20
+ * so they would persist in plaintext inside the history),
20
21
  * `buyerNote`/`sellerNote` (they ride in the entry's `note`), and
21
22
  * `updatedAt`/`respondedAt` (noise that would burn the FIFO cap).
22
23
  */
@@ -72,13 +72,13 @@ export declare const sellerStoreSchema: z.ZodObject<{
72
72
  totalReviews: z.ZodNumber;
73
73
  averageRating: z.ZodOptional<z.ZodNumber>;
74
74
  }, "strip", z.ZodTypeAny, {
75
- itemsSold: number;
76
75
  totalProducts: number;
76
+ itemsSold: number;
77
77
  totalReviews: number;
78
78
  averageRating?: number | undefined;
79
79
  }, {
80
- itemsSold: number;
81
80
  totalProducts: number;
81
+ itemsSold: number;
82
82
  totalReviews: number;
83
83
  averageRating?: number | undefined;
84
84
  }>>;
@@ -96,8 +96,8 @@ export declare const sellerStoreSchema: z.ZodObject<{
96
96
  location?: string | undefined;
97
97
  returnPolicy?: string | undefined;
98
98
  stats?: {
99
- itemsSold: number;
100
99
  totalProducts: number;
100
+ itemsSold: number;
101
101
  totalReviews: number;
102
102
  averageRating?: number | undefined;
103
103
  } | undefined;
@@ -128,8 +128,8 @@ export declare const sellerStoreSchema: z.ZodObject<{
128
128
  location?: string | undefined;
129
129
  returnPolicy?: string | undefined;
130
130
  stats?: {
131
- itemsSold: number;
132
131
  totalProducts: number;
132
+ itemsSold: number;
133
133
  totalReviews: number;
134
134
  averageRating?: number | undefined;
135
135
  } | undefined;
@@ -32,10 +32,30 @@ export declare const sellerOrderUpdateSchema: z.ZodEffects<z.ZodObject<{
32
32
  }>;
33
33
  export type SellerOrderUpdateValues = z.infer<typeof sellerOrderUpdateSchema>;
34
34
  /**
35
- * Manual shipment entry. `method` is a literal rather than an enum because
36
- * `ManualShippingProvider` is the only provider — see § "Provider Resolution".
35
+ * Shipment entry — manual, or booked through the platform.
36
+ *
37
+ * 🛑 **`method` was `z.literal("custom")`, and that literal WAS bug 0e.**
38
+ *
39
+ * Its comment said *"a literal rather than an enum because
40
+ * `ManualShippingProvider` is the only provider"* — no longer true:
41
+ * `providers/shipping-shiprocket/` is 570 lines with all six methods, and
42
+ * `providers.config.ts` already resolves it from
43
+ * `siteSettings.shipping.provider`. The seller simply could not EXPRESS
44
+ * "book this through us", so there was no option to offer, and
45
+ * `platformBookedShipment` — which `order-math.ts` gates reimbursement on —
46
+ * had zero writers.
47
+ *
48
+ * A DISCRIMINATED UNION rather than an enum plus optional fields, because
49
+ * the two branches need different things and an optional-everything shape
50
+ * cannot say so:
51
+ *
52
+ * · `custom` — the seller posted it and types the carrier and tracking id.
53
+ * Those are REQUIRED, which is the rule enforced in three places today.
54
+ * · `platform` — we book it, so there is no tracking id to type; it comes
55
+ * back from the carrier. Requiring one here would be asking the seller to
56
+ * invent the thing the booking produces.
37
57
  */
38
- export declare const shipOrderSchema: z.ZodObject<{
58
+ export declare const shipOrderSchema: z.ZodDiscriminatedUnion<"method", [z.ZodObject<{
39
59
  method: z.ZodLiteral<"custom">;
40
60
  shippingCarrier: z.ZodString;
41
61
  trackingNumber: z.ZodString;
@@ -50,7 +70,44 @@ export declare const shipOrderSchema: z.ZodObject<{
50
70
  shippingCarrier: string;
51
71
  trackingUrl: string;
52
72
  method: "custom";
53
- }>;
73
+ }>, z.ZodObject<{
74
+ method: z.ZodLiteral<"platform">;
75
+ pickupAddressId: z.ZodString;
76
+ dimensions: z.ZodObject<{
77
+ weight: z.ZodNumber;
78
+ length: z.ZodNumber;
79
+ width: z.ZodNumber;
80
+ height: z.ZodNumber;
81
+ }, "strip", z.ZodTypeAny, {
82
+ length: number;
83
+ weight: number;
84
+ height: number;
85
+ width: number;
86
+ }, {
87
+ length: number;
88
+ weight: number;
89
+ height: number;
90
+ width: number;
91
+ }>;
92
+ }, "strip", z.ZodTypeAny, {
93
+ pickupAddressId: string;
94
+ method: "platform";
95
+ dimensions: {
96
+ length: number;
97
+ weight: number;
98
+ height: number;
99
+ width: number;
100
+ };
101
+ }, {
102
+ pickupAddressId: string;
103
+ method: "platform";
104
+ dimensions: {
105
+ length: number;
106
+ weight: number;
107
+ height: number;
108
+ width: number;
109
+ };
110
+ }>]>;
54
111
  export type ShipOrderValues = z.infer<typeof shipOrderSchema>;
55
112
  /**
56
113
  * Marking one EMI installment paid. `installmentIndex` is 1-based, matching
@@ -92,15 +92,48 @@ export const sellerOrderUpdateSchema = z
92
92
  * it is also called directly from a server action, where no route ran first.
93
93
  */
94
94
  /**
95
- * Manual shipment entry. `method` is a literal rather than an enum because
96
- * `ManualShippingProvider` is the only provider — see § "Provider Resolution".
95
+ * Shipment entry — manual, or booked through the platform.
96
+ *
97
+ * 🛑 **`method` was `z.literal("custom")`, and that literal WAS bug 0e.**
98
+ *
99
+ * Its comment said *"a literal rather than an enum because
100
+ * `ManualShippingProvider` is the only provider"* — no longer true:
101
+ * `providers/shipping-shiprocket/` is 570 lines with all six methods, and
102
+ * `providers.config.ts` already resolves it from
103
+ * `siteSettings.shipping.provider`. The seller simply could not EXPRESS
104
+ * "book this through us", so there was no option to offer, and
105
+ * `platformBookedShipment` — which `order-math.ts` gates reimbursement on —
106
+ * had zero writers.
107
+ *
108
+ * A DISCRIMINATED UNION rather than an enum plus optional fields, because
109
+ * the two branches need different things and an optional-everything shape
110
+ * cannot say so:
111
+ *
112
+ * · `custom` — the seller posted it and types the carrier and tracking id.
113
+ * Those are REQUIRED, which is the rule enforced in three places today.
114
+ * · `platform` — we book it, so there is no tracking id to type; it comes
115
+ * back from the carrier. Requiring one here would be asking the seller to
116
+ * invent the thing the booking produces.
97
117
  */
98
- export const shipOrderSchema = z.object({
99
- method: z.literal("custom"),
100
- shippingCarrier: z.string().min(1),
101
- trackingNumber: z.string().min(1),
102
- trackingUrl: z.string().url(),
103
- });
118
+ export const shipOrderSchema = z.discriminatedUnion("method", [
119
+ z.object({
120
+ method: z.literal("custom"),
121
+ shippingCarrier: z.string().min(1),
122
+ trackingNumber: z.string().min(1),
123
+ trackingUrl: z.string().url(),
124
+ }),
125
+ z.object({
126
+ method: z.literal("platform"),
127
+ /* The store address the courier collects from. */
128
+ pickupAddressId: z.string().min(1),
129
+ dimensions: z.object({
130
+ weight: z.number().positive().max(50),
131
+ length: z.number().positive().max(200),
132
+ width: z.number().positive().max(200),
133
+ height: z.number().positive().max(200),
134
+ }),
135
+ }),
136
+ ]);
104
137
  /**
105
138
  * Marking one EMI installment paid. `installmentIndex` is 1-based, matching
106
139
  * how the schedule is presented to the buyer.
@@ -43,7 +43,32 @@ export interface ShipmentTotals {
43
43
  export declare const DEFAULT_SHIPMENT_TOTALS: ShipmentTotals;
44
44
  export interface ShipmentDocument extends BaseDocument {
45
45
  shipmentNumber: string;
46
+ /**
47
+ * The supplier as TYPED. Kept, and kept required, for display.
48
+ *
49
+ * 🛑 It is free text, so four shipments from one supplier are four
50
+ * unjoined strings — "Acme Toys", "acme toys", "Acme Toys Ltd", "ACME".
51
+ * Nothing can total what we have spent with anyone, which is the first
52
+ * question anybody asks of procurement data.
53
+ */
46
54
  supplierName: string;
55
+ /**
56
+ * The joinable identity — a `vendors` row.
57
+ *
58
+ * 🛑 OPTIONAL, and that is deliberate rather than a migration left half
59
+ * done. Every existing shipment has only the string, and backfilling by
60
+ * fuzzy-matching names is precisely how two real suppliers get merged into
61
+ * one spend figure that nobody can unpick afterwards. A shipment gains an
62
+ * id when a human links it; until then it is honestly unattributed, and
63
+ * the vendor report can say how many are.
64
+ *
65
+ * 🛑 The NAME is not derived from the vendor row on read. Two reasons: a
66
+ * vendor rename would retroactively rewrite what every past shipment says
67
+ * it was bought from, and the typed string is evidence of what the invoice
68
+ * actually said. Same stance the order item's frozen listing snapshot
69
+ * takes (Part 14).
70
+ */
71
+ supplierId?: string;
47
72
  originCountry?: string;
48
73
  status: ShipmentStatus;
49
74
  trackingNumber?: string;
@@ -39,5 +39,44 @@ export declare class ShippingBoxesRepository extends BaseRepository<ShippingBoxD
39
39
  box: ShippingBoxDocument;
40
40
  created: boolean;
41
41
  }>;
42
+ /**
43
+ * Every preset, retired included — the staff management list.
44
+ *
45
+ * Separate from `listActive` for the same reason the dispute reasons are: the
46
+ * picker must never offer a retired row, and a management view must be able
47
+ * to find one in order to put it back.
48
+ */
49
+ listAll(model: SieveModel): Promise<FirebaseSieveResult<ShippingBoxDocument>>;
50
+ /**
51
+ * Correct a preset's dimensions. **Staff only** — enforced at the route.
52
+ *
53
+ * ## 🛑 This does NOT change any dispatched parcel, by construction
54
+ *
55
+ * The dimensions are COPIED onto the shipment at use time (the schema's own
56
+ * header: *"a preset is a DEFAULT, never a lock"*), so there is no stored
57
+ * reference for this to reach through. That is precisely why staff editing is
58
+ * safe to offer at all: a seller's past declaration is a snapshot, and the
59
+ * courier billed against the figures at dispatch.
60
+ *
61
+ * It is staff-only for the mirror reason — these numbers are what the NEXT
62
+ * seller's dispatch form defaults to, so a wrong edit misdeclares other
63
+ * people's parcels. A seller who finds a preset wrong adds a new one; staff
64
+ * decide whether the shared row changes.
65
+ *
66
+ * 🛑 The NAME is deliberately not editable. The id is `box-<slug of name>`,
67
+ * so renaming would either leave the id disagreeing with the name (the
68
+ * mirror-drift trap) or require a new document, which is just a create. Add
69
+ * the correctly-named preset and retire this one.
70
+ */
71
+ updatePreset(id: string, input: {
72
+ length?: number;
73
+ width?: number;
74
+ height?: number;
75
+ tareWeight?: number;
76
+ }): Promise<void>;
77
+ /** Soft-retire. See the schema for why this is never a delete. */
78
+ retire(id: string): Promise<void>;
79
+ /** Put a retired preset back in the picker. */
80
+ reactivate(id: string): Promise<void>;
42
81
  }
43
82
  export declare const shippingBoxesRepository: ShippingBoxesRepository;
@@ -58,6 +58,61 @@ export class ShippingBoxesRepository extends BaseRepository {
58
58
  await this.createWithId(id, box);
59
59
  return { box, created: true };
60
60
  }
61
+ /**
62
+ * Every preset, retired included — the staff management list.
63
+ *
64
+ * Separate from `listActive` for the same reason the dispute reasons are: the
65
+ * picker must never offer a retired row, and a management view must be able
66
+ * to find one in order to put it back.
67
+ */
68
+ async listAll(model) {
69
+ return this.sieveQuery(model, ShippingBoxesRepository.SIEVE_FIELDS, {
70
+ baseQuery: this.getCollection(),
71
+ defaultPageSize: 20,
72
+ maxPageSize: 50,
73
+ });
74
+ }
75
+ /**
76
+ * Correct a preset's dimensions. **Staff only** — enforced at the route.
77
+ *
78
+ * ## 🛑 This does NOT change any dispatched parcel, by construction
79
+ *
80
+ * The dimensions are COPIED onto the shipment at use time (the schema's own
81
+ * header: *"a preset is a DEFAULT, never a lock"*), so there is no stored
82
+ * reference for this to reach through. That is precisely why staff editing is
83
+ * safe to offer at all: a seller's past declaration is a snapshot, and the
84
+ * courier billed against the figures at dispatch.
85
+ *
86
+ * It is staff-only for the mirror reason — these numbers are what the NEXT
87
+ * seller's dispatch form defaults to, so a wrong edit misdeclares other
88
+ * people's parcels. A seller who finds a preset wrong adds a new one; staff
89
+ * decide whether the shared row changes.
90
+ *
91
+ * 🛑 The NAME is deliberately not editable. The id is `box-<slug of name>`,
92
+ * so renaming would either leave the id disagreeing with the name (the
93
+ * mirror-drift trap) or require a new document, which is just a create. Add
94
+ * the correctly-named preset and retire this one.
95
+ */
96
+ async updatePreset(id, input) {
97
+ const patch = { updatedAt: new Date() };
98
+ if (input.length !== undefined)
99
+ patch.length = clampCm(input.length);
100
+ if (input.width !== undefined)
101
+ patch.width = clampCm(input.width);
102
+ if (input.height !== undefined)
103
+ patch.height = clampCm(input.height);
104
+ if (input.tareWeight !== undefined)
105
+ patch.tareWeight = clampKg(input.tareWeight);
106
+ await this.update(id, patch);
107
+ }
108
+ /** Soft-retire. See the schema for why this is never a delete. */
109
+ async retire(id) {
110
+ await this.update(id, { isActive: false, updatedAt: new Date() });
111
+ }
112
+ /** Put a retired preset back in the picker. */
113
+ async reactivate(id) {
114
+ await this.update(id, { isActive: true, updatedAt: new Date() });
115
+ }
61
116
  }
62
117
  ShippingBoxesRepository.SIEVE_FIELDS = {
63
118
  /*
@@ -27,10 +27,10 @@
27
27
  * cannot dispute without the parcel. The form says so rather than treating it
28
28
  * as a required field to get past.
29
29
  */
30
+ import type { BaseDocument } from "../../../_internal/shared/types/base-document";
30
31
  /** `box-` prefixed slug, so the id reads as what it is. */
31
32
  export declare const SHIPPING_BOX_COLLECTION = "shippingBoxes";
32
- export interface ShippingBoxDocument {
33
- id: string;
33
+ export interface ShippingBoxDocument extends BaseDocument {
34
34
  /** What a seller picks it by: "Standard Beyblade carton", "Poly mailer S". */
35
35
  name: string;
36
36
  /** cm. */
@@ -55,8 +55,6 @@ export interface ShippingBoxDocument {
55
55
  * not vanish mid-dispatch, and a deleted row takes its author with it.
56
56
  */
57
57
  isActive: boolean;
58
- createdAt: Date;
59
- updatedAt: Date;
60
58
  }
61
59
  export declare const SHIPPING_BOX_FIELDS: {
62
60
  readonly ID: "id";
@@ -29,6 +29,24 @@ export declare class StoreRepository extends BaseRepository<StoreDocument> {
29
29
  * Find a store by its URL slug (also the document ID).
30
30
  */
31
31
  findBySlug(storeSlug: string): Promise<StoreDocument | null>;
32
+ /**
33
+ * The slugs of every store paid at shipping rather than at delivery.
34
+ *
35
+ * 🛑 A `Set` of IDS, not the documents. The payout sweep needs only
36
+ * membership, and loading full store documents for a question answered by a
37
+ * string would spend the ~3-round-trip budget (Rule #6) on data it discards.
38
+ *
39
+ * 🛑 An EQUALITY on `trustedSeller == true`, never `!= false`. A Firestore
40
+ * inequality excludes every document that LACKS the field — which is every
41
+ * store, since the flag is new and optional — so `!=` would return the
42
+ * trusted set and nothing else, or everything, depending on which way you
43
+ * wrote it. Both are wrong and neither raises.
44
+ *
45
+ * Bounded at 500: trust is granted by hand, one store at a time, on risk
46
+ * grounds. If this ever saturates, the grant has stopped being deliberate
47
+ * and that is the thing to fix rather than the limit.
48
+ */
49
+ findTrustedStoreIds(): Promise<Set<string>>;
32
50
  /**
33
51
  * Find the store owned by a specific user UID.
34
52
  */