@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
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Booking a parcel through the platform's own carrier account — Part 1′.
|
|
3
|
+
*
|
|
4
|
+
* ## 🛑 Why this could not be built until Part 14
|
|
5
|
+
*
|
|
6
|
+
* `CreateShipmentInput` needs a structured `{name, phone, addressLine1, city,
|
|
7
|
+
* state, pincode, country}` for BOTH ends. Until the delivery snapshot landed
|
|
8
|
+
* the order carried `shippingAddress?: string` — flat free text with no
|
|
9
|
+
* pincode and no phone — and the only way to produce a party from it was to
|
|
10
|
+
* parse prose. The `placeOfSupply` comment already refuses that for tax, and
|
|
11
|
+
* the same argument holds here: a mis-parsed pincode is a parcel that goes to
|
|
12
|
+
* the wrong city, except nothing errors and the label prints fine.
|
|
13
|
+
*
|
|
14
|
+
* `deliveryPartyFor(order)` now returns exactly that shape, or `null` when a
|
|
15
|
+
* required field is missing — which is the only honest answer for an order
|
|
16
|
+
* placed before the snapshot existed.
|
|
17
|
+
*
|
|
18
|
+
* ## 🛑 `platformBookedShipment` is the flag this writes
|
|
19
|
+
*
|
|
20
|
+
* `order-math.ts` gates shipping reimbursement on it
|
|
21
|
+
* (`if (order.platformBookedShipment === true) return 0`) and
|
|
22
|
+
* `quote.ts:241` records that it has had **ZERO writers** — so the
|
|
23
|
+
* reimbursement rule has been reading a flag nothing sets. This is its
|
|
24
|
+
* writer, which is why the booking and the dispatch form are one unit of
|
|
25
|
+
* work and why the form must not ship first: a "Book through us" button with
|
|
26
|
+
* no booking behind it is a control the server refuses.
|
|
27
|
+
*/
|
|
28
|
+
import { getProviders } from "../../../../contracts/registry.js";
|
|
29
|
+
import { orderRepository } from "../../../../features/orders/repository/orders.repository.js";
|
|
30
|
+
import { storeRepository } from "../../../../features/stores/repository/store.repository.js";
|
|
31
|
+
import { addressesRepository } from "../../../../features/addresses/repository/addresses.repository.js";
|
|
32
|
+
import { deliveryPartyFor } from "../../../shared/features/orders/delivery-snapshot.js";
|
|
33
|
+
import { revealPii } from "../../../../security/pii-accessors.js";
|
|
34
|
+
import { ORDER_PII_FIELDS } from "../../../../security/pii-schemas.js";
|
|
35
|
+
import { ValidationError } from "../../../../errors/validation-error.js";
|
|
36
|
+
import { NotFoundError } from "../../../../errors/not-found-error.js";
|
|
37
|
+
import { normalizeError } from "../../../../errors/normalize.js";
|
|
38
|
+
import { serverLogger } from "../../../../monitoring/server-logger.js";
|
|
39
|
+
/**
|
|
40
|
+
* Book one parcel.
|
|
41
|
+
*
|
|
42
|
+
* ## 🛑 The wallet is checked BEFORE the booking, and a failure is a 409
|
|
43
|
+
*
|
|
44
|
+
* A carrier account with no balance rejects the booking — but some providers
|
|
45
|
+
* accept it and then silently fail to hand over, which produces a tracking id
|
|
46
|
+
* that never moves. Checking first turns "the parcel vanished" into "top up
|
|
47
|
+
* the account", and the 409 is deliberate: the request is well-formed and the
|
|
48
|
+
* seller is not confused, the world is simply not ready.
|
|
49
|
+
*
|
|
50
|
+
* 🛑 `getWalletBalance` is NOT abstract on `IShippingProvider` — manual
|
|
51
|
+
* shipping has no wallet, so a provider that does not implement it returns
|
|
52
|
+
* undefined and the check is SKIPPED rather than failing. A missing balance
|
|
53
|
+
* must never block a manual dispatch.
|
|
54
|
+
*/
|
|
55
|
+
export async function bookShipment(input) {
|
|
56
|
+
const order = (await orderRepository.findById(input.orderId));
|
|
57
|
+
if (!order)
|
|
58
|
+
throw new NotFoundError("Order not found.");
|
|
59
|
+
if (order.trackingNumber) {
|
|
60
|
+
/*
|
|
61
|
+
* 409, not 400. Double-booking is the expensive mistake here — two
|
|
62
|
+
* parcels, two charges, one of them never collected — and it is easy to
|
|
63
|
+
* cause by refreshing a form.
|
|
64
|
+
*/
|
|
65
|
+
throw new ValidationError("This order already has a shipment.", {
|
|
66
|
+
orderId: `Already booked as ${order.trackingNumber}.`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/*
|
|
70
|
+
* 🛑 DECRYPTED first. `OrderDocument`'s delivery name, phone and address
|
|
71
|
+
* lines are `Encrypted<string>` (ciphertext-by-default), and `Encrypted<T>`
|
|
72
|
+
* is deliberately assignable to nothing that wants a plain `string` — which
|
|
73
|
+
* is what made this a compile error rather than a courier label printed
|
|
74
|
+
* with `enc:v1:…` on it.
|
|
75
|
+
*
|
|
76
|
+
* A courier booking is the one place the plaintext is genuinely required:
|
|
77
|
+
* the carrier has to deliver to a real person at a real door. Masked would
|
|
78
|
+
* be the wrong default HERE and the right one almost everywhere else, which
|
|
79
|
+
* is why the two accessors are separately named.
|
|
80
|
+
*/
|
|
81
|
+
const delivery = deliveryPartyFor(revealPii(order, ORDER_PII_FIELDS));
|
|
82
|
+
if (!delivery) {
|
|
83
|
+
/*
|
|
84
|
+
* An order placed before the delivery snapshot existed. Refused, loudly,
|
|
85
|
+
* rather than guessed at — see the module header on parsing prose.
|
|
86
|
+
*/
|
|
87
|
+
throw new ValidationError("This order has no structured delivery address, so it cannot be booked automatically.", { orderId: "Placed before structured addresses; dispatch it manually." });
|
|
88
|
+
}
|
|
89
|
+
const pickupDoc = await addressesRepository.findById(input.pickupAddressId);
|
|
90
|
+
const store = order.storeId ? await storeRepository.findBySlug(order.storeId) : null;
|
|
91
|
+
/*
|
|
92
|
+
* 🛑 The pickup address must belong to the ORDER'S STORE. Checking only
|
|
93
|
+
* that it exists would let one seller's parcel be collected from another
|
|
94
|
+
* seller's premises — the same class of hole `assertOwnedAddresses` closes
|
|
95
|
+
* for coordinated purchases, and it is reachable here by id alone.
|
|
96
|
+
*/
|
|
97
|
+
if (!pickupDoc ||
|
|
98
|
+
pickupDoc.ownerType !== "store" ||
|
|
99
|
+
!store ||
|
|
100
|
+
pickupDoc.ownerId !== store.id) {
|
|
101
|
+
throw new ValidationError("That pickup address is not available for this store.", {
|
|
102
|
+
pickupAddressId: "Pick one of this store's own addresses.",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const pickupPlain = addressesRepository.revealAddress(pickupDoc);
|
|
106
|
+
const pickup = {
|
|
107
|
+
name: String(pickupPlain.fullName ?? store.storeName ?? ""),
|
|
108
|
+
phone: String(pickupPlain.phone ?? ""),
|
|
109
|
+
addressLine1: String(pickupPlain.addressLine1 ?? ""),
|
|
110
|
+
...(pickupPlain.addressLine2 ? { addressLine2: String(pickupPlain.addressLine2) } : {}),
|
|
111
|
+
city: pickupPlain.city ?? "",
|
|
112
|
+
state: pickupPlain.state ?? "",
|
|
113
|
+
pincode: pickupPlain.postalCode ?? "",
|
|
114
|
+
country: pickupPlain.country ?? "",
|
|
115
|
+
};
|
|
116
|
+
/*
|
|
117
|
+
* 🛑 `getProviders().shipping` is OPTIONAL on the registry, and refusing
|
|
118
|
+
* here is the honest handling. `providers.config.ts` always registers one
|
|
119
|
+
* today, so this is unreachable in this app — but a non-null assertion
|
|
120
|
+
* would turn the day somebody ships a build without it into a
|
|
121
|
+
* `Cannot read properties of undefined` at the moment money is involved,
|
|
122
|
+
* instead of a sentence a seller can act on.
|
|
123
|
+
*/
|
|
124
|
+
const provider = getProviders().shipping;
|
|
125
|
+
if (!provider) {
|
|
126
|
+
throw new ValidationError("No shipping provider is configured.", {
|
|
127
|
+
orderId: "Automatic booking is unavailable — dispatch this order manually.",
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/*
|
|
131
|
+
* 🛑 `WalletBalance` is a THREE-state union and collapsing it is the whole
|
|
132
|
+
* hazard the contract's own header describes. Only `{known: true, amount: 0}`
|
|
133
|
+
* is a decision to skip. `{known: false}` means either "no wallet exists"
|
|
134
|
+
* (manual shipping) or "could not read it right now" — and refusing on an
|
|
135
|
+
* unreadable balance would fail every dispatch on a transient blip.
|
|
136
|
+
*
|
|
137
|
+
* An earlier draft of this function tested `typeof balance === "number"`,
|
|
138
|
+
* which is never true against this contract: the guard was dead, so a
|
|
139
|
+
* genuinely empty wallet would have been booked into anyway.
|
|
140
|
+
*/
|
|
141
|
+
const balance = await provider.getWalletBalance().catch((err) => {
|
|
142
|
+
void normalizeError(err);
|
|
143
|
+
serverLogger.warn("bookShipment: wallet balance unavailable", { orderId: input.orderId });
|
|
144
|
+
return { known: false, supported: true };
|
|
145
|
+
});
|
|
146
|
+
if (balance.known && balance.amount <= 0) {
|
|
147
|
+
throw new ValidationError("The courier account has no balance, so the booking was not attempted.", { orderId: "Top up the carrier wallet and try again." });
|
|
148
|
+
}
|
|
149
|
+
const payload = {
|
|
150
|
+
orderId: input.orderId,
|
|
151
|
+
dimensions: input.dimensions,
|
|
152
|
+
pickup,
|
|
153
|
+
delivery,
|
|
154
|
+
...(order.paymentMethod === "cod"
|
|
155
|
+
? { isCod: true, codAmount: order.codRemainingAmount ?? order.totalPrice }
|
|
156
|
+
: {}),
|
|
157
|
+
};
|
|
158
|
+
const shipment = await provider.createShipment(payload);
|
|
159
|
+
/*
|
|
160
|
+
* 🛑 `platformBookedShipment: true` is the WHOLE POINT of this function.
|
|
161
|
+
*
|
|
162
|
+
* `order-math.ts` already gates shipping reimbursement on it and nothing
|
|
163
|
+
* has ever set it. Writing the tracking id without it would book the parcel
|
|
164
|
+
* AND reimburse the seller for postage the platform just paid.
|
|
165
|
+
*/
|
|
166
|
+
await orderRepository.update(input.orderId, {
|
|
167
|
+
trackingNumber: shipment.trackingId,
|
|
168
|
+
...(shipment.courier ? { carrier: shipment.courier } : {}),
|
|
169
|
+
platformBookedShipment: true,
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
trackingId: shipment.trackingId,
|
|
173
|
+
...(shipment.courier ? { courier: shipment.courier } : {}),
|
|
174
|
+
...(shipment.estimatedDelivery ? { estimatedDelivery: shipment.estimatedDelivery } : {}),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raising and clearing a courier block, and telling the ADMIN about it.
|
|
3
|
+
*
|
|
4
|
+
* ## The problem this exists for
|
|
5
|
+
*
|
|
6
|
+
* Shiprocket is PREPAID and has no recharge API, so topping up the courier
|
|
7
|
+
* wallet is irreducibly a human action. If the platform books couriers, an
|
|
8
|
+
* empty wallet silently stops dispatch on orders buyers have already paid for.
|
|
9
|
+
*
|
|
10
|
+
* ## The two rules
|
|
11
|
+
*
|
|
12
|
+
* 1. 🛑 **The order is never auto-cancelled over it.** `seller-dispatch`'s
|
|
13
|
+
* `precondition` carries `!hasOpenCourierBlock(o)`, so its 2-business-day
|
|
14
|
+
* `onDue: "act"` clock PAUSES while blocked instead of cancelling the order
|
|
15
|
+
* and refunding the buyer with the seller blamed for our funding state.
|
|
16
|
+
*
|
|
17
|
+
* 2. 🛑 **The admin alert is deduped on the WALLET, never on the order.** Fifty
|
|
18
|
+
* blocked orders are ONE problem with one fix. The daily email ceiling
|
|
19
|
+
* applies to staff mail too (`send-guard.ts:92` — the kill switch spares
|
|
20
|
+
* staff, the ceiling does not), so fifty per-order emails would spend the
|
|
21
|
+
* whole 80/day budget saying the same thing fifty times and leave the
|
|
22
|
+
* operator blind to everything else.
|
|
23
|
+
*/
|
|
24
|
+
import type { OrderDocument } from "../../../../features/orders/schemas/firestore";
|
|
25
|
+
import type { WalletBalance } from "../../../../contracts";
|
|
26
|
+
/**
|
|
27
|
+
* ## 🛑 What is HERE and what deliberately is not
|
|
28
|
+
*
|
|
29
|
+
* Shipped: the wallet THRESHOLD check and the whole admin-alert path, driven by
|
|
30
|
+
* the daily digest. That is a real producer today and needs nothing new — and
|
|
31
|
+
* it is the better signal anyway: reacting only to failures means the first
|
|
32
|
+
* warning is always a buyer's order already stuck.
|
|
33
|
+
*
|
|
34
|
+
* NOT shipped: raising a block on a specific order. That belongs with the
|
|
35
|
+
* booking path, and nothing platform-books yet (`createShipment` has zero
|
|
36
|
+
* callers). A `raiseCourierBlock` with no producer is the speculative-infra
|
|
37
|
+
* pattern, so it lands when the caller does.
|
|
38
|
+
*
|
|
39
|
+
* `OrderDocument.courierBlock` and `seller-dispatch`'s
|
|
40
|
+
* `!hasOpenCourierBlock(o)` guard ARE already in place, and that ordering is
|
|
41
|
+
* deliberate: a safety property must exist BEFORE the thing it guards against,
|
|
42
|
+
* or the very first block auto-cancels a paid order. Inert today, correct the
|
|
43
|
+
* moment anything writes the field.
|
|
44
|
+
*/
|
|
45
|
+
/** Why we could not book. Mirrors `OrderDocument.courierBlock.reason`. */
|
|
46
|
+
export type CourierBlockReason = NonNullable<OrderDocument["courierBlock"]>["reason"];
|
|
47
|
+
export interface CourierWalletCheck {
|
|
48
|
+
/** `null` when the provider has no wallet, or we could not read it. */
|
|
49
|
+
balance: number | null;
|
|
50
|
+
low: boolean;
|
|
51
|
+
alerted: boolean;
|
|
52
|
+
reason?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Read the courier wallet and, if it is running low, alert the admin ONCE.
|
|
56
|
+
*
|
|
57
|
+
* Called from `dailyStatusDigest` — 🛑 deliberately NOT a new scheduled
|
|
58
|
+
* function. Cloud Scheduler bills per registered job and this is one read a
|
|
59
|
+
* day; the same reasoning that put the ledger reconciler inside
|
|
60
|
+
* `dailyDataCleanup`.
|
|
61
|
+
*
|
|
62
|
+
* Never throws: a wallet read is diagnostics, and a digest that fails because
|
|
63
|
+
* one provider was unreachable stops reporting the fifteen things that were
|
|
64
|
+
* fine.
|
|
65
|
+
*/
|
|
66
|
+
export declare function checkCourierWalletAndAlert(provider: {
|
|
67
|
+
name: string;
|
|
68
|
+
getWalletBalance: () => Promise<WalletBalance>;
|
|
69
|
+
} | undefined): Promise<CourierWalletCheck>;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Raising and clearing a courier block, and telling the ADMIN about it.
|
|
4
|
+
*
|
|
5
|
+
* ## The problem this exists for
|
|
6
|
+
*
|
|
7
|
+
* Shiprocket is PREPAID and has no recharge API, so topping up the courier
|
|
8
|
+
* wallet is irreducibly a human action. If the platform books couriers, an
|
|
9
|
+
* empty wallet silently stops dispatch on orders buyers have already paid for.
|
|
10
|
+
*
|
|
11
|
+
* ## The two rules
|
|
12
|
+
*
|
|
13
|
+
* 1. 🛑 **The order is never auto-cancelled over it.** `seller-dispatch`'s
|
|
14
|
+
* `precondition` carries `!hasOpenCourierBlock(o)`, so its 2-business-day
|
|
15
|
+
* `onDue: "act"` clock PAUSES while blocked instead of cancelling the order
|
|
16
|
+
* and refunding the buyer with the seller blamed for our funding state.
|
|
17
|
+
*
|
|
18
|
+
* 2. 🛑 **The admin alert is deduped on the WALLET, never on the order.** Fifty
|
|
19
|
+
* blocked orders are ONE problem with one fix. The daily email ceiling
|
|
20
|
+
* applies to staff mail too (`send-guard.ts:92` — the kill switch spares
|
|
21
|
+
* staff, the ceiling does not), so fifty per-order emails would spend the
|
|
22
|
+
* whole 80/day budget saying the same thing fifty times and leave the
|
|
23
|
+
* operator blind to everything else.
|
|
24
|
+
*/
|
|
25
|
+
import { Fragment } from "react";
|
|
26
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
27
|
+
import { EmailContainer, EmailDoc, EmailRow, } from "../../../../features/email/primitives.js";
|
|
28
|
+
import { paymentCasesRepository } from "../../../../features/payment-cases/repository/payment-cases.repository.js";
|
|
29
|
+
// `adminNotificationsRepository` lives behind the repositories barrel — the
|
|
30
|
+
// same import the two existing staff-signal call sites use.
|
|
31
|
+
import { adminNotificationsRepository } from "../../../../repositories/index.js";
|
|
32
|
+
import { siteSettingsRepository } from "../../../../features/admin/repository/site-settings.repository.js";
|
|
33
|
+
import { normalizeError } from "../../../../errors/normalize.js";
|
|
34
|
+
import { serverLogger } from "../../../../monitoring/server-logger.js";
|
|
35
|
+
import { sendEmail } from "../../../../features/contact/email.js";
|
|
36
|
+
/**
|
|
37
|
+
* The dedupe key: the PROVIDER, not the order.
|
|
38
|
+
*
|
|
39
|
+
* One string, so a hundred stuck orders collapse into one case whose
|
|
40
|
+
* `observedCount` is the number of orders waiting — which is also the only
|
|
41
|
+
* number an operator actually needs in order to decide how urgently to top up.
|
|
42
|
+
*/
|
|
43
|
+
function walletCaseKey(provider) {
|
|
44
|
+
return `courier-wallet:${provider}`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* How many shipments' worth of balance counts as "low".
|
|
48
|
+
*
|
|
49
|
+
* 🛑 Expressed in SHIPMENTS, not rupees. A ₹500 floor is meaningless without
|
|
50
|
+
* knowing what a booking costs, and the question an operator is really asking
|
|
51
|
+
* is "how many more parcels can I send before this stops". Four is roughly a
|
|
52
|
+
* day's slack at this volume, which is the point: the alert should arrive while
|
|
53
|
+
* topping up is still routine.
|
|
54
|
+
*
|
|
55
|
+
* `TYPICAL_BOOKING_COST` is an ESTIMATE and is labelled one — the real figure
|
|
56
|
+
* per parcel comes from `checkServiceability` and varies by weight and
|
|
57
|
+
* destination. It only has to be the right order of magnitude to make the
|
|
58
|
+
* warning land before the wallet is actually empty.
|
|
59
|
+
*/
|
|
60
|
+
const LOW_BALANCE_SHIPMENTS = 4;
|
|
61
|
+
const TYPICAL_BOOKING_COST = 80;
|
|
62
|
+
/**
|
|
63
|
+
* Read the courier wallet and, if it is running low, alert the admin ONCE.
|
|
64
|
+
*
|
|
65
|
+
* Called from `dailyStatusDigest` — 🛑 deliberately NOT a new scheduled
|
|
66
|
+
* function. Cloud Scheduler bills per registered job and this is one read a
|
|
67
|
+
* day; the same reasoning that put the ledger reconciler inside
|
|
68
|
+
* `dailyDataCleanup`.
|
|
69
|
+
*
|
|
70
|
+
* Never throws: a wallet read is diagnostics, and a digest that fails because
|
|
71
|
+
* one provider was unreachable stops reporting the fifteen things that were
|
|
72
|
+
* fine.
|
|
73
|
+
*/
|
|
74
|
+
export async function checkCourierWalletAndAlert(
|
|
75
|
+
/*
|
|
76
|
+
* 🛑 Accepts `undefined` rather than being handed a cast.
|
|
77
|
+
* `ProviderRegistry.shipping` is declared optional, and although
|
|
78
|
+
* `providers.config.ts` always populates it, that is a runtime guarantee the
|
|
79
|
+
* TYPE does not make — and `as IShippingProvider` would silence the one
|
|
80
|
+
* signal that the registry was never initialised.
|
|
81
|
+
*/
|
|
82
|
+
provider) {
|
|
83
|
+
if (!provider) {
|
|
84
|
+
return { balance: null, low: false, alerted: false, reason: "no shipping provider" };
|
|
85
|
+
}
|
|
86
|
+
let balance;
|
|
87
|
+
try {
|
|
88
|
+
balance = await provider.getWalletBalance();
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
const e = normalizeError(err);
|
|
92
|
+
return { balance: null, low: false, alerted: false, reason: e.message };
|
|
93
|
+
}
|
|
94
|
+
if (!balance.known) {
|
|
95
|
+
/*
|
|
96
|
+
* 🛑 No alert, and no "balance 0" either. An unreadable balance is not a
|
|
97
|
+
* low balance — alerting on it would page an operator every time the
|
|
98
|
+
* network hiccupped, and they would learn to ignore the one that mattered.
|
|
99
|
+
* The reason is returned so the digest can SAY it was unreadable.
|
|
100
|
+
*/
|
|
101
|
+
return {
|
|
102
|
+
balance: null,
|
|
103
|
+
low: false,
|
|
104
|
+
alerted: false,
|
|
105
|
+
reason: balance.supported ? (balance.reason ?? "unreadable") : "no wallet",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const low = balance.amount < LOW_BALANCE_SHIPMENTS * TYPICAL_BOOKING_COST;
|
|
109
|
+
if (!low)
|
|
110
|
+
return { balance: balance.amount, low: false, alerted: false };
|
|
111
|
+
const alerted = await alertAdminOnce({
|
|
112
|
+
provider: provider.name,
|
|
113
|
+
reason: "insufficient_funds",
|
|
114
|
+
detail: `balance ${balance.amount}`,
|
|
115
|
+
}).catch((err) => {
|
|
116
|
+
const e = normalizeError(err);
|
|
117
|
+
serverLogger.error("courier-wallet: admin alert failed", {
|
|
118
|
+
provider: provider.name,
|
|
119
|
+
error: e.message,
|
|
120
|
+
});
|
|
121
|
+
return false;
|
|
122
|
+
});
|
|
123
|
+
return { balance: balance.amount, low: true, alerted };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* One case, one in-app alert, one email — per WALLET, per unresolved episode.
|
|
127
|
+
*
|
|
128
|
+
* `openOrObserve` is what makes this idempotent: a second blocked order raises
|
|
129
|
+
* `observedCount` on the existing case rather than opening a new one, and the
|
|
130
|
+
* email is only sent when a case was actually OPENED.
|
|
131
|
+
*/
|
|
132
|
+
async function alertAdminOnce(input) {
|
|
133
|
+
const { provider, reason, detail } = input;
|
|
134
|
+
const key = walletCaseKey(provider);
|
|
135
|
+
const title = reason === "insufficient_funds"
|
|
136
|
+
? `Courier wallet needs a top-up (${provider})`
|
|
137
|
+
: `Courier booking failing (${provider})`;
|
|
138
|
+
const result = await paymentCasesRepository().openOrObserve({
|
|
139
|
+
kind: "courier_unavailable",
|
|
140
|
+
/*
|
|
141
|
+
* 🛑 `subjectId` identifies THE PROBLEM, not the observation — the
|
|
142
|
+
* repository's own words, and here the problem is the wallet. Passing the
|
|
143
|
+
* order id would open one case per stuck order, which is the exact noise
|
|
144
|
+
* `openOrObserve` exists to prevent ("an operator learns to ignore it, and
|
|
145
|
+
* the first real chargeback is lost in 8,000 rows").
|
|
146
|
+
*/
|
|
147
|
+
subjectId: key,
|
|
148
|
+
severity: "error",
|
|
149
|
+
title,
|
|
150
|
+
detail: reason === "insufficient_funds"
|
|
151
|
+
? `Bookings are failing for lack of funds. Paid orders are waiting and are NOT being auto-cancelled. Top up the ${provider} wallet, then retry from the order.`
|
|
152
|
+
: `Bookings are failing (${reason}). Paid orders are waiting and are NOT being auto-cancelled.${detail ? ` Upstream: ${detail.slice(0, 200)}` : ""}`,
|
|
153
|
+
/*
|
|
154
|
+
* The entity is the STORE-agnostic provider, so `entityType: "store"` would
|
|
155
|
+
* be a lie and `"order"` would re-key it to one order. `"job"` is the
|
|
156
|
+
* closest honest member of that union: this is a piece of our own
|
|
157
|
+
* machinery failing, not one record being wrong.
|
|
158
|
+
*/
|
|
159
|
+
entityType: "job",
|
|
160
|
+
entityId: key,
|
|
161
|
+
});
|
|
162
|
+
/*
|
|
163
|
+
* 🛑 Only on a NEWLY CREATED case. This is the whole dedupe: an already-open
|
|
164
|
+
* episode has already been emailed, and re-sending it daily — or once per
|
|
165
|
+
* blocked order, once booking is wired — is the budget bug this function
|
|
166
|
+
* exists to avoid. The daily ceiling applies to staff mail too.
|
|
167
|
+
*/
|
|
168
|
+
if (!result.created)
|
|
169
|
+
return false;
|
|
170
|
+
// One durable staff row the admin inbox reads and the digest counts —
|
|
171
|
+
// `audienceUserIds: []` is the "staff signal is a record, not a blast"
|
|
172
|
+
// pattern, rather than N personal notifications.
|
|
173
|
+
await adminNotificationsRepository
|
|
174
|
+
.create({
|
|
175
|
+
/*
|
|
176
|
+
* No cast. The fields are the real `AdminNotificationDocument` ones —
|
|
177
|
+
* `category`, not `type` — because a cast here would silence every other
|
|
178
|
+
* field's check too, which is how the `as never` on the older catalogue
|
|
179
|
+
* handler hid a wrong `createdAt` (CLAUDE.md § Notifications).
|
|
180
|
+
*/
|
|
181
|
+
category: "system",
|
|
182
|
+
severity: "error",
|
|
183
|
+
title,
|
|
184
|
+
body: `${title}. Paid orders are waiting; none will be auto-cancelled while this is open.`,
|
|
185
|
+
isRead: false,
|
|
186
|
+
entityType: "job",
|
|
187
|
+
entityId: key,
|
|
188
|
+
audienceUserIds: [],
|
|
189
|
+
})
|
|
190
|
+
.catch((err) => {
|
|
191
|
+
void normalizeError(err);
|
|
192
|
+
});
|
|
193
|
+
await emailStaff({ title, provider, reason });
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* The staff email.
|
|
198
|
+
*
|
|
199
|
+
* 🛑 `feature` is a NAMED FLOW, not a `NotificationType`, and that is a
|
|
200
|
+
* deliberate correction to the plan this came from.
|
|
201
|
+
*
|
|
202
|
+
* `SendGuardContext.feature` is documented as "a notification type, or a named
|
|
203
|
+
* non-notification flow (`daily_digest`, `checkout_otp`, `payout_summary`)",
|
|
204
|
+
* and this is the second kind. A `NotificationType` is addressed to a USER and
|
|
205
|
+
* carries preferences, templates and an audience — none of which make sense
|
|
206
|
+
* here: nobody can opt out of "the courier wallet is empty", and there is no
|
|
207
|
+
* user it belongs to. Adding one would have meant four compile-checked map
|
|
208
|
+
* entries describing a notification nobody receives.
|
|
209
|
+
*
|
|
210
|
+
* `audience: "staff"` is load-bearing: the admin kill switch suppresses
|
|
211
|
+
* user-facing mail only, so switching customer email off must not stop the
|
|
212
|
+
* operator finding out that paid orders have stopped moving.
|
|
213
|
+
*/
|
|
214
|
+
async function emailStaff(input) {
|
|
215
|
+
const settings = await siteSettingsRepository.getSingleton().catch((err) => {
|
|
216
|
+
void normalizeError(err);
|
|
217
|
+
return null;
|
|
218
|
+
});
|
|
219
|
+
// Same recipient list the daily digest uses — one place to configure "who
|
|
220
|
+
// hears about operational problems".
|
|
221
|
+
const recipients = (settings?.emailSettings?.dailyDigest?.recipients ?? []).filter((r) => r.trim().length > 0);
|
|
222
|
+
if (recipients.length === 0) {
|
|
223
|
+
serverLogger.warn("courier-block: no staff recipients configured — case raised, no email", {
|
|
224
|
+
provider: input.provider,
|
|
225
|
+
});
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const body = input.reason === "insufficient_funds"
|
|
229
|
+
? `Courier bookings are failing for lack of funds on the ${input.provider} wallet.\n\nPaid orders are waiting. They are NOT being auto-cancelled — the dispatch clock is paused while this is open.\n\nTop up the wallet, then retry from the order or the money-cases queue.`
|
|
230
|
+
: `Courier bookings are failing on ${input.provider} (${input.reason}).\n\nPaid orders are waiting. They are NOT being auto-cancelled — the dispatch clock is paused while this is open.`;
|
|
231
|
+
const { error } = await sendEmail({
|
|
232
|
+
to: recipients,
|
|
233
|
+
subject: input.title,
|
|
234
|
+
/*
|
|
235
|
+
* Composed from the Email primitives rather than hand-built HTML.
|
|
236
|
+
*
|
|
237
|
+
* The old line string-replaced `\n\n` into `</p><p>`, which produces
|
|
238
|
+
* markup no email client was ever tested against and which
|
|
239
|
+
* `audit-email-raw-html` blocks for that reason. `EmailDoc` renders the
|
|
240
|
+
* table-based, inline-styled structure clients actually honour, and the
|
|
241
|
+
* paragraph split now happens on the DATA before it reaches the markup.
|
|
242
|
+
*/
|
|
243
|
+
html: renderStaffAlertEmail(input.title, body),
|
|
244
|
+
text: body,
|
|
245
|
+
}, { channel: "email", feature: "courier_wallet_low", audience: "staff" });
|
|
246
|
+
if (error) {
|
|
247
|
+
serverLogger.error("courier-block: staff email failed", {
|
|
248
|
+
provider: input.provider,
|
|
249
|
+
error: String(error),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* The staff courier-block alert, as an email document.
|
|
255
|
+
*
|
|
256
|
+
* Kept local to this file rather than exported: it is one alert with one
|
|
257
|
+
* shape, and a shared "generic staff alert" helper would invite the next
|
|
258
|
+
* caller to pass admin-editable text through it — which is how an email
|
|
259
|
+
* template becomes an injection surface. The body here is built entirely from
|
|
260
|
+
* our own constants and the provider/reason enums.
|
|
261
|
+
*/
|
|
262
|
+
function renderStaffAlertEmail(title, body) {
|
|
263
|
+
return `<!DOCTYPE html>${renderToStaticMarkup(_jsx(EmailDoc, { title: title, children: _jsx(EmailContainer, { children: _jsx(EmailRow, { children: body.split("\n\n").map((para, i) => (_jsxs(Fragment, { children: [i > 0 ? _jsx("br", {}) : null, i > 0 ? _jsx("br", {}) : null, para.split("\n").map((line, j) => (_jsxs(Fragment, { children: [j > 0 ? _jsx("br", {}) : null, line] }, j)))] }, i))) }) }) }))}`;
|
|
264
|
+
}
|
|
@@ -37,9 +37,9 @@ import type { PublicSiteSettings } from "../../../shared/features/site-settings/
|
|
|
37
37
|
* was traced to a call site; anything without one belongs in the private list,
|
|
38
38
|
* because an unused public field is pure attack surface.
|
|
39
39
|
*/
|
|
40
|
-
export declare const PUBLIC_SITE_SETTINGS_FIELDS: readonly ["contact", "payment", "listings", "notificationChannels", "announcementBar", "navConfig", "actionConfig", "background", "watermark", "disabledRoutes", "chat", "search"];
|
|
40
|
+
export declare const PUBLIC_SITE_SETTINGS_FIELDS: readonly ["contact", "payment", "listings", "notificationChannels", "announcementBar", "support", "navConfig", "actionConfig", "background", "watermark", "disabledRoutes", "chat", "search"];
|
|
41
41
|
/** Everything else, with the reason it stays server-side. */
|
|
42
|
-
export declare const PRIVATE_SITE_SETTINGS_FIELDS: readonly ["id", "createdAt", "updatedAt", "credentials", "emailSettings", "legalPages", "commissions", "emi", "gst", "laborRate", "adSettings", "integrations", "platformLimits", "auctionConfig", "siteName", "tagline", "motto", "logo", "favicon", "seo", "socialLinks", "features", "shipping", "returns", "faq", "aboutContent", "navbarConfig", "footerConfig", "theme", "featuredResults", "messaging"];
|
|
42
|
+
export declare const PRIVATE_SITE_SETTINGS_FIELDS: readonly ["rateChanges", "id", "createdAt", "updatedAt", "credentials", "emailSettings", "legalPages", "commissions", "emi", "gst", "laborRate", "adSettings", "integrations", "platformLimits", "auctionConfig", "siteName", "tagline", "motto", "logo", "favicon", "seo", "socialLinks", "features", "shipping", "returns", "faq", "aboutContent", "navbarConfig", "footerConfig", "theme", "featuredResults", "messaging"];
|
|
43
43
|
export interface ToPublicSiteSettingsOptions {
|
|
44
44
|
/**
|
|
45
45
|
* The resolved marker → wordmark → text watermark chain. Injected because
|
|
@@ -9,6 +9,18 @@ export const PUBLIC_SITE_SETTINGS_FIELDS = [
|
|
|
9
9
|
"listings", // useListingTypeFlags — narrowed to listings.listingTypes; categoryTypes has no client reader
|
|
10
10
|
"notificationChannels", // NotificationPreferencesPanel reads the per-channel toggles
|
|
11
11
|
"announcementBar", // the homepage banner (AdminSiteSettingsView edits it)
|
|
12
|
+
/*
|
|
13
|
+
* `support.enabled` — a REAL client reader, which is the bar this list sets:
|
|
14
|
+
* `getUserNavGroups` filters the buyer's support nav entry on it, and
|
|
15
|
+
* `UserLayoutClient` is a client component.
|
|
16
|
+
*
|
|
17
|
+
* 🛑 Publishing it is also harmless by inspection, and that is worth stating
|
|
18
|
+
* rather than assuming: the group holds exactly one boolean, and "is support
|
|
19
|
+
* reachable" is a fact a buyer discovers by looking at the page anyway. If a
|
|
20
|
+
* second field is ever added here, triage it separately — the group being
|
|
21
|
+
* public does not make its future members public.
|
|
22
|
+
*/
|
|
23
|
+
"support",
|
|
12
24
|
"navConfig", // NavbarWithSettings, DashboardLayoutClient
|
|
13
25
|
"actionConfig", // useAuthGate
|
|
14
26
|
"background", // DashboardLayoutClient (background.light / .dark)
|
|
@@ -54,6 +66,20 @@ export const PUBLIC_SITE_SETTINGS_FIELDS = [
|
|
|
54
66
|
];
|
|
55
67
|
/** Everything else, with the reason it stays server-side. */
|
|
56
68
|
export const PRIVATE_SITE_SETTINGS_FIELDS = [
|
|
69
|
+
/*
|
|
70
|
+
* 🛑 `rateChanges` — the rate-change log behind /admin/pricing.
|
|
71
|
+
*
|
|
72
|
+
* Every entry names an internal rate, who moved it and why. The WHY is the
|
|
73
|
+
* worst of it: a reason reads "undercutting X" or "margin too thin on
|
|
74
|
+
* bundles", which is commercial strategy typed by an admin who was told it
|
|
75
|
+
* was an internal note.
|
|
76
|
+
*
|
|
77
|
+
* Half the rates are already private (`gatewayFeePercent`,
|
|
78
|
+
* `payoutHoldDays`, `minPayoutAmount`), so publishing their history would
|
|
79
|
+
* also publish the values themselves — the current figure is the newest
|
|
80
|
+
* entry's `newValue`.
|
|
81
|
+
*/
|
|
82
|
+
"rateChanges",
|
|
57
83
|
"id", // singleton bookkeeping
|
|
58
84
|
"createdAt", // bookkeeping
|
|
59
85
|
"updatedAt", // bookkeeping
|
|
@@ -114,6 +140,9 @@ export function toPublicSiteSettings(doc, opts) {
|
|
|
114
140
|
},
|
|
115
141
|
notificationChannels: doc.notificationChannels,
|
|
116
142
|
announcementBar: doc.announcementBar,
|
|
143
|
+
// Narrowed to the one boolean a client reads, not the group by reference —
|
|
144
|
+
// so a field added to `support` later is not published by accident.
|
|
145
|
+
support: doc.support ? { enabled: doc.support.enabled !== false } : undefined,
|
|
117
146
|
navConfig: doc.navConfig,
|
|
118
147
|
actionConfig: doc.actionConfig,
|
|
119
148
|
background: doc.background,
|
|
@@ -25,9 +25,9 @@ import type { StoreDetail, StoreListItem } from "../../../../features/stores/typ
|
|
|
25
25
|
* Document fields that may reach an unauthenticated client. Keep in sync with
|
|
26
26
|
* the object literals below — the audit asserts both directions.
|
|
27
27
|
*/
|
|
28
|
-
export declare const PUBLIC_STORE_FIELDS: readonly ["id", "storeSlug", "ownerId", "storeName", "storeDescription", "storeCategory", "storeLogoURL", "storeBannerURL", "status", "isPublic", "stats", "bio", "location", "website", "socialLinks", "returnPolicy", "shippingPolicy", "isVacationMode", "vacationMessage", "supportsShippingProtection", "supportsSaleProtection", "createdAt"];
|
|
28
|
+
export declare const PUBLIC_STORE_FIELDS: readonly ["id", "storeSlug", "ownerId", "storeName", "storeDescription", "storeCategory", "storeLogoURL", "storeBannerURL", "status", "isPublic", "stats", "bio", "location", "website", "socialLinks", "returnPolicy", "shippingPolicy", "isVacationMode", "vacationMessage", "supportsShippingProtection", "supportsSaleProtection", "allowSharedPurchase", "sharedPurchaseHandlingFee", "sharedPurchaseExtraShippingPerDestination", "createdAt"];
|
|
29
29
|
/** Everything else, with the reason it stays server-side. */
|
|
30
|
-
export declare const PRIVATE_STORE_FIELDS: readonly ["searchTxt", "updatedAt", "adminNotes", "statusHistory", "statusHistoryTruncated", "isVerified", "suspensionReason", "capabilities", "customCommissionRate", "googleReviews", "shippingConfig", "emiEnabled", "pricesIncludeGst", "shippingPricing", "returnsDirectToSeller", "isFeatured", "vacationReturnDate", "isTestData", "testDataExpiresAt", "gstin", "legalName", "registeredAddress"];
|
|
30
|
+
export declare const PRIVATE_STORE_FIELDS: readonly ["trustedSeller", "acceptsPoints", "searchTxt", "updatedAt", "adminNotes", "statusHistory", "statusHistoryTruncated", "isVerified", "suspensionReason", "capabilities", "customCommissionRate", "googleReviews", "shippingConfig", "emiEnabled", "pricesIncludeGst", "shippingPricing", "returnsDirectToSeller", "isFeatured", "vacationReturnDate", "isTestData", "testDataExpiresAt", "gstin", "legalName", "registeredAddress"];
|
|
31
31
|
/**
|
|
32
32
|
* Accepted input: a `StoreDocument` (the common case) or a loosely-typed
|
|
33
33
|
* Firestore row. The legacy top-level counters below predate `stats` and are
|