@mohasinac/appkit 4.59.0 → 4.60.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 +62 -5
- package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +2 -2
- package/dist/_internal/server/features/brands/actions.js +78 -4
- package/dist/_internal/server/features/checkout/actions.d.ts +36 -13
- package/dist/_internal/server/features/checkout/actions.js +668 -183
- package/dist/_internal/server/features/checkout/supplier-identity.js +18 -1
- package/dist/_internal/server/features/gateway-events/index.d.ts +10 -0
- package/dist/_internal/server/features/gateway-events/index.js +10 -0
- package/dist/_internal/server/features/gateway-events/ingest.d.ts +127 -0
- package/dist/_internal/server/features/gateway-events/ingest.js +188 -0
- package/dist/_internal/server/features/gateway-events/redact.d.ts +66 -0
- package/dist/_internal/server/features/gateway-events/redact.js +151 -0
- package/dist/_internal/server/features/grievance/actions.d.ts +63 -0
- package/dist/_internal/server/features/grievance/actions.js +173 -0
- package/dist/_internal/server/features/grievance/index.d.ts +8 -0
- package/dist/_internal/server/features/grievance/index.js +8 -0
- package/dist/_internal/server/features/grouped/data.js +28 -3
- package/dist/_internal/server/features/lottery/actions.js +99 -4
- package/dist/_internal/server/features/order-completion/actions.d.ts +92 -0
- package/dist/_internal/server/features/order-completion/actions.js +336 -0
- package/dist/_internal/server/features/order-completion/index.d.ts +7 -0
- package/dist/_internal/server/features/order-completion/index.js +7 -0
- package/dist/_internal/server/features/orders/actions.d.ts +13 -0
- package/dist/_internal/server/features/orders/actions.js +56 -2
- package/dist/_internal/server/features/orders/adapters.js +26 -0
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
- package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
- package/dist/_internal/server/features/orders/index.d.ts +2 -1
- package/dist/_internal/server/features/orders/index.js +1 -1
- package/dist/_internal/server/features/orders/service.d.ts +60 -0
- package/dist/_internal/server/features/orders/service.js +74 -0
- package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
- package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
- package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
- package/dist/_internal/server/features/payment-cases/index.js +9 -0
- package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
- package/dist/_internal/server/features/payouts/actions.js +84 -10
- package/dist/_internal/server/features/points/adjust.d.ts +61 -0
- package/dist/_internal/server/features/points/adjust.js +121 -0
- package/dist/_internal/server/features/points/award.d.ts +65 -0
- package/dist/_internal/server/features/points/award.js +145 -0
- package/dist/_internal/server/features/price-index/market.d.ts +33 -0
- package/dist/_internal/server/features/price-index/market.js +54 -1
- package/dist/_internal/server/features/products/data.js +40 -22
- package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
- package/dist/_internal/server/features/protection-claims/actions.js +228 -0
- package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
- package/dist/_internal/server/features/protection-claims/index.js +8 -0
- package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
- package/dist/_internal/server/features/refunds/actions.js +259 -17
- package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
- package/dist/_internal/server/features/refunds/capacity.js +37 -0
- package/dist/_internal/server/features/reviews/actions.js +3 -2
- package/dist/_internal/server/features/search/index-build/sources.js +20 -1
- package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
- package/dist/_internal/server/features/shipping/reconcile.js +34 -3
- package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
- package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
- package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
- package/dist/_internal/server/features/stores/tabs.js +24 -4
- package/dist/_internal/server/functions/https.d.ts +31 -1
- package/dist/_internal/server/functions/https.js +52 -1
- package/dist/_internal/server/functions/scheduled.d.ts +72 -1
- package/dist/_internal/server/functions/scheduled.js +124 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
- package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
- package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
- package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
- package/dist/_internal/server/jobs/core/index.d.ts +2 -0
- package/dist/_internal/server/jobs/core/index.js +2 -0
- package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
- package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
- package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
- package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
- package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
- package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
- package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
- package/dist/_internal/server/jobs/handlers/index.js +5 -0
- package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
- package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
- package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
- package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +71 -0
- package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
- package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
- package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
- package/dist/_internal/shared/checkout/lanes.js +46 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
- package/dist/_internal/shared/checkout/order-math.js +251 -3
- package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
- package/dist/_internal/shared/checkout/refundable-base.js +122 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
- package/dist/_internal/shared/features/auctions/config.js +30 -1
- package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
- package/dist/_internal/shared/features/brands/schema.js +18 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
- package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
- package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
- package/dist/_internal/shared/features/print/label-profiles.js +350 -0
- package/dist/_internal/shared/features/products/to-product-item.js +33 -5
- package/dist/_internal/shared/fees/calculator.d.ts +31 -2
- package/dist/_internal/shared/fees/calculator.js +103 -5
- package/dist/_internal/shared/fees/india-states.d.ts +195 -0
- package/dist/_internal/shared/fees/india-states.js +190 -0
- package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
- package/dist/_internal/shared/fees/payout-hold.js +74 -0
- package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
- package/dist/_internal/shared/obligations/deadlines.js +155 -0
- package/dist/_internal/shared/shipping/quote.d.ts +96 -0
- package/dist/_internal/shared/shipping/quote.js +67 -0
- package/dist/_internal/shared/time/business-days.d.ts +91 -0
- package/dist/_internal/shared/time/business-days.js +185 -0
- package/dist/cli/index.js +0 -2
- package/dist/client.d.ts +9 -4
- package/dist/client.js +26 -3
- package/dist/configs/tailwind.d.ts +0 -12
- package/dist/configs/tailwind.js +28 -15
- package/dist/constants/api-endpoints.d.ts +51 -9
- package/dist/constants/api-endpoints.js +23 -7
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.js +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/payment.d.ts +131 -0
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/about/components/DeveloperView.js +5 -2
- package/dist/features/about/components/FAQPageView.js +2 -1
- package/dist/features/about/components/FeesView.js +2 -1
- package/dist/features/about/components/HelpPageView.js +2 -1
- package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
- package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
- package/dist/features/about/components/HowEmiWorksView.js +2 -1
- package/dist/features/about/components/HowOffersWorkView.js +2 -1
- package/dist/features/about/components/HowOrdersWorkView.js +2 -1
- package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
- package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
- package/dist/features/about/components/HowReviewsWorkView.js +2 -1
- package/dist/features/about/components/PolicyPageView.js +2 -1
- package/dist/features/about/components/ProseHero.d.ts +73 -0
- package/dist/features/about/components/ProseHero.js +18 -0
- package/dist/features/about/components/SecurityPrivacyView.js +2 -1
- package/dist/features/about/components/ShippingPolicyView.js +2 -1
- package/dist/features/about/components/TrackOrderView.js +2 -1
- package/dist/features/about/components/UnauthorizedView.js +1 -1
- package/dist/features/about/index.d.ts +2 -0
- package/dist/features/about/index.js +5 -0
- package/dist/features/account/actions/address-actions.d.ts +26 -2
- package/dist/features/account/actions/address-actions.js +25 -1
- package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
- package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
- package/dist/features/account/components/UserOrdersView.js +17 -0
- package/dist/features/account/components/UserSidebar.js +1 -1
- package/dist/features/account/hooks/useNotifications.js +29 -0
- package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
- package/dist/features/admin/components/AdminBeysView.js +2 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
- package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
- package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
- package/dist/features/admin/components/AdminCategoriesView.js +1 -1
- package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
- package/dist/features/admin/components/AdminContactEditorView.js +1 -1
- package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
- package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
- package/dist/features/admin/components/AdminFaqsView.js +2 -2
- package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
- package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
- package/dist/features/admin/components/AdminGuideHubView.js +1 -1
- package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
- package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
- package/dist/features/admin/components/AdminPartsView.js +2 -2
- package/dist/features/admin/components/AdminProductsView.js +75 -4
- package/dist/features/admin/components/AdminSidebar.js +2 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
- package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
- package/dist/features/admin/components/AdminUsersView.js +72 -1
- package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
- package/dist/features/admin/components/AdminViewCards.js +4 -4
- package/dist/features/admin/components/DataListingView.d.ts +28 -0
- package/dist/features/admin/components/DataListingView.js +175 -14
- package/dist/features/admin/components/DataTable.d.ts +12 -1
- package/dist/features/admin/components/DataTable.js +3 -3
- package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
- package/dist/features/admin/components/OwnerQuickActions.js +60 -2
- package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
- package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
- package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
- package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
- package/dist/features/admin/components/index.d.ts +4 -0
- package/dist/features/admin/components/index.js +6 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
- package/dist/features/admin/constants/filter-tabs.js +106 -0
- package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
- package/dist/features/admin/repository/site-settings.repository.js +82 -1
- package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
- package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
- package/dist/features/admin/schemas/firestore.js +5 -1
- package/dist/features/admin/schemas/site-settings-form.js +30 -1
- package/dist/features/auctions/actions/bid-actions.js +13 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
- package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
- package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
- package/dist/features/audit-log/schemas/firestore.js +14 -0
- package/dist/features/auth/permissions/constants.d.ts +17 -1
- package/dist/features/auth/permissions/constants.js +18 -0
- package/dist/features/blog/components/BlogPostView.js +1 -1
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
- package/dist/features/categories/api/[id]/description-template/route.js +107 -1
- package/dist/features/categories/components/BrandDetailPageView.js +2 -2
- package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
- package/dist/features/categories/components/BundleDetailView.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
- package/dist/features/categories/components/CategoryForm.js +12 -1
- package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
- package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
- package/dist/features/categories/repository/market-price-index.repository.js +105 -0
- package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
- package/dist/features/categories/repository/price-observation.repository.js +132 -0
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/events/components/EventStatusBadge.js +1 -1
- package/dist/features/faq/components/FAQAccordion.js +2 -1
- package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
- package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
- package/dist/features/faq/components/RelatedFAQs.js +1 -1
- package/dist/features/filters/FilterFacetSection.js +1 -1
- package/dist/features/filters/SwitchFilter.js +1 -1
- package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
- package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
- package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
- package/dist/features/gateway-events/schemas/firestore.js +125 -0
- package/dist/features/gateway-events/schemas/index.d.ts +81 -0
- package/dist/features/gateway-events/schemas/index.js +79 -0
- package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
- package/dist/features/homepage/components/AnnouncementBar.js +37 -1
- package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CTABannerSection.js +1 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
- package/dist/features/homepage/components/HeroCarousel.js +2 -2
- package/dist/features/homepage/components/SocialPostCard.js +1 -1
- package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
- package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
- package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
- package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
- package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
- package/dist/features/inventory/components/ReceiveStationView.js +413 -0
- package/dist/features/inventory/components/operator-message.d.ts +29 -0
- package/dist/features/inventory/components/operator-message.js +41 -0
- package/dist/features/inventory/index.d.ts +13 -0
- package/dist/features/inventory/index.js +28 -0
- package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
- package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
- package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
- package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
- package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
- package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
- package/dist/features/inventory/schemas/firestore.d.ts +197 -0
- package/dist/features/inventory/schemas/firestore.js +159 -0
- package/dist/features/layout/AppLayoutShell.d.ts +12 -1
- package/dist/features/layout/AppLayoutShell.js +25 -12
- package/dist/features/layout/AutoBreadcrumbs.js +162 -3
- package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
- package/dist/features/layout/BottomNavbar.d.ts +1 -1
- package/dist/features/layout/BottomNavbar.js +2 -2
- package/dist/features/media/MediaAvatar.js +1 -1
- package/dist/features/media/MediaImage.d.ts +24 -4
- package/dist/features/media/MediaImage.js +44 -4
- package/dist/features/media/MediaLightbox.js +1 -1
- package/dist/features/media/modals/ImageCropModal.js +1 -1
- package/dist/features/media/modals/ImageEditor.js +1 -1
- package/dist/features/media/upload/MediaUploadField.js +91 -3
- package/dist/features/obligations/constants/policies.d.ts +92 -0
- package/dist/features/obligations/constants/policies.js +378 -0
- package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
- package/dist/features/obligations/repository/obligations.repository.js +158 -0
- package/dist/features/obligations/schemas/firestore.d.ts +160 -0
- package/dist/features/obligations/schemas/firestore.js +87 -0
- package/dist/features/orders/actions/order-actions.d.ts +43 -4
- package/dist/features/orders/actions/order-actions.js +60 -3
- package/dist/features/orders/actions/refund-actions.js +70 -10
- package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
- package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
- package/dist/features/orders/components/OrdersList.js +2 -2
- package/dist/features/orders/components/RefundHistoryTable.js +3 -1
- package/dist/features/orders/components/index.d.ts +2 -0
- package/dist/features/orders/components/index.js +6 -0
- package/dist/features/orders/constants/transitions.d.ts +59 -0
- package/dist/features/orders/constants/transitions.js +104 -0
- package/dist/features/orders/repository/orders.repository.d.ts +109 -6
- package/dist/features/orders/repository/orders.repository.js +464 -21
- package/dist/features/orders/schemas/firestore.d.ts +270 -0
- package/dist/features/orders/schemas/firestore.js +24 -0
- package/dist/features/orders/schemas/index.d.ts +61 -0
- package/dist/features/orders/schemas/index.js +22 -0
- package/dist/features/orders/types/index.d.ts +79 -0
- package/dist/features/parts/components/PartsIndexPageView.js +8 -1
- package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
- package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
- package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
- package/dist/features/payment-cases/schemas/firestore.js +105 -0
- package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
- package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
- package/dist/features/payments/repository/payout.repository.d.ts +0 -1
- package/dist/features/payments/repository/payout.repository.js +0 -1
- package/dist/features/points/repository/points.repository.d.ts +122 -0
- package/dist/features/points/repository/points.repository.js +304 -1
- package/dist/features/points/schemas/adjust-form.d.ts +58 -0
- package/dist/features/points/schemas/adjust-form.js +63 -0
- package/dist/features/points/schemas/firestore.d.ts +108 -1
- package/dist/features/points/schemas/firestore.js +84 -0
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
- package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
- package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
- package/dist/features/price-index/market-stats.d.ts +82 -0
- package/dist/features/price-index/market-stats.js +230 -0
- package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
- package/dist/features/print/components/BoxLabelPrintView.js +66 -0
- package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
- package/dist/features/print/components/LabelProfilePicker.js +38 -0
- package/dist/features/print/components/labels.d.ts +117 -0
- package/dist/features/print/components/labels.js +258 -0
- package/dist/features/print/components/labels.style.css +46 -0
- package/dist/features/print/index.d.ts +6 -0
- package/dist/features/print/index.js +16 -0
- package/dist/features/products/columns/productTableColumns.js +5 -4
- package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
- package/dist/features/products/components/PrizeDrawCollage.js +1 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
- package/dist/features/products/components/PrizeRevealModal.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.js +18 -4
- package/dist/features/products/components/ProductForm.js +34 -23
- package/dist/features/products/components/ProductGrid.js +86 -62
- package/dist/features/products/constants/card-badges.d.ts +97 -0
- package/dist/features/products/constants/card-badges.js +100 -0
- package/dist/features/products/repository/products.repository.js +34 -1
- package/dist/features/products/schemas/firestore.d.ts +46 -3
- package/dist/features/products/schemas/firestore.js +21 -0
- package/dist/features/promotions/components/PromotionsView.js +1 -1
- package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
- package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
- package/dist/features/promotions/schemas/firestore.js +30 -3
- package/dist/features/promotions/schemas/index.d.ts +6 -6
- package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
- package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
- package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
- package/dist/features/protection-claims/schemas/firestore.js +130 -0
- package/dist/features/reviews/actions/review-actions.js +3 -2
- package/dist/features/reviews/components/ReviewModal.js +1 -1
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/reviews/schemas/firestore.d.ts +20 -1
- package/dist/features/scams/constants/scam-types.d.ts +2 -0
- package/dist/features/scams/constants/scam-types.js +18 -0
- package/dist/features/scams/schemas/report-form.d.ts +3 -3
- package/dist/features/scams/schemas/report-form.js +26 -1
- package/dist/features/search/components/Search.js +9 -9
- package/dist/features/search/components/SearchResultsClient.js +18 -3
- package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
- package/dist/features/search/components/SearchResultsPageView.js +2 -2
- package/dist/features/seller/actions/offer-actions.js +16 -1
- package/dist/features/seller/actions/seller-actions.js +14 -3
- package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
- package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
- package/dist/features/seller/components/PrintCenterView.js +41 -30
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
- package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
- package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
- package/dist/features/seller/components/SellerProductShell.js +123 -13
- package/dist/features/seller/components/SellerReviewsView.js +4 -2
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
- package/dist/features/seller/schemas/coupon-form.js +27 -0
- package/dist/features/seller/schemas/product-form.d.ts +3 -0
- package/dist/features/seller/schemas/product-form.js +5 -0
- package/dist/features/shell/FormShell.js +2 -2
- package/dist/features/shell/QuickFormDrawer.js +14 -0
- package/dist/features/shell/SectionForm.d.ts +19 -0
- package/dist/features/shell/SectionForm.js +133 -9
- package/dist/features/shell/SectionForm.style.css +54 -0
- package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
- package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
- package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
- package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
- package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
- package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
- package/dist/features/stores/components/StoreGuideHubView.js +1 -1
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/features/support/schemas/firestore.d.ts +73 -2
- package/dist/features/support/schemas/firestore.js +59 -0
- package/dist/index.d.ts +16 -24
- package/dist/index.js +38 -47
- package/dist/next/ErrorBoundary.js +1 -1
- package/dist/next/routing/route-map.d.ts +24 -0
- package/dist/next/routing/route-map.js +40 -0
- package/dist/providers/payment-manual/index.d.ts +15 -1
- package/dist/providers/payment-manual/index.js +37 -0
- package/dist/providers/payment-phonepe/index.d.ts +14 -1
- package/dist/providers/payment-phonepe/index.js +36 -0
- package/dist/providers/payment-razorpay/index.d.ts +44 -1
- package/dist/providers/payment-razorpay/index.js +99 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
- package/dist/providers/shipping-shiprocket/index.js +18 -0
- package/dist/schemas/registry.d.ts +44 -11
- package/dist/server-entry.d.ts +2 -0
- package/dist/server-entry.js +27 -0
- package/dist/server.d.ts +42 -0
- package/dist/server.js +93 -0
- package/dist/styles.css +418 -149
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +15 -12
- package/dist/tokens/themes/default-light.js +14 -11
- package/dist/tokens/tokens.css +106 -23
- package/dist/ui/DataTable.style.css +4 -4
- package/dist/ui/columns/cell-renderers.d.ts +20 -0
- package/dist/ui/columns/cell-renderers.js +26 -1
- package/dist/ui/columns/index.d.ts +1 -1
- package/dist/ui/columns/index.js +4 -1
- package/dist/ui/components/Badge.style.css +1 -1
- package/dist/ui/components/Breadcrumb.js +19 -1
- package/dist/ui/components/BulkActionBar.style.css +4 -4
- package/dist/ui/components/Button.style.css +1 -1
- package/dist/ui/components/Checkbox.style.css +1 -1
- package/dist/ui/components/FallbackShell.js +1 -1
- package/dist/ui/components/Form.d.ts +14 -1
- package/dist/ui/components/Form.js +2 -2
- package/dist/ui/components/FormField.js +18 -4
- package/dist/ui/components/FormField.style.css +43 -3
- package/dist/ui/components/GatedPrice.style.css +1 -1
- package/dist/ui/components/IconButton.style.css +1 -1
- package/dist/ui/components/Input.js +22 -1
- package/dist/ui/components/Input.style.css +38 -22
- package/dist/ui/components/ListingFilterDrawer.js +1 -1
- package/dist/ui/components/ListingToolbar.js +1 -1
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/dist/ui/components/Radio.style.css +1 -1
- package/dist/ui/components/Repeater.d.ts +53 -0
- package/dist/ui/components/Repeater.js +7 -0
- package/dist/ui/components/RichTextEditor.d.ts +20 -1
- package/dist/ui/components/RichTextEditor.js +23 -2
- package/dist/ui/components/Select.style.css +7 -21
- package/dist/ui/components/Semantic.js +27 -3
- package/dist/ui/components/TagInput.style.css +4 -9
- package/dist/ui/components/Textarea.style.css +4 -18
- package/dist/ui/components/Toggle.style.css +1 -1
- package/dist/ui/components/Typography.d.ts +3 -3
- package/dist/ui/components/Typography.js +15 -15
- package/dist/ui/components/ViewToggle.style.css +4 -4
- package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
- package/dist/ui/forms/AddAnotherToggle.js +56 -0
- package/dist/ui/forms/ColorPickerField.js +15 -2
- package/dist/ui/forms/FieldCheckbox.js +15 -2
- package/dist/ui/forms/FieldInput.d.ts +22 -1
- package/dist/ui/forms/FieldInput.js +19 -5
- package/dist/ui/forms/FieldLabel.d.ts +35 -0
- package/dist/ui/forms/FieldLabel.js +70 -0
- package/dist/ui/forms/FieldSelect.d.ts +4 -1
- package/dist/ui/forms/FieldSelect.js +27 -5
- package/dist/ui/forms/FieldTextarea.d.ts +4 -1
- package/dist/ui/forms/FieldTextarea.js +22 -5
- package/dist/ui/forms/FormShell.d.ts +17 -1
- package/dist/ui/forms/FormShell.js +36 -2
- package/dist/ui/forms/FormShell.style.css +41 -33
- package/dist/ui/forms/index.d.ts +5 -1
- package/dist/ui/forms/index.js +2 -0
- package/dist/ui/icons/icon-registry.d.ts +1 -0
- package/dist/ui/icons/icon-registry.js +17 -1
- package/dist/utils/id-generators.d.ts +80 -0
- package/dist/utils/id-generators.js +93 -0
- package/dist/utils/search-txt-builders.d.ts +12 -0
- package/dist/utils/search-txt-builders.js +17 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +106 -5
- package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
- package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
- package/dist/features/loyalty/api/balance/route.d.ts +0 -12
- package/dist/features/loyalty/api/balance/route.js +0 -41
- package/dist/features/loyalty/api/earn/route.d.ts +0 -12
- package/dist/features/loyalty/api/earn/route.js +0 -38
- package/dist/features/loyalty/api/history/route.d.ts +0 -12
- package/dist/features/loyalty/api/history/route.js +0 -58
- package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
- package/dist/features/loyalty/api/redeem/route.js +0 -38
- package/dist/features/loyalty/columns/index.d.ts +0 -17
- package/dist/features/loyalty/columns/index.js +0 -40
- package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
- package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
- package/dist/features/loyalty/components/index.d.ts +0 -1
- package/dist/features/loyalty/components/index.js +0 -1
- package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
- package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
- package/dist/features/loyalty/index.d.ts +0 -6
- package/dist/features/loyalty/index.js +0 -6
- package/dist/features/loyalty/manifest.d.ts +0 -2
- package/dist/features/loyalty/manifest.js +0 -16
- package/dist/features/loyalty/messages/en.json +0 -19
- package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
- package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
- package/dist/features/loyalty/schemas/index.d.ts +0 -85
- package/dist/features/loyalty/schemas/index.js +0 -40
- package/dist/features/loyalty/server.d.ts +0 -6
- package/dist/features/loyalty/server.js +0 -6
- package/dist/features/loyalty/types/index.d.ts +0 -31
- package/dist/features/loyalty/types/index.js +0 -1
- package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
- package/dist/features/loyalty/types/loyalty-config.js +0 -1
- package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
- package/dist/features/loyalty/types/loyalty-math.js +0 -27
- package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
- package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdminGatewayEventsView — the webhook inbox, readable for the first time.
|
|
3
|
+
*
|
|
4
|
+
* ## What this is for
|
|
5
|
+
*
|
|
6
|
+
* `gatewayEvents` records every delivery verbatim, including event types we are
|
|
7
|
+
* subscribed to but do not implement — which is the whole reason the inbox
|
|
8
|
+
* exists rather than a logger line. Before it, a `payment.dispute.created`
|
|
9
|
+
* arriving ahead of the dispute handler would simply have vanished.
|
|
10
|
+
*
|
|
11
|
+
* This page answers three operator questions: did it arrive, what did we do
|
|
12
|
+
* with it, and can it be made to arrive again.
|
|
13
|
+
*
|
|
14
|
+
* ## 🛑 "Reopen for redelivery" is NOT "reprocess", and the UI must not imply it
|
|
15
|
+
*
|
|
16
|
+
* Nothing here can replay an event. R1's own header explains why: dispatching
|
|
17
|
+
* from anywhere but the webhook route would duplicate that route's whole switch
|
|
18
|
+
* and its RTDB signalling in a second place (Root Cause #75). The action only
|
|
19
|
+
* stops the inbox from refusing the NEXT delivery as a duplicate — the operator
|
|
20
|
+
* still presses Resend in the gateway dashboard.
|
|
21
|
+
*
|
|
22
|
+
* So the button says what it does, and the confirmation says what is left to
|
|
23
|
+
* do. A button labelled "Reprocess" doing only this would be the worst kind of
|
|
24
|
+
* half-truth: it would look like the work had happened.
|
|
25
|
+
*
|
|
26
|
+
* ## 🛑 Two statuses that look like failures and are not
|
|
27
|
+
*
|
|
28
|
+
* `ignored` means an event type we do not implement was recorded verbatim —
|
|
29
|
+
* healthy, and the inbox's purpose. `conflict` means a LATE event would have
|
|
30
|
+
* regressed an order's status, so it was recorded and not applied; the ack was
|
|
31
|
+
* 200 because the event is not wrong, it is out of date. Labelling either as an
|
|
32
|
+
* error would send operators chasing non-problems, which is how a queue stops
|
|
33
|
+
* being read.
|
|
34
|
+
*
|
|
35
|
+
* ## 🛑 No search box, and no sort on `status`
|
|
36
|
+
*
|
|
37
|
+
* `searchPlaceholder` is omitted because the endpoint does not read `q`;
|
|
38
|
+
* declaring it is what makes a dead box exist. And `status` is `canSort: false`
|
|
39
|
+
* in `SIEVE_FIELDS`, so a sort on it would be DROPPED silently by sievejs
|
|
40
|
+
* (Root Cause #63) — the status FILTER is the control, and it is in the panel.
|
|
41
|
+
*
|
|
42
|
+
* EXPORTS: AdminGatewayEventsView, type AdminGatewayEventsViewProps
|
|
43
|
+
*
|
|
44
|
+
* @tag domain:gateway-events
|
|
45
|
+
* @tag layer:component
|
|
46
|
+
* @tag pattern:listing
|
|
47
|
+
* @tag access:client
|
|
48
|
+
* @tag consumers:admin/maintenance/gateway-events
|
|
49
|
+
* @tag sideEffects:network
|
|
50
|
+
*/
|
|
51
|
+
import React from "react";
|
|
52
|
+
import type { ListingLayoutProps } from "../../../ui";
|
|
53
|
+
export type AdminGatewayEventsViewProps = ListingLayoutProps;
|
|
54
|
+
export declare function AdminGatewayEventsView({ children, ...props }: AdminGatewayEventsViewProps): React.JSX.Element;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* AdminGatewayEventsView — the webhook inbox, readable for the first time.
|
|
5
|
+
*
|
|
6
|
+
* ## What this is for
|
|
7
|
+
*
|
|
8
|
+
* `gatewayEvents` records every delivery verbatim, including event types we are
|
|
9
|
+
* subscribed to but do not implement — which is the whole reason the inbox
|
|
10
|
+
* exists rather than a logger line. Before it, a `payment.dispute.created`
|
|
11
|
+
* arriving ahead of the dispute handler would simply have vanished.
|
|
12
|
+
*
|
|
13
|
+
* This page answers three operator questions: did it arrive, what did we do
|
|
14
|
+
* with it, and can it be made to arrive again.
|
|
15
|
+
*
|
|
16
|
+
* ## 🛑 "Reopen for redelivery" is NOT "reprocess", and the UI must not imply it
|
|
17
|
+
*
|
|
18
|
+
* Nothing here can replay an event. R1's own header explains why: dispatching
|
|
19
|
+
* from anywhere but the webhook route would duplicate that route's whole switch
|
|
20
|
+
* and its RTDB signalling in a second place (Root Cause #75). The action only
|
|
21
|
+
* stops the inbox from refusing the NEXT delivery as a duplicate — the operator
|
|
22
|
+
* still presses Resend in the gateway dashboard.
|
|
23
|
+
*
|
|
24
|
+
* So the button says what it does, and the confirmation says what is left to
|
|
25
|
+
* do. A button labelled "Reprocess" doing only this would be the worst kind of
|
|
26
|
+
* half-truth: it would look like the work had happened.
|
|
27
|
+
*
|
|
28
|
+
* ## 🛑 Two statuses that look like failures and are not
|
|
29
|
+
*
|
|
30
|
+
* `ignored` means an event type we do not implement was recorded verbatim —
|
|
31
|
+
* healthy, and the inbox's purpose. `conflict` means a LATE event would have
|
|
32
|
+
* regressed an order's status, so it was recorded and not applied; the ack was
|
|
33
|
+
* 200 because the event is not wrong, it is out of date. Labelling either as an
|
|
34
|
+
* error would send operators chasing non-problems, which is how a queue stops
|
|
35
|
+
* being read.
|
|
36
|
+
*
|
|
37
|
+
* ## 🛑 No search box, and no sort on `status`
|
|
38
|
+
*
|
|
39
|
+
* `searchPlaceholder` is omitted because the endpoint does not read `q`;
|
|
40
|
+
* declaring it is what makes a dead box exist. And `status` is `canSort: false`
|
|
41
|
+
* in `SIEVE_FIELDS`, so a sort on it would be DROPPED silently by sievejs
|
|
42
|
+
* (Root Cause #63) — the status FILTER is the control, and it is in the panel.
|
|
43
|
+
*
|
|
44
|
+
* EXPORTS: AdminGatewayEventsView, type AdminGatewayEventsViewProps
|
|
45
|
+
*
|
|
46
|
+
* @tag domain:gateway-events
|
|
47
|
+
* @tag layer:component
|
|
48
|
+
* @tag pattern:listing
|
|
49
|
+
* @tag access:client
|
|
50
|
+
* @tag consumers:admin/maintenance/gateway-events
|
|
51
|
+
* @tag sideEffects:network
|
|
52
|
+
*/
|
|
53
|
+
import React, { useState } from "react";
|
|
54
|
+
import { sieveFilter, SIEVE_OP, sortBy, } from "@mohasinac/appkit/client";
|
|
55
|
+
import { FilterChipGroup, ListingLayout, Modal, RecordDetailModal, Row, RowActionMenu, Stack, Text, Button, } from "../../../ui/index.js";
|
|
56
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
57
|
+
import { ADMIN_GATEWAY_EVENT_KIND_TABS, ADMIN_GATEWAY_EVENT_STATUS_TABS, } from "../constants/filter-tabs.js";
|
|
58
|
+
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData.js";
|
|
59
|
+
import { DataListingView } from "./DataListingView.js";
|
|
60
|
+
/** Operator-facing status names — see the 🛑 about `ignored` and `conflict`. */
|
|
61
|
+
const STATUS_LABEL = {
|
|
62
|
+
received: "Received, not yet settled",
|
|
63
|
+
processing: "Being processed",
|
|
64
|
+
processed: "Processed",
|
|
65
|
+
failed: "Failed",
|
|
66
|
+
conflict: "Arrived late — recorded, not applied",
|
|
67
|
+
ignored: "Recorded, no handler for this type",
|
|
68
|
+
};
|
|
69
|
+
export function AdminGatewayEventsView({ children, ...props }) {
|
|
70
|
+
const [detail, setDetail] = useState(null);
|
|
71
|
+
const [reopenId, setReopenId] = useState(null);
|
|
72
|
+
const [busy, setBusy] = useState(false);
|
|
73
|
+
const [revision, setRevision] = useState(0);
|
|
74
|
+
if (React.Children.count(children) > 0) {
|
|
75
|
+
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
76
|
+
}
|
|
77
|
+
const reopen = async (id) => {
|
|
78
|
+
setBusy(true);
|
|
79
|
+
try {
|
|
80
|
+
const res = await fetch(ADMIN_ENDPOINTS.MAINTENANCE_GATEWAY_EVENTS, {
|
|
81
|
+
method: "PATCH",
|
|
82
|
+
headers: { "Content-Type": "application/json" },
|
|
83
|
+
body: JSON.stringify({ id }),
|
|
84
|
+
});
|
|
85
|
+
if (!res.ok)
|
|
86
|
+
throw new Error(`Could not reopen that event (${res.status})`);
|
|
87
|
+
setRevision((r) => r + 1);
|
|
88
|
+
setReopenId(null);
|
|
89
|
+
setDetail(null);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
setBusy(false);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const config = {
|
|
96
|
+
portal: "admin",
|
|
97
|
+
title: "Gateway events",
|
|
98
|
+
subtitle: "Every webhook delivery, recorded verbatim — including event types we have no handler for. Read-only apart from reopening one for redelivery.",
|
|
99
|
+
emptyLabel: "No gateway events recorded",
|
|
100
|
+
// 🛑 No `searchPlaceholder` — the endpoint does not read `q`.
|
|
101
|
+
filterKeys: ["status", "kind"],
|
|
102
|
+
/*
|
|
103
|
+
* `receivedAt`, which every row has. 🛑 NOT `status`, which is
|
|
104
|
+
* `canSort: false` in SIEVE_FIELDS and would therefore be dropped by
|
|
105
|
+
* sievejs with no error (Root Cause #63); and not `processedAt`, which is
|
|
106
|
+
* optional — a Firestore `orderBy` excludes documents lacking the field, so
|
|
107
|
+
* it would hide every unsettled event, i.e. exactly the ones worth seeing
|
|
108
|
+
* (Root Cause #100).
|
|
109
|
+
*/
|
|
110
|
+
defaultSort: sortBy("receivedAt", "DESC"),
|
|
111
|
+
queryKey: ["admin", "gateway-events", "listing", String(revision)],
|
|
112
|
+
endpoint: ADMIN_ENDPOINTS.MAINTENANCE_GATEWAY_EVENTS,
|
|
113
|
+
/*
|
|
114
|
+
* 🛑 ONLY `receivedAt`, and the omission is an index-weight decision rather
|
|
115
|
+
* than a shortfall.
|
|
116
|
+
*
|
|
117
|
+
* `attempts` is `canSort: true`, so "Most attempts" would work — but each
|
|
118
|
+
* filter+sort combination needs its own composite index, and a sort on a
|
|
119
|
+
* second field triples them: `(status, attempts)`, `(kind, attempts)`,
|
|
120
|
+
* `(status, kind, attempts)` on top of the three `receivedAt` ones.
|
|
121
|
+
*
|
|
122
|
+
* `gatewayEvents` takes a row for every webhook delivery, which makes it
|
|
123
|
+
* the highest-write collection in this subsystem — and a composite index is
|
|
124
|
+
* a write multiplier on EVERY document, forever, not a read-time cost. Six
|
|
125
|
+
* indexes to rank a rarely-opened diagnostic by a number the `failed` status
|
|
126
|
+
* filter already finds is the wrong trade.
|
|
127
|
+
*
|
|
128
|
+
* Two indexes are added for the filter combinations that remain:
|
|
129
|
+
* `(status, receivedAt desc)` and `(status, kind, receivedAt desc)`.
|
|
130
|
+
* `(kind, receivedAt desc)` already existed.
|
|
131
|
+
*/
|
|
132
|
+
sortOptions: [
|
|
133
|
+
{ value: sortBy("receivedAt", "DESC"), label: "Newest" },
|
|
134
|
+
{ value: sortBy("receivedAt", "ASC"), label: "Oldest" },
|
|
135
|
+
],
|
|
136
|
+
mapRows: (response) => toRecordArray(response.items).map((item, index) => {
|
|
137
|
+
const status = toStringValue(item.status, "received");
|
|
138
|
+
const attempts = typeof item.attempts === "number" ? item.attempts : 0;
|
|
139
|
+
return {
|
|
140
|
+
id: toStringValue(item.id, `event-${index}`),
|
|
141
|
+
primary: `${toStringValue(item.gateway, "?")} · ${toStringValue(item.kind, "other")}`,
|
|
142
|
+
secondary: [
|
|
143
|
+
STATUS_LABEL[status] ?? status,
|
|
144
|
+
toStringValue(item.gatewayOrderId, "") || null,
|
|
145
|
+
toStringValue(item.gatewayPaymentId, "") || null,
|
|
146
|
+
// Only when informative — "1 attempt" is the normal case and noise.
|
|
147
|
+
attempts > 1 ? `${attempts} attempts` : null,
|
|
148
|
+
item.terminal === true ? "terminal" : null,
|
|
149
|
+
]
|
|
150
|
+
.filter(Boolean)
|
|
151
|
+
.join(" · "),
|
|
152
|
+
status,
|
|
153
|
+
updatedAt: toRelativeDate(item.receivedAt),
|
|
154
|
+
_raw: item,
|
|
155
|
+
};
|
|
156
|
+
}),
|
|
157
|
+
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
158
|
+
onRowClick: (row) => setDetail(row._raw),
|
|
159
|
+
buildFilters: (state) => {
|
|
160
|
+
const parts = [];
|
|
161
|
+
if (state.status && state.status !== "All")
|
|
162
|
+
parts.push(sieveFilter("status", SIEVE_OP.EQ, state.status));
|
|
163
|
+
if (state.kind && state.kind !== "All")
|
|
164
|
+
parts.push(sieveFilter("kind", SIEVE_OP.EQ, state.kind));
|
|
165
|
+
return parts.length > 0 ? parts.join(",") : undefined;
|
|
166
|
+
},
|
|
167
|
+
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
168
|
+
{ label: "View details", onClick: () => setDetail(row._raw) },
|
|
169
|
+
{ label: "Reopen for redelivery", onClick: () => setReopenId(row.id) },
|
|
170
|
+
] })),
|
|
171
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_GATEWAY_EVENT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Kind", tabs: ADMIN_GATEWAY_EVENT_KIND_TABS, value: pendingFilters.kind ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, kind: id })) })] })),
|
|
172
|
+
};
|
|
173
|
+
const detailStatus = detail ? toStringValue(detail.status, "") : "";
|
|
174
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(RecordDetailModal, { isOpen: detail !== null, onClose: () => setDetail(null), title: detail
|
|
175
|
+
? `${toStringValue(detail.gateway, "?")} · ${toStringValue(detail.kind, "other")}`
|
|
176
|
+
: "Gateway event", badges: detail
|
|
177
|
+
? [
|
|
178
|
+
{ label: STATUS_LABEL[detailStatus] ?? detailStatus },
|
|
179
|
+
...(detail.terminal === true
|
|
180
|
+
? [{ label: "terminal", variant: "warning" }]
|
|
181
|
+
: []),
|
|
182
|
+
]
|
|
183
|
+
: undefined, fields: detail
|
|
184
|
+
? [
|
|
185
|
+
{ label: "Event id", value: toStringValue(detail.id, "") },
|
|
186
|
+
{ label: "Provider event id", value: toStringValue(detail.providerEventId, "") || "— (deduped on the body digest instead)" },
|
|
187
|
+
{ label: "Body SHA-256", value: toStringValue(detail.bodySha256, "") },
|
|
188
|
+
{ label: "Gateway order", value: toStringValue(detail.gatewayOrderId, "") || "—" },
|
|
189
|
+
{ label: "Gateway payment", value: toStringValue(detail.gatewayPaymentId, "") || "—" },
|
|
190
|
+
{ label: "Amount", value: typeof detail.amount === "number" ? `₹${detail.amount.toFixed(2)}` : "—" },
|
|
191
|
+
{ label: "Orders placed", value: Array.isArray(detail.orderIds) ? detail.orderIds.join(", ") : "—" },
|
|
192
|
+
{ label: "Received", value: toStringValue(detail.receivedAt, "") },
|
|
193
|
+
{ label: "Settled", value: toStringValue(detail.processedAt, "") || "— not settled" },
|
|
194
|
+
{ label: "Attempts", value: String(detail.attempts ?? 0) },
|
|
195
|
+
{ label: "Lease expires", value: toStringValue(detail.leaseExpiresAt, "") },
|
|
196
|
+
{ label: "Last error", value: toStringValue(detail.lastError, "") || "—" },
|
|
197
|
+
{ label: "Retention expiry", value: toStringValue(detail.expiresAt, "") },
|
|
198
|
+
]
|
|
199
|
+
: undefined,
|
|
200
|
+
/*
|
|
201
|
+
* `payload` is the REDACTED projection, never the raw body — Razorpay's
|
|
202
|
+
* payment entity carries email, contact, card.last4, vpa and bank, and
|
|
203
|
+
* `redactGatewayPayload` strips them recursively. `bodySha256` above
|
|
204
|
+
* preserves integrity regardless, which is what makes redaction safe.
|
|
205
|
+
*/
|
|
206
|
+
metadata: detail && detail.payload && typeof detail.payload === "object"
|
|
207
|
+
? detail.payload
|
|
208
|
+
: undefined, footer: detail ? (_jsx(Button, { variant: "outline", size: "sm", onClick: () => setReopenId(toStringValue(detail.id, "")), children: "Reopen for redelivery" })) : undefined }), _jsx(Modal, { isOpen: reopenId !== null, onClose: () => setReopenId(null), title: "Reopen this event for redelivery", size: "sm", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Text, { size: "sm", children: ["This clears the terminal flag and the attempt count so the inbox will ", _jsx("strong", { children: "accept" }), " the next delivery of this event instead of refusing it as a duplicate."] }), _jsx(Row, { surface: "warning-surface", padding: "sm", rounded: "default", children: _jsxs(Text, { size: "sm", color: "warning", children: ["It does not reprocess anything by itself. Nothing outside the webhook route knows how to handle an event, so after this you still need to press ", _jsx("strong", { children: "Resend" }), " on this delivery in the gateway dashboard \u2014 or wait for the gateway\u2019s own retry."] }) }), _jsxs(Row, { justify: "end", gap: "sm", children: [_jsx(Button, { variant: "ghost", onClick: () => setReopenId(null), disabled: busy, children: "Cancel" }), _jsx(Button, { variant: "primary", isLoading: busy, disabled: busy, onClick: () => reopen(reopenId), children: "Reopen" })] })] }) })] }));
|
|
209
|
+
}
|
|
@@ -81,5 +81,5 @@ export function AdminGuideHubView({ permissions = [], isFullAdmin = false }) {
|
|
|
81
81
|
const hasPermission = (perm) => isFullAdmin || permissions.includes(perm);
|
|
82
82
|
const visibleCards = GUIDE_CARDS.filter((c) => hasPermission(c.permission));
|
|
83
83
|
const hasAny = visibleCards.length > 0;
|
|
84
|
-
return (_jsxs(Stack, { gap: "xl", padding: "b-2xl", children: [_jsx(Section, { overflow: "hidden", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "2xl", shadow: "sm", children: _jsxs(Div, { paddingX: "x-md-xl", paddingY: "y-xl", className: "[background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_60%,var(--appkit-color-secondary-400)_100%)]", children: [_jsxs(Row, { className: "mb-3", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10", surface: "default", align: "center", justify: "center", rounded: "xl", children: _jsx(BookOpen, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { color: "inverse", className: "
|
|
84
|
+
return (_jsxs(Stack, { gap: "xl", padding: "b-2xl", children: [_jsx(Section, { overflow: "hidden", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "2xl", shadow: "sm", children: _jsxs(Div, { paddingX: "x-md-xl", paddingY: "y-xl", className: "[background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_60%,var(--appkit-color-secondary-400)_100%)]", children: [_jsxs(Row, { className: "mb-3", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10", surface: "default", align: "center", justify: "center", rounded: "xl", children: _jsx(BookOpen, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { color: "inverse-muted", className: "tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Admin & Employee Guide" })] }), _jsx(Heading, { color: "inverse", level: 1, className: "mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: "LetItRip Internal Guide" }), _jsx(Text, { color: "inverse-muted", size: "base", children: "Everything you need to operate and manage LetItRip \u2014 India's largest collectibles marketplace." })] }) }), _jsx(Alert, { variant: "info", compact: true, children: "These guides describe expected platform behaviour. Real access to each section is enforced independently by the permission system \u2014 this page is reference only." }), !hasAny && (_jsx(Alert, { variant: "info", title: "Limited access", children: "Your permission set doesn't include read access to any admin sections yet. Contact your admin to request the permissions you need." })), hasAny && (_jsxs(Section, { children: [_jsx(Heading, { level: 2, className: "text-[var(--appkit-color-text)] mb-4", size: "lg", weight: "semibold", children: "Guides" }), _jsx(Grid, { cols: "cards", gap: "md", children: visibleCards.map(({ Icon, title, description, href }) => (_jsxs(TextLink, { variant: "bare", href: href, rounded: "xl", layout: "flex-col", gap: "md", paddingX: "lg", paddingY: "lg", shadow: "hover-md", className: "group border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] hover:border-[var(--appkit-color-primary)] transition-all", children: [_jsxs(Row, { align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-9 h-9 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "lg", children: _jsx(Icon, { className: "w-4 h-4 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text)] group-hover:text-[var(--appkit-color-primary)] transition-colors", weight: "semibold", children: title })] }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] leading-relaxed", size: "sm", children: description }), _jsx(Text, { className: "text-[var(--appkit-color-primary)] mt-auto", size: "xs", weight: "medium", children: "Read guide \u2192" })] }, title))) })] })), _jsx(Section, { className: CLS_WARN_PANEL, children: _jsxs(Row, { align: "start", gap: "3", children: [_jsx(AlertTriangle, { className: "w-5 h-5 text-warning flex-shrink-0 mt-0.5" }), _jsxs(Div, { children: [_jsx(Text, { className: CLS_WARN_HEAD, children: "Remember: with great access comes responsibility" }), _jsx(Text, { className: CLS_WARN_BODY, children: "Admin actions are logged. Never access user PII (email, phone) without a legitimate support reason. Never grant capabilities or permissions without documented approval. When in doubt, escalate to a senior admin." })] })] }) })] }));
|
|
85
85
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdminMoneyCasesView — the operator queue for everything where money is at
|
|
3
|
+
* risk, and the FIRST surface `paymentCases` has ever had.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this page is the point of the whole payment-protection spine
|
|
6
|
+
*
|
|
7
|
+
* The event inbox, the four reconcilers, the obligation sweep, the dispute
|
|
8
|
+
* ingest, the protection claims and the grievance intake all converge on one
|
|
9
|
+
* collection — and until this view existed, **nothing could read it.** Thirty-six
|
|
10
|
+
* commits of detection wrote rows into a queue with no screen. That is Root
|
|
11
|
+
* Cause #56 at the scale of a subsystem: *a feature is built, one of its
|
|
12
|
+
* surfaces is missing, and nothing notices.*
|
|
13
|
+
*
|
|
14
|
+
* The daily digest was the only consumer, and it was built first for exactly
|
|
15
|
+
* that reason — it PUSHES, so the queue had one reader from day one. This is
|
|
16
|
+
* the pull side.
|
|
17
|
+
*
|
|
18
|
+
* ## 🛑 It is NOT under `/admin/maintenance/`
|
|
19
|
+
*
|
|
20
|
+
* Maintenance means "something broke, look when you can" and is granted to
|
|
21
|
+
* `maintenance_employee`. A case here can carry a gateway's own `respond_by` —
|
|
22
|
+
* miss it and the chargeback is lost by default irrespective of who was right —
|
|
23
|
+
* and an `amountAtRisk` in real rupees. Hence its own section and
|
|
24
|
+
* `admin:payment-cases:read|write`, admin only.
|
|
25
|
+
*
|
|
26
|
+
* ## 🛑 NO SEARCH BOX, and that is a decision
|
|
27
|
+
*
|
|
28
|
+
* `ListingViewConfig.searchPlaceholder` is optional and *"declaring it is what
|
|
29
|
+
* makes the box exist"*. `/api/admin/payment-cases` does not read `q`, and
|
|
30
|
+
* ~39 views in this codebase already render a box whose endpoint ignores the
|
|
31
|
+
* term — the user types, the request carries it, nothing changes, no error.
|
|
32
|
+
* Omitting it is the fix; a placeholder describing a capability the endpoint
|
|
33
|
+
* lacks is the defect.
|
|
34
|
+
*
|
|
35
|
+
* ## 🛑 The default sort cannot be `dueAt`
|
|
36
|
+
*
|
|
37
|
+
* `dueAt` is OPTIONAL and most cases have none — only a dispute and a grievance
|
|
38
|
+
* carry an external clock. A Firestore `orderBy` on an optional field silently
|
|
39
|
+
* EXCLUDES every document that lacks it (Root Cause #100), so sorting by
|
|
40
|
+
* deadline would hide the great majority of the queue while looking like it had
|
|
41
|
+
* simply ordered it. `lastObservedAt` is written by `openOrObserve` on every
|
|
42
|
+
* case, every time, so it is the only safe default — and it happens to be the
|
|
43
|
+
* right one: "what has been seen most recently".
|
|
44
|
+
*
|
|
45
|
+
* Deadline pressure is surfaced instead by the `dueAt` row text and by R4,
|
|
46
|
+
* which escalates and re-notifies at T-72h and T-24h.
|
|
47
|
+
*
|
|
48
|
+
* EXPORTS: AdminMoneyCasesView, type AdminMoneyCasesViewProps
|
|
49
|
+
*
|
|
50
|
+
* @tag domain:payment-cases
|
|
51
|
+
* @tag layer:component
|
|
52
|
+
* @tag pattern:listing
|
|
53
|
+
* @tag access:client
|
|
54
|
+
* @tag consumers:admin/money-cases
|
|
55
|
+
* @tag sideEffects:network
|
|
56
|
+
*/
|
|
57
|
+
import React from "react";
|
|
58
|
+
import type { ListingLayoutProps } from "../../../ui";
|
|
59
|
+
export type AdminMoneyCasesViewProps = ListingLayoutProps;
|
|
60
|
+
export declare function AdminMoneyCasesView({ children, ...props }: AdminMoneyCasesViewProps): React.JSX.Element;
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* AdminMoneyCasesView — the operator queue for everything where money is at
|
|
5
|
+
* risk, and the FIRST surface `paymentCases` has ever had.
|
|
6
|
+
*
|
|
7
|
+
* ## Why this page is the point of the whole payment-protection spine
|
|
8
|
+
*
|
|
9
|
+
* The event inbox, the four reconcilers, the obligation sweep, the dispute
|
|
10
|
+
* ingest, the protection claims and the grievance intake all converge on one
|
|
11
|
+
* collection — and until this view existed, **nothing could read it.** Thirty-six
|
|
12
|
+
* commits of detection wrote rows into a queue with no screen. That is Root
|
|
13
|
+
* Cause #56 at the scale of a subsystem: *a feature is built, one of its
|
|
14
|
+
* surfaces is missing, and nothing notices.*
|
|
15
|
+
*
|
|
16
|
+
* The daily digest was the only consumer, and it was built first for exactly
|
|
17
|
+
* that reason — it PUSHES, so the queue had one reader from day one. This is
|
|
18
|
+
* the pull side.
|
|
19
|
+
*
|
|
20
|
+
* ## 🛑 It is NOT under `/admin/maintenance/`
|
|
21
|
+
*
|
|
22
|
+
* Maintenance means "something broke, look when you can" and is granted to
|
|
23
|
+
* `maintenance_employee`. A case here can carry a gateway's own `respond_by` —
|
|
24
|
+
* miss it and the chargeback is lost by default irrespective of who was right —
|
|
25
|
+
* and an `amountAtRisk` in real rupees. Hence its own section and
|
|
26
|
+
* `admin:payment-cases:read|write`, admin only.
|
|
27
|
+
*
|
|
28
|
+
* ## 🛑 NO SEARCH BOX, and that is a decision
|
|
29
|
+
*
|
|
30
|
+
* `ListingViewConfig.searchPlaceholder` is optional and *"declaring it is what
|
|
31
|
+
* makes the box exist"*. `/api/admin/payment-cases` does not read `q`, and
|
|
32
|
+
* ~39 views in this codebase already render a box whose endpoint ignores the
|
|
33
|
+
* term — the user types, the request carries it, nothing changes, no error.
|
|
34
|
+
* Omitting it is the fix; a placeholder describing a capability the endpoint
|
|
35
|
+
* lacks is the defect.
|
|
36
|
+
*
|
|
37
|
+
* ## 🛑 The default sort cannot be `dueAt`
|
|
38
|
+
*
|
|
39
|
+
* `dueAt` is OPTIONAL and most cases have none — only a dispute and a grievance
|
|
40
|
+
* carry an external clock. A Firestore `orderBy` on an optional field silently
|
|
41
|
+
* EXCLUDES every document that lacks it (Root Cause #100), so sorting by
|
|
42
|
+
* deadline would hide the great majority of the queue while looking like it had
|
|
43
|
+
* simply ordered it. `lastObservedAt` is written by `openOrObserve` on every
|
|
44
|
+
* case, every time, so it is the only safe default — and it happens to be the
|
|
45
|
+
* right one: "what has been seen most recently".
|
|
46
|
+
*
|
|
47
|
+
* Deadline pressure is surfaced instead by the `dueAt` row text and by R4,
|
|
48
|
+
* which escalates and re-notifies at T-72h and T-24h.
|
|
49
|
+
*
|
|
50
|
+
* EXPORTS: AdminMoneyCasesView, type AdminMoneyCasesViewProps
|
|
51
|
+
*
|
|
52
|
+
* @tag domain:payment-cases
|
|
53
|
+
* @tag layer:component
|
|
54
|
+
* @tag pattern:listing
|
|
55
|
+
* @tag access:client
|
|
56
|
+
* @tag consumers:admin/money-cases
|
|
57
|
+
* @tag sideEffects:network
|
|
58
|
+
*/
|
|
59
|
+
import React, { useState } from "react";
|
|
60
|
+
import { sieveFilter, SIEVE_OP, sortBy, } from "@mohasinac/appkit/client";
|
|
61
|
+
import { Button, FilterChipGroup, ListingLayout, RecordDetailModal, Row, RowActionMenu, Stack, Text, } from "../../../ui/index.js";
|
|
62
|
+
import { RecordStatusTimeline } from "../../status-history/components/RecordStatusTimeline.js";
|
|
63
|
+
import { ReviewDecisionModal } from "../../store-extensions/components/ReviewDecisionModal.js";
|
|
64
|
+
import { paymentCaseResolveFormSchema, } from "../../payment-cases/schemas/resolve-form.js";
|
|
65
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
66
|
+
import { ADMIN_PAYMENT_CASE_KIND_TABS, ADMIN_PAYMENT_CASE_SEVERITY_TABS, ADMIN_PAYMENT_CASE_STATUS_TABS, } from "../constants/filter-tabs.js";
|
|
67
|
+
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData.js";
|
|
68
|
+
import { DataListingView } from "./DataListingView.js";
|
|
69
|
+
/**
|
|
70
|
+
* Operator-facing names. Deliberately describe the PROBLEM rather than echo
|
|
71
|
+
* the field value — `missing_order` is "Captured, no order", which is what has
|
|
72
|
+
* actually happened and what the next action depends on.
|
|
73
|
+
*
|
|
74
|
+
* Kept beside the chip labels rather than shared with them: the chips are terse
|
|
75
|
+
* because they sit in a grid, these head a row and can afford a clause.
|
|
76
|
+
*/
|
|
77
|
+
const KIND_LABEL = {
|
|
78
|
+
missing_order: "Captured payment with no order",
|
|
79
|
+
unprocessed_event: "Gateway event never processed",
|
|
80
|
+
status_conflict: "Late event would have regressed an order",
|
|
81
|
+
dispute: "Chargeback",
|
|
82
|
+
refund_mismatch: "Refund totals disagree with the gateway",
|
|
83
|
+
amount_mismatch: "Captured amount ≠ recorded total",
|
|
84
|
+
dead_job: "Job exhausted its retries",
|
|
85
|
+
grievance: "Statutory grievance",
|
|
86
|
+
shipping_unpriced: "Store shipping could not be priced",
|
|
87
|
+
protection_claim: "Protection claim",
|
|
88
|
+
manual_refund_due: "Refund owed, no automatic rail",
|
|
89
|
+
};
|
|
90
|
+
/** Rupees, for a row. Plain `toFixed` — these are already decimal rupees. */
|
|
91
|
+
function money(value) {
|
|
92
|
+
return typeof value === "number" ? `₹${value.toFixed(2)}` : null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Narrow a JSON value to status-history entries.
|
|
96
|
+
*
|
|
97
|
+
* 🛑 A FUNCTION, NOT A CAST. This was `detail.statusHistory as
|
|
98
|
+
* StatusChangeEntry[] | undefined`, which `tsc` rejects as insufficiently
|
|
99
|
+
* overlapping — and the suggested `as unknown as` is the double cast this
|
|
100
|
+
* repo bans outright, because it has zero runtime effect and silences the one
|
|
101
|
+
* type-level signal there was.
|
|
102
|
+
*
|
|
103
|
+
* `detail` is a `JsonObject` off the wire, so narrowing is genuinely needed;
|
|
104
|
+
* the file already does exactly this for scalars via `toStringValue`. An
|
|
105
|
+
* array whose members are not objects returns undefined rather than being
|
|
106
|
+
* waved through — the timeline renders nothing, which is the honest outcome
|
|
107
|
+
* for data it cannot read.
|
|
108
|
+
*/
|
|
109
|
+
function toStatusHistory(value) {
|
|
110
|
+
if (!Array.isArray(value))
|
|
111
|
+
return undefined;
|
|
112
|
+
const ok = value.every((e) => typeof e === "object" && e !== null);
|
|
113
|
+
return ok ? value : undefined;
|
|
114
|
+
}
|
|
115
|
+
export function AdminMoneyCasesView({ children, ...props }) {
|
|
116
|
+
const [detail, setDetail] = useState(null);
|
|
117
|
+
const [resolveTargetId, setResolveTargetId] = useState(null);
|
|
118
|
+
const [dismissTargetId, setDismissTargetId] = useState(null);
|
|
119
|
+
/*
|
|
120
|
+
* Bumped after every mutation. `DataListingView` keys its react-query cache
|
|
121
|
+
* on `queryKey`, so changing the key is what forces a refetch — the reports
|
|
122
|
+
* page calls its own `load()` because it owns its fetch; this one does not.
|
|
123
|
+
*/
|
|
124
|
+
const [revision, setRevision] = useState(0);
|
|
125
|
+
if (React.Children.count(children) > 0) {
|
|
126
|
+
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
127
|
+
}
|
|
128
|
+
const patch = async (id, body) => {
|
|
129
|
+
const res = await fetch(ADMIN_ENDPOINTS.PAYMENT_CASE_BY_ID(id), {
|
|
130
|
+
method: "PATCH",
|
|
131
|
+
headers: { "Content-Type": "application/json" },
|
|
132
|
+
body: JSON.stringify(body),
|
|
133
|
+
});
|
|
134
|
+
if (!res.ok) {
|
|
135
|
+
/*
|
|
136
|
+
* Thrown so `ReviewDecisionModal` stays OPEN and keeps the note the
|
|
137
|
+
* operator just typed. Its catch shows a toast and leaves the dialog up —
|
|
138
|
+
* a decision that did not save must not look like one that did.
|
|
139
|
+
*/
|
|
140
|
+
throw new Error(`Could not update this case (${res.status})`);
|
|
141
|
+
}
|
|
142
|
+
setRevision((r) => r + 1);
|
|
143
|
+
setDetail(null);
|
|
144
|
+
};
|
|
145
|
+
const config = {
|
|
146
|
+
portal: "admin",
|
|
147
|
+
title: "Money cases",
|
|
148
|
+
subtitle: "Everything where money is at risk — chargebacks, captured payments with no order, refunds owed, protection claims and grievances. Opened automatically; only a human can close one.",
|
|
149
|
+
emptyLabel: "No open money cases",
|
|
150
|
+
// 🛑 No `searchPlaceholder` / `search` — the endpoint does not read `q`.
|
|
151
|
+
filterKeys: ["status", "kind", "severity"],
|
|
152
|
+
defaultSort: sortBy("lastObservedAt", "DESC"),
|
|
153
|
+
queryKey: ["admin", "payment-cases", "listing", String(revision)],
|
|
154
|
+
endpoint: ADMIN_ENDPOINTS.PAYMENT_CASES,
|
|
155
|
+
/*
|
|
156
|
+
* ── Two traps avoided here, both measured rather than assumed ──────────
|
|
157
|
+
*
|
|
158
|
+
* 🛑 THERE IS NO "SORT BY SEVERITY" OPTION, even though `severity` is
|
|
159
|
+
* `canSort: true` and the option is the obvious one to offer. It stores the
|
|
160
|
+
* STRINGS `error` / `warning` / `info`, so Firestore orders them
|
|
161
|
+
* alphabetically — and `DESC` therefore yields **warning, info, error**,
|
|
162
|
+
* putting the most urgent cases LAST. A sort that silently inverts the
|
|
163
|
+
* thing it claims to rank is worse than no sort (Root Cause #63), and it
|
|
164
|
+
* would be believed. `PAYMENT_CASE_SEVERITY_RANK` exists in TypeScript but
|
|
165
|
+
* is not a stored field, so there is nothing to order by; the severity
|
|
166
|
+
* FILTER is the correct control and it is in the panel.
|
|
167
|
+
*
|
|
168
|
+
* 🛑 The two optional-field sorts NAME their own exclusion. `dueAt` and
|
|
169
|
+
* `amountAtRisk` are both optional, and a Firestore `orderBy` excludes
|
|
170
|
+
* every document that LACKS the field (Root Cause #100) — so these sorts
|
|
171
|
+
* genuinely do shrink the list. For these two that is the right semantics
|
|
172
|
+
* ("soonest deadline" is a question about dated cases), but the shrinking
|
|
173
|
+
* must be stated: an unexplained shorter list is how someone concludes the
|
|
174
|
+
* queue is empty when it is not.
|
|
175
|
+
*/
|
|
176
|
+
sortOptions: [
|
|
177
|
+
{ value: sortBy("lastObservedAt", "DESC"), label: "Most recently seen" },
|
|
178
|
+
{ value: sortBy("lastObservedAt", "ASC"), label: "Longest unseen" },
|
|
179
|
+
{ value: sortBy("dueAt", "ASC"), label: "Soonest deadline (dated cases only)" },
|
|
180
|
+
{ value: sortBy("amountAtRisk", "DESC"), label: "Largest amount (money cases only)" },
|
|
181
|
+
{ value: sortBy("observedCount", "DESC"), label: "Most often re-seen" },
|
|
182
|
+
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
183
|
+
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
184
|
+
],
|
|
185
|
+
mapRows: (response) => toRecordArray(response.items).map((item, index) => {
|
|
186
|
+
const kind = toStringValue(item.kind, "unknown");
|
|
187
|
+
const observed = typeof item.observedCount === "number" ? item.observedCount : 1;
|
|
188
|
+
const amount = money(item.amountAtRisk);
|
|
189
|
+
const due = toStringValue(item.dueAt, "");
|
|
190
|
+
return {
|
|
191
|
+
id: toStringValue(item.id, `case-${index}`),
|
|
192
|
+
primary: toStringValue(item.title, KIND_LABEL[kind] ?? kind),
|
|
193
|
+
secondary: [
|
|
194
|
+
KIND_LABEL[kind] ?? kind,
|
|
195
|
+
amount ? `${amount} at risk` : null,
|
|
196
|
+
// An external deadline is the one thing worth putting in a row.
|
|
197
|
+
due ? `due ${new Date(due).toLocaleString()}` : null,
|
|
198
|
+
// Only mentioned when it is informative — "seen once" is noise.
|
|
199
|
+
observed > 1 ? `seen ${observed}×` : null,
|
|
200
|
+
]
|
|
201
|
+
.filter(Boolean)
|
|
202
|
+
.join(" · "),
|
|
203
|
+
status: toStringValue(item.status, "open"),
|
|
204
|
+
updatedAt: toRelativeDate(item.lastObservedAt ?? item.createdAt),
|
|
205
|
+
_raw: item,
|
|
206
|
+
};
|
|
207
|
+
}),
|
|
208
|
+
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
209
|
+
onRowClick: (row) => setDetail(row._raw),
|
|
210
|
+
buildFilters: (state) => {
|
|
211
|
+
const parts = [];
|
|
212
|
+
if (state.status && state.status !== "All")
|
|
213
|
+
parts.push(sieveFilter("status", SIEVE_OP.EQ, state.status));
|
|
214
|
+
if (state.kind && state.kind !== "All")
|
|
215
|
+
parts.push(sieveFilter("kind", SIEVE_OP.EQ, state.kind));
|
|
216
|
+
if (state.severity && state.severity !== "All")
|
|
217
|
+
parts.push(sieveFilter("severity", SIEVE_OP.EQ, state.severity));
|
|
218
|
+
return parts.length > 0 ? parts.join(",") : undefined;
|
|
219
|
+
},
|
|
220
|
+
renderRowActions: (row) => {
|
|
221
|
+
const status = row.status;
|
|
222
|
+
const closed = status === "actioned" || status === "dismissed";
|
|
223
|
+
return (_jsx(RowActionMenu, { actions: [
|
|
224
|
+
{ label: "View details", onClick: () => setDetail(row._raw) },
|
|
225
|
+
/*
|
|
226
|
+
* "Take" needs no note — it records that someone is looking, which
|
|
227
|
+
* the schema's own `superRefine` exempts from the resolution floor.
|
|
228
|
+
* Hidden once closed: re-opening a closed case is not a thing this
|
|
229
|
+
* surface offers, because `resolve()` is one-way by design.
|
|
230
|
+
*/
|
|
231
|
+
...(closed
|
|
232
|
+
? []
|
|
233
|
+
: [
|
|
234
|
+
{
|
|
235
|
+
label: status === "under-review" ? "Already taken" : "Take",
|
|
236
|
+
onClick: () => patch(row.id, { status: "under-review" }),
|
|
237
|
+
disabled: status === "under-review",
|
|
238
|
+
},
|
|
239
|
+
{ label: "Resolve", onClick: () => setResolveTargetId(row.id) },
|
|
240
|
+
{ label: "Dismiss", onClick: () => setDismissTargetId(row.id) },
|
|
241
|
+
]),
|
|
242
|
+
] }));
|
|
243
|
+
},
|
|
244
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PAYMENT_CASE_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Severity", tabs: ADMIN_PAYMENT_CASE_SEVERITY_TABS, value: pendingFilters.severity ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, severity: id })) }), _jsx(FilterChipGroup, { label: "Kind", tabs: ADMIN_PAYMENT_CASE_KIND_TABS, value: pendingFilters.kind ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, kind: id })) })] })),
|
|
245
|
+
};
|
|
246
|
+
const detailKind = detail ? toStringValue(detail.kind, "") : "";
|
|
247
|
+
const detailStatus = detail ? toStringValue(detail.status, "") : "";
|
|
248
|
+
const selfHealed = detail ? toStringValue(detail.selfHealedAt, "") : "";
|
|
249
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(RecordDetailModal, { isOpen: detail !== null, onClose: () => setDetail(null), title: detail ? toStringValue(detail.title, "Money case") : "Money case", badges: detail
|
|
250
|
+
? [
|
|
251
|
+
{ label: detailStatus || "open" },
|
|
252
|
+
{ label: KIND_LABEL[detailKind] ?? detailKind },
|
|
253
|
+
{ label: toStringValue(detail.severity, "info") },
|
|
254
|
+
]
|
|
255
|
+
: undefined, description: detail ? toStringValue(detail.detail, "") : undefined, fields: detail
|
|
256
|
+
? [
|
|
257
|
+
{ label: "Case id", value: toStringValue(detail.id, "") },
|
|
258
|
+
{ label: "Subject", value: `${toStringValue(detail.entityType, "")} ${toStringValue(detail.entityId, "")}` },
|
|
259
|
+
{ label: "Amount at risk", value: money(detail.amountAtRisk) ?? "—" },
|
|
260
|
+
{
|
|
261
|
+
label: "External deadline",
|
|
262
|
+
value: toStringValue(detail.dueAt, "") || "none — this case has no clock",
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
label: "Times observed",
|
|
266
|
+
value: String(detail.observedCount ?? 1),
|
|
267
|
+
},
|
|
268
|
+
{ label: "Last seen", value: toStringValue(detail.lastObservedAt, "") },
|
|
269
|
+
{ label: "Assigned to", value: toStringValue(detail.assignedTo, "") || "—" },
|
|
270
|
+
{ label: "Gateway payment", value: toStringValue(detail.gatewayPaymentId, "") || "—" },
|
|
271
|
+
{ label: "Resolution", value: toStringValue(detail.resolution, "") || "—" },
|
|
272
|
+
]
|
|
273
|
+
: undefined,
|
|
274
|
+
/*
|
|
275
|
+
* 🛑 `selfHealedAt` is rendered as a QUESTION, never as a resolution.
|
|
276
|
+
*
|
|
277
|
+
* A reconciler that stops observing a problem writes this field and
|
|
278
|
+
* nothing else — the case stays `open` on purpose. "The symptom went
|
|
279
|
+
* away" and "a human understands why" are different facts, and on money
|
|
280
|
+
* only the second one closes anything: a self-healed `missing_order`
|
|
281
|
+
* might mean the retry worked, or might mean somebody refunded it by
|
|
282
|
+
* hand and the order will never exist.
|
|
283
|
+
*/
|
|
284
|
+
extra: detail ? (_jsxs(Stack, { gap: "md", children: [selfHealed ? (_jsx(Row, { surface: "info-surface", padding: "sm", rounded: "default", children: _jsxs(Text, { size: "sm", color: "info", children: ["A reconciler no longer sees this problem (as of", " ", new Date(selfHealed).toLocaleString(), "). That is not a resolution \u2014 confirm what happened, then resolve or dismiss it."] }) })) : null, _jsx(RecordStatusTimeline, { entries: toStatusHistory(detail.statusHistory), truncatedCount: typeof detail.statusHistoryTruncated === "number"
|
|
285
|
+
? detail.statusHistoryTruncated
|
|
286
|
+
: undefined, emptyLabel: "Opened automatically; nothing has happened to it yet" })] })) : undefined, footer: detail && detailStatus !== "actioned" && detailStatus !== "dismissed" ? (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setResolveTargetId(toStringValue(detail.id, "")), children: "Resolve" }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setDismissTargetId(toStringValue(detail.id, "")), children: "Dismiss" })] })) : undefined }), _jsx(ReviewDecisionModal, { isOpen: resolveTargetId !== null, onClose: () => setResolveTargetId(null), title: "Resolve this case", schema: paymentCaseResolveFormSchema, status: "actioned", noteField: "resolution", noteLabel: "What did you do?", noteHelp: "Required. The next person reading this case has only what you write here.", notePlaceholder: "e.g. Refunded \u20B92,511.80 to the buyer from the Razorpay dashboard; the order could not be placed because the address was missing.", confirmLabel: "Resolve case", onConfirm: (values) => patch(resolveTargetId, values) }), _jsx(ReviewDecisionModal, { isOpen: dismissTargetId !== null, onClose: () => setDismissTargetId(null), title: "Dismiss this case", schema: paymentCaseResolveFormSchema, status: "dismissed", noteField: "resolution", noteLabel: "Why is nothing wrong here?", noteHelp: "Required. A dismissed case with no reason reads identically to one nobody looked at.", notePlaceholder: "e.g. Duplicate of the event two rows above \u2014 same payment id, already refunded.", confirmLabel: "Dismiss case", onConfirm: (values) => patch(dismissTargetId, values) })] }));
|
|
287
|
+
}
|