@mohasinac/appkit 4.61.0 → 4.62.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +19 -3
- package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +1 -1
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +1 -1
- package/dist/_internal/server/features/checkout/actions.d.ts +80 -0
- package/dist/_internal/server/features/checkout/actions.js +542 -26
- package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
- package/dist/_internal/server/features/invoicing/allocate.js +162 -0
- package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
- package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
- package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
- package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
- package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
- package/dist/_internal/server/features/invoicing/issue.js +202 -0
- package/dist/_internal/server/features/orders/actions.d.ts +8 -1
- package/dist/_internal/server/features/orders/actions.js +81 -8
- package/dist/_internal/server/features/orders/adapters.js +33 -0
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
- package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
- package/dist/_internal/server/features/points/award.js +46 -4
- package/dist/_internal/server/features/refunds/actions.js +26 -0
- package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
- package/dist/_internal/server/features/shipping/courier-block.js +239 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
- package/dist/_internal/server/features/site-settings/adapters.js +15 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
- package/dist/_internal/server/features/stores/adapters.js +51 -0
- package/dist/_internal/server/functions/scheduled.js +38 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +9 -2
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
- package/dist/_internal/server/jobs/core/index.d.ts +2 -1
- package/dist/_internal/server/jobs/core/index.js +2 -1
- package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
- package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +62 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.js +87 -97
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
- package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
- package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +10 -2
- package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
- package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
- package/dist/_internal/shared/actions/action-registry.js +177 -9
- package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
- package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
- package/dist/_internal/shared/checkout/order-math.d.ts +145 -0
- package/dist/_internal/shared/checkout/order-math.js +173 -0
- package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
- package/dist/_internal/shared/checkout/refundable-base.js +13 -1
- package/dist/_internal/shared/checkout/split.d.ts +87 -0
- package/dist/_internal/shared/checkout/split.js +100 -0
- package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
- package/dist/_internal/shared/content/disclaimers.js +217 -0
- package/dist/_internal/shared/content/published-values.d.ts +88 -0
- package/dist/_internal/shared/content/published-values.js +198 -0
- package/dist/_internal/shared/features/admin/integrations.d.ts +109 -0
- package/dist/_internal/shared/features/admin/integrations.js +253 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
- package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
- package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
- package/dist/_internal/shared/features/points/accepts.d.ts +36 -0
- package/dist/_internal/shared/features/points/accepts.js +43 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
- package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
- package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
- package/dist/_internal/shared/fees/calculator.d.ts +33 -1
- package/dist/_internal/shared/fees/calculator.js +32 -2
- package/dist/_internal/shared/media/limits.d.ts +10 -0
- package/dist/_internal/shared/media/limits.js +10 -0
- package/dist/_internal/shared/types/action-result.js +15 -1
- package/dist/client/api/surface-error.js +16 -2
- package/dist/client/api/useApiMutation.js +17 -1
- package/dist/client.d.ts +12 -1
- package/dist/client.js +31 -1
- package/dist/constants/api-endpoints.d.ts +45 -0
- package/dist/constants/api-endpoints.js +15 -0
- package/dist/constants/field-names.d.ts +18 -0
- package/dist/constants/field-names.js +28 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/shipping.d.ts +45 -0
- package/dist/contracts/shipping.js +15 -0
- package/dist/core/server-action.js +19 -3
- package/dist/errors/error-mapping.js +26 -1
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
- package/dist/features/account/components/UserOffersPanel.js +5 -3
- package/dist/features/account/components/UserOrdersView.js +19 -33
- package/dist/features/admin/components/AdminActionIndexView.js +100 -5
- package/dist/features/admin/components/AdminAddressClustersView.js +21 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +8 -8
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
- package/dist/features/admin/components/AdminCarouselView.js +96 -4
- package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
- package/dist/features/admin/components/AdminFulfillmentView.js +3 -2
- package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
- package/dist/features/admin/components/AdminInvoicingView.js +65 -0
- package/dist/features/admin/components/AdminMediaView.js +36 -3
- package/dist/features/admin/components/AdminNavigationView.js +4 -1
- package/dist/features/admin/components/AdminOffersView.js +2 -1
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +18 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +11 -2
- package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
- package/dist/features/admin/components/AdminOrdersView.js +67 -5
- package/dist/features/admin/components/AdminPaymentClustersView.js +19 -2
- package/dist/features/admin/components/AdminProductEditorView.js +2 -2
- package/dist/features/admin/components/AdminSectionsView.d.ts +10 -0
- package/dist/features/admin/components/AdminSectionsView.js +486 -224
- package/dist/features/admin/components/OwnerQuickActions.js +24 -2
- package/dist/features/admin/components/ReferenceRecordEditor.js +1 -1
- package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
- package/dist/features/admin/constants/filter-tabs.js +6 -0
- package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
- package/dist/features/admin/hooks/useAdminListingData.js +16 -0
- package/dist/features/admin/hooks/useOptimisticRowFlag.d.ts +33 -0
- package/dist/features/admin/hooks/useOptimisticRowFlag.js +87 -0
- package/dist/features/admin/repository/notification.repository.d.ts +15 -0
- package/dist/features/admin/repository/notification.repository.js +32 -0
- package/dist/features/admin/schemas/action-index-form.d.ts +34 -0
- package/dist/features/admin/schemas/action-index-form.js +115 -0
- package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
- package/dist/features/admin/schemas/firestore.d.ts +30 -0
- package/dist/features/admin/schemas/firestore.js +5 -0
- package/dist/features/audit-log/schemas/firestore.d.ts +30 -0
- package/dist/features/audit-log/schemas/firestore.js +15 -0
- package/dist/features/auth/components/RegisterForm.js +2 -1
- package/dist/features/auth/permissions/constants.js +13 -0
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +13 -0
- package/dist/features/cart/components/CartPriceBreakdown.js +6 -2
- package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
- package/dist/features/cart/components/PointsTenderPicker.js +15 -0
- package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
- package/dist/features/cart/components/index.d.ts +2 -0
- package/dist/features/cart/components/index.js +1 -0
- package/dist/features/cart/repository/cart.repository.d.ts +19 -0
- package/dist/features/cart/repository/cart.repository.js +27 -0
- package/dist/features/cart/schemas/firestore.d.ts +92 -0
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +3 -3
- package/dist/features/contact/email.d.ts +27 -10
- package/dist/features/contact/email.js +20 -3
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +38 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +80 -0
- package/dist/features/dispute-reasons/schemas/firestore.d.ts +64 -0
- package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
- package/dist/features/events/components/AdminEventEditorView.js +8 -6
- package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
- package/dist/features/faq/components/FAQAccordion.js +29 -4
- package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
- package/dist/features/invoicing/schemas/firestore.js +45 -0
- package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
- package/dist/features/invoicing/utils/financial-year.js +53 -0
- package/dist/features/jobs/actions/enqueue-job.js +18 -1
- package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
- package/dist/features/jobs/repository/jobs.repository.js +84 -1
- package/dist/features/jobs/schemas/firestore.d.ts +80 -0
- package/dist/features/jobs/schemas/firestore.js +37 -0
- package/dist/features/layout/hooks/useFormBottomActions.js +20 -1
- package/dist/features/lottery/schemas/zod.d.ts +6 -6
- package/dist/features/media/MediaImage.js +22 -1
- package/dist/features/media/upload/ImageUpload.js +1 -1
- package/dist/features/obligations/constants/policies.js +24 -1
- package/dist/features/orders/components/EmiInstallmentsPanel.js +25 -7
- package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
- package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
- package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
- package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
- package/dist/features/orders/components/RaiseDisputeForm.js +111 -0
- package/dist/features/orders/components/RefundRequestView.js +22 -4
- package/dist/features/orders/components/index.d.ts +4 -0
- package/dist/features/orders/components/index.js +2 -0
- package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
- package/dist/features/orders/constants/order-lane-tabs.js +88 -0
- package/dist/features/orders/repository/orders.repository.d.ts +57 -0
- package/dist/features/orders/repository/orders.repository.js +145 -0
- package/dist/features/orders/schemas/firestore.d.ts +196 -1
- package/dist/features/orders/schemas/index.d.ts +60 -12
- package/dist/features/orders/schemas/index.js +12 -0
- package/dist/features/orders/types/index.d.ts +26 -0
- package/dist/features/orders/utils/courier-block.d.ts +25 -0
- package/dist/features/orders/utils/courier-block.js +23 -0
- package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
- package/dist/features/payment-cases/schemas/firestore.js +1 -0
- package/dist/features/products/components/GroupMemberPicker.js +4 -1
- package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
- package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
- package/dist/features/products/components/ProductDetailActions.js +2 -1
- package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
- package/dist/features/products/components/ProductDetailPageView.js +2 -2
- package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
- package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
- package/dist/features/promotions/actions/coupon-stacking.js +21 -2
- package/dist/features/promotions/components/CouponCard.js +13 -0
- package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
- package/dist/features/promotions/constants/coupon-help.js +9 -1
- package/dist/features/promotions/schemas/firestore.d.ts +24 -0
- package/dist/features/promotions/schemas/firestore.js +4 -0
- package/dist/features/promotions/types/index.d.ts +19 -1
- package/dist/features/scams/components/ScamProfileView.js +2 -2
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/search/action-index/tab-entries.js +1 -1
- package/dist/features/seller/actions/seller-actions.js +33 -3
- package/dist/features/seller/components/FulfillmentView.js +3 -2
- package/dist/features/seller/components/SellerAddressesView.js +32 -4
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +62 -3
- package/dist/features/seller/components/SellerBundlesView.js +53 -14
- package/dist/features/seller/components/SellerGroupedListingsView.js +23 -3
- package/dist/features/seller/components/SellerListingTypeView.js +56 -10
- package/dist/features/seller/components/SellerOrdersView.js +129 -27
- package/dist/features/seller/components/SellerPayoutMethodsView.js +33 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +48 -9
- package/dist/features/seller/components/SellerProductsView.js +5 -1
- package/dist/features/seller/components/SellerShippingConfigsView.js +33 -1
- package/dist/features/seller/components/SellerShippingView.js +2 -1
- package/dist/features/seller/components/SellerStoreCategoriesView.js +33 -1
- package/dist/features/seller/components/SellerSupportView.js +69 -2
- package/dist/features/seller/hooks/useSellerListingData.js +25 -0
- package/dist/features/shell/SectionForm.js +20 -16
- package/dist/features/shell/build-sections.js +14 -1
- package/dist/features/shipments/components/AdminShipmentLotItemsView.js +54 -1
- package/dist/features/shipments/components/AdminShipmentProjectionsView.js +3 -2
- package/dist/features/shipments/schemas/validation.d.ts +4 -4
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +43 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +90 -0
- package/dist/features/shipping-boxes/schemas/firestore.d.ts +82 -0
- package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
- package/dist/features/sticker-trace/components/StickerCommitPanel.js +2 -1
- package/dist/features/sticker-trace/components/StickerPrintPanel.js +2 -1
- package/dist/features/sticker-trace/components/StickerRetryPanel.js +24 -4
- package/dist/features/sticker-trace/components/StickerTraceStudio.js +2 -1
- package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
- package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
- package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
- package/dist/features/stores/schemas/firestore.d.ts +71 -0
- package/dist/features/stores/types/index.d.ts +19 -0
- package/dist/features/support/schemas/index.d.ts +6 -6
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +3 -2
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.js +17 -2
- package/dist/next/routing/route-map.d.ts +24 -0
- package/dist/next/routing/route-map.js +19 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
- package/dist/providers/shipping-shiprocket/index.js +84 -1
- package/dist/schemas/registry.d.ts +51 -22
- package/dist/schemas/sieve.d.ts +5 -5
- package/dist/server-entry.d.ts +2 -2
- package/dist/server-entry.js +8 -1
- package/dist/server.d.ts +11 -2
- package/dist/server.js +40 -2
- package/dist/styles.css +64 -80
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +1 -0
- package/dist/tokens/themes/default-light.js +1 -0
- package/dist/tokens/themes/required.d.ts +1 -1
- package/dist/tokens/themes/required.js +1 -0
- package/dist/tokens/tokens.css +15 -0
- package/dist/ui/components/Checkbox.js +10 -2
- package/dist/ui/components/DateInput.js +8 -1
- package/dist/ui/components/Disclaimer.d.ts +19 -0
- package/dist/ui/components/Disclaimer.js +74 -0
- package/dist/ui/components/Input.js +22 -2
- package/dist/ui/components/PaginatedSelect.d.ts +8 -19
- package/dist/ui/components/PaginatedSelect.js +43 -9
- package/dist/ui/components/Radio.js +8 -1
- package/dist/ui/components/Select.js +10 -2
- package/dist/ui/components/TagInput.d.ts +8 -1
- package/dist/ui/components/TagInput.js +4 -3
- package/dist/ui/components/Textarea.js +10 -2
- package/dist/ui/components/tabs-overflow.js +64 -11
- package/dist/ui/forms/FieldCheckbox.js +1 -1
- package/dist/ui/forms/FormErrorList.d.ts +0 -16
- package/dist/ui/forms/FormErrorList.js +34 -1
- package/dist/ui/forms/apply-zod-issues.d.ts +19 -1
- package/dist/ui/forms/apply-zod-issues.js +23 -1
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +6 -0
- package/dist/ui/rich-text/RichText.style.css +48 -79
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +1831 -1822
|
@@ -38,7 +38,24 @@ export const REFUND_COPY = {
|
|
|
38
38
|
acknowledgments: [
|
|
39
39
|
"I understand this refund request, once approved, permanently removes my ability to file any dispute or RMA claim on this order.",
|
|
40
40
|
"I confirm the item has not been used, worn, or damaged by me, and I am returning it in original condition (if a return is required).",
|
|
41
|
-
|
|
41
|
+
/*
|
|
42
|
+
* 🛑 Phase 9 — TOKENS, resolved by `resolvePublishedCopy` at render.
|
|
43
|
+
*
|
|
44
|
+
* This literal was one of the three contradicting owners of the refund
|
|
45
|
+
* timeline: "5–7 business days" here, "5–7 business days via Razorpay"
|
|
46
|
+
* on the policy page (naming a gateway that was not even the one in
|
|
47
|
+
* use), and "3–5 business days" in the FAQ. Three statements of one
|
|
48
|
+
* fact, no reconciliation, and the FAQ one admin-editable so it could
|
|
49
|
+
* drift further after deployment.
|
|
50
|
+
*
|
|
51
|
+
* The caller renders this through `resolvePublishedCopy`, so the number
|
|
52
|
+
* now has exactly one definition. Note this string is an
|
|
53
|
+
* ACKNOWLEDGEMENT the buyer ticks — a token left unresolved here would
|
|
54
|
+
* appear in something they are agreeing to, which is the strongest
|
|
55
|
+
* possible argument for the unknown-token rule leaving `{{…}}` visible
|
|
56
|
+
* rather than blanking it.
|
|
57
|
+
*/
|
|
58
|
+
"I understand the refund amount may take {{refundBusinessDaysMin}}–{{refundBusinessDaysMax}} business days to reflect and that platform fees may be non-recoverable.",
|
|
42
59
|
],
|
|
43
60
|
},
|
|
44
61
|
siblingPayments: {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does this store accept buyer points as part-payment?
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is a function and not a bare field read
|
|
5
|
+
*
|
|
6
|
+
* Decision 3 has two halves: *"the main store always accepts, others opt in"*.
|
|
7
|
+
* The opt-in half is `StoreDocument.acceptsPoints`; the always half is the
|
|
8
|
+
* platform's own storefront, which cannot decline to fund its own programme.
|
|
9
|
+
*
|
|
10
|
+
* 🛑 The platform store is resolved in CODE rather than seeded `true`, because a
|
|
11
|
+
* seeded flag can be switched off by an edit, a reseed, or an admin clicking the
|
|
12
|
+
* wrong toggle — and the one store that must never stop accepting is the one the
|
|
13
|
+
* whole programme is advertised on. `UserPointsView` tells buyers points are
|
|
14
|
+
* spendable; if the only store could opt out, that promise would be a lie that
|
|
15
|
+
* nothing detected.
|
|
16
|
+
*
|
|
17
|
+
* ## 🛑 Absent means NOT accepted
|
|
18
|
+
*
|
|
19
|
+
* Matching `supportsSaleProtection`'s rule on the same document, and for the
|
|
20
|
+
* same reason: the accepting store ABSORBS the redemption against its payout,
|
|
21
|
+
* so defaulting absent to `true` would enrol every existing seller in funding
|
|
22
|
+
* discounts they never agreed to. A tri-state would be worse still — a buyer
|
|
23
|
+
* cannot tell a seller who declined from one who has not been asked.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* The platform's own storefront.
|
|
27
|
+
*
|
|
28
|
+
* Hard-coded deliberately: this is an identity, not a setting. It is the same
|
|
29
|
+
* id `realListingsOnly` and the seed's `REAL_STORE_IDS` already treat as the
|
|
30
|
+
* platform's own.
|
|
31
|
+
*/
|
|
32
|
+
export declare const PLATFORM_STORE_ID = "store-letitrip-official";
|
|
33
|
+
export declare function storeAcceptsPoints(store: {
|
|
34
|
+
id?: string;
|
|
35
|
+
acceptsPoints?: boolean;
|
|
36
|
+
} | null | undefined, storeId?: string): boolean;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does this store accept buyer points as part-payment?
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is a function and not a bare field read
|
|
5
|
+
*
|
|
6
|
+
* Decision 3 has two halves: *"the main store always accepts, others opt in"*.
|
|
7
|
+
* The opt-in half is `StoreDocument.acceptsPoints`; the always half is the
|
|
8
|
+
* platform's own storefront, which cannot decline to fund its own programme.
|
|
9
|
+
*
|
|
10
|
+
* 🛑 The platform store is resolved in CODE rather than seeded `true`, because a
|
|
11
|
+
* seeded flag can be switched off by an edit, a reseed, or an admin clicking the
|
|
12
|
+
* wrong toggle — and the one store that must never stop accepting is the one the
|
|
13
|
+
* whole programme is advertised on. `UserPointsView` tells buyers points are
|
|
14
|
+
* spendable; if the only store could opt out, that promise would be a lie that
|
|
15
|
+
* nothing detected.
|
|
16
|
+
*
|
|
17
|
+
* ## 🛑 Absent means NOT accepted
|
|
18
|
+
*
|
|
19
|
+
* Matching `supportsSaleProtection`'s rule on the same document, and for the
|
|
20
|
+
* same reason: the accepting store ABSORBS the redemption against its payout,
|
|
21
|
+
* so defaulting absent to `true` would enrol every existing seller in funding
|
|
22
|
+
* discounts they never agreed to. A tri-state would be worse still — a buyer
|
|
23
|
+
* cannot tell a seller who declined from one who has not been asked.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* The platform's own storefront.
|
|
27
|
+
*
|
|
28
|
+
* Hard-coded deliberately: this is an identity, not a setting. It is the same
|
|
29
|
+
* id `realListingsOnly` and the seed's `REAL_STORE_IDS` already treat as the
|
|
30
|
+
* platform's own.
|
|
31
|
+
*/
|
|
32
|
+
export const PLATFORM_STORE_ID = "store-letitrip-official";
|
|
33
|
+
export function storeAcceptsPoints(store, storeId) {
|
|
34
|
+
/*
|
|
35
|
+
* The id is taken from either source so a caller holding only an id (the
|
|
36
|
+
* order-group loop) and one holding the document (an admin surface) get the
|
|
37
|
+
* same answer. Mismatched sources would be two rules again.
|
|
38
|
+
*/
|
|
39
|
+
const id = store?.id ?? storeId;
|
|
40
|
+
if (id === PLATFORM_STORE_ID)
|
|
41
|
+
return true;
|
|
42
|
+
return store?.acceptsPoints === true;
|
|
43
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared purchase, S5a — one payer, N destinations.
|
|
3
|
+
*
|
|
4
|
+
* ## What this is, and what it deliberately is not
|
|
5
|
+
*
|
|
6
|
+
* The coordinator buys everything and assigns a destination per item, so one
|
|
7
|
+
* paid order becomes N parcels. That is **~20% of the shared-purchase work for
|
|
8
|
+
* most of its value** (the plan's own assessment), because it needs none of
|
|
9
|
+
* the hard parts: no multi-party payment, no coordinator deposit, no approval
|
|
10
|
+
* cascade, no per-buyer invoicing, no per-buyer GST, and no stock-reservation
|
|
11
|
+
* primitive.
|
|
12
|
+
*
|
|
13
|
+
* 🛑 It is NOT S5b. There is exactly ONE payer and ONE place of supply, which
|
|
14
|
+
* is what keeps the tax, the invoice and the refund path unchanged. The moment
|
|
15
|
+
* a second person pays, every one of those becomes per-buyer and the design is
|
|
16
|
+
* a different design.
|
|
17
|
+
*
|
|
18
|
+
* ## 🛑 Why the fees are SNAPSHOTTED, and the precedent that proves it
|
|
19
|
+
*
|
|
20
|
+
* A coordinator commits to a quoted total. If the handling fee or the
|
|
21
|
+
* per-destination shipping is re-read at settle, a seller can raise it after
|
|
22
|
+
* the quote and the coordinator absorbs the difference.
|
|
23
|
+
*
|
|
24
|
+
* The codebase already has this exact hole and this exact fix: the refund path
|
|
25
|
+
* reads `finalSale` from the ORDER snapshot rather than the live product,
|
|
26
|
+
* because — in its own words — "re-reading the live product would let a seller
|
|
27
|
+
* flip the switch afterwards". D61 generalises it: *any agreement with a gap
|
|
28
|
+
* between "terms agreed" and "money taken" must snapshot its terms at
|
|
29
|
+
* agreement.*
|
|
30
|
+
*
|
|
31
|
+
* So `quoteSharedPurchase` takes the rates as ARGUMENTS and the caller
|
|
32
|
+
* snapshots the result onto the order. There is no settings read in here.
|
|
33
|
+
*/
|
|
34
|
+
/** The two rates, read from the store document at quote time. */
|
|
35
|
+
export interface SharedPurchaseRates {
|
|
36
|
+
allowSharedPurchase?: boolean;
|
|
37
|
+
sharedPurchaseHandlingFee?: number;
|
|
38
|
+
sharedPurchaseExtraShippingPerDestination?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface SharedPurchaseQuoteInput {
|
|
41
|
+
/**
|
|
42
|
+
* Distinct destinations in this order group, INCLUDING the first.
|
|
43
|
+
*
|
|
44
|
+
* 🛑 A count of DISTINCT addresses, not of items. Three items to one address
|
|
45
|
+
* is one parcel and no extra leg; two items to two addresses is two. Passing
|
|
46
|
+
* an item count would charge a coordinator for splitting an order they did
|
|
47
|
+
* not split.
|
|
48
|
+
*/
|
|
49
|
+
destinationCount: number;
|
|
50
|
+
/** The store's snapshotted rates. */
|
|
51
|
+
rates: SharedPurchaseRates;
|
|
52
|
+
}
|
|
53
|
+
export interface SharedPurchaseQuote {
|
|
54
|
+
/** True when this is actually a multi-destination order. */
|
|
55
|
+
isShared: boolean;
|
|
56
|
+
/** Flat handling fee, or 0. */
|
|
57
|
+
handlingFee: number;
|
|
58
|
+
/** Extra shipping for destinations beyond the first, or 0. */
|
|
59
|
+
extraShippingFee: number;
|
|
60
|
+
/** `handlingFee + extraShippingFee`, the figure added to the order total. */
|
|
61
|
+
total: number;
|
|
62
|
+
/**
|
|
63
|
+
* Set when the arrangement cannot be priced — the seller allows it but has
|
|
64
|
+
* not configured a rate.
|
|
65
|
+
*
|
|
66
|
+
* 🛑 The caller MUST act on this rather than treating the fee as ₹0. That is
|
|
67
|
+
* D44 exactly: `shippingUnavailable` existed with its own docstring saying
|
|
68
|
+
* "the caller MUST act on this rather than trusting `shippingFee: 0`", had
|
|
69
|
+
* ZERO consumers, and an `estimate`-mode store shipped free on every path as
|
|
70
|
+
* a result. An unpriced arrangement must be refused, not given away.
|
|
71
|
+
*/
|
|
72
|
+
unpriced?: "handling" | "extra-shipping";
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Price a shared-purchase arrangement for ONE order group.
|
|
76
|
+
*
|
|
77
|
+
* Pure: no settings read, no Firestore, no clock. The rates come in, the
|
|
78
|
+
* figure comes out, and the caller snapshots it.
|
|
79
|
+
*/
|
|
80
|
+
export declare function quoteSharedPurchase(input: SharedPurchaseQuoteInput): SharedPurchaseQuote;
|
|
81
|
+
/**
|
|
82
|
+
* Distinct destinations across a set of lines.
|
|
83
|
+
*
|
|
84
|
+
* 🛑 Lines with NO `destinationAddressId` collapse into one bucket — the
|
|
85
|
+
* order's own address — rather than counting as a destination each. Otherwise
|
|
86
|
+
* an ordinary three-item order would look like three destinations and be
|
|
87
|
+
* charged two extra legs.
|
|
88
|
+
*/
|
|
89
|
+
export declare function countDestinations(lines: readonly {
|
|
90
|
+
destinationAddressId?: string;
|
|
91
|
+
}[]): number;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared purchase, S5a — one payer, N destinations.
|
|
3
|
+
*
|
|
4
|
+
* ## What this is, and what it deliberately is not
|
|
5
|
+
*
|
|
6
|
+
* The coordinator buys everything and assigns a destination per item, so one
|
|
7
|
+
* paid order becomes N parcels. That is **~20% of the shared-purchase work for
|
|
8
|
+
* most of its value** (the plan's own assessment), because it needs none of
|
|
9
|
+
* the hard parts: no multi-party payment, no coordinator deposit, no approval
|
|
10
|
+
* cascade, no per-buyer invoicing, no per-buyer GST, and no stock-reservation
|
|
11
|
+
* primitive.
|
|
12
|
+
*
|
|
13
|
+
* 🛑 It is NOT S5b. There is exactly ONE payer and ONE place of supply, which
|
|
14
|
+
* is what keeps the tax, the invoice and the refund path unchanged. The moment
|
|
15
|
+
* a second person pays, every one of those becomes per-buyer and the design is
|
|
16
|
+
* a different design.
|
|
17
|
+
*
|
|
18
|
+
* ## 🛑 Why the fees are SNAPSHOTTED, and the precedent that proves it
|
|
19
|
+
*
|
|
20
|
+
* A coordinator commits to a quoted total. If the handling fee or the
|
|
21
|
+
* per-destination shipping is re-read at settle, a seller can raise it after
|
|
22
|
+
* the quote and the coordinator absorbs the difference.
|
|
23
|
+
*
|
|
24
|
+
* The codebase already has this exact hole and this exact fix: the refund path
|
|
25
|
+
* reads `finalSale` from the ORDER snapshot rather than the live product,
|
|
26
|
+
* because — in its own words — "re-reading the live product would let a seller
|
|
27
|
+
* flip the switch afterwards". D61 generalises it: *any agreement with a gap
|
|
28
|
+
* between "terms agreed" and "money taken" must snapshot its terms at
|
|
29
|
+
* agreement.*
|
|
30
|
+
*
|
|
31
|
+
* So `quoteSharedPurchase` takes the rates as ARGUMENTS and the caller
|
|
32
|
+
* snapshots the result onto the order. There is no settings read in here.
|
|
33
|
+
*/
|
|
34
|
+
import { roundRupees } from "../../../../utils/number.formatter.js";
|
|
35
|
+
/**
|
|
36
|
+
* Price a shared-purchase arrangement for ONE order group.
|
|
37
|
+
*
|
|
38
|
+
* Pure: no settings read, no Firestore, no clock. The rates come in, the
|
|
39
|
+
* figure comes out, and the caller snapshots it.
|
|
40
|
+
*/
|
|
41
|
+
export function quoteSharedPurchase(input) {
|
|
42
|
+
const none = {
|
|
43
|
+
isShared: false,
|
|
44
|
+
handlingFee: 0,
|
|
45
|
+
extraShippingFee: 0,
|
|
46
|
+
total: 0,
|
|
47
|
+
};
|
|
48
|
+
/*
|
|
49
|
+
* One destination is an ordinary order. Checked BEFORE the allow flag, so a
|
|
50
|
+
* seller who has not opted in is unaffected by this code path entirely —
|
|
51
|
+
* rather than being refused an order they were always able to take.
|
|
52
|
+
*/
|
|
53
|
+
const destinations = Math.max(1, Math.floor(input.destinationCount));
|
|
54
|
+
if (destinations <= 1)
|
|
55
|
+
return none;
|
|
56
|
+
if (input.rates.allowSharedPurchase !== true) {
|
|
57
|
+
/*
|
|
58
|
+
* 🛑 Returns `isShared: false` rather than throwing, and the CALLER
|
|
59
|
+
* refuses. This function prices; it does not authorise. A thrown error
|
|
60
|
+
* here would be caught by a preview path and rendered as a broken page,
|
|
61
|
+
* where the correct behaviour is to tell the buyer this seller does not
|
|
62
|
+
* split orders.
|
|
63
|
+
*/
|
|
64
|
+
return none;
|
|
65
|
+
}
|
|
66
|
+
const handling = input.rates.sharedPurchaseHandlingFee;
|
|
67
|
+
const perExtra = input.rates.sharedPurchaseExtraShippingPerDestination;
|
|
68
|
+
/*
|
|
69
|
+
* 🛑 `undefined` is UNPRICED, not zero — see the `unpriced` field. A seller
|
|
70
|
+
* who opted in without setting a rate has not agreed to do it for free, and
|
|
71
|
+
* charging ₹0 would hand them the extra labour at their own cost silently.
|
|
72
|
+
*/
|
|
73
|
+
if (typeof handling !== "number" || !Number.isFinite(handling)) {
|
|
74
|
+
return { ...none, isShared: true, unpriced: "handling" };
|
|
75
|
+
}
|
|
76
|
+
if (typeof perExtra !== "number" || !Number.isFinite(perExtra)) {
|
|
77
|
+
return { ...none, isShared: true, unpriced: "extra-shipping" };
|
|
78
|
+
}
|
|
79
|
+
const handlingFee = roundRupees(Math.max(0, handling));
|
|
80
|
+
// BEYOND THE FIRST: the first destination's shipping is what the order would
|
|
81
|
+
// have paid anyway, so only the marginal legs are charged here.
|
|
82
|
+
const extraShippingFee = roundRupees(Math.max(0, perExtra) * (destinations - 1));
|
|
83
|
+
return {
|
|
84
|
+
isShared: true,
|
|
85
|
+
handlingFee,
|
|
86
|
+
extraShippingFee,
|
|
87
|
+
total: roundRupees(handlingFee + extraShippingFee),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Distinct destinations across a set of lines.
|
|
92
|
+
*
|
|
93
|
+
* 🛑 Lines with NO `destinationAddressId` collapse into one bucket — the
|
|
94
|
+
* order's own address — rather than counting as a destination each. Otherwise
|
|
95
|
+
* an ordinary three-item order would look like three destinations and be
|
|
96
|
+
* charged two extra legs.
|
|
97
|
+
*/
|
|
98
|
+
export function countDestinations(lines) {
|
|
99
|
+
const ids = new Set();
|
|
100
|
+
let usesOrderAddress = false;
|
|
101
|
+
for (const line of lines) {
|
|
102
|
+
if (line.destinationAddressId)
|
|
103
|
+
ids.add(line.destinationAddressId);
|
|
104
|
+
else
|
|
105
|
+
usesOrderAddress = true;
|
|
106
|
+
}
|
|
107
|
+
return ids.size + (usesOrderAddress ? 1 : 0);
|
|
108
|
+
}
|
|
@@ -42,6 +42,17 @@ export interface PublicSiteSettings {
|
|
|
42
42
|
chat?: {
|
|
43
43
|
enabled: boolean;
|
|
44
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Is the support desk reachable — read by `UserLayoutClient`, a client
|
|
47
|
+
* component. Narrowed to the one boolean rather than passed by reference,
|
|
48
|
+
* so a field added to the document's `support` group later is not published
|
|
49
|
+
* by accident; `PUBLIC_SITE_SETTINGS_FIELDS` carries the same caveat beside
|
|
50
|
+
* its entry. Optional here because the group is absent on a settings
|
|
51
|
+
* document written before it existed.
|
|
52
|
+
*/
|
|
53
|
+
support?: {
|
|
54
|
+
enabled: boolean;
|
|
55
|
+
};
|
|
45
56
|
/**
|
|
46
57
|
* The browser-side search index kill switch. **ON by default** — the
|
|
47
58
|
* projection emits `indexEnabled: doc.search?.indexEnabled !== false`, so
|
|
@@ -79,6 +79,17 @@ export interface PayoutDeduction {
|
|
|
79
79
|
* plus ₹80 postage" is a sentence; one net figure is not.
|
|
80
80
|
*/
|
|
81
81
|
shippingReimbursement: number;
|
|
82
|
+
/**
|
|
83
|
+
* Rupee value of buyer points this order was settled with, deducted from
|
|
84
|
+
* `netAmount` (D46/D47, decisions 3 and 17 — the accepting store funds the
|
|
85
|
+
* redemption).
|
|
86
|
+
*
|
|
87
|
+
* Returned as its own figure rather than folded into `totalDeduction` for the
|
|
88
|
+
* same reason `shippingReimbursement` is: *"'₹900 less ₹30 commission plus
|
|
89
|
+
* ₹80 postage' is a sentence; one net figure is not."* A seller looking at a
|
|
90
|
+
* short payout needs to see which of the four terms shortened it.
|
|
91
|
+
*/
|
|
92
|
+
pointsFunded: number;
|
|
82
93
|
}
|
|
83
94
|
/**
|
|
84
95
|
* What a seller is actually paid.
|
|
@@ -101,8 +112,29 @@ export interface PayoutDeduction {
|
|
|
101
112
|
*
|
|
102
113
|
* Defaulted to 0, so the three call sites that do not yet pass it keep their
|
|
103
114
|
* current behaviour exactly rather than silently gaining a term.
|
|
115
|
+
*
|
|
116
|
+
* ## 🛑 `pointsFunded` is the mirror of that, and it is a DEDUCTION
|
|
117
|
+
*
|
|
118
|
+
* Decisions 3 and 17: the programme is seller-funded at both ends — the
|
|
119
|
+
* accepting store absorbs the redemption, the originating store bears the
|
|
120
|
+
* earn — and the platform funds neither. So points spent on an order reduce
|
|
121
|
+
* that order's payout by the rupee value spent.
|
|
122
|
+
*
|
|
123
|
+
* It sits in the SAME position as postage and for the inverse reason: outside
|
|
124
|
+
* the commission and gateway bases. The seller agreed to fund a discount, not
|
|
125
|
+
* to pay us a commission on the discount they funded, and a gateway percentage
|
|
126
|
+
* on money no gateway touched is not a card fee at all.
|
|
127
|
+
*
|
|
128
|
+
* 🛑 So `grossAmount` is untouched and the fee figures are IDENTICAL whether
|
|
129
|
+
* points were used or not. That is what keeps the commission the seller is
|
|
130
|
+
* charged a function of the sale rather than of how the buyer chose to settle
|
|
131
|
+
* it — and it is why this cannot be implemented by passing a smaller
|
|
132
|
+
* `grossAmount`, which is the obvious shortcut and silently reduces our own
|
|
133
|
+
* commission by 2% of every redemption.
|
|
134
|
+
*
|
|
135
|
+
* Also defaulted to 0, for the same reason.
|
|
104
136
|
*/
|
|
105
|
-
export declare function computePayoutDeduction(grossAmount: number, commissions: FeeCommissionRates, shippingReimbursement?: number): PayoutDeduction;
|
|
137
|
+
export declare function computePayoutDeduction(grossAmount: number, commissions: FeeCommissionRates, shippingReimbursement?: number, pointsFunded?: number): PayoutDeduction;
|
|
106
138
|
export interface CodHandlingFeeRates {
|
|
107
139
|
/**
|
|
108
140
|
* Rupee CEILING on the COD handling fee. Optional so a `siteSettings`
|
|
@@ -114,8 +114,29 @@ export function allocateCheckoutFees(groupSubtotals, fees) {
|
|
|
114
114
|
*
|
|
115
115
|
* Defaulted to 0, so the three call sites that do not yet pass it keep their
|
|
116
116
|
* current behaviour exactly rather than silently gaining a term.
|
|
117
|
+
*
|
|
118
|
+
* ## 🛑 `pointsFunded` is the mirror of that, and it is a DEDUCTION
|
|
119
|
+
*
|
|
120
|
+
* Decisions 3 and 17: the programme is seller-funded at both ends — the
|
|
121
|
+
* accepting store absorbs the redemption, the originating store bears the
|
|
122
|
+
* earn — and the platform funds neither. So points spent on an order reduce
|
|
123
|
+
* that order's payout by the rupee value spent.
|
|
124
|
+
*
|
|
125
|
+
* It sits in the SAME position as postage and for the inverse reason: outside
|
|
126
|
+
* the commission and gateway bases. The seller agreed to fund a discount, not
|
|
127
|
+
* to pay us a commission on the discount they funded, and a gateway percentage
|
|
128
|
+
* on money no gateway touched is not a card fee at all.
|
|
129
|
+
*
|
|
130
|
+
* 🛑 So `grossAmount` is untouched and the fee figures are IDENTICAL whether
|
|
131
|
+
* points were used or not. That is what keeps the commission the seller is
|
|
132
|
+
* charged a function of the sale rather than of how the buyer chose to settle
|
|
133
|
+
* it — and it is why this cannot be implemented by passing a smaller
|
|
134
|
+
* `grossAmount`, which is the obvious shortcut and silently reduces our own
|
|
135
|
+
* commission by 2% of every redemption.
|
|
136
|
+
*
|
|
137
|
+
* Also defaulted to 0, for the same reason.
|
|
117
138
|
*/
|
|
118
|
-
export function computePayoutDeduction(grossAmount, commissions, shippingReimbursement = 0) {
|
|
139
|
+
export function computePayoutDeduction(grossAmount, commissions, shippingReimbursement = 0, pointsFunded = 0) {
|
|
119
140
|
const platformFee = roundRupees(grossAmount * (commissions.platformFeePercent / 100));
|
|
120
141
|
const gatewayFee = roundRupees(grossAmount * ((commissions.gatewayFeePercent ?? 0) / 100));
|
|
121
142
|
const gstOnFee = roundRupees(platformFee * (commissions.gstPercent / 100));
|
|
@@ -123,7 +144,15 @@ export function computePayoutDeduction(grossAmount, commissions, shippingReimbur
|
|
|
123
144
|
const postage = Number.isFinite(shippingReimbursement)
|
|
124
145
|
? Math.max(0, shippingReimbursement)
|
|
125
146
|
: 0;
|
|
126
|
-
const
|
|
147
|
+
const points = Number.isFinite(pointsFunded) ? Math.max(0, pointsFunded) : 0;
|
|
148
|
+
/*
|
|
149
|
+
* Clamped at zero, as it already was. A redemption cannot exceed 50% of the
|
|
150
|
+
* order total, so it cannot plausibly exceed the net — but a floor here means
|
|
151
|
+
* no caller can ever hand a seller a negative payout, which would be written
|
|
152
|
+
* to a `PayoutDocument.amount` that every downstream sum then treats as a
|
|
153
|
+
* credit against the platform.
|
|
154
|
+
*/
|
|
155
|
+
const netAmount = Math.max(0, roundRupees(grossAmount - totalDeduction + postage - points));
|
|
127
156
|
return {
|
|
128
157
|
platformFee,
|
|
129
158
|
gatewayFee,
|
|
@@ -131,6 +160,7 @@ export function computePayoutDeduction(grossAmount, commissions, shippingReimbur
|
|
|
131
160
|
totalDeduction,
|
|
132
161
|
netAmount,
|
|
133
162
|
shippingReimbursement: roundRupees(postage),
|
|
163
|
+
pointsFunded: roundRupees(points),
|
|
134
164
|
};
|
|
135
165
|
}
|
|
136
166
|
/** Buyer-facing COD handling ceiling, in decimal rupees. */
|
|
@@ -27,6 +27,16 @@ export type MediaKind = "image" | "video" | "pdf";
|
|
|
27
27
|
export declare const PRODUCT_MAX_IMAGES = 24;
|
|
28
28
|
/** Videos per listing. Bounded by `ProductDocument.video` being singular. */
|
|
29
29
|
export declare const PRODUCT_MAX_VIDEOS = 1;
|
|
30
|
+
/**
|
|
31
|
+
* Photos a buyer may attach to one review.
|
|
32
|
+
*
|
|
33
|
+
* 🛑 Deliberately NOT `PRODUCT_MAX_IMAGES`, which is 24 and is a SELLER's
|
|
34
|
+
* ceiling for a whole listing. `audit-media-limit-literals` suggests that
|
|
35
|
+
* constant for any `images: z.array(…).max(n)`, and taking the suggestion
|
|
36
|
+
* here would have quadrupled what a review accepts — the right shape, the
|
|
37
|
+
* wrong number. A review is a few photos of one thing.
|
|
38
|
+
*/
|
|
39
|
+
export declare const REVIEW_MAX_IMAGES = 6;
|
|
30
40
|
/**
|
|
31
41
|
* Highest legal `index` in a product media filename.
|
|
32
42
|
*
|
|
@@ -41,6 +41,16 @@ export const MAX_VIDEO_BYTES = 50 * MEGABYTE;
|
|
|
41
41
|
export const PRODUCT_MAX_IMAGES = 24;
|
|
42
42
|
/** Videos per listing. Bounded by `ProductDocument.video` being singular. */
|
|
43
43
|
export const PRODUCT_MAX_VIDEOS = 1;
|
|
44
|
+
/**
|
|
45
|
+
* Photos a buyer may attach to one review.
|
|
46
|
+
*
|
|
47
|
+
* 🛑 Deliberately NOT `PRODUCT_MAX_IMAGES`, which is 24 and is a SELLER's
|
|
48
|
+
* ceiling for a whole listing. `audit-media-limit-literals` suggests that
|
|
49
|
+
* constant for any `images: z.array(…).max(n)`, and taking the suggestion
|
|
50
|
+
* here would have quadrupled what a review accepts — the right shape, the
|
|
51
|
+
* wrong number. A review is a few photos of one thing.
|
|
52
|
+
*/
|
|
53
|
+
export const REVIEW_MAX_IMAGES = 6;
|
|
44
54
|
/**
|
|
45
55
|
* Highest legal `index` in a product media filename.
|
|
46
56
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { normalizeError } from "../../../errors/normalize.js";
|
|
2
2
|
import { mapToHttpError } from "../../../errors/error-mapping.js";
|
|
3
|
+
import { toApiIssues } from "../../../errors/error-envelope.js";
|
|
3
4
|
import { describeCauseChain } from "../../../errors/describe-cause.js";
|
|
4
5
|
import { reportActionError } from "./action-error-reporter.js";
|
|
5
6
|
/**
|
|
@@ -57,7 +58,20 @@ export async function wrapAction(fn) {
|
|
|
57
58
|
ok: false,
|
|
58
59
|
code: mapped.code,
|
|
59
60
|
error: mapped.message,
|
|
60
|
-
|
|
61
|
+
/*
|
|
62
|
+
* 🛑 `toApiIssues`, not the raw issues — this was the ONE producer in
|
|
63
|
+
* the codebase that skipped the scrubber, and it is the one with 221
|
|
64
|
+
* call sites.
|
|
65
|
+
*
|
|
66
|
+
* A raw `ZodIssue` carries `received`: the value the user actually
|
|
67
|
+
* submitted. On a failed signup, address save or payout-details save
|
|
68
|
+
* that is an email, a phone number or a bank account, echoed straight
|
|
69
|
+
* back into the client payload. `toApiIssues` narrows each issue to
|
|
70
|
+
* `{ message, path?, code? }`, which is exactly why it exists — every
|
|
71
|
+
* other producer (`routeHandler`, `createApiHandler`, the offer
|
|
72
|
+
* actions) already routes through it.
|
|
73
|
+
*/
|
|
74
|
+
...(mapped.issues ? { issues: toApiIssues(mapped.issues) } : {}),
|
|
61
75
|
};
|
|
62
76
|
}
|
|
63
77
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isApiError } from "./ApiError.js";
|
|
2
2
|
import { ApiClientError } from "../../http/ApiClient.js";
|
|
3
|
-
import { applyZodIssues, hasAttachableIssue } from "../../ui/forms/apply-zod-issues.js";
|
|
3
|
+
import { applyZodIssues, hasAttachableIssue, rootLevelMessages } from "../../ui/forms/apply-zod-issues.js";
|
|
4
4
|
import { normalizeError } from "../../errors/normalize.js";
|
|
5
5
|
import { getErrorDisplay, toUserMessage } from "../../errors/error-display-map.js";
|
|
6
6
|
import { HTTP_ERROR_CODES } from "../../errors/error-mapping.js";
|
|
@@ -36,7 +36,21 @@ export function surfaceError(err, opts) {
|
|
|
36
36
|
const issues = issuesOf(err);
|
|
37
37
|
if (setFieldError && hasAttachableIssue(issues)) {
|
|
38
38
|
applyZodIssues(issues, (name, message) => setFieldError(name, message ?? ""));
|
|
39
|
-
|
|
39
|
+
/*
|
|
40
|
+
* 🛑 Root-level issues are NOT covered by the line above and must still be
|
|
41
|
+
* surfaced, or a cross-field rule fails silently.
|
|
42
|
+
*
|
|
43
|
+
* `applyZodIssues` skips any issue with an empty `path` — there is no
|
|
44
|
+
* field to put it on. For a mixed response (one field error plus one
|
|
45
|
+
* cross-field `.refine()`, the shape of "passwords must match" or "end
|
|
46
|
+
* date must be after start date") `hasAttachableIssue` is true, so we used
|
|
47
|
+
* to take this branch and `return` with the refine message discarded: the
|
|
48
|
+
* save was refused and nothing on screen said why.
|
|
49
|
+
*/
|
|
50
|
+
for (const message of rootLevelMessages(issues)) {
|
|
51
|
+
showToast(message, "error");
|
|
52
|
+
}
|
|
53
|
+
return; // field errors inline; only the unattachable ones toast
|
|
40
54
|
}
|
|
41
55
|
if (hasStableErrorCode(err)) {
|
|
42
56
|
const display = getErrorDisplay(err.code);
|
|
@@ -3,6 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import { useMutation, } from "@tanstack/react-query";
|
|
4
4
|
import { useToast } from "../../ui/components/Toast.js";
|
|
5
5
|
import { surfaceError } from "./surface-error.js";
|
|
6
|
+
import { toUserMessage } from "../../errors/error-display-map.js";
|
|
6
7
|
export function useApiMutation(options) {
|
|
7
8
|
const { showToast, updateToast } = useToast();
|
|
8
9
|
const loadingIdRef = React.useRef(null);
|
|
@@ -40,7 +41,22 @@ export function useApiMutation(options) {
|
|
|
40
41
|
onSettled: (data, err, variables, context, ctx) => {
|
|
41
42
|
if (loadingIdRef.current) {
|
|
42
43
|
if (err) {
|
|
43
|
-
|
|
44
|
+
/*
|
|
45
|
+
* 🛑 This was `err instanceof Error ? err.message : "Action failed"`
|
|
46
|
+
* — verbatim the inverted ternary `audit-raw-error-text`'s own
|
|
47
|
+
* header calls "the dominant shape", sitting two lines below the
|
|
48
|
+
* correct `surfaceError` call in the same hook. The authored
|
|
49
|
+
* sentence showed only when the value was NOT an Error, i.e. almost
|
|
50
|
+
* never, so the normal case published the raw message.
|
|
51
|
+
*
|
|
52
|
+
* It was dormant only because `loadingMessage` currently has no
|
|
53
|
+
* call sites; the first consumer to set one would have got two
|
|
54
|
+
* toasts, one of them raw. `updateToast` is also absent from that
|
|
55
|
+
* audit's SINKS list, so nothing would have caught it.
|
|
56
|
+
*/
|
|
57
|
+
updateToast(loadingIdRef.current, "error", toUserMessage(err?.code, undefined, {
|
|
58
|
+
fallback: "Action failed",
|
|
59
|
+
}));
|
|
44
60
|
}
|
|
45
61
|
else if (!successMessage) {
|
|
46
62
|
updateToast(loadingIdRef.current, "success", "Done");
|
package/dist/client.d.ts
CHANGED
|
@@ -283,6 +283,8 @@ export { useLogout, useLogin, useGoogleLogin, useLinkGoogleAccount, useRegister,
|
|
|
283
283
|
export type { LoginCredentials, RegisterData, ForgotPasswordData, ResetPasswordData, VerifyEmailData, ChangeEmailData } from "./features/auth/index";
|
|
284
284
|
export { CartView, CartItemRow, CartGroupLineRow, CartSummary, CartPriceBreakdown, CartDrawer, CheckoutView, CheckoutSuccessView, CheckoutAddressStep, useGuestCart, useCartCount, useAddToCart, useCart, useGuestCartMerge, useCartQuery } from "./features/cart/index";
|
|
285
285
|
export { StoreAddonsPicker, hasAnyStoreAddon } from "./features/cart/index";
|
|
286
|
+
export { PointsTenderPicker } from "./features/cart/index";
|
|
287
|
+
export type { PointsTenderPickerProps } from "./features/cart/index";
|
|
286
288
|
export { clientLineTotal } from "./features/cart/utils/line-total";
|
|
287
289
|
export type { PricedCartLine } from "./features/cart/utils/line-total";
|
|
288
290
|
export type { CartPriceBreakdownData, CartPriceBreakdownStore, StoreAddonsValue, StoreAddonsRates } from "./features/cart/index";
|
|
@@ -389,6 +391,11 @@ export type { UserAccountHubViewProps, UserAccountHubViewLabels, UserOrdersViewP
|
|
|
389
391
|
export { useOrders, useOrder, OrdersList } from "./features/orders/index";
|
|
390
392
|
export { OrderStatusTimeline } from "./features/orders/components/OrderStatusTimeline";
|
|
391
393
|
export type { OrderStatusTimelineProps } from "./features/orders/components/OrderStatusTimeline";
|
|
394
|
+
export { OrderProvenanceCard } from "./features/orders/components/OrderProvenanceCard";
|
|
395
|
+
export type { OrderProvenanceCardProps } from "./features/orders/components/OrderProvenanceCard";
|
|
396
|
+
export { RaiseDisputeForm } from "./features/orders/components/RaiseDisputeForm";
|
|
397
|
+
export type { RaiseDisputeFormProps } from "./features/orders/components/RaiseDisputeForm";
|
|
398
|
+
export type { OrderSourceContext } from "./features/orders/schemas/firestore";
|
|
392
399
|
export { PROTECTION_CLAIM_REASON_LABEL } from "./features/protection-claims/schemas/firestore";
|
|
393
400
|
export type { ProtectionClaimReason } from "./features/protection-claims/schemas/firestore";
|
|
394
401
|
export { OrderCompletionPanel } from "./features/orders/components/OrderCompletionPanel";
|
|
@@ -455,7 +462,7 @@ export type { OwnerQuickActionsProps, OwnerQuickActionsResource } from "./featur
|
|
|
455
462
|
export type { StoreListingEntitlements } from "./features/seller/hooks/useStoreListingEntitlements";
|
|
456
463
|
export type { ListingTypePlugin } from "./_internal/shared/listing-types/_registry";
|
|
457
464
|
export type { ListingType } from "./features/products/types/index";
|
|
458
|
-
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits";
|
|
465
|
+
export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, REVIEW_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits";
|
|
459
466
|
export type { MediaKind, AllowedImageMime, AllowedVideoMime, AllowedDocMime, AllowedMime, } from "./_internal/shared/media/limits";
|
|
460
467
|
export { ScamAwarenessModal } from "./features/scams/components/ScamAwarenessModal";
|
|
461
468
|
export type { ScamAwarenessModalProps } from "./features/scams/components/ScamAwarenessModal";
|
|
@@ -586,6 +593,10 @@ export type { SellerSupportViewProps } from "./features/seller/components/Seller
|
|
|
586
593
|
export { REFUND_COPY } from "./_internal/shared/features/orders/refund-copy";
|
|
587
594
|
export { RETURN_REASON, FINAL_SALE_EXEMPT_REASONS, RETURN_REASON_LABEL, RETURN_REASON_GROUP_LABEL, isFinalSaleExempt, isReturnReason, selectableReturnReasons, } from "./_internal/shared/features/orders/return-reasons";
|
|
588
595
|
export type { ReturnReason } from "./_internal/shared/features/orders/return-reasons";
|
|
596
|
+
export { DISPUTE_KIND, DISPUTE_KIND_LABEL, DISPUTE_GATE, DISPUTE_TRANSIT_WINDOW_DAYS, DISPUTE_NOT_REACHED_GRACE_DAYS, isDisputeKind, checkDisputeGate, resolveDisputeEtd, disputeNotReachedFilableFrom, } from "./_internal/shared/features/orders/dispute-reasons";
|
|
597
|
+
export type { DisputeKind, DisputeGateOrder, DisputeGateResult, } from "./_internal/shared/features/orders/dispute-reasons";
|
|
598
|
+
export { INTEGRATIONS, INTEGRATION_ORDER, readIntegrationStates, integrationPatch, allIntegrationsPatch, } from "./_internal/shared/features/admin/integrations";
|
|
599
|
+
export type { IntegrationId, IntegrationDescriptor, IntegrationState, } from "./_internal/shared/features/admin/integrations";
|
|
589
600
|
export { RefundRequestView } from "./features/orders/components/RefundRequestView";
|
|
590
601
|
export type { RefundRequestViewProps, RefundRequestSubmission, } from "./features/orders/components/RefundRequestView";
|
|
591
602
|
export { ORDER_RETURN_WINDOW_DAYS, ORDER_RETURN_WINDOW_MS, ORDER_CANCELLABLE_STATUSES, } from "./_internal/shared/features/orders/config";
|