@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,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { Alert, Badge, Button, Div, Heading, Row, Stack, Text, } from "../../../ui/index.js";
|
|
5
|
+
import { useToast } from "../../../ui/components/Toast.js";
|
|
6
|
+
import { normalizeError } from "../../../errors/normalize.js";
|
|
7
|
+
import { ACTIONS } from "../../../_internal/shared/actions/action-registry.js";
|
|
8
|
+
import { INTEGRATIONS, INTEGRATION_ORDER, allIntegrationsPatch, integrationPatch, readIntegrationStates, } from "../../../_internal/shared/features/admin/integrations.js";
|
|
9
|
+
/** What the switch currently is, in the admin's vocabulary rather than a boolean. */
|
|
10
|
+
function stateLabel(id, enabled) {
|
|
11
|
+
/*
|
|
12
|
+
* 🛑 Shipping is NOT on/off, and this is the one row where saying so matters.
|
|
13
|
+
*
|
|
14
|
+
* "Off" selects `provider: "manual"` — the self-ship flow sellers already
|
|
15
|
+
* use and claim postage back through. Rendering that as "Disabled" beside
|
|
16
|
+
* three switches that genuinely stop things would invite an admin to read it
|
|
17
|
+
* as "shipping is broken", and the descriptor's own note says the switch
|
|
18
|
+
* "selects manual rather than stopping anything".
|
|
19
|
+
*/
|
|
20
|
+
if (id === "shipping")
|
|
21
|
+
return enabled ? "We book couriers" : "Sellers self-ship";
|
|
22
|
+
return enabled ? "On" : "Off";
|
|
23
|
+
}
|
|
24
|
+
export function AdminIntegrationsView({ settings, onSave }) {
|
|
25
|
+
const { showToast } = useToast();
|
|
26
|
+
const [pending, setPending] = useState(null);
|
|
27
|
+
/*
|
|
28
|
+
* 🛑 Derived from the prop, NOT copied into state.
|
|
29
|
+
*
|
|
30
|
+
* A `useState(readIntegrationStates(settings))` would go stale the moment
|
|
31
|
+
* the page refetched, and the staleness would be invisible: the row would
|
|
32
|
+
* read "On" while the guard enforcing it read "off". This page's entire job
|
|
33
|
+
* is to agree with the guard, so it re-derives rather than remembers.
|
|
34
|
+
*/
|
|
35
|
+
const states = useMemo(() => readIntegrationStates(settings), [settings]);
|
|
36
|
+
const apply = useCallback(async (key, patch, label) => {
|
|
37
|
+
setPending(key);
|
|
38
|
+
try {
|
|
39
|
+
await onSave(patch);
|
|
40
|
+
showToast(`${label} saved.`, "success");
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
void normalizeError(err);
|
|
44
|
+
/*
|
|
45
|
+
* 🛑 No optimistic flip anywhere in this component, so a failure needs
|
|
46
|
+
* no rollback — the row still shows what the server last told us. On a
|
|
47
|
+
* kill switch the cost of the two mistakes is wildly asymmetric: a
|
|
48
|
+
* row that briefly lags is a nuisance, while a row claiming "payments
|
|
49
|
+
* off" over a live gateway is the failure the model's own header warns
|
|
50
|
+
* about.
|
|
51
|
+
*/
|
|
52
|
+
showToast(`Couldn't save ${label.toLowerCase()}. Nothing was changed.`, "error");
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
setPending(null);
|
|
56
|
+
}
|
|
57
|
+
}, [onSave, showToast]);
|
|
58
|
+
return (_jsxs(Stack, { gap: "lg", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 1, size: "xl", weight: "semibold", color: "primary", children: "Integrations" }), _jsx(Text, { size: "sm", color: "muted", children: "What the platform talks to, and what happens when it stops. Each switch writes the same settings field the code enforcing it reads." })] }), _jsx(Alert, { variant: "warning", title: "Switch payments off only when nobody is checking out", children: "Turning online payments off also closes the routes that FINISH a payment, including the gateway webhook. A buyer part-way through the gateway can be charged without an order being placed, and the gateway will not retry. There is no safe mid-checkout moment." }), _jsx(Stack, { gap: "md", children: INTEGRATION_ORDER.map((id) => {
|
|
59
|
+
const d = INTEGRATIONS[id];
|
|
60
|
+
const enabled = states[id].enabled;
|
|
61
|
+
const busy = pending === id || pending === "all";
|
|
62
|
+
const action = enabled
|
|
63
|
+
? ACTIONS.ADMIN["disable-integration"]
|
|
64
|
+
: ACTIONS.ADMIN["enable-integration"];
|
|
65
|
+
return (_jsx(Div, { surface: d.severe && enabled ? "warning-surface" : "card", padding: "md", rounded: "lg", border: "default", children: _jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { align: "start", justify: "between", gap: "md", className: "min-w-0", children: [_jsxs(Stack, { gap: "xs", className: "min-w-0", children: [_jsxs(Row, { align: "center", gap: "sm", className: "min-w-0", children: [_jsx(Text, { weight: "semibold", color: "primary", className: "min-w-0", children: d.label }), _jsx(Badge, { variant: enabled ? "success" : "inactive", children: stateLabel(id, enabled) }), d.severe && _jsx(Badge, { variant: "warning", children: "High impact" })] }), _jsx(Text, { size: "sm", color: "muted", children: d.summary })] }), _jsx(Button, { action: action,
|
|
66
|
+
/*
|
|
67
|
+
* 🛑 The dialog body is composed HERE from the descriptor.
|
|
68
|
+
* The ActionDef ships an empty body on purpose — see its
|
|
69
|
+
* note. A per-integration consequence copied into the
|
|
70
|
+
* registry would be a second source for one sentence.
|
|
71
|
+
*/
|
|
72
|
+
confirmationBody: enabled ? d.offConsequence : undefined, isLoading: busy, disabled: busy, onClick: () => apply(id, integrationPatch(id, !enabled), `${d.label} ${enabled ? "off" : "on"}`) })] }), _jsxs(Text, { size: "xs", color: "muted", children: [_jsxs(Text, { as: "span", size: "xs", weight: "semibold", color: "muted", children: ["Off means:", " "] }), d.offConsequence] }), _jsxs(Row, { align: "center", gap: "sm", className: "min-w-0", children: [_jsx(Text, { size: "xs", color: "faint", numeric: true, className: "min-w-0", truncate: 1, children: d.field }), _jsx(Text, { size: "xs", color: "faint", children: "\u00B7" }), _jsx(Text, { size: "xs", color: "faint", className: "min-w-0", truncate: 1, children: d.routeGuard === "gated" ? "API routes close when off" : "no route gate" })] })] }) }, id));
|
|
73
|
+
}) }), _jsxs(Row, { align: "center", gap: "sm", border: "top-subtle", padding: "t-md", children: [_jsx(Button, { action: ACTIONS.ADMIN["enable-all-integrations"], isLoading: pending === "all", disabled: pending !== null, onClick: () => apply("all", allIntegrationsPatch(true), "All integrations on") }), _jsx(Button, { action: ACTIONS.ADMIN["disable-all-integrations"], isLoading: pending === "all", disabled: pending !== null, onClick: () => apply("all", allIntegrationsPatch(false), "All integrations off") })] })] }));
|
|
74
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Button, Card, Disclaimer, Div, Heading, Row, Stack, Text } from "../../../ui/index.js";
|
|
5
|
+
import { FieldInput } from "../../../ui/forms/FieldInput.js";
|
|
6
|
+
import { Icon } from "../../../ui/components/Icon.js";
|
|
7
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
8
|
+
/**
|
|
9
|
+
* Admin → Finance → Invoicing: the GSTR-1 draft export (Phase 7d).
|
|
10
|
+
*
|
|
11
|
+
* ## 🛑 Why this page exists at all
|
|
12
|
+
*
|
|
13
|
+
* The export is an API route, and an API route with no surface is a dead end —
|
|
14
|
+
* reachable only by someone who already knows the URL, which is Root Cause
|
|
15
|
+
* #56's shape (16 of 70 listing views had no way to open a row). The person
|
|
16
|
+
* who needs this is an accountant at quarter end, and they will not type a
|
|
17
|
+
* query string.
|
|
18
|
+
*
|
|
19
|
+
* ## 🛑 It downloads; it does not file
|
|
20
|
+
*
|
|
21
|
+
* Nothing here talks to the GST portal — there is no webhook facility in
|
|
22
|
+
* existence and no GSP contract. The copy says "draft" everywhere it can,
|
|
23
|
+
* because a page that implied a return had been FILED when it had not is the
|
|
24
|
+
* most consequential thing this feature could get wrong.
|
|
25
|
+
*/
|
|
26
|
+
/** Indian FY quarters, which is the unit a GSTR-1 is filed in. */
|
|
27
|
+
const QUARTERS = [
|
|
28
|
+
{ label: "Q1 — Apr to Jun", fromMonth: "04-01", toMonth: "06-30" },
|
|
29
|
+
{ label: "Q2 — Jul to Sep", fromMonth: "07-01", toMonth: "09-30" },
|
|
30
|
+
{ label: "Q3 — Oct to Dec", fromMonth: "10-01", toMonth: "12-31" },
|
|
31
|
+
/*
|
|
32
|
+
* 🛑 Q4 SPANS A CALENDAR YEAR BOUNDARY. Jan–Mar of FY 26-27 is calendar
|
|
33
|
+
* 2027, not 2026 — the same off-by-one the `financialYearLabel` helper
|
|
34
|
+
* exists to contain, surfacing here because a quarter picker has to know it
|
|
35
|
+
* too. `yearOffset` is what carries it; dropping it would export the wrong
|
|
36
|
+
* three months with no visible error.
|
|
37
|
+
*/
|
|
38
|
+
{ label: "Q4 — Jan to Mar", fromMonth: "01-01", toMonth: "03-31", yearOffset: 1 },
|
|
39
|
+
];
|
|
40
|
+
function defaultFyStartYear() {
|
|
41
|
+
const now = new Date();
|
|
42
|
+
// Zero-based month: April is 3. Jan–Mar belongs to the FY that started last
|
|
43
|
+
// calendar year.
|
|
44
|
+
return now.getMonth() >= 3 ? now.getFullYear() : now.getFullYear() - 1;
|
|
45
|
+
}
|
|
46
|
+
export function AdminInvoicingView() {
|
|
47
|
+
const [fyStart, setFyStart] = useState(String(defaultFyStartYear()));
|
|
48
|
+
const [from, setFrom] = useState("");
|
|
49
|
+
const [to, setTo] = useState("");
|
|
50
|
+
const applyQuarter = (q) => {
|
|
51
|
+
const start = Number(fyStart);
|
|
52
|
+
if (!Number.isFinite(start))
|
|
53
|
+
return;
|
|
54
|
+
const offset = "yearOffset" in q ? q.yearOffset : 0;
|
|
55
|
+
setFrom(`${start + offset}-${q.fromMonth}`);
|
|
56
|
+
setTo(`${start + offset}-${q.toMonth}`);
|
|
57
|
+
};
|
|
58
|
+
const href = from && to
|
|
59
|
+
? `${ADMIN_ENDPOINTS.INVOICING_GSTR1}?from=${from}&to=${to}`
|
|
60
|
+
: undefined;
|
|
61
|
+
return (_jsxs(Stack, { gap: "lg", children: [_jsxs(Div, { children: [_jsx(Heading, { level: 1, children: "Invoicing" }), _jsx(Text, { color: "muted", size: "sm", children: "Export the invoice and credit-note register for a period, as a draft for your accountant." })] }), _jsx(Card, { padding: "md", children: _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, children: "GSTR-1 draft" }), _jsx(Disclaimer, { id: "accounting-output" }), _jsx(Row, { gap: "sm", wrap: true, children: _jsx(Div, { className: "w-40", children: _jsx(FieldInput, { name: "fyStart", label: "FY starting", value: fyStart, onChange: setFyStart, hint: "e.g. 2026 for FY 26-27" }) }) }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Quarter" }), _jsx(Row, { gap: "sm", wrap: true, children: QUARTERS.map((q) => (_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => applyQuarter(q), children: q.label }, q.label))) })] }), _jsxs(Row, { gap: "sm", wrap: true, children: [_jsx(Div, { className: "w-48", children: _jsx(FieldInput, { name: "from", label: "From", value: from, onChange: setFrom }) }), _jsx(Div, { className: "w-48", children: _jsx(FieldInput, { name: "to", label: "To", value: to, onChange: setTo }) })] }), _jsxs(Row, { gap: "sm", align: "center", children: [_jsxs(Button, { type: "button", variant: "primary", disabled: !href, onClick: () => {
|
|
62
|
+
if (href)
|
|
63
|
+
window.location.href = href;
|
|
64
|
+
}, children: [_jsx(Icon, { name: "download", size: "sm" }), "Download draft CSV"] }), !href && (_jsx(Text, { color: "muted", size: "xs", children: "Pick a quarter, or enter both dates as YYYY-MM-DD." }))] }), _jsx(Text, { color: "muted", size: "xs", children: "Credit notes appear as negative rows, so the TOTAL row is net of refunds. Any paid order in the window that carries no invoice number is listed at the bottom of the file as a warning \u2014 those are not included in the totals and need looking at." })] }) })] }));
|
|
65
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type OrderAddonBadgesOrder } from "../../orders/components/OrderAddonBadges";
|
|
2
|
+
import type { OrderSourceContext } from "../../orders/schemas/firestore";
|
|
2
3
|
import React from "react";
|
|
3
4
|
export interface AdminOrderItemRow {
|
|
4
5
|
productId: string;
|
|
@@ -7,6 +8,10 @@ export interface AdminOrderItemRow {
|
|
|
7
8
|
quantity: number;
|
|
8
9
|
unitPrice: number;
|
|
9
10
|
totalPrice: number;
|
|
11
|
+
groupSlug?: string;
|
|
12
|
+
groupTitle?: string;
|
|
13
|
+
bundleCategorySlug?: string;
|
|
14
|
+
bundleProductIds?: string[];
|
|
10
15
|
}
|
|
11
16
|
export interface AdminOrderEditorViewProps {
|
|
12
17
|
open: boolean;
|
|
@@ -36,5 +41,32 @@ export interface AdminOrderEditorViewProps {
|
|
|
36
41
|
* coupon is what an admin needs when reconciling a discounted order.
|
|
37
42
|
*/
|
|
38
43
|
addons?: OrderAddonBadgesOrder;
|
|
44
|
+
/**
|
|
45
|
+
* How the buyer earned this price — a won auction, a buyout, an agreed
|
|
46
|
+
* offer, a pre-order deposit, prize-draw entries.
|
|
47
|
+
*
|
|
48
|
+
* Support is the surface that needs this most: a buyer disputing "I agreed
|
|
49
|
+
* ₹X" is otherwise unanswerable from the admin side, because the negotiated
|
|
50
|
+
* amount lives only in `sourceContext` and nothing rendered it anywhere.
|
|
51
|
+
* `GET /api/admin/orders/[id]` already returns it via `maskedOrder`.
|
|
52
|
+
*/
|
|
53
|
+
sourceContext?: OrderSourceContext;
|
|
54
|
+
/** For the provenance card's money formatting; falls back to the default. */
|
|
55
|
+
currency?: string;
|
|
56
|
+
/**
|
|
57
|
+
* D55 — the pre-order balance block, present once a seller has opened it.
|
|
58
|
+
*
|
|
59
|
+
* 🛑 Admin is the ONLY role that sees `proofUrl` here, matching the deposit:
|
|
60
|
+
* the seller's own endpoint strips it, because a bank screenshot is the
|
|
61
|
+
* buyer's and verification is not the seller's job. Reviewing a payment to
|
|
62
|
+
* yourself is not a review.
|
|
63
|
+
*/
|
|
64
|
+
balance?: {
|
|
65
|
+
amount: number;
|
|
66
|
+
status: "awaiting" | "submitted" | "paid" | "rejected";
|
|
67
|
+
proofUrl?: string;
|
|
68
|
+
transactionId?: string;
|
|
69
|
+
reviewNote?: string;
|
|
70
|
+
};
|
|
39
71
|
}
|
|
40
|
-
export declare function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, }: AdminOrderEditorViewProps): React.JSX.Element;
|
|
72
|
+
export declare function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, sourceContext, currency, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, balance, }: AdminOrderEditorViewProps): React.JSX.Element;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { OrderAddonBadges } from "../../orders/components/OrderAddonBadges.js";
|
|
4
|
+
import { OrderProvenanceCard } from "../../orders/components/OrderProvenanceCard.js";
|
|
5
|
+
import { groupOrderItemsByLine } from "../../orders/utils/bundle-grouping.js";
|
|
4
6
|
import { isManualPaymentMethod } from "../../orders/constants/payment-window.js";
|
|
5
7
|
import { useApiMutation } from "@mohasinac/appkit/client";
|
|
6
8
|
import React, { useState } from "react";
|
|
@@ -18,6 +20,13 @@ import { useFormShellState, FormShellContext } from "../../../ui/forms/index.js"
|
|
|
18
20
|
import { applyZodIssues } from "../../../ui/forms/apply-zod-issues.js";
|
|
19
21
|
import { FormErrorSummary } from "../../../ui/forms/FormErrorSummary.js";
|
|
20
22
|
import { ValidationError } from "../../../errors/validation-error.js";
|
|
23
|
+
/**
|
|
24
|
+
* One item row. Extracted so the single and grouped render paths share exactly
|
|
25
|
+
* one piece of markup rather than two that can drift.
|
|
26
|
+
*/
|
|
27
|
+
function adminOrderItemRow(item, key) {
|
|
28
|
+
return (_jsxs(Div, { layout: "flex", align: "center", gap: "3", padding: "sm", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) }), _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title }), _jsxs(Text, { size: "xs", color: "muted", children: ["Qty: ", item.quantity, " \u00D7 ", toCurrency(item.unitPrice)] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "semibold", children: toCurrency(item.totalPrice) })] }, key));
|
|
29
|
+
}
|
|
21
30
|
const STATUS_OPTIONS = [
|
|
22
31
|
{ label: "Pending", value: "pending" },
|
|
23
32
|
{ label: "Processing", value: "processing" },
|
|
@@ -37,7 +46,7 @@ const CARRIER_OPTIONS = [
|
|
|
37
46
|
{ label: "Other", value: "Other" },
|
|
38
47
|
];
|
|
39
48
|
// --- Component ---------------------------------------------------------------
|
|
40
|
-
export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, }) {
|
|
49
|
+
export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, sourceContext, currency, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, balance, }) {
|
|
41
50
|
const queryClient = useQueryClient();
|
|
42
51
|
const { showToast } = useToast();
|
|
43
52
|
const [draft, setDraft] = React.useState({
|
|
@@ -74,6 +83,19 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
|
|
|
74
83
|
const [reviewNoteError, setReviewNoteError] = React.useState(null);
|
|
75
84
|
const [isRequestingReupload, setIsRequestingReupload] = useState(false);
|
|
76
85
|
const [isRejectingFraud, setIsRejectingFraud] = useState(false);
|
|
86
|
+
/*
|
|
87
|
+
* D55 — the balance review's OWN note and its own pending flags.
|
|
88
|
+
*
|
|
89
|
+
* 🛑 Not shared with `reviewNote` above, which belongs to the deposit's
|
|
90
|
+
* three actions. One note box feeding two reviews is the bug where an admin
|
|
91
|
+
* types "blurry screenshot" meaning the balance and sends it as a
|
|
92
|
+
* fraud-rejection reason on the deposit — and the deposit's rejection bans
|
|
93
|
+
* the account for 7 days.
|
|
94
|
+
*/
|
|
95
|
+
const [balanceNote, setBalanceNote] = React.useState("");
|
|
96
|
+
const [balanceNoteError, setBalanceNoteError] = React.useState(null);
|
|
97
|
+
const [isAcceptingBalance, setIsAcceptingBalance] = useState(false);
|
|
98
|
+
const [isRejectingBalance, setIsRejectingBalance] = useState(false);
|
|
77
99
|
// This used to inline `paymentMethod === "cash" || === "upi_manual"`, which
|
|
78
100
|
// omitted `emi` — so the whole proof panel never rendered for an EMI order and
|
|
79
101
|
// an admin had no way to verify one, even though the list row said "Awaiting
|
|
@@ -96,6 +118,10 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
|
|
|
96
118
|
notes: "",
|
|
97
119
|
});
|
|
98
120
|
setReviewNote("");
|
|
121
|
+
// Reset with its sibling — a note typed for one order must not reappear
|
|
122
|
+
// under the next one the drawer opens.
|
|
123
|
+
setBalanceNote("");
|
|
124
|
+
setBalanceNoteError(null);
|
|
99
125
|
}
|
|
100
126
|
}, [open, currentStatus]);
|
|
101
127
|
const saveMutation = useApiMutation({
|
|
@@ -207,7 +233,49 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
|
|
|
207
233
|
setIsRejectingFraud(false);
|
|
208
234
|
}
|
|
209
235
|
};
|
|
210
|
-
|
|
236
|
+
/**
|
|
237
|
+
* D55 — accept or reject the pre-order BALANCE proof.
|
|
238
|
+
*
|
|
239
|
+
* 🛑 One handler, because the two verdicts differ by a boolean and a note
|
|
240
|
+
* and nothing else. The deposit has three handlers because its three
|
|
241
|
+
* outcomes are genuinely different operations (verify / re-ask / cancel +
|
|
242
|
+
* ban); mirroring that shape here would be three copies of one request.
|
|
243
|
+
*
|
|
244
|
+
* The note requirement is checked in BOTH places on purpose: here so the
|
|
245
|
+
* admin is told before the round trip, and in `reviewBalanceProof` because
|
|
246
|
+
* the route is reachable without this component.
|
|
247
|
+
*/
|
|
248
|
+
const handleBalanceReview = async (approve) => {
|
|
249
|
+
if (!orderId)
|
|
250
|
+
return;
|
|
251
|
+
if (!approve && !balanceNote.trim()) {
|
|
252
|
+
setBalanceNoteError("Say what was wrong — without it the buyer will send the same proof again.");
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
setBalanceNoteError(null);
|
|
256
|
+
const setPending = approve ? setIsAcceptingBalance : setIsRejectingBalance;
|
|
257
|
+
setPending(true);
|
|
258
|
+
try {
|
|
259
|
+
await apiClient.post(ADMIN_ENDPOINTS.ORDER_BALANCE_REVIEW(orderId), {
|
|
260
|
+
approve,
|
|
261
|
+
...(balanceNote.trim() ? { note: balanceNote.trim() } : {}),
|
|
262
|
+
});
|
|
263
|
+
showToast(approve
|
|
264
|
+
? "Balance accepted. The order is clear to ship."
|
|
265
|
+
: "Proof returned to the buyer with your note.", "success");
|
|
266
|
+
queryClient.invalidateQueries({ queryKey: ["admin", "orders"] });
|
|
267
|
+
onClose();
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
void normalizeError(err);
|
|
271
|
+
// Authored copy — `err.message` is server text (Root Cause #86).
|
|
272
|
+
showToast("Could not record that decision.", "error");
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
setPending(false);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: orderLabel ? `Order: ${orderLabel}` : "Update Order", children: _jsx(FormShellContext.Provider, { value: form.shellCtx, children: _jsxs(Stack, { gap: "md", padding: "md", children: [_jsx(FormErrorSummary, {}), addons && _jsx(OrderAddonBadges, { order: addons, variant: "detail" }), _jsx(OrderProvenanceCard, { sourceContext: sourceContext, currency: currency }), items && items.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Label, { size: "sm", weight: "medium", color: "primary", children: ["Items (", items.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: groupOrderItemsByLine(items).map((group, gi) => group.kind === "single" ? (adminOrderItemRow(group.item, `s-${group.index}`)) : (_jsxs(Div, { children: [_jsxs(Div, { layout: "flex", align: "center", gap: "2", padding: "x-sm", surface: "muted", children: [_jsx(Text, { size: "xs", weight: "semibold", children: group.groupTitle ?? "Grouped purchase" }), _jsx(Text, { size: "xs", color: "muted", children: `${group.memberCount} ${group.memberCount === 1 ? "item" : "items"} · one line` })] }), _jsx(Div, { paddingX: "l-md", children: group.items.map(({ item, index }) => adminOrderItemRow(item, `m-${index}`)) })] }, `g-${gi}`))) })] })), _jsx(SectionForm, { sections: sections, values: draft, onChange: (partial) => setDraft((d) => ({ ...d, ...partial })), onSubmit: () => {
|
|
211
279
|
// SectionForm surfaces errors and then submits regardless, so the
|
|
212
280
|
// guard is here.
|
|
213
281
|
form.clearErrors();
|
|
@@ -217,5 +285,15 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
|
|
|
217
285
|
return;
|
|
218
286
|
}
|
|
219
287
|
saveMutation.mutate();
|
|
220
|
-
}, onValidationChange: () => form.validate(draft), schema: adminOrderUpdateSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, submitLabel: "Save changes", cancelLabel: "Cancel", onCancel: onClose, isLoading: saveMutation.isPending }), isManualPayment && (_jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Payment Proof" }), isRejected && (_jsx(Text, { size: "xs", className: "text-error", weight: "medium", children: "Payment rejected as fraud \u2014 no further action available here." })), isReuploadRequested && !isVerified && (_jsx(Text, { size: "xs", className: "text-warning", weight: "medium", children: "Re-upload requested \u2014 waiting for the buyer to submit a new proof." })), paymentProofUrl ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Div, { border: "default", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: paymentProofUrl, alt: "Payment screenshot", size: "card" }) }), paymentTransactionId && (_jsxs(Text, { size: "xs", color: "muted", children: ["UTR: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: paymentTransactionId })] })), needsVerification && (_jsxs(Stack, { gap: "xs", children: [(displayedUpiId || buyerReportedUpiId) && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Expected UPI: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: displayedUpiId || "—" }), " · ", "Buyer reported: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerReportedUpiId || "—" })] }), paymentUpiMismatch && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-error/20", surface: "danger-surface", children: _jsx(Text, { size: "xs", className: "text-error", weight: "semibold", children: "UPI mismatch \u2014 buyer-reported ID doesn't match the one shown for this order." }) }))] })), _jsxs(Text, { size: "xs", color: "muted", children: ["Buyer marked as paid: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerMarkedPaid ? "Yes" : "No" }), " · ", "Fraud agreement accepted: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerFraudAgreementAccepted ? "Yes" : "No" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Review note" }), _jsxs(Text, { size: "xs", color: "muted", children: ["Sent with ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: "Request re-upload" }), " or", " ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: "Reject as fraud" }), " below. Not saved by \u201CSave changes\u201D."] }), _jsx(Textarea, { value: reviewNote, onChange: (e) => handleReviewNoteChange(e.target.value), rows: 2, "aria-invalid": reviewNoteError ? true : undefined, placeholder: "e.g. Screenshot is blurry \u2014 amount not readable / UPI ID doesn't match order" }), reviewNoteError && (_jsx(Text, { size: "xs", color: "error", role: "alert", children: reviewNoteError }))] }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["verify-payment"], onClick: handleVerifyPayment, isLoading: isVerifyingPayment, disabled: isVerifyingPayment, variant: "primary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["request-payment-reupload"], onClick: handleRequestReupload, isLoading: isRequestingReupload, disabled: isRequestingReupload, variant: "secondary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["reject-payment-fraud"], onClick: handleRejectFraud, isLoading: isRejectingFraud, disabled: isRejectingFraud, variant: "danger", className: "w-full" })] })), isVerified && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-success/20", surface: "success-surface", children: _jsx(Text, { size: "xs", className: "text-success", weight: "medium", children: "Payment verified" }) }))] })) : (_jsx(Text, { size: "xs", color: "faint", children: "No proof uploaded yet." }))] }))
|
|
288
|
+
}, onValidationChange: () => form.validate(draft), schema: adminOrderUpdateSchema, openIds: nav.openIds, onOpenChange: nav.setOpenIds, submitLabel: "Save changes", cancelLabel: "Cancel", onCancel: onClose, isLoading: saveMutation.isPending }), isManualPayment && (_jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Payment Proof" }), isRejected && (_jsx(Text, { size: "xs", className: "text-error", weight: "medium", children: "Payment rejected as fraud \u2014 no further action available here." })), isReuploadRequested && !isVerified && (_jsx(Text, { size: "xs", className: "text-warning", weight: "medium", children: "Re-upload requested \u2014 waiting for the buyer to submit a new proof." })), paymentProofUrl ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Div, { border: "default", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: paymentProofUrl, alt: "Payment screenshot", size: "card" }) }), paymentTransactionId && (_jsxs(Text, { size: "xs", color: "muted", children: ["UTR: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: paymentTransactionId })] })), needsVerification && (_jsxs(Stack, { gap: "xs", children: [(displayedUpiId || buyerReportedUpiId) && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Expected UPI: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: displayedUpiId || "—" }), " · ", "Buyer reported: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerReportedUpiId || "—" })] }), paymentUpiMismatch && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-error/20", surface: "danger-surface", children: _jsx(Text, { size: "xs", className: "text-error", weight: "semibold", children: "UPI mismatch \u2014 buyer-reported ID doesn't match the one shown for this order." }) }))] })), _jsxs(Text, { size: "xs", color: "muted", children: ["Buyer marked as paid: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerMarkedPaid ? "Yes" : "No" }), " · ", "Fraud agreement accepted: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerFraudAgreementAccepted ? "Yes" : "No" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Review note" }), _jsxs(Text, { size: "xs", color: "muted", children: ["Sent with ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: "Request re-upload" }), " or", " ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: "Reject as fraud" }), " below. Not saved by \u201CSave changes\u201D."] }), _jsx(Textarea, { value: reviewNote, onChange: (e) => handleReviewNoteChange(e.target.value), rows: 2, "aria-invalid": reviewNoteError ? true : undefined, placeholder: "e.g. Screenshot is blurry \u2014 amount not readable / UPI ID doesn't match order" }), reviewNoteError && (_jsx(Text, { size: "xs", color: "error", role: "alert", children: reviewNoteError }))] }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["verify-payment"], onClick: handleVerifyPayment, isLoading: isVerifyingPayment, disabled: isVerifyingPayment, variant: "primary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["request-payment-reupload"], onClick: handleRequestReupload, isLoading: isRequestingReupload, disabled: isRequestingReupload, variant: "secondary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["reject-payment-fraud"], onClick: handleRejectFraud, isLoading: isRejectingFraud, disabled: isRejectingFraud, variant: "danger", className: "w-full" })] })), isVerified && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-success/20", surface: "success-surface", children: _jsx(Text, { size: "xs", className: "text-success", weight: "medium", children: "Payment verified" }) }))] })) : (_jsx(Text, { size: "xs", color: "faint", children: "No proof uploaded yet." }))] })), balance && (_jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Pre-order balance" }), _jsxs(Text, { size: "xs", color: "muted", children: [toCurrency(balance.amount), " \u00B7", " ", balance.status === "paid"
|
|
289
|
+
? "accepted"
|
|
290
|
+
: balance.status === "submitted"
|
|
291
|
+
? "proof submitted, awaiting your decision"
|
|
292
|
+
: balance.status === "rejected"
|
|
293
|
+
? "returned to the buyer"
|
|
294
|
+
: "open — the buyer has not paid yet", balance.transactionId ? ` · Ref ${balance.transactionId}` : ""] }), balance.status === "rejected" && balance.reviewNote && (_jsxs(Text, { size: "xs", color: "muted", children: ["Last note: ", balance.reviewNote] })), balance.status === "submitted" && (_jsxs(Stack, { gap: "xs", children: [balance.proofUrl && (_jsx(Div, { border: "default", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: balance.proofUrl, alt: "Balance payment screenshot", size: "card" }) })), _jsx(Text, { size: "xs", color: "muted", children: "Check the reference against the bank before accepting. Accepting does NOT post a ledger entry \u2014 the sale was recognised in full when the order was created, so a second entry would double it." }), _jsx(Textarea, { value: balanceNote, onChange: (e) => {
|
|
295
|
+
setBalanceNote(e.target.value);
|
|
296
|
+
if (balanceNoteError)
|
|
297
|
+
setBalanceNoteError(null);
|
|
298
|
+
}, rows: 2, "aria-invalid": balanceNoteError ? true : undefined, placeholder: "Required when rejecting \u2014 e.g. reference does not match any credit we received" }), balanceNoteError && (_jsx(Text, { size: "xs", color: "error", role: "alert", children: balanceNoteError })), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["verify-balance"], onClick: () => void handleBalanceReview(true), isLoading: isAcceptingBalance, disabled: isAcceptingBalance || isRejectingBalance, variant: "primary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["reject-balance"], onClick: () => void handleBalanceReview(false), isLoading: isRejectingBalance, disabled: isAcceptingBalance || isRejectingBalance, variant: "secondary", className: "w-full" })] }))] }))] }) }) }));
|
|
221
299
|
}
|
|
@@ -2,6 +2,16 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { ShoppingBag, Hash, Scale, Wallet, RotateCcw, DollarSign, Gavel } from "lucide-react";
|
|
3
3
|
import { Code, Div, Heading, Li, Row, Section, Span, Stack, Text, Ul } from "../../../ui/index.js";
|
|
4
4
|
import { GC } from "../../_guide-cls.js";
|
|
5
|
+
import { resolvePublishedValues } from "../../../_internal/shared/content/published-values.js";
|
|
6
|
+
/*
|
|
7
|
+
* Phase 9 — read from the published-value registry rather than restated.
|
|
8
|
+
*
|
|
9
|
+
* 🛑 No settings argument, deliberately: this is the OBLIGATION-derived window
|
|
10
|
+
* and it has no settings override, so passing settings would imply an
|
|
11
|
+
* admin-editable number that does not exist (the 18-dead-settings pattern,
|
|
12
|
+
* from the "control that appears to do something the code ignores" direction).
|
|
13
|
+
*/
|
|
14
|
+
const { returnRespondHours: RETURN_RESPOND_HOURS, refundBusinessDaysMin: REFUND_DAYS_MIN, refundBusinessDaysMax: REFUND_DAYS_MAX, } = resolvePublishedValues();
|
|
5
15
|
export function AdminOrdersGuideView() {
|
|
6
16
|
return (_jsxs(Stack, { className: "max-w-3xl mx-auto", padding: "b-2xl", gap: "xl", children: [_jsxs(Section, { children: [_jsxs(Row, { className: "mb-2", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "xl", children: _jsx(ShoppingBag, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin Guide" })] }), _jsx(Heading, { level: 1, className: "text-[var(--appkit-color-text)] mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "Orders & Finance" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Order statuses, payouts, disputes, returns, and commission math on LetItRip." })] }), [
|
|
7
17
|
{
|
|
@@ -11,7 +21,18 @@ export function AdminOrdersGuideView() {
|
|
|
11
21
|
["PROCESSING", "Seller has acknowledged and is packing."],
|
|
12
22
|
["SHIPPED", "Dispatched. Tracking number provided by seller."],
|
|
13
23
|
["DELIVERED", "Carrier confirmed delivery."],
|
|
14
|
-
|
|
24
|
+
/*
|
|
25
|
+
* 🛑 Phase 9, decision 23 — this said "3 days" while the FAQ
|
|
26
|
+
* said 48 hours, and the FAQ WINS: it is the buyer-facing
|
|
27
|
+
* promise and the one a buyer has already read. The number now
|
|
28
|
+
* comes from `OBLIGATION_POLICIES["return-respond"]`, which is
|
|
29
|
+
* what actually closes the window — so a staff-facing guide can
|
|
30
|
+
* no longer quietly contradict what the buyer was told.
|
|
31
|
+
*/
|
|
32
|
+
[
|
|
33
|
+
"RETURN_REQUESTED",
|
|
34
|
+
`Buyer opened a return. Seller has ${RETURN_RESPOND_HOURS} hours to respond.`,
|
|
35
|
+
],
|
|
15
36
|
["REFUNDED", "Return accepted and refund issued via Razorpay."],
|
|
16
37
|
["CANCELLED", "Cancelled before shipping. Payment returned."],
|
|
17
38
|
].map(([status, note]) => (_jsxs(Row, { gap: "3", children: [_jsx(Text, { className: "flex-shrink-0 font-mono text-[var(--appkit-color-primary)] w-40", size: "xs", weight: "semibold", children: status }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: note })] }, status))), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] pt-[0.5rem]", size: "sm", children: "Admin-only transitions: force-cancel (any status before DELIVERED), force-refund (post-DELIVERED dispute)." })] })),
|
|
@@ -30,7 +51,7 @@ export function AdminOrdersGuideView() {
|
|
|
30
51
|
},
|
|
31
52
|
{
|
|
32
53
|
Icon: RotateCcw, title: "Return Requests",
|
|
33
|
-
content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsx(Li, { children: "Admin can view the return reason and attached photos on the order detail page." }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "When to override a seller rejection" }), ": Only when photographic evidence clearly supports the buyer's claim of a significant not-as-described defect. Do not override for buyer's remorse."] }),
|
|
54
|
+
content: (_jsxs(Ul, { className: GC.listMuted, children: [_jsx(Li, { children: "Admin can view the return reason and attached photos on the order detail page." }), _jsxs(Li, { children: [_jsx(Span, { weight: "bold", children: "When to override a seller rejection" }), ": Only when photographic evidence clearly supports the buyer's claim of a significant not-as-described defect. Do not override for buyer's remorse."] }), _jsxs(Li, { children: ["After admin override: the refund is issued automatically (", REFUND_DAYS_MIN, "\u2013", REFUND_DAYS_MAX, " business days for card, 1\u20132 for UPI). Seller payout for that order is withheld."] })] })),
|
|
34
55
|
},
|
|
35
56
|
{
|
|
36
57
|
Icon: DollarSign, title: "Commission & Settlement Math",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { groupOrderItemsByLine } from "../../orders/utils/bundle-grouping.js";
|
|
3
4
|
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit/client";
|
|
4
5
|
import { sortBy } from "@mohasinac/appkit/client";
|
|
5
6
|
import React, { useState, useCallback } from "react";
|
|
@@ -61,14 +62,70 @@ function toOrderItemRows(item) {
|
|
|
61
62
|
quantity: typeof entry.quantity === "number" ? entry.quantity : 1,
|
|
62
63
|
unitPrice: typeof entry.unitPrice === "number" ? entry.unitPrice : 0,
|
|
63
64
|
totalPrice: typeof entry.totalPrice === "number" ? entry.totalPrice : 0,
|
|
65
|
+
/*
|
|
66
|
+
* The collapse keys, carried so the editor can group a bundle or
|
|
67
|
+
* buyer-assembled group back into one line the way the buyer's receipt
|
|
68
|
+
* does. Narrowed by `typeof` like every field above rather than cast —
|
|
69
|
+
* this function's whole job is turning a loose record into a typed row,
|
|
70
|
+
* and a cast here is how `title`/`price` once slipped through (RC#45).
|
|
71
|
+
*/
|
|
72
|
+
groupSlug: typeof entry.groupSlug === "string" ? entry.groupSlug : undefined,
|
|
73
|
+
groupTitle: typeof entry.groupTitle === "string" ? entry.groupTitle : undefined,
|
|
74
|
+
bundleCategorySlug: typeof entry.bundleCategorySlug === "string" ? entry.bundleCategorySlug : undefined,
|
|
75
|
+
bundleProductIds: Array.isArray(entry.bundleProductIds)
|
|
76
|
+
? entry.bundleProductIds.filter((v) => typeof v === "string")
|
|
77
|
+
: undefined,
|
|
64
78
|
}));
|
|
65
79
|
}
|
|
80
|
+
/*
|
|
81
|
+
* The row's headline, counted in LINES rather than item rows.
|
|
82
|
+
*
|
|
83
|
+
* 🛑 `extraCount` deliberately changed meaning when grouping landed: it was
|
|
84
|
+
* `items.length - 1`, so a bundle of four read "Dranzer S +3 more" — naming an
|
|
85
|
+
* arbitrary member and implying four separate things were bought. It is now
|
|
86
|
+
* `groups.length - 1`, so the same order reads "Starter Bundle +0" and a
|
|
87
|
+
* bundle plus one standalone reads "+1 more". The number a buyer or admin
|
|
88
|
+
* reads on a row should match the number of lines they will see inside it.
|
|
89
|
+
*/
|
|
90
|
+
/*
|
|
91
|
+
* `sourceContext` arrives as JsonValue off the raw admin-list doc, so it can
|
|
92
|
+
* be a string, an array or null — none of which `OrderProvenanceCard` can
|
|
93
|
+
* switch on. This narrows to "an object carrying a `path` tag" and nothing
|
|
94
|
+
* further: that is deliberately NOT the card's own switch duplicated, it only
|
|
95
|
+
* establishes that there is a tag to switch on, which is what makes the single
|
|
96
|
+
* cast true instead of hopeful. `as unknown as OrderSourceContext` would
|
|
97
|
+
* assert exactly the same thing while checking nothing (Root Cause #98).
|
|
98
|
+
*/
|
|
99
|
+
function toSourceContext(value) {
|
|
100
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
101
|
+
return undefined;
|
|
102
|
+
/*
|
|
103
|
+
* Via `object` rather than straight off the JsonValue union, for two
|
|
104
|
+
* reasons that pull the same way. `Array.isArray` does not narrow
|
|
105
|
+
* `JsonArray | JsonObject` apart, so `value.path` does not resolve; and
|
|
106
|
+
* `OrderSourceContext` is not assignable to `JsonObject` (its optional
|
|
107
|
+
* members admit `undefined`, which is not a JsonValue), so TS refuses that
|
|
108
|
+
* conversion outright. Probing as `{ path?: string }` keeps both the probe
|
|
109
|
+
* and the assertion legal without an `unknown` anywhere.
|
|
110
|
+
*/
|
|
111
|
+
const obj = value;
|
|
112
|
+
return typeof obj.path === "string"
|
|
113
|
+
? obj
|
|
114
|
+
: undefined;
|
|
115
|
+
}
|
|
66
116
|
function firstOrderItem(item) {
|
|
67
|
-
const
|
|
117
|
+
const groups = groupOrderItemsByLine(toOrderItemRows(item));
|
|
118
|
+
const first = groups[0];
|
|
119
|
+
const extraCount = Math.max(0, groups.length - 1);
|
|
120
|
+
if (!first)
|
|
121
|
+
return { extraCount: 0 };
|
|
122
|
+
if (first.kind === "single") {
|
|
123
|
+
return { image: first.item.image, title: first.item.title, extraCount };
|
|
124
|
+
}
|
|
68
125
|
return {
|
|
69
|
-
image: items[0]?.image,
|
|
70
|
-
title:
|
|
71
|
-
extraCount
|
|
126
|
+
image: first.items[0]?.item.image,
|
|
127
|
+
title: first.groupTitle ?? "Grouped purchase",
|
|
128
|
+
extraCount,
|
|
72
129
|
};
|
|
73
130
|
}
|
|
74
131
|
/**
|
|
@@ -206,6 +263,30 @@ export function AdminOrdersView({ children, ...props }) {
|
|
|
206
263
|
couponCode: toStringValue(item.couponCode) || undefined,
|
|
207
264
|
couponDiscount: Number(item.couponDiscount ?? 0) || undefined,
|
|
208
265
|
},
|
|
266
|
+
// Raw doc in, so no serializer entry — but unlike `addons` above,
|
|
267
|
+
// which coerces every field by hand, this one needs a tag check
|
|
268
|
+
// before it can be trusted. See `toSourceContext`.
|
|
269
|
+
sourceContext: toSourceContext(item.sourceContext),
|
|
270
|
+
/*
|
|
271
|
+
* D55. Coerced field by field like `addons` above, deliberately
|
|
272
|
+
* NOT `as AdminOrderEditorViewProps["balance"]` — a cast at a
|
|
273
|
+
* raw-document boundary is where Root Cause #98 lives, and main
|
|
274
|
+
* had just removed the same cast from `sourceContext` one line up.
|
|
275
|
+
*
|
|
276
|
+
* `proofUrl` is omitted ON PURPOSE: this is the LIST payload, and
|
|
277
|
+
* the screenshot is admin-detail only. A list row never needs it.
|
|
278
|
+
*/
|
|
279
|
+
balance: item.balance
|
|
280
|
+
? {
|
|
281
|
+
amount: Number(item.balance.amount ?? 0),
|
|
282
|
+
status: toStringValue(item.balance.status, "awaiting"),
|
|
283
|
+
transactionId: toStringValue(item.balance.transactionId) ||
|
|
284
|
+
undefined,
|
|
285
|
+
reviewNote: toStringValue(item.balance.reviewNote) ||
|
|
286
|
+
undefined,
|
|
287
|
+
}
|
|
288
|
+
: undefined,
|
|
289
|
+
currency: toStringValue(item.currency, "") || undefined,
|
|
209
290
|
};
|
|
210
291
|
}),
|
|
211
292
|
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
@@ -289,5 +370,5 @@ export function AdminOrdersView({ children, ...props }) {
|
|
|
289
370
|
] })),
|
|
290
371
|
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_ORDER_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id, paymentReview: "" })) }), _jsx(FilterChipGroup, { label: "Manual payment", tabs: ADMIN_ORDER_PAYMENT_REVIEW_TABS, value: pendingFilters.paymentReview ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, paymentReview: id, status: "" })) })] })),
|
|
291
372
|
};
|
|
292
|
-
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminOrderEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), orderId: selectedRow?.id, orderLabel: selectedRow?.primary, currentStatus: selectedRow?.status, items: selectedRow?.items, paymentProofUrl: selectedRow?.paymentProofUrl, paymentTransactionId: selectedRow?.paymentTransactionId, paymentMethod: selectedRow?.paymentMethod, paymentStatus: selectedRow?.paymentStatus, displayedUpiId: selectedRow?.displayedUpiId, buyerReportedUpiId: selectedRow?.buyerReportedUpiId, paymentUpiMismatch: selectedRow?.paymentUpiMismatch, buyerMarkedPaid: selectedRow?.buyerMarkedPaid, buyerFraudAgreementAccepted: selectedRow?.buyerFraudAgreementAccepted, paymentReviewOutcome: selectedRow?.paymentReviewOutcome, addons: selectedRow?.addons })] }));
|
|
373
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminOrderEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), orderId: selectedRow?.id, orderLabel: selectedRow?.primary, currentStatus: selectedRow?.status, items: selectedRow?.items, paymentProofUrl: selectedRow?.paymentProofUrl, paymentTransactionId: selectedRow?.paymentTransactionId, paymentMethod: selectedRow?.paymentMethod, paymentStatus: selectedRow?.paymentStatus, displayedUpiId: selectedRow?.displayedUpiId, buyerReportedUpiId: selectedRow?.buyerReportedUpiId, paymentUpiMismatch: selectedRow?.paymentUpiMismatch, buyerMarkedPaid: selectedRow?.buyerMarkedPaid, buyerFraudAgreementAccepted: selectedRow?.buyerFraudAgreementAccepted, paymentReviewOutcome: selectedRow?.paymentReviewOutcome, addons: selectedRow?.addons, sourceContext: selectedRow?.sourceContext, balance: selectedRow?.balance, currency: selectedRow?.currency })] }));
|
|
293
374
|
}
|
|
@@ -236,6 +236,28 @@ export function OwnerQuickActions({ resource, editHref, onDelete, onClone, owner
|
|
|
236
236
|
* 🛑 `inline-flex`, never `w-full`. In normal flow this must shrink to its
|
|
237
237
|
* own content — a full-width bar would read as a section divider and would
|
|
238
238
|
* stretch across a card body it is only a corner of.
|
|
239
|
+
*
|
|
240
|
+
* 🛑 `shrink-0` IS THE MOBILE FIX, and it is one word because the bug was
|
|
241
|
+
* one missing word.
|
|
242
|
+
*
|
|
243
|
+
* A flex item's default is `flex-shrink: 1`, so in the title/actions row
|
|
244
|
+
* this toolbar lives in — `<Row justify="between">` with an `<h1>` on the
|
|
245
|
+
* left — the heading and the toolbar compete for width, and at ~390px the
|
|
246
|
+
* toolbar LOSES. It compresses below its own content: the icons slide under
|
|
247
|
+
* each other, the padding collapses, and the 44px tap targets stop being
|
|
248
|
+
* 44px wide. That is the "works incorrectly on mobile" report.
|
|
249
|
+
*
|
|
250
|
+
* With `shrink-0` the toolbar keeps its intrinsic size and the HEADING gives
|
|
251
|
+
* way instead — which is right, because a heading wraps gracefully onto a
|
|
252
|
+
* second line and a row of hit targets does not.
|
|
253
|
+
*
|
|
254
|
+
* 🛑 Note what this is NOT: the desktop fix (`d94496c42`) gave the toolbar a
|
|
255
|
+
* container and added an audit whose two rules are about DOM ORDER — must
|
|
256
|
+
* come after the `<h1>`, must not share the heading's row unless that row is
|
|
257
|
+
* `justify="between"`. Both pass here. R2's exemption actively WHITELISTS
|
|
258
|
+
* the layout that breaks, so the surfaces that satisfy that audit are
|
|
259
|
+
* precisely the ones most likely to be wrong at phone width. A source-order
|
|
260
|
+
* rule cannot see a width problem.
|
|
239
261
|
*/
|
|
240
262
|
const boxClass = onDark
|
|
241
263
|
? /*
|
|
@@ -244,12 +266,12 @@ export function OwnerQuickActions({ resource, editHref, onDelete, onClone, owner
|
|
|
244
266
|
* LIGHT background in every light theme, so a themed box on a dark hero
|
|
245
267
|
* photo is a white rectangle over the artwork.
|
|
246
268
|
*/
|
|
247
|
-
"inline-flex rounded-[var(--appkit-radius-lg)] bg-black/55 backdrop-blur-sm p-[var(--appkit-space-1)] shadow-md"
|
|
269
|
+
"inline-flex shrink-0 rounded-[var(--appkit-radius-lg)] bg-black/55 backdrop-blur-sm p-[var(--appkit-space-1)] shadow-md"
|
|
248
270
|
: /*
|
|
249
271
|
* Off media: the real surface tokens, so it inverts with the theme
|
|
250
272
|
* instead of being a fixed near-white that disappears in dark mode.
|
|
251
273
|
*/
|
|
252
|
-
"inline-flex rounded-[var(--appkit-radius-lg)] bg-[var(--appkit-color-surface-elevated)] border border-[var(--appkit-color-border)] p-[var(--appkit-space-1)] shadow-sm";
|
|
274
|
+
"inline-flex shrink-0 rounded-[var(--appkit-radius-lg)] bg-[var(--appkit-color-surface-elevated)] border border-[var(--appkit-color-border)] p-[var(--appkit-space-1)] shadow-sm";
|
|
253
275
|
return (_jsxs(_Fragment, { children: [_jsxs(Row, { gap: "xs", align: "center", className: `${boxClass} ${className}`.trim(),
|
|
254
276
|
/*
|
|
255
277
|
* Stops the click reaching an ancestor `<Link>` or the card's own
|
|
@@ -457,6 +457,9 @@ export declare const ADMIN_COUPON_TYPE_TABS: readonly [{
|
|
|
457
457
|
}, {
|
|
458
458
|
readonly id: "buy_x_get_y";
|
|
459
459
|
readonly label: "Buy X Get Y";
|
|
460
|
+
}, {
|
|
461
|
+
readonly id: "points_earn";
|
|
462
|
+
readonly label: "Points Back";
|
|
460
463
|
}];
|
|
461
464
|
/** Empty-sentinel variant for views (like SellerBidsView) that drive the
|
|
462
465
|
* filter URL with `""` instead of `"All"` so the `"" == no filter` invariant
|
|
@@ -757,6 +760,9 @@ export declare const ADMIN_PAYMENT_CASE_KIND_TABS: readonly [{
|
|
|
757
760
|
}, {
|
|
758
761
|
readonly id: "shipping_unpriced";
|
|
759
762
|
readonly label: "Shipping unpriced";
|
|
763
|
+
}, {
|
|
764
|
+
readonly id: "courier_unavailable";
|
|
765
|
+
readonly label: "Courier unavailable";
|
|
760
766
|
}, {
|
|
761
767
|
readonly id: "dead_job";
|
|
762
768
|
readonly label: "Dead job";
|
|
@@ -245,6 +245,11 @@ export const ADMIN_COUPON_TYPE_TABS = [
|
|
|
245
245
|
{ id: "fixed", label: "Fixed" },
|
|
246
246
|
{ id: "free_shipping", label: "Free Shipping" },
|
|
247
247
|
{ id: "buy_x_get_y", label: "Buy X Get Y" },
|
|
248
|
+
// D51. `audit-filter-tab-enums`' COVERAGE rule fails on a real union value
|
|
249
|
+
// with no chip — which is what it was extended to catch after ten such gaps
|
|
250
|
+
// were found at once (Root Cause #72), including sellers being unable to
|
|
251
|
+
// filter their own `in_review` listings.
|
|
252
|
+
{ id: "points_earn", label: "Points Back" },
|
|
248
253
|
];
|
|
249
254
|
// ---------------------------------------------------------------------------
|
|
250
255
|
// Seller-dashboard filter-chip tab sets
|
|
@@ -441,6 +446,7 @@ export const ADMIN_PAYMENT_CASE_KIND_TABS = [
|
|
|
441
446
|
{ id: "protection_claim", label: "Protection claim" },
|
|
442
447
|
{ id: "grievance", label: "Grievance" },
|
|
443
448
|
{ id: "shipping_unpriced", label: "Shipping unpriced" },
|
|
449
|
+
{ id: "courier_unavailable", label: "Courier unavailable" },
|
|
444
450
|
{ id: "dead_job", label: "Dead job" },
|
|
445
451
|
{ id: "ledger_drift", label: "Points drift" },
|
|
446
452
|
];
|