@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.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -29
- package/dist/_internal/client/features/layout/filterNavItems.d.ts +28 -0
- package/dist/_internal/client/features/layout/filterNavItems.js +35 -0
- package/dist/_internal/server/features/ads/booking.d.ts +118 -0
- package/dist/_internal/server/features/ads/booking.js +326 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +51 -11
- package/dist/_internal/server/features/checkout/actions.js +335 -80
- package/dist/_internal/server/features/checkout/expand-order-items.d.ts +1 -2
- package/dist/_internal/server/features/checkout/expand-order-items.js +32 -0
- package/dist/_internal/server/features/checkout/freeze-parts.d.ts +50 -0
- package/dist/_internal/server/features/checkout/freeze-parts.js +94 -0
- package/dist/_internal/server/features/checkout/value-otp-gate.d.ts +55 -0
- package/dist/_internal/server/features/checkout/value-otp-gate.js +60 -0
- package/dist/_internal/server/features/dashboard/attention.d.ts +128 -0
- package/dist/_internal/server/features/dashboard/attention.js +183 -0
- package/dist/_internal/server/features/dashboard/data.d.ts +47 -0
- package/dist/_internal/server/features/dashboard/data.js +82 -0
- package/dist/_internal/server/features/inventory/reservations.d.ts +99 -0
- package/dist/_internal/server/features/inventory/reservations.js +273 -0
- package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
- package/dist/_internal/server/features/offers/destinations.js +65 -0
- package/dist/_internal/server/features/orders/adapters.js +21 -0
- package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
- package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
- package/dist/_internal/server/features/orders/balance.d.ts +89 -0
- package/dist/_internal/server/features/orders/balance.js +256 -0
- package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
- package/dist/_internal/server/features/pricing/actions.js +144 -0
- package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
- package/dist/_internal/server/features/shipping/book-split.js +130 -0
- package/dist/_internal/server/features/shipping/book.d.ts +61 -0
- package/dist/_internal/server/features/shipping/book.js +176 -0
- package/dist/_internal/server/features/shipping/courier-block.js +26 -1
- package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
- package/dist/_internal/server/features/site-settings/adapters.js +14 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +14 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +57 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +2 -0
- package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +4 -1
- package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
- package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +15 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +1 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +268 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +31 -2
- package/dist/_internal/shared/checkout/order-math.js +22 -36
- package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
- package/dist/_internal/shared/checkout/rules/index.js +1 -1
- package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
- package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
- package/dist/_internal/shared/checkout/split.d.ts +0 -12
- package/dist/_internal/shared/checkout/split.js +22 -2
- package/dist/_internal/shared/content/disclaimers.js +7 -7
- package/dist/_internal/shared/content/published-values.d.ts +6 -1
- package/dist/_internal/shared/content/published-values.js +8 -0
- package/dist/_internal/shared/features/admin/integrations.d.ts +32 -2
- package/dist/_internal/shared/features/admin/integrations.js +19 -9
- package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
- package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
- package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
- package/dist/_internal/shared/features/emi/choice.js +69 -0
- package/dist/_internal/shared/features/emi/group.d.ts +84 -0
- package/dist/_internal/shared/features/emi/group.js +126 -0
- package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
- package/dist/_internal/shared/features/ledger/posting.js +165 -0
- package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
- package/dist/_internal/shared/features/ledger/reports.js +127 -0
- package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
- package/dist/_internal/shared/features/ledger/rules.js +221 -0
- package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
- package/dist/_internal/shared/features/offers/coordination.js +80 -0
- package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
- package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
- package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
- package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
- package/dist/_internal/shared/features/points/accepts.d.ts +1 -8
- package/dist/_internal/shared/features/points/accepts.js +12 -6
- package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
- package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
- package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
- package/dist/_internal/shared/features/shipping/parcel.js +139 -0
- package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
- package/dist/_internal/shared/features/site-settings/fees.js +12 -8
- package/dist/_internal/shared/fees/explain.d.ts +81 -0
- package/dist/_internal/shared/fees/explain.js +101 -0
- package/dist/_internal/shared/history/index.d.ts +1 -1
- package/dist/_internal/shared/history/index.js +1 -1
- package/dist/_internal/shared/history/types.d.ts +7 -1
- package/dist/_internal/shared/history/types.js +25 -0
- package/dist/client.d.ts +11 -1
- package/dist/client.js +13 -0
- package/dist/constants/api-endpoints.d.ts +72 -0
- package/dist/constants/api-endpoints.js +41 -0
- package/dist/constants/field-names.d.ts +41 -0
- package/dist/constants/field-names.js +47 -0
- package/dist/features/account/components/UserOrdersView.js +28 -2
- package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
- package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +16 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +72 -3
- package/dist/features/admin/components/AdminOrdersView.js +20 -1
- package/dist/features/admin/schemas/firestore.d.ts +32 -0
- package/dist/features/ads/schemas/booking-write.d.ts +157 -0
- package/dist/features/ads/schemas/booking-write.js +88 -0
- package/dist/features/ads/schemas/creative-form.d.ts +18 -0
- package/dist/features/ads/schemas/creative-form.js +51 -0
- package/dist/features/ads/schemas/firestore.d.ts +140 -0
- package/dist/features/ads/schemas/firestore.js +109 -0
- package/dist/features/auctions/repository/bid.repository.js +3 -1
- package/dist/features/audit-log/schemas/firestore.d.ts +110 -0
- package/dist/features/audit-log/schemas/firestore.js +74 -0
- package/dist/features/auth/actions/profile-actions.d.ts +69 -1
- package/dist/features/auth/actions/profile-actions.js +62 -1
- package/dist/features/auth/schemas/firestore.d.ts +16 -2
- package/dist/features/auth/schemas/firestore.js +2 -1
- package/dist/features/blog/actions/blog-actions.d.ts +6 -0
- package/dist/features/blog/actions/blog-actions.js +17 -2
- package/dist/features/blog/api/route.js +4 -1
- package/dist/features/blog/columns/index.js +5 -0
- package/dist/features/blog/components/BlogIndexPageView.js +2 -1
- package/dist/features/blog/repository/blog.repository.d.ts +4 -0
- package/dist/features/blog/repository/blog.repository.js +5 -0
- package/dist/features/blog/schemas/blog-form.d.ts +3 -0
- package/dist/features/blog/schemas/blog-form.js +1 -0
- package/dist/features/blog/schemas/firestore.d.ts +38 -1
- package/dist/features/blog/schemas/firestore.js +23 -0
- package/dist/features/blog/schemas/index.d.ts +6 -0
- package/dist/features/blog/schemas/index.js +2 -0
- package/dist/features/blog/types/index.d.ts +2 -0
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +12 -1
- package/dist/features/cart/components/CartPriceBreakdown.js +24 -5
- package/dist/features/cart/repository/cart.repository.d.ts +14 -0
- package/dist/features/cart/repository/cart.repository.js +20 -0
- package/dist/features/cart/schemas/firestore.d.ts +24 -0
- package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
- package/dist/features/dashboard/components/DashboardHubView.js +56 -0
- package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
- package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
- package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
- package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +24 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +28 -0
- package/dist/features/dispute-reasons/schemas/firestore.d.ts +3 -5
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/faq/components/FAQPageContent.js +2 -2
- package/dist/features/gateway-events/schemas/index.d.ts +2 -2
- package/dist/features/homepage/schemas/index.d.ts +8 -8
- package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
- package/dist/features/inventory/schemas/stock-reservation.js +103 -0
- package/dist/features/layout/AppLayoutShell.d.ts +9 -1
- package/dist/features/layout/AppLayoutShell.js +12 -4
- package/dist/features/layout/BottomActions.js +2 -1
- package/dist/features/layout/BottomNavbar.d.ts +10 -2
- package/dist/features/layout/SideActionDock.js +52 -2
- package/dist/features/layout/TitleBarLayout.js +13 -4
- package/dist/features/layout/TitleBarLayout.style.css +59 -0
- package/dist/features/layout/useCtaReveal.d.ts +16 -1
- package/dist/features/layout/useCtaReveal.js +17 -26
- package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
- package/dist/features/ledger/repository/journal.repository.js +312 -0
- package/dist/features/ledger/schemas/expenses.d.ts +87 -0
- package/dist/features/ledger/schemas/expenses.js +27 -0
- package/dist/features/ledger/schemas/firestore.d.ts +183 -0
- package/dist/features/ledger/schemas/firestore.js +69 -0
- package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
- package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
- package/dist/features/orders/components/RaiseDisputeForm.js +19 -11
- package/dist/features/orders/repository/orders.repository.d.ts +28 -1
- package/dist/features/orders/repository/orders.repository.js +60 -6
- package/dist/features/orders/schemas/balance.d.ts +40 -0
- package/dist/features/orders/schemas/balance.js +47 -0
- package/dist/features/orders/schemas/firestore.d.ts +202 -0
- package/dist/features/orders/schemas/firestore.js +43 -0
- package/dist/features/orders/schemas/index.d.ts +2 -2
- package/dist/features/orders/types/index.d.ts +21 -0
- package/dist/features/payments/repository/payout.repository.js +3 -2
- package/dist/features/payments/schemas/firestore.d.ts +92 -1
- package/dist/features/payments/schemas/firestore.js +23 -0
- package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
- package/dist/features/pricing/components/AdminPricingView.js +157 -0
- package/dist/features/products/schemas/firestore.d.ts +26 -1
- package/dist/features/products/schemas/firestore.js +2 -0
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
- package/dist/features/reviews/repository/reviews.repository.js +16 -0
- package/dist/features/scams/repository/scammer.repository.js +2 -1
- package/dist/features/scams/schemas/firestore.d.ts +2 -1
- package/dist/features/scams/schemas/firestore.js +2 -1
- package/dist/features/seller/actions/offer-actions.js +56 -0
- package/dist/features/seller/actions/seller-actions.d.ts +1 -1
- package/dist/features/seller/actions/seller-actions.js +4 -0
- package/dist/features/seller/components/DispatchForm.d.ts +11 -0
- package/dist/features/seller/components/DispatchForm.js +190 -0
- package/dist/features/seller/components/SellerOrdersView.js +94 -2
- package/dist/features/seller/components/index.d.ts +1 -0
- package/dist/features/seller/components/index.js +1 -0
- package/dist/features/seller/repository/offer.repository.js +4 -3
- package/dist/features/seller/schemas/firestore.d.ts +23 -2
- package/dist/features/seller/schemas/firestore.js +3 -2
- package/dist/features/seller/schemas/index.d.ts +4 -4
- package/dist/features/seller/schemas/order-forms.d.ts +61 -4
- package/dist/features/seller/schemas/order-forms.js +41 -8
- package/dist/features/shipments/schemas/firestore.d.ts +25 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +39 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +55 -0
- package/dist/features/shipping-boxes/schemas/firestore.d.ts +2 -4
- package/dist/features/stores/repository/store.repository.d.ts +18 -0
- package/dist/features/stores/repository/store.repository.js +27 -1
- package/dist/features/stores/schemas/firestore.d.ts +41 -0
- package/dist/features/stores/schemas/firestore.js +20 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- package/dist/features/support/repository/support.repository.js +72 -6
- package/dist/features/support/schemas/firestore.d.ts +17 -0
- package/dist/features/support/schemas/index.d.ts +2 -2
- package/dist/index.d.ts +5 -0
- package/dist/index.js +19 -0
- package/dist/next/routing/route-map.d.ts +92 -0
- package/dist/next/routing/route-map.js +53 -0
- package/dist/react/contexts/SessionContext.d.ts +17 -1
- package/dist/schemas/registry.d.ts +22 -16
- package/dist/security/pii-schemas.d.ts +1 -1
- package/dist/security/pii-schemas.js +45 -0
- package/dist/server-entry.d.ts +1 -1
- package/dist/server-entry.js +10 -1
- package/dist/server.d.ts +29 -1
- package/dist/server.js +158 -0
- package/dist/styles.css +349 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Button.d.ts +10 -1
- package/dist/ui/components/Button.js +25 -3
- package/dist/ui/components/Chart.d.ts +63 -0
- package/dist/ui/components/Chart.js +169 -0
- package/dist/ui/components/Chart.style.css +214 -0
- package/dist/ui/components/Disclaimer.js +10 -1
- package/dist/ui/components/OverflowRow.js +54 -2
- package/dist/ui/components/OverflowRow.style.css +38 -0
- package/dist/ui/components/RichTextEditor.js +99 -8
- package/dist/ui/components/chart-spec.d.ts +68 -0
- package/dist/ui/components/chart-spec.js +140 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/components/surface-tokens.d.ts +1 -1
- package/dist/ui/components/surface-tokens.js +9 -0
- package/dist/ui/components/toolbar-collapse.d.ts +13 -0
- package/dist/ui/components/toolbar-collapse.js +10 -1
- package/dist/ui/index.d.ts +6 -0
- package/dist/ui/index.js +3 -0
- package/dist/ui/rich-text/RichText.d.ts +10 -1
- package/dist/ui/rich-text/RichText.js +33 -2
- package/dist/ui/rich-text/RichText.style.css +35 -0
- package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
- package/dist/ui/rich-text/RichTextRenderer.js +34 -2
- package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
- package/dist/ui/rich-text/chart-blocks.js +87 -0
- package/package.json +1 -1
- 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, {
|
|
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 }))] })),
|
|
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`
|
|
251
|
-
//
|
|
252
|
-
//
|
|
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 —
|
|
97
|
-
*
|
|
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 —
|
|
19
|
-
*
|
|
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
|
-
*
|
|
36
|
-
*
|
|
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
|
-
*
|
|
96
|
-
*
|
|
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.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
*/
|