@mohasinac/appkit 4.62.0 → 4.63.0
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 +129 -9
- package/dist/_internal/server/features/checkout/actions.js +850 -79
- 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/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/offers/destinations.d.ts +40 -0
- package/dist/_internal/server/features/offers/destinations.js +65 -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 +54 -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/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/pricing/actions.d.ts +58 -0
- package/dist/_internal/server/features/pricing/actions.js +144 -0
- package/dist/_internal/server/features/refunds/actions.js +26 -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.d.ts +69 -0
- package/dist/_internal/server/features/shipping/courier-block.js +264 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
- package/dist/_internal/server/features/site-settings/adapters.js +29 -0
- package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
- package/dist/_internal/server/features/stores/adapters.js +65 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +95 -2
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +11 -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/jobRunners.js +45 -0
- 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 +65 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
- 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/runJobOnce.d.ts +48 -0
- package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +25 -2
- 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/onOrderStatusChange.js +17 -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/server/notifications/channel-health.d.ts +26 -3
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +296 -0
- 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 +174 -0
- package/dist/_internal/shared/checkout/order-math.js +159 -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/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 +75 -0
- package/dist/_internal/shared/checkout/split.js +120 -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 +93 -0
- package/dist/_internal/shared/content/published-values.js +206 -0
- package/dist/_internal/shared/features/admin/integrations.d.ts +139 -0
- package/dist/_internal/shared/features/admin/integrations.js +263 -0
- 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/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/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 +29 -0
- package/dist/_internal/shared/features/points/accepts.js +49 -0
- 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/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/fees.d.ts +16 -8
- package/dist/_internal/shared/features/site-settings/fees.js +12 -8
- 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/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 +21 -0
- package/dist/client.js +43 -0
- package/dist/constants/api-endpoints.d.ts +114 -0
- package/dist/constants/api-endpoints.js +55 -0
- package/dist/constants/field-names.d.ts +42 -0
- package/dist/constants/field-names.js +52 -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/features/about/components/AboutView.js +1 -1
- package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
- package/dist/features/account/components/UserOrdersView.js +47 -35
- package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
- package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
- package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
- package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
- package/dist/features/admin/components/AdminInvoicingView.js +65 -0
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +33 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +81 -3
- package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
- package/dist/features/admin/components/AdminOrdersView.js +86 -5
- package/dist/features/admin/components/OwnerQuickActions.js +24 -2
- 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/schemas/admin-user-form.d.ts +2 -2
- package/dist/features/admin/schemas/firestore.d.ts +62 -0
- package/dist/features/admin/schemas/firestore.js +5 -0
- package/dist/features/ads/schemas/booking-write.d.ts +120 -0
- package/dist/features/ads/schemas/booking-write.js +75 -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 +140 -0
- package/dist/features/audit-log/schemas/firestore.js +89 -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/permissions/constants.js +13 -0
- 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 +25 -1
- package/dist/features/cart/components/CartPriceBreakdown.js +29 -6
- 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 +33 -0
- package/dist/features/cart/repository/cart.repository.js +47 -0
- package/dist/features/cart/schemas/firestore.d.ts +116 -0
- package/dist/features/contact/email.d.ts +27 -10
- package/dist/features/contact/email.js +20 -3
- 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 +62 -0
- package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +108 -0
- package/dist/features/dispute-reasons/schemas/firestore.d.ts +62 -0
- package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
- package/dist/features/faq/components/FAQAccordion.js +29 -4
- 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/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/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/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/BalancePaymentPanel.d.ts +12 -0
- package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
- 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 +119 -0
- package/dist/features/orders/components/RefundRequestView.js +19 -1
- 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 +85 -1
- package/dist/features/orders/repository/orders.repository.js +205 -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 +398 -1
- package/dist/features/orders/schemas/firestore.js +43 -0
- package/dist/features/orders/schemas/index.d.ts +56 -8
- package/dist/features/orders/schemas/index.js +12 -0
- package/dist/features/orders/types/index.d.ts +47 -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/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/components/PrizeDrawDetailPageView.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
- package/dist/features/products/components/ProductDetailPageView.js +2 -2
- 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/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/reviews/repository/reviews.repository.d.ts +14 -0
- package/dist/features/reviews/repository/reviews.repository.js +16 -0
- package/dist/features/scams/components/ScamProfileView.js +2 -2
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- 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/search/action-index/tab-entries.js +1 -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 +37 -3
- 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 +221 -27
- package/dist/features/seller/components/index.d.ts +1 -0
- package/dist/features/seller/components/index.js +1 -0
- package/dist/features/seller/hooks/useSellerListingData.js +25 -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/shipments/schemas/validation.d.ts +4 -4
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +82 -0
- package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +145 -0
- package/dist/features/shipping-boxes/schemas/firestore.d.ts +80 -0
- package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
- 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/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 +112 -0
- package/dist/features/stores/schemas/firestore.js +20 -0
- package/dist/features/stores/schemas/index.d.ts +6 -6
- package/dist/features/stores/types/index.d.ts +19 -0
- 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 +8 -8
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +33 -1
- package/dist/next/routing/route-map.d.ts +112 -0
- package/dist/next/routing/route-map.js +63 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
- package/dist/providers/shipping-shiprocket/index.js +84 -1
- package/dist/react/contexts/SessionContext.d.ts +17 -1
- package/dist/schemas/registry.d.ts +69 -34
- package/dist/schemas/sieve.d.ts +2 -2
- package/dist/security/pii-schemas.d.ts +1 -1
- package/dist/security/pii-schemas.js +45 -0
- package/dist/server-entry.d.ts +2 -2
- package/dist/server-entry.js +8 -1
- package/dist/server.d.ts +36 -1
- package/dist/server.js +183 -1
- package/dist/styles.css +412 -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/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.d.ts +19 -0
- package/dist/ui/components/Disclaimer.js +83 -0
- 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/tabs-overflow.js +64 -11
- 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 +8 -0
- package/dist/ui/index.js +9 -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 +83 -79
- 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 +1834 -1822
|
@@ -72,6 +72,29 @@ export interface CouponDocument extends BaseDocument {
|
|
|
72
72
|
discount: DiscountConfig;
|
|
73
73
|
bxgy?: BXGYConfig;
|
|
74
74
|
tiers?: TieredDiscount[];
|
|
75
|
+
/**
|
|
76
|
+
* D51 — percentage of the amount PAID that a `points_earn` coupon awards as
|
|
77
|
+
* points. Meaningless, and ignored, on every other type.
|
|
78
|
+
*
|
|
79
|
+
* ## 🛑 Why this is not `discount.value`
|
|
80
|
+
*
|
|
81
|
+
* `discount` is read by `computeGroupCouponDiscount` to reduce the order.
|
|
82
|
+
* Overloading its `value` to mean "percent awarded as points" would make a
|
|
83
|
+
* points coupon discount the order by the same number it was supposed to
|
|
84
|
+
* credit — the exact overloading that `bxgy` and `tiers` are separate fields
|
|
85
|
+
* to avoid.
|
|
86
|
+
*
|
|
87
|
+
* `discount` stays REQUIRED on the interface because every existing coupon
|
|
88
|
+
* has one and widening it to optional would ripple through ~30 readers; a
|
|
89
|
+
* points coupon carries `{ value: 0 }`. The protection that matters is in
|
|
90
|
+
* the discount loop, which skips on TYPE rather than trusting that zero
|
|
91
|
+
* (`coupon-discount.ts`) — bad data cannot bypass a type check.
|
|
92
|
+
*
|
|
93
|
+
* The base is the post-discount amount actually paid in money, which is
|
|
94
|
+
* `POINTS_EARN_PERCENT`'s existing rule; this field exists so a campaign can
|
|
95
|
+
* raise it (the festival double event) without editing a constant.
|
|
96
|
+
*/
|
|
97
|
+
pointsPercent?: number;
|
|
75
98
|
usage: UsageConfig;
|
|
76
99
|
validity: ValidityConfig;
|
|
77
100
|
restrictions: RestrictionsConfig;
|
|
@@ -143,6 +166,7 @@ export declare const COUPON_FIELDS: {
|
|
|
143
166
|
readonly FIXED: CouponType;
|
|
144
167
|
readonly FREE_SHIPPING: CouponType;
|
|
145
168
|
readonly BUY_X_GET_Y: CouponType;
|
|
169
|
+
readonly POINTS_EARN: CouponType;
|
|
146
170
|
};
|
|
147
171
|
readonly SCOPE_VALUES: {
|
|
148
172
|
readonly ADMIN: "admin";
|
|
@@ -48,6 +48,7 @@ export const COUPON_FIELDS = {
|
|
|
48
48
|
FIXED: "fixed",
|
|
49
49
|
FREE_SHIPPING: "free_shipping",
|
|
50
50
|
BUY_X_GET_Y: "buy_x_get_y",
|
|
51
|
+
POINTS_EARN: "points_earn",
|
|
51
52
|
},
|
|
52
53
|
SCOPE_VALUES: {
|
|
53
54
|
ADMIN: "admin",
|
|
@@ -59,6 +60,9 @@ export const COUPON_TYPE_LABELS = {
|
|
|
59
60
|
fixed: "Fixed Amount Discount",
|
|
60
61
|
free_shipping: "Free Shipping",
|
|
61
62
|
buy_x_get_y: "Buy X Get Y",
|
|
63
|
+
// Named for what the buyer gets, not for the mechanism. "Points earn" is
|
|
64
|
+
// our word for it; "Points back" is what appears on their balance.
|
|
65
|
+
points_earn: "Points Back",
|
|
62
66
|
};
|
|
63
67
|
export const DEFAULT_COUPON_DATA = {
|
|
64
68
|
usage: { currentUsage: 0 },
|
|
@@ -1,4 +1,22 @@
|
|
|
1
|
-
export type CouponType = "percentage" | "fixed" | "free_shipping" | "buy_x_get_y"
|
|
1
|
+
export type CouponType = "percentage" | "fixed" | "free_shipping" | "buy_x_get_y"
|
|
2
|
+
/**
|
|
3
|
+
* D51 — a coupon that awards POINTS rather than discounting the order.
|
|
4
|
+
*
|
|
5
|
+
* 🛑 It carries `pointsPercent`, not a discount value, and it must never
|
|
6
|
+
* reach the discount arithmetic: a `points_earn` coupon with a `discount`
|
|
7
|
+
* field read by `computeGroupCouponDiscount` would silently discount the
|
|
8
|
+
* order as well as award points.
|
|
9
|
+
*
|
|
10
|
+
* It exists because `RIPLI05` is a PERMANENT, site-wide coupon, and
|
|
11
|
+
* `detectCouponConflict` permits exactly one admin coupon site-wide — so
|
|
12
|
+
* putting it in the admin slot would make every other platform coupon
|
|
13
|
+
* unusable for as long as it exists. It gets its own stacking bucket.
|
|
14
|
+
*
|
|
15
|
+
* Adding it here is a compile error at `COUPON_TYPE_LABELS`, which is a
|
|
16
|
+
* `Record<CouponType, string>` — that is the point of declaring the union
|
|
17
|
+
* first.
|
|
18
|
+
*/
|
|
19
|
+
| "points_earn";
|
|
2
20
|
export type CouponScope = "admin" | "seller";
|
|
3
21
|
export interface CouponItem {
|
|
4
22
|
id: string;
|
|
@@ -52,6 +52,20 @@ declare class ReviewRepository extends BaseRepository<ReviewDocument> {
|
|
|
52
52
|
findApprovedByStore(storeId: string, limit?: number): Promise<ReviewDocument[]>;
|
|
53
53
|
findByUser(userId: string): Promise<ReviewDocument[]>;
|
|
54
54
|
findApprovedByUser(userId: string): Promise<ReviewDocument[]>;
|
|
55
|
+
/**
|
|
56
|
+
* How many reviews this account has WRITTEN.
|
|
57
|
+
*
|
|
58
|
+
* Deliberately every status and both reviewer roles — it counts the same set
|
|
59
|
+
* `GET /api/user/reviews` lists by default (`findByUser`), so the number in
|
|
60
|
+
* the sidebar and the rows on the page it links to agree by construction. A
|
|
61
|
+
* count that quietly excluded pending reviews would read as the author's
|
|
62
|
+
* work having vanished.
|
|
63
|
+
*
|
|
64
|
+
* An aggregation, not `findByUser(...).length`: that sibling downloads every
|
|
65
|
+
* review the account has ever written to learn how many there are, and the
|
|
66
|
+
* caller here runs on every session profile fetch.
|
|
67
|
+
*/
|
|
68
|
+
countByUser(userId: string): Promise<number>;
|
|
55
69
|
findPending(): Promise<ReviewDocument[]>;
|
|
56
70
|
findByStatus(status: ReviewStatus): Promise<ReviewDocument[]>;
|
|
57
71
|
/**
|
|
@@ -175,6 +175,22 @@ class ReviewRepository extends BaseRepository {
|
|
|
175
175
|
.get();
|
|
176
176
|
return snapshot.docs.map((doc) => this.mapDoc(doc));
|
|
177
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* How many reviews this account has WRITTEN.
|
|
180
|
+
*
|
|
181
|
+
* Deliberately every status and both reviewer roles — it counts the same set
|
|
182
|
+
* `GET /api/user/reviews` lists by default (`findByUser`), so the number in
|
|
183
|
+
* the sidebar and the rows on the page it links to agree by construction. A
|
|
184
|
+
* count that quietly excluded pending reviews would read as the author's
|
|
185
|
+
* work having vanished.
|
|
186
|
+
*
|
|
187
|
+
* An aggregation, not `findByUser(...).length`: that sibling downloads every
|
|
188
|
+
* review the account has ever written to learn how many there are, and the
|
|
189
|
+
* caller here runs on every session profile fetch.
|
|
190
|
+
*/
|
|
191
|
+
async countByUser(userId) {
|
|
192
|
+
return getFirestoreCount(this.getCollection().where(REVIEW_FIELDS.USER_ID, "==", userId));
|
|
193
|
+
}
|
|
178
194
|
async findPending() {
|
|
179
195
|
return this.findBy(REVIEW_FIELDS.STATUS, "pending");
|
|
180
196
|
}
|
|
@@ -4,7 +4,7 @@ import { formatCurrency } from "../../../utils/number.formatter.js";
|
|
|
4
4
|
const CLS_OUTLINE_BTN = "appkit-button appkit-button--outline appkit-button--md flex w-full items-center gap-[var(--appkit-space-2)]";
|
|
5
5
|
import Link from "next/link.js";
|
|
6
6
|
import { Shield, Phone, Wallet, Mail, AlertOctagon, Flag, Eye, Users, Calendar, ExternalLink, FileText, MessageSquare, Link2, } from "lucide-react";
|
|
7
|
-
import { Alert, Anchor, Badge, Breadcrumb, Card, CardBody, CardHeader, Container, Div, EmptyState, Grid, Heading, Main, Row, Section, Span, Stack, Text, } from "../../../ui/index.js";
|
|
7
|
+
import { Alert, Anchor, Badge, Breadcrumb, Card, CardBody, CardHeader, Container, Disclaimer, Div, EmptyState, Grid, Heading, Main, Row, Section, Span, Stack, Text, } from "../../../ui/index.js";
|
|
8
8
|
import { MediaImage } from "../../media/MediaImage.js";
|
|
9
9
|
import { SCAM_PLATFORM_LABELS, SCAMMER_STATUS_LABELS, SOCIAL_PLATFORM_LABELS, CONTEST_TYPE_LABELS, } from "../schemas/firestore.js";
|
|
10
10
|
import { SCAM_TYPE_LABELS, getScamType } from "../constants/scam-types.js";
|
|
@@ -32,7 +32,7 @@ function statusVariant(status) {
|
|
|
32
32
|
}
|
|
33
33
|
// ─── Sub-components ───────────────────────────────────────────────────────────
|
|
34
34
|
function ScammerHeaderCard({ scammer }) {
|
|
35
|
-
return (_jsx(Card, { variant: "elevated", padding: "lg", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", gap: "md", align: "start", wrap: true, children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 1, weight: "bold", size: "2xl", children: scammer.displayNames[0] }), scammer.displayNames.length > 1 && (_jsxs(Text, { variant: "secondary", size: "sm", children: ["Also known as: ", scammer.displayNames.slice(1).join(", ")] }))] }), _jsx(Badge, { variant: statusVariant(scammer.status), children: SCAMMER_STATUS_LABELS[scammer.status] ?? scammer.status })] }), _jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Badge, { variant: "warning", children: SCAM_TYPE_LABELS[scammer.scamType] ?? scammer.scamType }), _jsxs(Badge, { variant: "default", children: ["via ", SCAM_PLATFORM_LABELS[scammer.scamPlatform] ?? scammer.scamPlatform] }), scammer.tags.includes("repeat_offender") && (_jsx(Badge, { variant: "danger", children: "Repeat Offender" }))] }), _jsxs(Row, { gap: "md", wrap: true, children: [_jsxs(Text, { variant: "secondary", layout: "flex", gap: "1.5", size: "sm", children: [_jsx(Eye, { className: "h-4 w-4" }), scammer.views, " views"] }), _jsxs(Text, { variant: "secondary", layout: "flex", gap: "1.5", size: "sm", children: [_jsx(Users, { className: "h-4 w-4" }), scammer.incidentCount + 1, " victim", scammer.incidentCount !== 0 ? "s" : "", " reported"] }), _jsxs(Text, { variant: "secondary", layout: "flex", gap: "1.5", size: "sm", children: [_jsx(Calendar, { className: "h-4 w-4" }), "Reported ", formatDate(scammer.createdAt)] }), scammer.amountLost ? (_jsxs(Text, { className: "text-[color:var(--appkit-color-danger,theme(colors.red.600))]", size: "sm", weight: "medium", children: [formatCurrency(scammer.amountLost), " lost (primary incident)"] })) : null] })] }) }));
|
|
35
|
+
return (_jsx(Card, { variant: "elevated", padding: "lg", children: _jsxs(Stack, { gap: "md", children: [_jsx(Disclaimer, { id: "scam-registry" }), _jsxs(Row, { justify: "between", gap: "md", align: "start", wrap: true, children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 1, weight: "bold", size: "2xl", children: scammer.displayNames[0] }), scammer.displayNames.length > 1 && (_jsxs(Text, { variant: "secondary", size: "sm", children: ["Also known as: ", scammer.displayNames.slice(1).join(", ")] }))] }), _jsx(Badge, { variant: statusVariant(scammer.status), children: SCAMMER_STATUS_LABELS[scammer.status] ?? scammer.status })] }), _jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Badge, { variant: "warning", children: SCAM_TYPE_LABELS[scammer.scamType] ?? scammer.scamType }), _jsxs(Badge, { variant: "default", children: ["via ", SCAM_PLATFORM_LABELS[scammer.scamPlatform] ?? scammer.scamPlatform] }), scammer.tags.includes("repeat_offender") && (_jsx(Badge, { variant: "danger", children: "Repeat Offender" }))] }), _jsxs(Row, { gap: "md", wrap: true, children: [_jsxs(Text, { variant: "secondary", layout: "flex", gap: "1.5", size: "sm", children: [_jsx(Eye, { className: "h-4 w-4" }), scammer.views, " views"] }), _jsxs(Text, { variant: "secondary", layout: "flex", gap: "1.5", size: "sm", children: [_jsx(Users, { className: "h-4 w-4" }), scammer.incidentCount + 1, " victim", scammer.incidentCount !== 0 ? "s" : "", " reported"] }), _jsxs(Text, { variant: "secondary", layout: "flex", gap: "1.5", size: "sm", children: [_jsx(Calendar, { className: "h-4 w-4" }), "Reported ", formatDate(scammer.createdAt)] }), scammer.amountLost ? (_jsxs(Text, { className: "text-[color:var(--appkit-color-danger,theme(colors.red.600))]", size: "sm", weight: "medium", children: [formatCurrency(scammer.amountLost), " lost (primary incident)"] })) : null] })] }) }));
|
|
36
36
|
}
|
|
37
37
|
function ScammerIncidentsSection({ incidents }) {
|
|
38
38
|
return (_jsxs(Stack, { gap: "sm", children: [_jsx(Row, { justify: "between", align: "center", children: _jsxs(Heading, { level: 2, size: "base", weight: "semibold", children: ["Additional Incidents", incidents.length > 0 && ` (${incidents.length})`] }) }), incidents.length === 0 ? (_jsx(EmptyState, { icon: _jsx(FileText, { className: "h-8 w-8" }), title: "No additional incidents yet", description: "Other victims' verified reports linked to this profile will appear here." })) : (_jsx(Stack, { gap: "sm", children: incidents.map((inc) => (_jsx(Card, { variant: "outlined", padding: "md", children: _jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", align: "start", gap: "sm", wrap: true, children: [_jsxs(Row, { gap: "xs", wrap: true, children: [_jsx(Badge, { variant: "warning", children: SCAM_TYPE_LABELS[inc.scamType] ?? inc.scamType }), _jsxs(Badge, { variant: "default", children: ["via ", SCAM_PLATFORM_LABELS[inc.scamPlatform] ?? inc.scamPlatform] })] }), _jsx(Text, { variant: "secondary", size: "xs", children: formatDate(inc.createdAt) })] }), inc.itemInvolved && (_jsxs(Text, { variant: "secondary", size: "xs", children: ["Item: ", inc.itemInvolved] })), inc.amountLost ? (_jsxs(Text, { className: "text-[color:var(--appkit-color-danger,theme(colors.red.600))]", size: "xs", weight: "medium", children: [formatCurrency(inc.amountLost), " lost"] })) : null, _jsx(Text, { variant: "secondary", className: "leading-relaxed", size: "sm", children: inc.description.length > 200
|
|
@@ -7,7 +7,7 @@ const CLS_ICON_SM = "h-3 w-3 shrink-0";
|
|
|
7
7
|
const CLS_ROW_CHEVRON = "h-4 w-4 shrink-0 transition group-hover:text-error text-[var(--appkit-color-text-faint)]";
|
|
8
8
|
const CLS_PILL_LINK = "inline-flex items-center gap-[var(--appkit-space-1)] rounded-full bg-error-surface dark:bg-error-surface px-[var(--appkit-space-2-5)] py-[var(--appkit-space-0-5)] text-[length:var(--appkit-text-xs)] font-medium text-error dark:text-error hover:bg-error-surface transition-colors";
|
|
9
9
|
import { Shield, Phone, Wallet, Mail, ChevronRight, Search } from "lucide-react";
|
|
10
|
-
import { Button, Container, Div, Form, Heading, Main, Section, Select, Text, Grid, Stack, Row, Card, Badge, Alert, EmptyState, Input, } from "../../../ui/index.js";
|
|
10
|
+
import { Button, Container, Disclaimer, Div, Form, Heading, Main, Section, Select, Text, Grid, Stack, Row, Card, Badge, Alert, EmptyState, Input, } from "../../../ui/index.js";
|
|
11
11
|
import { listVerifiedScammers } from "../actions/scam-actions.js";
|
|
12
12
|
import { SCAM_PLATFORM_LABELS } from "../schemas/firestore.js";
|
|
13
13
|
import { SCAM_TYPES, SCAM_TYPE_LABELS } from "../constants/scam-types.js";
|
|
@@ -61,7 +61,7 @@ export async function ScamRegistryView({ searchParams = {} }) {
|
|
|
61
61
|
const qs = p.toString();
|
|
62
62
|
return `/scams${qs ? `?${qs}` : ""}`;
|
|
63
63
|
}
|
|
64
|
-
return (_jsxs(Main, { children: [_jsx(Div, { className: "border-b appkit-alert--warning", rounded: "none", children: _jsx(Container, { padding: "y-xs", size: "xl", className: "", children: _jsxs(Alert, { variant: "warning", compact: true, children: ["All profiles on this page have been verified by our moderation team. If you recognise a scammer not listed here,", " ", _jsx(Link, { href: reportHref, className: "font-medium underline hover:no-underline", children: "report them" }), "."] }) }) }), _jsx(Section, { padding: "y-2xl", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "xl", children: [_jsxs(Row, { justify: "between", gap: "md", align: "end", wrap: true, children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 1, weight: "semibold", size: "3xl", children: "Scam Registry" }), _jsx(Text, { variant: "secondary", size: "sm", children: "
|
|
64
|
+
return (_jsxs(Main, { children: [_jsx(Div, { className: "border-b appkit-alert--warning", rounded: "none", children: _jsx(Container, { padding: "y-xs", size: "xl", className: "", children: _jsxs(Alert, { variant: "warning", compact: true, children: ["All profiles on this page have been verified by our moderation team. If you recognise a scammer not listed here,", " ", _jsx(Link, { href: reportHref, className: "font-medium underline hover:no-underline", children: "report them" }), "."] }) }) }), _jsx(Section, { padding: "y-2xl", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "xl", children: [_jsxs(Row, { justify: "between", gap: "md", align: "end", wrap: true, children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 1, weight: "semibold", size: "3xl", children: "Scam Registry" }), _jsx(Text, { variant: "secondary", size: "sm", children: "Reports of scams in India's collectibles community, submitted by buyers and sellers. Search by name, phone, or UPI." })] }), _jsxs(Link, { href: reportHref, className: "appkit-button appkit-button--danger appkit-button--md shrink-0", children: [_jsx(Shield, { className: "h-4 w-4" }), "Report a Scammer"] })] }), _jsx(Disclaimer, { id: "scam-registry" }), _jsx(Form, { method: "GET", children: _jsxs(Row, { wrap: true, gap: "sm", children: [_jsx(Div, { className: "flex-1 min-w-48", children: _jsx(Input, { type: "search", name: "q", defaultValue: query, placeholder: "Search name, phone, UPI ID, or email\u2026", icon: _jsx(Search, { className: "h-4 w-4" }) }) }), _jsx(Select, { name: "scamType", defaultValue: scamType, options: SCAM_TYPES.map((t) => ({ value: t.id, label: t.label })), placeholder: "All scam types" }), _jsx(Select, { name: "sort", defaultValue: sort, options: SORT_OPTIONS.map((o) => ({ value: o.value, label: o.label })) }), _jsx(Button, { type: "submit", variant: "primary", size: "sm", children: "Search" }), (query || scamType || sort !== "-createdAt") && (_jsx(Link, { href: ROUTES.PUBLIC.SCAMS, className: "rounded-lg border border-[var(--appkit-color-border)] px-[var(--appkit-space-4)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)] hover:bg-surface-hover transition-colors", children: "Clear" }))] }) }), scamType && SCAM_TYPE_LABELS[scamType] && (_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Text, { variant: "secondary", size: "xs", children: "Filtering by:" }), _jsxs(Link, { href: buildHref({ scamType: "" }), className: CLS_PILL_LINK, children: [SCAM_TYPE_LABELS[scamType], _jsx(Span, { "aria-hidden": "true", children: "\u00D7" })] })] })), result.items.length === 0 ? (_jsx(EmptyState, { icon: _jsx(Shield, { className: "h-12 w-12" }), title: query || scamType
|
|
65
65
|
? "No verified scammers matched your filters"
|
|
66
66
|
: "No verified scammers in the registry yet", description: query || scamType
|
|
67
67
|
? "Try a different search or clear your filters."
|
|
@@ -142,7 +142,8 @@ class ScammerRepository extends BaseRepository {
|
|
|
142
142
|
reason: ctx?.reason,
|
|
143
143
|
note: ctx?.note,
|
|
144
144
|
// A scammer profile is built entirely from identifying details, and
|
|
145
|
-
// `encryptPiiFields`
|
|
145
|
+
// `encryptPiiFields` covers a field only where it is NAMED. It CAN
|
|
146
|
+
// reach into an array via `array[].key`, but a bare key does not.
|
|
146
147
|
piiFields: SCAMMER_HISTORY_PII_FIELDS,
|
|
147
148
|
});
|
|
148
149
|
const data = prepareForFirestore(withEntry ?? { ...input, updatedAt: new Date() });
|
|
@@ -403,6 +403,7 @@ export declare const SCAMMER_TRACKED_FIELDS: readonly ["status", "verifiedBy", "
|
|
|
403
403
|
/**
|
|
404
404
|
* PII on this document, for `withHistory`'s scrub. A scammer profile is built
|
|
405
405
|
* ENTIRELY out of identifying details, so this list is long on purpose —
|
|
406
|
-
* `encryptPiiFields`
|
|
406
|
+
* `encryptPiiFields` covers only NAMED paths, and nobody is going to declare
|
|
407
|
+
* `statusHistory[].changes.<key>` for every field that might land there.
|
|
407
408
|
*/
|
|
408
409
|
export declare const SCAMMER_HISTORY_PII_FIELDS: readonly ["name", "displayName", "email", "phone", "phoneNumber", "upiId", "bankAccount", "accountNumber", "reporterEmail", "reporterName"];
|
|
@@ -248,7 +248,8 @@ export const SCAMMER_TRACKED_FIELDS = [
|
|
|
248
248
|
/**
|
|
249
249
|
* PII on this document, for `withHistory`'s scrub. A scammer profile is built
|
|
250
250
|
* ENTIRELY out of identifying details, so this list is long on purpose —
|
|
251
|
-
* `encryptPiiFields`
|
|
251
|
+
* `encryptPiiFields` covers only NAMED paths, and nobody is going to declare
|
|
252
|
+
* `statusHistory[].changes.<key>` for every field that might land there.
|
|
252
253
|
*/
|
|
253
254
|
export const SCAMMER_HISTORY_PII_FIELDS = [
|
|
254
255
|
"name",
|
|
@@ -103,7 +103,7 @@ export const CONSOLIDATED_TAB_SURFACES = [
|
|
|
103
103
|
tab: "navigation",
|
|
104
104
|
portal: "admin",
|
|
105
105
|
sectionPath: "Admin › System › Permissions Toggles",
|
|
106
|
-
requiredPermission: "settings:write",
|
|
106
|
+
requiredPermission: "admin:settings:write",
|
|
107
107
|
},
|
|
108
108
|
];
|
|
109
109
|
export function deriveConsolidatedTabEntries(hrefs) {
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
import { serverLogger } from "../../../monitoring/index.js";
|
|
8
8
|
import { offerRepository } from "../repository/offer.repository.js";
|
|
9
9
|
import { productRepository } from "../../products/repository/products.repository.js";
|
|
10
|
+
import { categoriesRepository } from "../../categories/repository/categories.repository.js";
|
|
11
|
+
import { resolveParcel } from "../../../_internal/shared/features/shipping/parcel.js";
|
|
12
|
+
import { checkDestinations, assertAllServiceable, } from "../../../_internal/server/features/shipping/book-split.js";
|
|
10
13
|
import { ProductStatusValues } from "../../products/schemas/index.js";
|
|
11
14
|
import { sendNotification } from "../../admin/actions/notification-actions.js";
|
|
12
15
|
import { userRepository } from "../../auth/repository/user.repository.js";
|
|
@@ -184,6 +187,59 @@ export async function respondToOffer(userId, input) {
|
|
|
184
187
|
throw new ValidationError(ERROR_MESSAGES.OFFER.EXPIRED);
|
|
185
188
|
let updated;
|
|
186
189
|
if (action === "accept") {
|
|
190
|
+
/*
|
|
191
|
+
* ── S5a: a coordinated offer must be SHIPPABLE before it is binding ──
|
|
192
|
+
*
|
|
193
|
+
* Accepting locks a price and starts the buyer's checkout deadline, so it
|
|
194
|
+
* is the last moment at which "the courier cannot reach one of these
|
|
195
|
+
* addresses" costs nobody anything. After it, the coordinator has paid
|
|
196
|
+
* and the only remedies left are a refund and an apology.
|
|
197
|
+
*
|
|
198
|
+
* 🛑 Deliberately NOT at booking time, which is the obvious place and the
|
|
199
|
+
* wrong one: by then the money is taken, and a four-destination order
|
|
200
|
+
* that fails on the fourth has three parcels the carrier already accepted
|
|
201
|
+
* and no rollback. `assertAllServiceable` still guards the booking path —
|
|
202
|
+
* this is the check that keeps it from ever being reached in that state.
|
|
203
|
+
*
|
|
204
|
+
* 🛑 Costs NOTHING on an ordinary offer. `coordination` is absent on
|
|
205
|
+
* every non-coordinated one, so this whole block is skipped — a check
|
|
206
|
+
* that charges every seller for a feature most never use is how Rule #6's
|
|
207
|
+
* round-trip budget gets spent by accident.
|
|
208
|
+
*
|
|
209
|
+
* 🛑 The weight comes from `resolveParcel`, NOT from a literal. That
|
|
210
|
+
* module's whole argument is that an item weight systematically
|
|
211
|
+
* under-estimates a packed box (a bey is ~30 g loose and ~300 g boxed),
|
|
212
|
+
* and a hard-coded `1` here would be a fourth, worse tier beside the
|
|
213
|
+
* three it defines. The shipment tier is deliberately absent — nothing is
|
|
214
|
+
* packed yet at accept time, which is exactly the case tier two and three
|
|
215
|
+
* exist for.
|
|
216
|
+
*
|
|
217
|
+
* 🛑 **No resolvable weight SKIPS the check** rather than guessing one.
|
|
218
|
+
* The carrier is being asked "can you reach this pincode with a parcel of
|
|
219
|
+
* this size", so an invented weight asks a question about a parcel that
|
|
220
|
+
* does not exist and may refuse a destination that is perfectly
|
|
221
|
+
* reachable. Not checking is honest; the booking path still has
|
|
222
|
+
* `assertAllServiceable`, and a listing with no weight at any tier has a
|
|
223
|
+
* data problem this is not the place to report.
|
|
224
|
+
*/
|
|
225
|
+
const members = offer.coordination?.members ?? [];
|
|
226
|
+
if (members.length > 0) {
|
|
227
|
+
const offerProduct = await productRepository.findById(offer.productId);
|
|
228
|
+
/*
|
|
229
|
+
* 🛑 `categorySlugs[0]` is the LEAF, and `findById` is the right lookup
|
|
230
|
+
* for it. Products carry the full ancestor chain `[leaf, …, root]` and
|
|
231
|
+
* categories are pure slugs (`id === slug`), so this is one get rather
|
|
232
|
+
* than the `where("slug","==")` query `findBySlugAndType` would run —
|
|
233
|
+
* and the leaf is the tier whose packed default is actually about this
|
|
234
|
+
* kind of item. A root's default would be the average of the catalogue.
|
|
235
|
+
*/
|
|
236
|
+
const leafSlug = offerProduct?.categorySlugs?.[0];
|
|
237
|
+
const leaf = leafSlug ? await categoriesRepository.findById(leafSlug) : null;
|
|
238
|
+
const parcel = resolveParcel(null, offerProduct, leaf?.productDefaults ?? null);
|
|
239
|
+
if (parcel) {
|
|
240
|
+
assertAllServiceable(await checkDestinations(members, parcel.weightG / 1000));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
187
243
|
updated = await offerRepository.accept(offerId, offer.offerAmount, sellerNote, new Date(Date.now() + OFFER_CHECKOUT_WINDOW_MS));
|
|
188
244
|
}
|
|
189
245
|
else if (action === "decline") {
|
|
@@ -181,7 +181,7 @@ export declare function listSellerPayouts(userId: string, params?: {
|
|
|
181
181
|
page?: number;
|
|
182
182
|
pageSize?: number;
|
|
183
183
|
}): Promise<{
|
|
184
|
-
items: import("../../payments").
|
|
184
|
+
items: import("../../payments").StorePayoutDocument[];
|
|
185
185
|
total: number;
|
|
186
186
|
page: number;
|
|
187
187
|
pageSize: number;
|
|
@@ -28,7 +28,7 @@ import { couponsRepository } from "../../promotions/repository/coupons.repositor
|
|
|
28
28
|
import { generateStoreSlug } from "../../stores/schemas/firestore.js";
|
|
29
29
|
import { siteSettingsRepository } from "../../admin/repository/site-settings.repository.js";
|
|
30
30
|
import { computePayoutDeduction } from "../../../_internal/shared/fees/calculator.js";
|
|
31
|
-
import { ordersTaxableValue, ordersShippingReimbursement, } from "../../../_internal/shared/checkout/order-math.js";
|
|
31
|
+
import { ordersTaxableValue, ordersShippingReimbursement, ordersPointsBorneByStore, } from "../../../_internal/shared/checkout/order-math.js";
|
|
32
32
|
import { getDefaultCurrency } from "../../../core/baseline-resolver.js";
|
|
33
33
|
import { finalizeStagedMediaUrl, finalizeStagedMediaField, finalizeStagedMediaArray, } from "../../media/finalize.js";
|
|
34
34
|
import { StoreStatusValues } from "../../stores/schemas/firestore.js";
|
|
@@ -249,7 +249,19 @@ async function computeSellerEarnings(sellerId) {
|
|
|
249
249
|
* alone.
|
|
250
250
|
*/
|
|
251
251
|
const shippingReimbursement = ordersShippingReimbursement(eligibleOrders);
|
|
252
|
-
|
|
252
|
+
/*
|
|
253
|
+
* D46/D47 — the points programme, which the STORE funds at both ends
|
|
254
|
+
* (decisions 3 and 17): points issued on these orders plus points spent on
|
|
255
|
+
* them. Deducted outside the commission and gateway bases, for the inverse
|
|
256
|
+
* of postage's reason — the seller agreed to fund a discount, not to pay us a
|
|
257
|
+
* commission on the discount they funded.
|
|
258
|
+
*
|
|
259
|
+
* 🛑 Passed at all FOUR call sites, exactly like `shippingReimbursement`
|
|
260
|
+
* above. `ordersPointsFunded` existed with zero consumers before this, which
|
|
261
|
+
* meant the platform had silently been funding the earn.
|
|
262
|
+
*/
|
|
263
|
+
const pointsFunded = ordersPointsBorneByStore(eligibleOrders);
|
|
264
|
+
const deduction = computePayoutDeduction(grossAmount, siteSettings.commissions, shippingReimbursement, pointsFunded);
|
|
253
265
|
return {
|
|
254
266
|
eligibleOrders,
|
|
255
267
|
grossAmount,
|
|
@@ -257,6 +269,22 @@ async function computeSellerEarnings(sellerId) {
|
|
|
257
269
|
gatewayFee: deduction.gatewayFee,
|
|
258
270
|
gstAmount: deduction.gstOnFee,
|
|
259
271
|
netAmount: deduction.netAmount,
|
|
272
|
+
/*
|
|
273
|
+
* Surfaced so a seller can see WHY a payout is short of
|
|
274
|
+
* `gross − fees`. Two terms now move `netAmount` without appearing in
|
|
275
|
+
* `totalDeduction` — postage up, points down — and a net figure that
|
|
276
|
+
* cannot be reconstructed from the figures beside it reads as an error in
|
|
277
|
+
* our arithmetic rather than as the two things the seller agreed to.
|
|
278
|
+
*
|
|
279
|
+
* 🛑 Returned rather than written to `PayoutDocument`, matching
|
|
280
|
+
* `shippingReimbursement`, which is also not on that schema. One of these
|
|
281
|
+
* should probably be persisted eventually — a settled payout cannot be
|
|
282
|
+
* re-explained from a preview — but adding a document field with no reader
|
|
283
|
+
* is the pattern this very function just finished closing, so it waits for
|
|
284
|
+
* the admin surface that would read it.
|
|
285
|
+
*/
|
|
286
|
+
shippingReimbursement: deduction.shippingReimbursement,
|
|
287
|
+
pointsFunded: deduction.pointsFunded,
|
|
260
288
|
productCount: products.length,
|
|
261
289
|
commissions: siteSettings.commissions,
|
|
262
290
|
};
|
|
@@ -271,6 +299,8 @@ export async function requestPayout(userId, userName, userEmail, input) {
|
|
|
271
299
|
if (earnings.netAmount <= 0 || earnings.eligibleOrders.length === 0)
|
|
272
300
|
throw new ValidationError("No eligible earnings available for payout.");
|
|
273
301
|
const payout = await payoutRepository.createPayout({
|
|
302
|
+
/* A seller requesting their own earnings — a liability settlement. */
|
|
303
|
+
payeeType: "store",
|
|
274
304
|
storeId: userId,
|
|
275
305
|
sellerName: userName,
|
|
276
306
|
sellerEmail: userEmail,
|
|
@@ -359,8 +389,12 @@ export async function bulkSellerOrder(userId, userRole, userDisplayName, userEma
|
|
|
359
389
|
* scheduled job pay differently for the same orders. */
|
|
360
390
|
const grossAmount = ordersTaxableValue(eligible);
|
|
361
391
|
const bulkSiteSettings = await siteSettingsRepository.getSingleton();
|
|
362
|
-
const bulkDeduction = computePayoutDeduction(grossAmount, bulkSiteSettings.commissions, ordersShippingReimbursement(eligible)
|
|
392
|
+
const bulkDeduction = computePayoutDeduction(grossAmount, bulkSiteSettings.commissions, ordersShippingReimbursement(eligible),
|
|
393
|
+
// The store-funded points, both ends — see the preview path above.
|
|
394
|
+
ordersPointsBorneByStore(eligible));
|
|
363
395
|
const payoutDoc = await payoutRepository.createPayout({
|
|
396
|
+
/* A seller requesting their own earnings — a liability settlement. */
|
|
397
|
+
payeeType: "store",
|
|
364
398
|
storeId: userId,
|
|
365
399
|
sellerName: userDisplayName,
|
|
366
400
|
sellerEmail: userEmail,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export declare function DispatchForm({ orderId,
|
|
3
|
+
/** Already dispatched? Then there is nothing to book and nothing to record. */
|
|
4
|
+
trackingNumber,
|
|
5
|
+
/** Set by `bookShipment`; a booked parcel must not be re-booked. */
|
|
6
|
+
platformBooked, onDispatched, }: {
|
|
7
|
+
orderId: string;
|
|
8
|
+
trackingNumber?: string;
|
|
9
|
+
platformBooked?: boolean;
|
|
10
|
+
onDispatched: () => void;
|
|
11
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/*
|
|
4
|
+
* WHY: Bug 0e — a seller could not express "book this through us". The write
|
|
5
|
+
* shape said `method: z.literal("custom")`, so there was no option to
|
|
6
|
+
* offer; `platformBookedShipment`, which `order-math.ts` gates shipping
|
|
7
|
+
* reimbursement on, had ZERO writers.
|
|
8
|
+
*
|
|
9
|
+
* The schema now carries a discriminated union and the server does the
|
|
10
|
+
* booking. This is the control that reaches it.
|
|
11
|
+
*
|
|
12
|
+
* WHAT: One dispatch panel with two branches — post it yourself, or book a
|
|
13
|
+
* pickup on the platform's carrier account.
|
|
14
|
+
*
|
|
15
|
+
* EXPORTS: DispatchForm
|
|
16
|
+
*
|
|
17
|
+
* @tag domain:seller,orders,shipping
|
|
18
|
+
* @tag layer:component
|
|
19
|
+
* @tag pattern:form
|
|
20
|
+
* @tag access:client
|
|
21
|
+
* @tag consumers:SellerOrderDetailPanel
|
|
22
|
+
* @tag sideEffects:network
|
|
23
|
+
*/
|
|
24
|
+
import React, { useState } from "react";
|
|
25
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
|
|
26
|
+
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
27
|
+
import { apiClient, ApiClientError } from "../../../http/index.js";
|
|
28
|
+
import { useApiMutation } from "../../../client/api/useApiMutation.js";
|
|
29
|
+
import { useQuery } from "@tanstack/react-query";
|
|
30
|
+
import { normalizeError } from "../../../errors/normalize.js";
|
|
31
|
+
import { toUserMessage } from "../../../errors/error-display-map.js";
|
|
32
|
+
import { Button } from "../../../ui/components/Button.js";
|
|
33
|
+
import { Div } from "../../../ui/components/Div.js";
|
|
34
|
+
import { Row } from "../../../ui/components/Layout.js";
|
|
35
|
+
import { Stack } from "../../../ui/components/Layout.js";
|
|
36
|
+
import { Text } from "../../../ui/components/Typography.js";
|
|
37
|
+
import { Form } from "../../../ui/components/Form.js";
|
|
38
|
+
import { FieldInput } from "../../../ui/forms/FieldInput.js";
|
|
39
|
+
import { FieldSelect } from "../../../ui/forms/FieldSelect.js";
|
|
40
|
+
import { shipOrderSchema } from "../schemas/order-forms.js";
|
|
41
|
+
import { applyZodIssues, rootLevelMessages } from "../../../ui/forms/apply-zod-issues.js";
|
|
42
|
+
/**
|
|
43
|
+
* Box presets, in centimetres and kilograms.
|
|
44
|
+
*
|
|
45
|
+
* 🛑 A PRESET rather than four number inputs, and that is the whole UX
|
|
46
|
+
* argument: a seller packing a Beyblade does not know the parcel's
|
|
47
|
+
* dimensions, and a carrier rejects a wrong one with an opaque error at
|
|
48
|
+
* pickup time rather than at booking time. Four free fields produce four
|
|
49
|
+
* chances to guess. "Custom" exists for the genuinely odd parcel.
|
|
50
|
+
*/
|
|
51
|
+
const BOX_PRESETS = [
|
|
52
|
+
{ id: "small", label: "Small — single top (0.3 kg)", weight: 0.3, length: 20, width: 15, height: 10 },
|
|
53
|
+
{ id: "medium", label: "Medium — a few items (1 kg)", weight: 1, length: 30, width: 25, height: 15 },
|
|
54
|
+
{ id: "large", label: "Large — stadium or bundle (3 kg)", weight: 3, length: 45, width: 35, height: 25 },
|
|
55
|
+
];
|
|
56
|
+
export function DispatchForm({ orderId,
|
|
57
|
+
/** Already dispatched? Then there is nothing to book and nothing to record. */
|
|
58
|
+
trackingNumber,
|
|
59
|
+
/** Set by `bookShipment`; a booked parcel must not be re-booked. */
|
|
60
|
+
platformBooked, onDispatched, }) {
|
|
61
|
+
const [method, setMethod] = useState("custom");
|
|
62
|
+
const [carrier, setCarrier] = useState("");
|
|
63
|
+
const [tracking, setTracking] = useState("");
|
|
64
|
+
const [trackingUrl, setTrackingUrl] = useState("");
|
|
65
|
+
const [pickupId, setPickupId] = useState("");
|
|
66
|
+
const [boxId, setBoxId] = useState(BOX_PRESETS[1].id);
|
|
67
|
+
const [error, setError] = useState(null);
|
|
68
|
+
/*
|
|
69
|
+
* 🛑 Fetched ONLY when the platform branch is open. The custom branch is
|
|
70
|
+
* the common case and needs no address at all, so loading this on mount
|
|
71
|
+
* would be a request on every order panel open for a field most sellers
|
|
72
|
+
* never reach (Rule #6).
|
|
73
|
+
*/
|
|
74
|
+
/*
|
|
75
|
+
* 🛑 `toUserMessage(code)` on failure, never the server's own `error`
|
|
76
|
+
* string — an unmapped code would print server internals into the panel,
|
|
77
|
+
* which is literally how a Node `MODULE_NOT_FOUND` with a require stack
|
|
78
|
+
* reached a buyer-facing modal (Root Cause #86). `ApiClientError` carries
|
|
79
|
+
* the stable code, so the mapping survives the move off raw `fetch`.
|
|
80
|
+
*/
|
|
81
|
+
/*
|
|
82
|
+
* 🛑 A REF, because `setFieldError` comes from `<Form>`'s render-prop and
|
|
83
|
+
* therefore does not exist in component scope where the mutation is
|
|
84
|
+
* declared. The mutation needs a STABLE sink — passing the render-prop
|
|
85
|
+
* helper directly is impossible, and re-creating the mutation per render to
|
|
86
|
+
* close over it would reset its pending state mid-request.
|
|
87
|
+
*
|
|
88
|
+
* It is assigned inside the render-prop below, so by the time any submit
|
|
89
|
+
* can fire it is populated. The `?.` is not defensive decoration: the first
|
|
90
|
+
* render completes before `<Form>` calls its child.
|
|
91
|
+
*/
|
|
92
|
+
const fieldErrorSink = React.useRef(null);
|
|
93
|
+
const shipMutation = useApiMutation({
|
|
94
|
+
/*
|
|
95
|
+
* 🛑 Passed so the server's own per-field `issues` land on the fields
|
|
96
|
+
* they name. The route parses the same `shipOrderSchema` this form does,
|
|
97
|
+
* so a rejection it produces that the client parse did not is real news
|
|
98
|
+
* — a carrier the courier does not recognise, a pickup address that
|
|
99
|
+
* stopped being serviceable — and collapsing that into one generic
|
|
100
|
+
* sentence hides which input to fix.
|
|
101
|
+
*/
|
|
102
|
+
setFieldError: (field, message) => fieldErrorSink.current?.(field, message),
|
|
103
|
+
mutationFn: (payload) => apiClient.post(SELLER_ENDPOINTS.ORDER_SHIP(orderId), payload),
|
|
104
|
+
onSuccess: () => onDispatched(),
|
|
105
|
+
onError: (err) => {
|
|
106
|
+
void normalizeError(err);
|
|
107
|
+
setError(toUserMessage(err instanceof ApiClientError ? err.code : undefined));
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
/*
|
|
111
|
+
* 🛑 `enabled` is what keeps this request off the common path. The custom
|
|
112
|
+
* branch needs no address at all, so loading pickups on mount would fire a
|
|
113
|
+
* request on every order panel a seller opens for a field most of them
|
|
114
|
+
* never reach (Rule #6). `useQuery` also dedupes and caches it, which the
|
|
115
|
+
* hand-rolled effect could not — it refetched on every branch toggle.
|
|
116
|
+
*/
|
|
117
|
+
const pickupsQuery = useQuery({
|
|
118
|
+
queryKey: ["store", "addresses", "pickup"],
|
|
119
|
+
enabled: method === "platform",
|
|
120
|
+
queryFn: () => apiClient.get(SELLER_ENDPOINTS.STORE_ADDRESSES),
|
|
121
|
+
});
|
|
122
|
+
const pickups = Array.isArray(pickupsQuery.data?.items)
|
|
123
|
+
? pickupsQuery.data.items
|
|
124
|
+
: [];
|
|
125
|
+
/*
|
|
126
|
+
* 🛑 A FAILED request and an empty list must not render the same. An empty
|
|
127
|
+
* dropdown reads as "this store has no pickup addresses" — a fact about the
|
|
128
|
+
* store — when it may be a network error, and the two need different
|
|
129
|
+
* actions from the seller.
|
|
130
|
+
*/
|
|
131
|
+
const pickupsFailed = pickupsQuery.isError;
|
|
132
|
+
React.useEffect(() => {
|
|
133
|
+
if (pickups.length === 1 && !pickupId)
|
|
134
|
+
setPickupId(pickups[0].id);
|
|
135
|
+
}, [pickups, pickupId]);
|
|
136
|
+
if (trackingNumber) {
|
|
137
|
+
return (_jsxs(Stack, { gap: "sm", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Dispatched" }), _jsx(Text, { size: "xs", color: "muted", children: platformBooked
|
|
138
|
+
? `Booked through LetItRip · ${trackingNumber}`
|
|
139
|
+
: `Posted by you · ${trackingNumber}` })] }));
|
|
140
|
+
}
|
|
141
|
+
const submit = async (
|
|
142
|
+
/* The `<Form>` render-prop's own signature — `applyZodIssues` clears a
|
|
143
|
+
* field by passing `null`, so a `string`-only sink is not assignable. */
|
|
144
|
+
setFieldError, clearErrors) => {
|
|
145
|
+
clearErrors();
|
|
146
|
+
setError(null);
|
|
147
|
+
const box = BOX_PRESETS.find((b) => b.id === boxId) ?? BOX_PRESETS[1];
|
|
148
|
+
const payload = method === "platform"
|
|
149
|
+
? {
|
|
150
|
+
method: "platform",
|
|
151
|
+
pickupAddressId: pickupId,
|
|
152
|
+
dimensions: {
|
|
153
|
+
weight: box.weight,
|
|
154
|
+
length: box.length,
|
|
155
|
+
width: box.width,
|
|
156
|
+
height: box.height,
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
: {
|
|
160
|
+
method: "custom",
|
|
161
|
+
shippingCarrier: carrier.trim(),
|
|
162
|
+
trackingNumber: tracking.trim(),
|
|
163
|
+
trackingUrl: trackingUrl.trim(),
|
|
164
|
+
};
|
|
165
|
+
/*
|
|
166
|
+
* Parsed against the SAME schema the route and the action use, so the
|
|
167
|
+
* branch's required fields are stated once. `applyZodIssues` can only
|
|
168
|
+
* attach path-bearing issues, so root-level refinements are surfaced
|
|
169
|
+
* separately or a refused save shows nothing at all.
|
|
170
|
+
*/
|
|
171
|
+
const parsed = shipOrderSchema.safeParse(payload);
|
|
172
|
+
if (!parsed.success) {
|
|
173
|
+
applyZodIssues(parsed.error.issues, setFieldError);
|
|
174
|
+
const root = rootLevelMessages(parsed.error.issues);
|
|
175
|
+
if (root.length > 0)
|
|
176
|
+
setError(root.join(" "));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
shipMutation.mutate(parsed.data);
|
|
180
|
+
};
|
|
181
|
+
return (_jsxs(Stack, { gap: "3", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Dispatch" }), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { size: "sm", variant: method === "custom" ? "primary" : "outline", "aria-pressed": method === "custom", onClick: () => setMethod("custom"), children: "I posted it" }), _jsx(Button, { size: "sm", variant: method === "platform" ? "primary" : "outline", "aria-pressed": method === "platform", onClick: () => setMethod("platform"), children: "Book through LetItRip" })] }), _jsx(Form, { onSubmit: (e) => e.preventDefault(), children: ({ setFieldError, clearErrors }) => {
|
|
182
|
+
fieldErrorSink.current = setFieldError;
|
|
183
|
+
return (_jsxs(Stack, { gap: "3", children: [method === "custom" ? (_jsxs(_Fragment, { children: [_jsx(FieldInput, { name: "shippingCarrier", label: "Carrier", required: true, value: carrier, onChange: setCarrier }), _jsx(FieldInput, { name: "trackingNumber", label: "Tracking number", required: true, value: tracking, onChange: setTracking }), _jsx(FieldInput, { name: "trackingUrl", label: "Tracking URL", required: true, type: "url", value: trackingUrl, onChange: setTrackingUrl })] })) : (_jsxs(_Fragment, { children: [pickupsFailed ? (_jsx(Text, { size: "xs", color: "error", role: "alert", children: "Could not load your pickup addresses. Reopen this panel to try again." })) : pickups.length === 0 ? (_jsx(Text, { size: "xs", color: "muted", children: "Loading your pickup addresses\u2026" })) : (_jsx(FieldSelect, { name: "pickupAddressId", label: "Collect from", required: true, value: pickupId, onChange: setPickupId, options: pickups.map((p) => ({
|
|
184
|
+
value: p.id,
|
|
185
|
+
label: [p.label, p.city, p.postalCode].filter(Boolean).join(" · ") || p.id,
|
|
186
|
+
})) })), _jsx(FieldSelect, { name: "box", label: "Parcel size", required: true, value: boxId, onChange: setBoxId, options: BOX_PRESETS.map((b) => ({ value: b.id, label: b.label })) }), _jsx(Text, { size: "xs", color: "muted", children: "The courier is booked on the platform's account and the tracking id comes back from them \u2014 you do not enter one." })] })), error && (_jsx(Div, { textSize: "xs", className: "border border-error/20", color: "error", surface: "danger-surface", padding: "inlineSm", rounded: "lg", role: "alert", children: error })), _jsx(Button, { type: "submit", action: method === "platform"
|
|
187
|
+
? ACTIONS.STORE["dispatch-platform"]
|
|
188
|
+
: ACTIONS.STORE["dispatch-custom"], isLoading: shipMutation.isPending, onClick: () => void submit(setFieldError, clearErrors) })] }));
|
|
189
|
+
} })] }));
|
|
190
|
+
}
|